class-wp-ms-sites-list-table.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  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. * Core class used to implement displaying sites 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_Sites_List_Table extends WP_List_Table {
  17. /**
  18. * Site status list.
  19. *
  20. * @since 4.3.0
  21. * @var array
  22. */
  23. public $status_list;
  24. /**
  25. * Constructor.
  26. *
  27. * @since 3.1.0
  28. *
  29. * @see WP_List_Table::__construct() for more information on default arguments.
  30. *
  31. * @param array $args An associative array of arguments.
  32. */
  33. public function __construct( $args = array() ) {
  34. $this->status_list = array(
  35. 'archived' => array( 'site-archived', __( 'Archived' ) ),
  36. 'spam' => array( 'site-spammed', _x( 'Spam', 'site' ) ),
  37. 'deleted' => array( 'site-deleted', __( 'Deleted' ) ),
  38. 'mature' => array( 'site-mature', __( 'Mature' ) ),
  39. );
  40. parent::__construct(
  41. array(
  42. 'plural' => 'sites',
  43. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
  44. )
  45. );
  46. }
  47. /**
  48. * @return bool
  49. */
  50. public function ajax_user_can() {
  51. return current_user_can( 'manage_sites' );
  52. }
  53. /**
  54. * Prepares the list of sites for display.
  55. *
  56. * @since 3.1.0
  57. *
  58. * @global string $mode List table view mode.
  59. * @global string $s
  60. * @global wpdb $wpdb WordPress database abstraction object.
  61. */
  62. public function prepare_items() {
  63. global $mode, $s, $wpdb;
  64. if ( ! empty( $_REQUEST['mode'] ) ) {
  65. $mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
  66. set_user_setting( 'sites_list_mode', $mode );
  67. } else {
  68. $mode = get_user_setting( 'sites_list_mode', 'list' );
  69. }
  70. $per_page = $this->get_items_per_page( 'sites_network_per_page' );
  71. $pagenum = $this->get_pagenum();
  72. $s = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST['s'] ) ) : '';
  73. $wild = '';
  74. if ( false !== strpos( $s, '*' ) ) {
  75. $wild = '*';
  76. $s = trim( $s, '*' );
  77. }
  78. /*
  79. * If the network is large and a search is not being performed, show only
  80. * the latest sites with no paging in order to avoid expensive count queries.
  81. */
  82. if ( ! $s && wp_is_large_network() ) {
  83. if ( ! isset( $_REQUEST['orderby'] ) ) {
  84. $_GET['orderby'] = '';
  85. $_REQUEST['orderby'] = '';
  86. }
  87. if ( ! isset( $_REQUEST['order'] ) ) {
  88. $_GET['order'] = 'DESC';
  89. $_REQUEST['order'] = 'DESC';
  90. }
  91. }
  92. $args = array(
  93. 'number' => (int) $per_page,
  94. 'offset' => (int) ( ( $pagenum - 1 ) * $per_page ),
  95. 'network_id' => get_current_network_id(),
  96. );
  97. if ( empty( $s ) ) {
  98. // Nothing to do.
  99. } elseif ( preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $s ) ||
  100. preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s ) ||
  101. preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s ) ||
  102. preg_match( '/^[0-9]{1,3}\.$/', $s ) ) {
  103. // IPv4 address.
  104. $sql = $wpdb->prepare( "SELECT blog_id FROM {$wpdb->registration_log} WHERE {$wpdb->registration_log}.IP LIKE %s", $wpdb->esc_like( $s ) . ( ! empty( $wild ) ? '%' : '' ) );
  105. $reg_blog_ids = $wpdb->get_col( $sql );
  106. if ( $reg_blog_ids ) {
  107. $args['site__in'] = $reg_blog_ids;
  108. }
  109. } elseif ( is_numeric( $s ) && empty( $wild ) ) {
  110. $args['ID'] = $s;
  111. } else {
  112. $args['search'] = $s;
  113. if ( ! is_subdomain_install() ) {
  114. $args['search_columns'] = array( 'path' );
  115. }
  116. }
  117. $order_by = isset( $_REQUEST['orderby'] ) ? $_REQUEST['orderby'] : '';
  118. if ( 'registered' === $order_by ) {
  119. // 'registered' is a valid field name.
  120. } elseif ( 'lastupdated' === $order_by ) {
  121. $order_by = 'last_updated';
  122. } elseif ( 'blogname' === $order_by ) {
  123. if ( is_subdomain_install() ) {
  124. $order_by = 'domain';
  125. } else {
  126. $order_by = 'path';
  127. }
  128. } elseif ( 'blog_id' === $order_by ) {
  129. $order_by = 'id';
  130. } elseif ( ! $order_by ) {
  131. $order_by = false;
  132. }
  133. $args['orderby'] = $order_by;
  134. if ( $order_by ) {
  135. $args['order'] = ( isset( $_REQUEST['order'] ) && 'DESC' === strtoupper( $_REQUEST['order'] ) ) ? 'DESC' : 'ASC';
  136. }
  137. if ( wp_is_large_network() ) {
  138. $args['no_found_rows'] = true;
  139. } else {
  140. $args['no_found_rows'] = false;
  141. }
  142. // Take into account the role the user has selected.
  143. $status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
  144. if ( in_array( $status, array( 'public', 'archived', 'mature', 'spam', 'deleted' ), true ) ) {
  145. $args[ $status ] = 1;
  146. }
  147. /**
  148. * Filters the arguments for the site query in the sites list table.
  149. *
  150. * @since 4.6.0
  151. *
  152. * @param array $args An array of get_sites() arguments.
  153. */
  154. $args = apply_filters( 'ms_sites_list_table_query_args', $args );
  155. $_sites = get_sites( $args );
  156. if ( is_array( $_sites ) ) {
  157. update_site_cache( $_sites );
  158. $this->items = array_slice( $_sites, 0, $per_page );
  159. }
  160. $total_sites = get_sites(
  161. array_merge(
  162. $args,
  163. array(
  164. 'count' => true,
  165. 'offset' => 0,
  166. 'number' => 0,
  167. )
  168. )
  169. );
  170. $this->set_pagination_args(
  171. array(
  172. 'total_items' => $total_sites,
  173. 'per_page' => $per_page,
  174. )
  175. );
  176. }
  177. /**
  178. */
  179. public function no_items() {
  180. _e( 'No sites found.' );
  181. }
  182. /**
  183. * Gets links to filter sites by status.
  184. *
  185. * @since 5.3.0
  186. *
  187. * @return array
  188. */
  189. protected function get_views() {
  190. $counts = wp_count_sites();
  191. $statuses = array(
  192. /* translators: %s: Number of sites. */
  193. 'all' => _nx_noop(
  194. 'All <span class="count">(%s)</span>',
  195. 'All <span class="count">(%s)</span>',
  196. 'sites'
  197. ),
  198. /* translators: %s: Number of sites. */
  199. 'public' => _n_noop(
  200. 'Public <span class="count">(%s)</span>',
  201. 'Public <span class="count">(%s)</span>'
  202. ),
  203. /* translators: %s: Number of sites. */
  204. 'archived' => _n_noop(
  205. 'Archived <span class="count">(%s)</span>',
  206. 'Archived <span class="count">(%s)</span>'
  207. ),
  208. /* translators: %s: Number of sites. */
  209. 'mature' => _n_noop(
  210. 'Mature <span class="count">(%s)</span>',
  211. 'Mature <span class="count">(%s)</span>'
  212. ),
  213. /* translators: %s: Number of sites. */
  214. 'spam' => _nx_noop(
  215. 'Spam <span class="count">(%s)</span>',
  216. 'Spam <span class="count">(%s)</span>',
  217. 'sites'
  218. ),
  219. /* translators: %s: Number of sites. */
  220. 'deleted' => _n_noop(
  221. 'Deleted <span class="count">(%s)</span>',
  222. 'Deleted <span class="count">(%s)</span>'
  223. ),
  224. );
  225. $view_links = array();
  226. $requested_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
  227. $url = 'sites.php';
  228. foreach ( $statuses as $status => $label_count ) {
  229. if ( (int) $counts[ $status ] > 0 ) {
  230. $label = sprintf( translate_nooped_plural( $label_count, $counts[ $status ] ), number_format_i18n( $counts[ $status ] ) );
  231. $full_url = 'all' === $status ? $url : add_query_arg( 'status', $status, $url );
  232. $view_links[ $status ] = array(
  233. 'url' => esc_url( $full_url ),
  234. 'label' => $label,
  235. 'current' => $requested_status === $status || ( '' === $requested_status && 'all' === $status ),
  236. );
  237. }
  238. }
  239. return $this->get_views_links( $view_links );
  240. }
  241. /**
  242. * @return array
  243. */
  244. protected function get_bulk_actions() {
  245. $actions = array();
  246. if ( current_user_can( 'delete_sites' ) ) {
  247. $actions['delete'] = __( 'Delete' );
  248. }
  249. $actions['spam'] = _x( 'Mark as spam', 'site' );
  250. $actions['notspam'] = _x( 'Not spam', 'site' );
  251. return $actions;
  252. }
  253. /**
  254. * @global string $mode List table view mode.
  255. *
  256. * @param string $which The location of the pagination nav markup: 'top' or 'bottom'.
  257. */
  258. protected function pagination( $which ) {
  259. global $mode;
  260. parent::pagination( $which );
  261. if ( 'top' === $which ) {
  262. $this->view_switcher( $mode );
  263. }
  264. }
  265. /**
  266. * Extra controls to be displayed between bulk actions and pagination.
  267. *
  268. * @since 5.3.0
  269. *
  270. * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
  271. */
  272. protected function extra_tablenav( $which ) {
  273. ?>
  274. <div class="alignleft actions">
  275. <?php
  276. if ( 'top' === $which ) {
  277. ob_start();
  278. /**
  279. * Fires before the Filter button on the MS sites list table.
  280. *
  281. * @since 5.3.0
  282. *
  283. * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
  284. */
  285. do_action( 'restrict_manage_sites', $which );
  286. $output = ob_get_clean();
  287. if ( ! empty( $output ) ) {
  288. echo $output;
  289. submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'site-query-submit' ) );
  290. }
  291. }
  292. ?>
  293. </div>
  294. <?php
  295. /**
  296. * Fires immediately following the closing "actions" div in the tablenav for the
  297. * MS sites list table.
  298. *
  299. * @since 5.3.0
  300. *
  301. * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
  302. */
  303. do_action( 'manage_sites_extra_tablenav', $which );
  304. }
  305. /**
  306. * @return array
  307. */
  308. public function get_columns() {
  309. $sites_columns = array(
  310. 'cb' => '<input type="checkbox" />',
  311. 'blogname' => __( 'URL' ),
  312. 'lastupdated' => __( 'Last Updated' ),
  313. 'registered' => _x( 'Registered', 'site' ),
  314. 'users' => __( 'Users' ),
  315. );
  316. if ( has_filter( 'wpmublogsaction' ) ) {
  317. $sites_columns['plugins'] = __( 'Actions' );
  318. }
  319. /**
  320. * Filters the displayed site columns in Sites list table.
  321. *
  322. * @since MU (3.0.0)
  323. *
  324. * @param string[] $sites_columns An array of displayed site columns. Default 'cb',
  325. * 'blogname', 'lastupdated', 'registered', 'users'.
  326. */
  327. return apply_filters( 'wpmu_blogs_columns', $sites_columns );
  328. }
  329. /**
  330. * @return array
  331. */
  332. protected function get_sortable_columns() {
  333. return array(
  334. 'blogname' => 'blogname',
  335. 'lastupdated' => 'lastupdated',
  336. 'registered' => 'blog_id',
  337. );
  338. }
  339. /**
  340. * Handles the checkbox column output.
  341. *
  342. * @since 4.3.0
  343. * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
  344. *
  345. * @param array $item Current site.
  346. */
  347. public function column_cb( $item ) {
  348. // Restores the more descriptive, specific name for use within this method.
  349. $blog = $item;
  350. if ( ! is_main_site( $blog['blog_id'] ) ) :
  351. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
  352. ?>
  353. <label class="screen-reader-text" for="blog_<?php echo $blog['blog_id']; ?>">
  354. <?php
  355. /* translators: %s: Site URL. */
  356. printf( __( 'Select %s' ), $blogname );
  357. ?>
  358. </label>
  359. <input type="checkbox" id="blog_<?php echo $blog['blog_id']; ?>" name="allblogs[]" value="<?php echo esc_attr( $blog['blog_id'] ); ?>" />
  360. <?php
  361. endif;
  362. }
  363. /**
  364. * Handles the ID column output.
  365. *
  366. * @since 4.4.0
  367. *
  368. * @param array $blog Current site.
  369. */
  370. public function column_id( $blog ) {
  371. echo $blog['blog_id'];
  372. }
  373. /**
  374. * Handles the site name column output.
  375. *
  376. * @since 4.3.0
  377. *
  378. * @global string $mode List table view mode.
  379. *
  380. * @param array $blog Current site.
  381. */
  382. public function column_blogname( $blog ) {
  383. global $mode;
  384. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
  385. ?>
  386. <strong>
  387. <a href="<?php echo esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ); ?>" class="edit"><?php echo $blogname; ?></a>
  388. <?php $this->site_states( $blog ); ?>
  389. </strong>
  390. <?php
  391. if ( 'list' !== $mode ) {
  392. switch_to_blog( $blog['blog_id'] );
  393. echo '<p>';
  394. printf(
  395. /* translators: 1: Site title, 2: Site tagline. */
  396. __( '%1$s &#8211; %2$s' ),
  397. get_option( 'blogname' ),
  398. '<em>' . get_option( 'blogdescription' ) . '</em>'
  399. );
  400. echo '</p>';
  401. restore_current_blog();
  402. }
  403. }
  404. /**
  405. * Handles the lastupdated column output.
  406. *
  407. * @since 4.3.0
  408. *
  409. * @global string $mode List table view mode.
  410. *
  411. * @param array $blog Current site.
  412. */
  413. public function column_lastupdated( $blog ) {
  414. global $mode;
  415. if ( 'list' === $mode ) {
  416. $date = __( 'Y/m/d' );
  417. } else {
  418. $date = __( 'Y/m/d g:i:s a' );
  419. }
  420. echo ( '0000-00-00 00:00:00' === $blog['last_updated'] ) ? __( 'Never' ) : mysql2date( $date, $blog['last_updated'] );
  421. }
  422. /**
  423. * Handles the registered column output.
  424. *
  425. * @since 4.3.0
  426. *
  427. * @global string $mode List table view mode.
  428. *
  429. * @param array $blog Current site.
  430. */
  431. public function column_registered( $blog ) {
  432. global $mode;
  433. if ( 'list' === $mode ) {
  434. $date = __( 'Y/m/d' );
  435. } else {
  436. $date = __( 'Y/m/d g:i:s a' );
  437. }
  438. if ( '0000-00-00 00:00:00' === $blog['registered'] ) {
  439. echo '&#x2014;';
  440. } else {
  441. echo mysql2date( $date, $blog['registered'] );
  442. }
  443. }
  444. /**
  445. * Handles the users column output.
  446. *
  447. * @since 4.3.0
  448. *
  449. * @param array $blog Current site.
  450. */
  451. public function column_users( $blog ) {
  452. $user_count = wp_cache_get( $blog['blog_id'] . '_user_count', 'blog-details' );
  453. if ( ! $user_count ) {
  454. $blog_users = new WP_User_Query(
  455. array(
  456. 'blog_id' => $blog['blog_id'],
  457. 'fields' => 'ID',
  458. 'number' => 1,
  459. 'count_total' => true,
  460. )
  461. );
  462. $user_count = $blog_users->get_total();
  463. wp_cache_set( $blog['blog_id'] . '_user_count', $user_count, 'blog-details', 12 * HOUR_IN_SECONDS );
  464. }
  465. printf(
  466. '<a href="%s">%s</a>',
  467. esc_url( network_admin_url( 'site-users.php?id=' . $blog['blog_id'] ) ),
  468. number_format_i18n( $user_count )
  469. );
  470. }
  471. /**
  472. * Handles the plugins column output.
  473. *
  474. * @since 4.3.0
  475. *
  476. * @param array $blog Current site.
  477. */
  478. public function column_plugins( $blog ) {
  479. if ( has_filter( 'wpmublogsaction' ) ) {
  480. /**
  481. * Fires inside the auxiliary 'Actions' column of the Sites list table.
  482. *
  483. * By default this column is hidden unless something is hooked to the action.
  484. *
  485. * @since MU (3.0.0)
  486. *
  487. * @param int $blog_id The site ID.
  488. */
  489. do_action( 'wpmublogsaction', $blog['blog_id'] );
  490. }
  491. }
  492. /**
  493. * Handles output for the default column.
  494. *
  495. * @since 4.3.0
  496. * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
  497. *
  498. * @param array $item Current site.
  499. * @param string $column_name Current column name.
  500. */
  501. public function column_default( $item, $column_name ) {
  502. /**
  503. * Fires for each registered custom column in the Sites list table.
  504. *
  505. * @since 3.1.0
  506. *
  507. * @param string $column_name The name of the column to display.
  508. * @param int $blog_id The site ID.
  509. */
  510. do_action( 'manage_sites_custom_column', $column_name, $item['blog_id'] );
  511. }
  512. /**
  513. * @global string $mode List table view mode.
  514. */
  515. public function display_rows() {
  516. foreach ( $this->items as $blog ) {
  517. $blog = $blog->to_array();
  518. $class = '';
  519. reset( $this->status_list );
  520. foreach ( $this->status_list as $status => $col ) {
  521. if ( 1 == $blog[ $status ] ) {
  522. $class = " class='{$col[0]}'";
  523. }
  524. }
  525. echo "<tr{$class}>";
  526. $this->single_row_columns( $blog );
  527. echo '</tr>';
  528. }
  529. }
  530. /**
  531. * Maybe output comma-separated site states.
  532. *
  533. * @since 5.3.0
  534. *
  535. * @param array $site
  536. */
  537. protected function site_states( $site ) {
  538. $site_states = array();
  539. // $site is still an array, so get the object.
  540. $_site = WP_Site::get_instance( $site['blog_id'] );
  541. if ( is_main_site( $_site->id ) ) {
  542. $site_states['main'] = __( 'Main' );
  543. }
  544. reset( $this->status_list );
  545. $site_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
  546. foreach ( $this->status_list as $status => $col ) {
  547. if ( ( 1 === (int) $_site->{$status} ) && ( $site_status !== $status ) ) {
  548. $site_states[ $col[0] ] = $col[1];
  549. }
  550. }
  551. /**
  552. * Filters the default site display states for items in the Sites list table.
  553. *
  554. * @since 5.3.0
  555. *
  556. * @param string[] $site_states An array of site states. Default 'Main',
  557. * 'Archived', 'Mature', 'Spam', 'Deleted'.
  558. * @param WP_Site $site The current site object.
  559. */
  560. $site_states = apply_filters( 'display_site_states', $site_states, $_site );
  561. if ( ! empty( $site_states ) ) {
  562. $state_count = count( $site_states );
  563. $i = 0;
  564. echo ' &mdash; ';
  565. foreach ( $site_states as $state ) {
  566. ++$i;
  567. $separator = ( $i < $state_count ) ? ', ' : '';
  568. echo "<span class='post-state'>{$state}{$separator}</span>";
  569. }
  570. }
  571. }
  572. /**
  573. * Gets the name of the default primary column.
  574. *
  575. * @since 4.3.0
  576. *
  577. * @return string Name of the default primary column, in this case, 'blogname'.
  578. */
  579. protected function get_default_primary_column_name() {
  580. return 'blogname';
  581. }
  582. /**
  583. * Generates and displays row action links.
  584. *
  585. * @since 4.3.0
  586. * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
  587. *
  588. * @param array $item Site being acted upon.
  589. * @param string $column_name Current column name.
  590. * @param string $primary Primary column name.
  591. * @return string Row actions output for sites in Multisite, or an empty string
  592. * if the current column is not the primary column.
  593. */
  594. protected function handle_row_actions( $item, $column_name, $primary ) {
  595. if ( $primary !== $column_name ) {
  596. return '';
  597. }
  598. // Restores the more descriptive, specific name for use within this method.
  599. $blog = $item;
  600. $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
  601. // Preordered.
  602. $actions = array(
  603. 'edit' => '',
  604. 'backend' => '',
  605. 'activate' => '',
  606. 'deactivate' => '',
  607. 'archive' => '',
  608. 'unarchive' => '',
  609. 'spam' => '',
  610. 'unspam' => '',
  611. 'delete' => '',
  612. 'visit' => '',
  613. );
  614. $actions['edit'] = '<a href="' . esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ) . '">' . __( 'Edit' ) . '</a>';
  615. $actions['backend'] = "<a href='" . esc_url( get_admin_url( $blog['blog_id'] ) ) . "' class='edit'>" . __( 'Dashboard' ) . '</a>';
  616. if ( get_network()->site_id != $blog['blog_id'] ) {
  617. if ( '1' == $blog['deleted'] ) {
  618. $actions['activate'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=activateblog&amp;id=' . $blog['blog_id'] ), 'activateblog_' . $blog['blog_id'] ) ) . '">' . __( 'Activate' ) . '</a>';
  619. } else {
  620. $actions['deactivate'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=deactivateblog&amp;id=' . $blog['blog_id'] ), 'deactivateblog_' . $blog['blog_id'] ) ) . '">' . __( 'Deactivate' ) . '</a>';
  621. }
  622. if ( '1' == $blog['archived'] ) {
  623. $actions['unarchive'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=unarchiveblog&amp;id=' . $blog['blog_id'] ), 'unarchiveblog_' . $blog['blog_id'] ) ) . '">' . __( 'Unarchive' ) . '</a>';
  624. } else {
  625. $actions['archive'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=archiveblog&amp;id=' . $blog['blog_id'] ), 'archiveblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Archive', 'verb; site' ) . '</a>';
  626. }
  627. if ( '1' == $blog['spam'] ) {
  628. $actions['unspam'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=unspamblog&amp;id=' . $blog['blog_id'] ), 'unspamblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Not Spam', 'site' ) . '</a>';
  629. } else {
  630. $actions['spam'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=spamblog&amp;id=' . $blog['blog_id'] ), 'spamblog_' . $blog['blog_id'] ) ) . '">' . _x( 'Spam', 'site' ) . '</a>';
  631. }
  632. if ( current_user_can( 'delete_site', $blog['blog_id'] ) ) {
  633. $actions['delete'] = '<a href="' . esc_url( wp_nonce_url( network_admin_url( 'sites.php?action=confirm&amp;action2=deleteblog&amp;id=' . $blog['blog_id'] ), 'deleteblog_' . $blog['blog_id'] ) ) . '">' . __( 'Delete' ) . '</a>';
  634. }
  635. }
  636. $actions['visit'] = "<a href='" . esc_url( get_home_url( $blog['blog_id'], '/' ) ) . "' rel='bookmark'>" . __( 'Visit' ) . '</a>';
  637. /**
  638. * Filters the action links displayed for each site in the Sites list table.
  639. *
  640. * The 'Edit', 'Dashboard', 'Delete', and 'Visit' links are displayed by
  641. * default for each site. The site's status determines whether to show the
  642. * 'Activate' or 'Deactivate' link, 'Unarchive' or 'Archive' links, and
  643. * 'Not Spam' or 'Spam' link for each site.
  644. *
  645. * @since 3.1.0
  646. *
  647. * @param string[] $actions An array of action links to be displayed.
  648. * @param int $blog_id The site ID.
  649. * @param string $blogname Site path, formatted depending on whether it is a sub-domain
  650. * or subdirectory multisite installation.
  651. */
  652. $actions = apply_filters( 'manage_sites_action_links', array_filter( $actions ), $blog['blog_id'], $blogname );
  653. return $this->row_actions( $actions );
  654. }
  655. }