/wp-admin/includes/class-wp-ms-sites-list-table.php

  1. <?php 
  2. /** 
  3. * List Table API: WP_MS_Sites_List_Table class 
  4. * 
  5. * @package WordPress 
  6. * @subpackage Administration 
  7. * @since 3.1.0 
  8. */ 
  9.  
  10. /** 
  11. * Core class used to implement displaying sites in a list table for the network admin. 
  12. * 
  13. * @since 3.1.0 
  14. * @access private 
  15. * 
  16. * @see WP_List_Table 
  17. */ 
  18. class WP_MS_Sites_List_Table extends WP_List_Table { 
  19.  
  20. /** 
  21. * Site status list. 
  22. * 
  23. * @since 4.3.0 
  24. * @access public 
  25. * @var array 
  26. */ 
  27. public $status_list; 
  28.  
  29. /** 
  30. * Constructor. 
  31. * 
  32. * @since 3.1.0 
  33. * @access public 
  34. * 
  35. * @see WP_List_Table::__construct() for more information on default arguments. 
  36. * 
  37. * @param array $args An associative array of arguments. 
  38. */ 
  39. public function __construct( $args = array() ) { 
  40. $this->status_list = array( 
  41. 'archived' => array( 'site-archived', __( 'Archived' ) ),  
  42. 'spam' => array( 'site-spammed', _x( 'Spam', 'site' ) ),  
  43. 'deleted' => array( 'site-deleted', __( 'Deleted' ) ),  
  44. 'mature' => array( 'site-mature', __( 'Mature' ) ) 
  45. ); 
  46.  
  47. parent::__construct( array( 
  48. 'plural' => 'sites',  
  49. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,  
  50. ) ); 
  51.  
  52. /** 
  53. * 
  54. * @return bool 
  55. */ 
  56. public function ajax_user_can() { 
  57. return current_user_can( 'manage_sites' ); 
  58.  
  59. /** 
  60. * Prepares the list of sites for display. 
  61. * 
  62. * @since 3.1.0 
  63. * 
  64. * @global string $s 
  65. * @global string $mode 
  66. * @global wpdb $wpdb 
  67. */ 
  68. public function prepare_items() { 
  69. global $s, $mode, $wpdb; 
  70.  
  71. if ( ! empty( $_REQUEST['mode'] ) ) { 
  72. $mode = $_REQUEST['mode'] === 'excerpt' ? 'excerpt' : 'list'; 
  73. set_user_setting( 'sites_list_mode', $mode ); 
  74. } else { 
  75. $mode = get_user_setting( 'sites_list_mode', 'list' ); 
  76.  
  77. $per_page = $this->get_items_per_page( 'sites_network_per_page' ); 
  78.  
  79. $pagenum = $this->get_pagenum(); 
  80.  
  81. $s = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST[ 's' ] ) ) : ''; 
  82. $wild = ''; 
  83. if ( false !== strpos($s, '*') ) { 
  84. $wild = '*'; 
  85. $s = trim($s, '*'); 
  86.  
  87. /** 
  88. * If the network is large and a search is not being performed, show only 
  89. * the latest sites with no paging in order to avoid expensive count queries. 
  90. */ 
  91. if ( !$s && wp_is_large_network() ) { 
  92. if ( !isset($_REQUEST['orderby']) ) 
  93. $_GET['orderby'] = $_REQUEST['orderby'] = ''; 
  94. if ( !isset($_REQUEST['order']) ) 
  95. $_GET['order'] = $_REQUEST['order'] = 'DESC'; 
  96.  
  97. $args = array( 
  98. 'number' => intval( $per_page ),  
  99. 'offset' => intval( ( $pagenum - 1 ) * $per_page ),  
  100. 'network_id' => get_current_network_id(),  
  101. ); 
  102.  
  103. if ( empty($s) ) { 
  104. // Nothing to do. 
  105. } elseif ( preg_match( '/^[0-9]{1, 3}\.[0-9]{1, 3}\.[0-9]{1, 3}\.[0-9]{1, 3}$/', $s ) || 
  106. preg_match( '/^[0-9]{1, 3}\.[0-9]{1, 3}\.[0-9]{1, 3}\.?$/', $s ) || 
  107. preg_match( '/^[0-9]{1, 3}\.[0-9]{1, 3}\.?$/', $s ) || 
  108. preg_match( '/^[0-9]{1, 3}\.$/', $s ) ) { 
  109. // IPv4 address 
  110. $sql = $wpdb->prepare( "SELECT blog_id FROM {$wpdb->registration_log} WHERE {$wpdb->registration_log}.IP LIKE %s", $wpdb->esc_like( $s ) . ( ! empty( $wild ) ? '%' : '' ) ); 
  111. $reg_blog_ids = $wpdb->get_col( $sql ); 
  112.  
  113. if ( $reg_blog_ids ) { 
  114. $args['site__in'] = $reg_blog_ids; 
  115. } elseif ( is_numeric( $s ) && empty( $wild ) ) { 
  116. $args['ID'] = $s; 
  117. } else { 
  118. $args['search'] = $s; 
  119.  
  120. if ( ! is_subdomain_install() ) { 
  121. $args['search_columns'] = array( 'path' ); 
  122.  
  123. $order_by = isset( $_REQUEST['orderby'] ) ? $_REQUEST['orderby'] : ''; 
  124. if ( 'registered' === $order_by ) { 
  125. // registered is a valid field name. 
  126. } elseif ( 'lastupdated' === $order_by ) { 
  127. $order_by = 'last_updated'; 
  128. } elseif ( 'blogname' === $order_by ) { 
  129. if ( is_subdomain_install() ) { 
  130. $order_by = 'domain'; 
  131. } else { 
  132. $order_by = 'path'; 
  133. } elseif ( 'blog_id' === $order_by ) { 
  134. $order_by = 'id'; 
  135. } elseif ( ! $order_by ) { 
  136. $order_by = false; 
  137.  
  138. $args['orderby'] = $order_by; 
  139.  
  140. if ( $order_by ) { 
  141. $args['order'] = ( isset( $_REQUEST['order'] ) && 'DESC' === strtoupper( $_REQUEST['order'] ) ) ? "DESC" : "ASC"; 
  142.  
  143. if ( wp_is_large_network() ) { 
  144. $args['no_found_rows'] = true; 
  145. } else { 
  146. $args['no_found_rows'] = false; 
  147.  
  148. /** 
  149. * Filters the arguments for the site query in the sites list table. 
  150. * 
  151. * @since 4.6.0 
  152. * 
  153. * @param array $args An array of get_sites() arguments. 
  154. */ 
  155. $args = apply_filters( 'ms_sites_list_table_query_args', $args ); 
  156.  
  157. $_sites = get_sites( $args ); 
  158. if ( is_array( $_sites ) ) { 
  159. update_site_cache( $_sites ); 
  160.  
  161. $this->items = array_slice( $_sites, 0, $per_page ); 
  162.  
  163. $total_sites = get_sites( array_merge( $args, array( 
  164. 'count' => true,  
  165. 'offset' => 0,  
  166. 'number' => 0,  
  167. ) ) ); 
  168.  
  169. $this->set_pagination_args( array( 
  170. 'total_items' => $total_sites,  
  171. 'per_page' => $per_page,  
  172. ) ); 
  173.  
  174. /** 
  175. * @access public 
  176. */ 
  177. public function no_items() { 
  178. _e( 'No sites found.' ); 
  179.  
  180. /** 
  181. * 
  182. * @return array 
  183. */ 
  184. protected function get_bulk_actions() { 
  185. $actions = array(); 
  186. if ( current_user_can( 'delete_sites' ) ) 
  187. $actions['delete'] = __( 'Delete' ); 
  188. $actions['spam'] = _x( 'Mark as Spam', 'site' ); 
  189. $actions['notspam'] = _x( 'Not Spam', 'site' ); 
  190.  
  191. return $actions; 
  192.  
  193. /** 
  194. * @global string $mode 
  195. * 
  196. * @param string $which 
  197. */ 
  198. protected function pagination( $which ) { 
  199. global $mode; 
  200.  
  201. parent::pagination( $which ); 
  202.  
  203. if ( 'top' === $which ) 
  204. $this->view_switcher( $mode ); 
  205.  
  206. /** 
  207. * @return array 
  208. */ 
  209. public function get_columns() { 
  210. $sites_columns = array( 
  211. 'cb' => '<input type="checkbox" />',  
  212. 'blogname' => __( 'URL' ),  
  213. 'lastupdated' => __( 'Last Updated' ),  
  214. 'registered' => _x( 'Registered', 'site' ),  
  215. 'users' => __( 'Users' ),  
  216. ); 
  217.  
  218. if ( has_filter( 'wpmublogsaction' ) ) { 
  219. $sites_columns['plugins'] = __( 'Actions' ); 
  220.  
  221. /** 
  222. * Filters the displayed site columns in Sites list table. 
  223. * 
  224. * @since MU 
  225. * 
  226. * @param array $sites_columns An array of displayed site columns. Default 'cb',  
  227. * 'blogname', 'lastupdated', 'registered', 'users'. 
  228. */ 
  229. return apply_filters( 'wpmu_blogs_columns', $sites_columns ); 
  230.  
  231. /** 
  232. * @return array 
  233. */ 
  234. protected function get_sortable_columns() { 
  235. return array( 
  236. 'blogname' => 'blogname',  
  237. 'lastupdated' => 'lastupdated',  
  238. 'registered' => 'blog_id',  
  239. ); 
  240.  
  241. /** 
  242. * Handles the checkbox column output. 
  243. * 
  244. * @since 4.3.0 
  245. * @access public 
  246. * 
  247. * @param array $blog Current site. 
  248. */ 
  249. public function column_cb( $blog ) { 
  250. if ( ! is_main_site( $blog['blog_id'] ) ) : 
  251. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] ); 
  252. ?> 
  253. <label class="screen-reader-text" for="blog_<?php echo $blog['blog_id']; ?>"><?php 
  254. printf( __( 'Select %s' ), $blogname ); 
  255. ?></label> 
  256. <input type="checkbox" id="blog_<?php echo $blog['blog_id'] ?>" name="allblogs[]" value="<?php echo esc_attr( $blog['blog_id'] ) ?>" /> 
  257. <?php endif; 
  258.  
  259. /** 
  260. * Handles the ID column output. 
  261. * 
  262. * @since 4.4.0 
  263. * @access public 
  264. * 
  265. * @param array $blog Current site. 
  266. */ 
  267. public function column_id( $blog ) { 
  268. echo $blog['blog_id']; 
  269.  
  270. /** 
  271. * Handles the site name column output. 
  272. * 
  273. * @since 4.3.0 
  274. * @access public 
  275. * 
  276. * @global string $mode 
  277. * 
  278. * @param array $blog Current site. 
  279. */ 
  280. public function column_blogname( $blog ) { 
  281. global $mode; 
  282.  
  283. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] ); 
  284. $blog_states = array(); 
  285. reset( $this->status_list ); 
  286.  
  287. foreach ( $this->status_list as $status => $col ) { 
  288. if ( $blog[ $status ] == 1 ) { 
  289. $blog_states[] = $col[1]; 
  290. $blog_state = ''; 
  291. if ( ! empty( $blog_states ) ) { 
  292. $state_count = count( $blog_states ); 
  293. $i = 0; 
  294. $blog_state .= ' - '; 
  295. foreach ( $blog_states as $state ) { 
  296. ++$i; 
  297. $sep = ( $i == $state_count ) ? '' : ', '; 
  298. $blog_state .= "<span class='post-state'>$state$sep</span>"; 
  299.  
  300. ?> 
  301. <a href="<?php echo esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ); ?>" class="edit"><?php echo $blogname . $blog_state; ?></a> 
  302. <?php 
  303. if ( 'list' !== $mode ) { 
  304. switch_to_blog( $blog['blog_id'] ); 
  305. echo '<p>'; 
  306. printf( 
  307. /** translators: 1: site name, 2: site tagline. */ 
  308. __( '%1$s – %2$s' ),  
  309. get_option( 'blogname' ),  
  310. '<em>' . get_option( 'blogdescription ' ) . '</em>' 
  311. ); 
  312. echo '</p>'; 
  313. restore_current_blog(); 
  314.  
  315. /** 
  316. * Handles the lastupdated column output. 
  317. * 
  318. * @since 4.3.0 
  319. * @access public 
  320. * 
  321. * @param array $blog Current site. 
  322. */ 
  323. public function column_lastupdated( $blog ) { 
  324. global $mode; 
  325.  
  326. if ( 'list' === $mode ) { 
  327. $date = __( 'Y/m/d' ); 
  328. } else { 
  329. $date = __( 'Y/m/d g:i:s a' ); 
  330.  
  331. echo ( $blog['last_updated'] === '0000-00-00 00:00:00' ) ? __( 'Never' ) : mysql2date( $date, $blog['last_updated'] ); 
  332.  
  333. /** 
  334. * Handles the registered column output. 
  335. * 
  336. * @since 4.3.0 
  337. * @access public 
  338. * 
  339. * @param array $blog Current site. 
  340. */ 
  341. public function column_registered( $blog ) { 
  342. global $mode; 
  343.  
  344. if ( 'list' === $mode ) { 
  345. $date = __( 'Y/m/d' ); 
  346. } else { 
  347. $date = __( 'Y/m/d g:i:s a' ); 
  348.  
  349. if ( $blog['registered'] === '0000-00-00 00:00:00' ) { 
  350. echo '—'; 
  351. } else { 
  352. echo mysql2date( $date, $blog['registered'] ); 
  353.  
  354. /** 
  355. * Handles the users column output. 
  356. * 
  357. * @since 4.3.0 
  358. * @access public 
  359. * 
  360. * @param array $blog Current site. 
  361. */ 
  362. public function column_users( $blog ) { 
  363. $user_count = wp_cache_get( $blog['blog_id'] . '_user_count', 'blog-details' ); 
  364. if ( ! $user_count ) { 
  365. $blog_users = get_users( array( 'blog_id' => $blog['blog_id'], 'fields' => 'ID' ) ); 
  366. $user_count = count( $blog_users ); 
  367. unset( $blog_users ); 
  368. wp_cache_set( $blog['blog_id'] . '_user_count', $user_count, 'blog-details', 12 * HOUR_IN_SECONDS ); 
  369.  
  370. printf( 
  371. '<a href="%s">%s</a>',  
  372. esc_url( network_admin_url( 'site-users.php?id=' . $blog['blog_id'] ) ),  
  373. number_format_i18n( $user_count ) 
  374. ); 
  375.  
  376. /** 
  377. * Handles the plugins column output. 
  378. * 
  379. * @since 4.3.0 
  380. * @access public 
  381. * 
  382. * @param array $blog Current site. 
  383. */ 
  384. public function column_plugins( $blog ) { 
  385. if ( has_filter( 'wpmublogsaction' ) ) { 
  386. /** 
  387. * Fires inside the auxiliary 'Actions' column of the Sites list table. 
  388. * 
  389. * By default this column is hidden unless something is hooked to the action. 
  390. * 
  391. * @since MU 
  392. * 
  393. * @param int $blog_id The site ID. 
  394. */ 
  395. do_action( 'wpmublogsaction', $blog['blog_id'] ); 
  396.  
  397. /** 
  398. * Handles output for the default column. 
  399. * 
  400. * @since 4.3.0 
  401. * @access public 
  402. * 
  403. * @param array $blog Current site. 
  404. * @param string $column_name Current column name. 
  405. */ 
  406. public function column_default( $blog, $column_name ) { 
  407. /** 
  408. * Fires for each registered custom column in the Sites list table. 
  409. * 
  410. * @since 3.1.0 
  411. * 
  412. * @param string $column_name The name of the column to display. 
  413. * @param int $blog_id The site ID. 
  414. */ 
  415. do_action( 'manage_sites_custom_column', $column_name, $blog['blog_id'] ); 
  416.  
  417. /** 
  418. * 
  419. * @global string $mode 
  420. */ 
  421. public function display_rows() { 
  422. foreach ( $this->items as $blog ) { 
  423. $blog = $blog->to_array(); 
  424. $class = ''; 
  425. reset( $this->status_list ); 
  426.  
  427. foreach ( $this->status_list as $status => $col ) { 
  428. if ( $blog[ $status ] == 1 ) { 
  429. $class = " class='{$col[0]}'"; 
  430.  
  431. echo "<tr{$class}>"; 
  432.  
  433. $this->single_row_columns( $blog ); 
  434.  
  435. echo '</tr>'; 
  436.  
  437. /** 
  438. * Gets the name of the default primary column. 
  439. * 
  440. * @since 4.3.0 
  441. * @access protected 
  442. * 
  443. * @return string Name of the default primary column, in this case, 'blogname'. 
  444. */ 
  445. protected function get_default_primary_column_name() { 
  446. return 'blogname'; 
  447.  
  448. /** 
  449. * Generates and displays row action links. 
  450. * 
  451. * @since 4.3.0 
  452. * @access protected 
  453. * 
  454. * @param object $blog Site being acted upon. 
  455. * @param string $column_name Current column name. 
  456. * @param string $primary Primary column name. 
  457. * @return string Row actions output. 
  458. */ 
  459. protected function handle_row_actions( $blog, $column_name, $primary ) { 
  460. if ( $primary !== $column_name ) { 
  461. return; 
  462.  
  463. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] ); 
  464.  
  465. // Preordered. 
  466. $actions = array( 
  467. 'edit' => '', 'backend' => '',  
  468. 'activate' => '', 'deactivate' => '',  
  469. 'archive' => '', 'unarchive' => '',  
  470. 'spam' => '', 'unspam' => '',  
  471. 'delete' => '',  
  472. 'visit' => '',  
  473. ); 
  474.  
  475. $actions['edit'] = '<a href="' . esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ) . '">' . __( 'Edit' ) . '</a>'; 
  476. $actions['backend'] = "<a href='" . esc_url( get_admin_url( $blog['blog_id'] ) ) . "' class='edit'>" . __( 'Dashboard' ) . '</a>'; 
  477. if ( get_network()->site_id != $blog['blog_id'] ) { 
  478. if ( $blog['deleted'] == '1' ) { 
  479. $actions['activate'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=activateblog&id=' . $blog['blog_id'] ), 'activateblog_' . $blog['blog_id'] ) ) . '">' . __( 'Activate' ) . '</a>'; 
  480. } else { 
  481. $actions['deactivate'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=deactivateblog&id=' . $blog['blog_id'] ), 'deactivateblog_' . $blog['blog_id'] ) ) . '">' . __( 'Deactivate' ) . '</a>'; 
  482.  
  483. if ( $blog['archived'] == '1' ) { 
  484. $actions['unarchive'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=unarchiveblog&id=' . $blog['blog_id'] ), 'unarchiveblog_' . $blog['blog_id'] ) ) . '">' . __( 'Unarchive' ) . '</a>'; 
  485. } else { 
  486. $actions['archive'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=archiveblog&id=' . $blog['blog_id'] ), 'archiveblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Archive', 'verb; site' ) . '</a>'; 
  487.  
  488. if ( $blog['spam'] == '1' ) { 
  489. $actions['unspam'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=unspamblog&id=' . $blog['blog_id'] ), 'unspamblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Not Spam', 'site' ) . '</a>'; 
  490. } else { 
  491. $actions['spam'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=spamblog&id=' . $blog['blog_id'] ), 'spamblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Spam', 'site' ) . '</a>'; 
  492.  
  493. if ( current_user_can( 'delete_site', $blog['blog_id'] ) ) { 
  494. $actions['delete'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&action2=deleteblog&id=' . $blog['blog_id'] ), 'deleteblog_' . $blog['blog_id'] ) ) . '">' . __( 'Delete' ) . '</a>'; 
  495.  
  496. $actions['visit'] = "<a href='" . esc_url( get_home_url( $blog['blog_id'], '/' ) ) . "' rel='permalink'>" . __( 'Visit' ) . '</a>'; 
  497.  
  498. /** 
  499. * Filters the action links displayed for each site in the Sites list table. 
  500. * 
  501. * The 'Edit', 'Dashboard', 'Delete', and 'Visit' links are displayed by 
  502. * default for each site. The site's status determines whether to show the 
  503. * 'Activate' or 'Deactivate' link, 'Unarchive' or 'Archive' links, and 
  504. * 'Not Spam' or 'Spam' link for each site. 
  505. * 
  506. * @since 3.1.0 
  507. * 
  508. * @param array $actions An array of action links to be displayed. 
  509. * @param int $blog_id The site ID. 
  510. * @param string $blogname Site path, formatted depending on whether it is a sub-domain 
  511. * or subdirectory multisite install. 
  512. */ 
  513. $actions = apply_filters( 'manage_sites_action_links', array_filter( $actions ), $blog['blog_id'], $blogname ); 
  514. return $this->row_actions( $actions ); 
.