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

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