[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-wp-ms-sites-list-table.php (source)

   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   *
  15   * @see WP_List_Table
  16   */
  17  class WP_MS_Sites_List_Table extends WP_List_Table {
  18  
  19      /**
  20       * Site status list.
  21       *
  22       * @since 4.3.0
  23       * @var array
  24       */
  25      public $status_list;
  26  
  27      /**
  28       * Constructor.
  29       *
  30       * @since 3.1.0
  31       *
  32       * @see WP_List_Table::__construct() for more information on default arguments.
  33       *
  34       * @param array $args An associative array of arguments.
  35       */
  36  	public function __construct( $args = array() ) {
  37          $this->status_list = array(
  38              'archived' => array( 'site-archived', __( 'Archived' ) ),
  39              'spam'     => array( 'site-spammed', _x( 'Spam', 'site' ) ),
  40              'deleted'  => array( 'site-deleted', __( 'Flagged for Deletion' ) ),
  41              'mature'   => array( 'site-mature', __( 'Mature' ) ),
  42          );
  43  
  44          parent::__construct(
  45              array(
  46                  'plural' => 'sites',
  47                  'screen' => $args['screen'] ?? null,
  48              )
  49          );
  50      }
  51  
  52      /**
  53       * Checks if the current user has permissions to manage sites.
  54       *
  55       * @since 3.1.0
  56       *
  57       * @return bool Whether the user can manage sites.
  58       */
  59  	public function ajax_user_can() {
  60          return current_user_can( 'manage_sites' );
  61      }
  62  
  63      /**
  64       * Prepares the list of sites for display.
  65       *
  66       * @since 3.1.0
  67       *
  68       * @global string $mode List table view mode.
  69       * @global string $s    Search string.
  70       * @global wpdb   $wpdb WordPress database abstraction object.
  71       */
  72  	public function prepare_items() {
  73          global $mode, $s, $wpdb;
  74  
  75          if ( ! empty( $_REQUEST['mode'] ) ) {
  76              $mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
  77              set_user_setting( 'sites_list_mode', $mode );
  78          } else {
  79              $mode = get_user_setting( 'sites_list_mode', 'list' );
  80          }
  81  
  82          $per_page = $this->get_items_per_page( 'sites_network_per_page' );
  83  
  84          $pagenum = $this->get_pagenum();
  85  
  86          $s    = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST['s'] ) ) : '';
  87          $wild = '';
  88          if ( str_contains( $s, '*' ) ) {
  89              $wild = '*';
  90              $s    = trim( $s, '*' );
  91          }
  92  
  93          /*
  94           * If the network is large and a search is not being performed, show only
  95           * the latest sites with no paging in order to avoid expensive count queries.
  96           */
  97          if ( ! $s && wp_is_large_network() ) {
  98              if ( ! isset( $_REQUEST['orderby'] ) ) {
  99                  $_GET['orderby']     = '';
 100                  $_REQUEST['orderby'] = '';
 101              }
 102              if ( ! isset( $_REQUEST['order'] ) ) {
 103                  $_GET['order']     = 'DESC';
 104                  $_REQUEST['order'] = 'DESC';
 105              }
 106          }
 107  
 108          $args = array(
 109              'number'     => (int) $per_page,
 110              'offset'     => (int) ( ( $pagenum - 1 ) * $per_page ),
 111              'network_id' => get_current_network_id(),
 112          );
 113  
 114          if ( empty( $s ) ) {
 115              // Nothing to do.
 116          } elseif ( preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $s )
 117              || preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s )
 118              || preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s )
 119              || preg_match( '/^[0-9]{1,3}\.$/', $s )
 120          ) {
 121              // IPv4 address.
 122              $reg_blog_ids = $wpdb->get_col(
 123                  $wpdb->prepare(
 124                      "SELECT blog_id FROM {$wpdb->registration_log} WHERE {$wpdb->registration_log}.IP LIKE %s",
 125                      $wpdb->esc_like( $s ) . ( ! empty( $wild ) ? '%' : '' )
 126                  )
 127              );
 128  
 129              if ( $reg_blog_ids ) {
 130                  $args['site__in'] = $reg_blog_ids;
 131              }
 132          } elseif ( is_numeric( $s ) && empty( $wild ) ) {
 133              $args['ID'] = $s;
 134          } else {
 135              $args['search'] = $s;
 136  
 137              if ( ! is_subdomain_install() ) {
 138                  $args['search_columns'] = array( 'path' );
 139              }
 140          }
 141  
 142          $order_by = $_REQUEST['orderby'] ?? '';
 143          if ( 'registered' === $order_by ) {
 144              // 'registered' is a valid field name.
 145          } elseif ( 'lastupdated' === $order_by ) {
 146              $order_by = 'last_updated';
 147          } elseif ( 'blogname' === $order_by ) {
 148              if ( is_subdomain_install() ) {
 149                  $order_by = 'domain';
 150              } else {
 151                  $order_by = 'path';
 152              }
 153          } elseif ( 'blog_id' === $order_by ) {
 154              $order_by = 'id';
 155          } elseif ( ! $order_by ) {
 156              $order_by = false;
 157          }
 158  
 159          $args['orderby'] = $order_by;
 160  
 161          if ( $order_by ) {
 162              $args['order'] = ( isset( $_REQUEST['order'] ) && 'DESC' === strtoupper( $_REQUEST['order'] ) ) ? 'DESC' : 'ASC';
 163          }
 164  
 165          if ( wp_is_large_network() ) {
 166              $args['no_found_rows'] = true;
 167          } else {
 168              $args['no_found_rows'] = false;
 169          }
 170  
 171          // Take into account the role the user has selected.
 172          $status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
 173          if ( in_array( $status, array( 'public', 'archived', 'mature', 'spam', 'deleted' ), true ) ) {
 174              $args[ $status ] = 1;
 175          }
 176  
 177          /**
 178           * Filters the arguments for the site query in the sites list table.
 179           *
 180           * @since 4.6.0
 181           *
 182           * @param array $args An array of get_sites() arguments.
 183           */
 184          $args = apply_filters( 'ms_sites_list_table_query_args', $args );
 185  
 186          $_sites = get_sites( $args );
 187          if ( is_array( $_sites ) ) {
 188              update_site_cache( $_sites );
 189  
 190              $this->items = array_slice( $_sites, 0, $per_page );
 191          }
 192  
 193          $total_sites = get_sites(
 194              array_merge(
 195                  $args,
 196                  array(
 197                      'count'  => true,
 198                      'offset' => 0,
 199                      'number' => 0,
 200                  )
 201              )
 202          );
 203  
 204          $this->set_pagination_args(
 205              array(
 206                  'total_items' => $total_sites,
 207                  'per_page'    => $per_page,
 208              )
 209          );
 210      }
 211  
 212      /**
 213       * Displays a message when no sites are found.
 214       *
 215       * @since 3.1.0
 216       */
 217  	public function no_items() {
 218          _e( 'No sites found.' );
 219      }
 220  
 221      /**
 222       * Gets links to filter sites by status.
 223       *
 224       * @since 5.3.0
 225       *
 226       * @return array
 227       */
 228  	protected function get_views() {
 229          $counts = wp_count_sites();
 230  
 231          $statuses = array(
 232              /* translators: %s: Number of sites. */
 233              'all'      => _nx_noop(
 234                  'All <span class="count">(%s)</span>',
 235                  'All <span class="count">(%s)</span>',
 236                  'sites'
 237              ),
 238  
 239              /* translators: %s: Number of sites. */
 240              'public'   => _n_noop(
 241                  'Public <span class="count">(%s)</span>',
 242                  'Public <span class="count">(%s)</span>'
 243              ),
 244  
 245              /* translators: %s: Number of sites. */
 246              'archived' => _n_noop(
 247                  'Archived <span class="count">(%s)</span>',
 248                  'Archived <span class="count">(%s)</span>'
 249              ),
 250  
 251              /* translators: %s: Number of sites. */
 252              'mature'   => _n_noop(
 253                  'Mature <span class="count">(%s)</span>',
 254                  'Mature <span class="count">(%s)</span>'
 255              ),
 256  
 257              /* translators: %s: Number of sites. */
 258              'spam'     => _nx_noop(
 259                  'Spam <span class="count">(%s)</span>',
 260                  'Spam <span class="count">(%s)</span>',
 261                  'sites'
 262              ),
 263  
 264              /* translators: %s: Number of sites. */
 265              'deleted'  => _n_noop(
 266                  'Flagged for Deletion <span class="count">(%s)</span>',
 267                  'Flagged for Deletion <span class="count">(%s)</span>'
 268              ),
 269          );
 270  
 271          $view_links       = array();
 272          $requested_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
 273          $url              = 'sites.php';
 274  
 275          foreach ( $statuses as $status => $label_count ) {
 276              if ( (int) $counts[ $status ] > 0 ) {
 277                  $label = sprintf(
 278                      translate_nooped_plural( $label_count, $counts[ $status ] ),
 279                      number_format_i18n( $counts[ $status ] )
 280                  );
 281  
 282                  $full_url = 'all' === $status ? $url : add_query_arg( 'status', $status, $url );
 283  
 284                  $view_links[ $status ] = array(
 285                      'url'     => esc_url( $full_url ),
 286                      'label'   => $label,
 287                      'current' => $requested_status === $status || ( '' === $requested_status && 'all' === $status ),
 288                  );
 289              }
 290          }
 291  
 292          return $this->get_views_links( $view_links );
 293      }
 294  
 295      /**
 296       * Gets an associative array of bulk actions for this table.
 297       *
 298       * @since 3.1.0
 299       *
 300       * @return array<string, string> An associative array of bulk actions.
 301       */
 302  	protected function get_bulk_actions() {
 303          $actions = array();
 304          if ( current_user_can( 'delete_sites' ) ) {
 305              $actions['delete'] = __( 'Delete' );
 306          }
 307          $actions['spam']    = _x( 'Mark as spam', 'site' );
 308          $actions['notspam'] = _x( 'Not spam', 'site' );
 309  
 310          return $actions;
 311      }
 312  
 313      /**
 314       * Displays the pagination.
 315       *
 316       * @since 3.1.0
 317       *
 318       * @global string $mode List table view mode.
 319       *
 320       * @param string $which The location of the pagination nav markup: Either 'top' or 'bottom'.
 321       *
 322       * @phpstan-param 'top'|'bottom' $which
 323       */
 324  	protected function pagination( $which ) {
 325          global $mode;
 326  
 327          parent::pagination( $which );
 328  
 329          if ( 'top' === $which ) {
 330              $this->view_switcher( $mode );
 331          }
 332      }
 333  
 334      /**
 335       * Displays extra controls between bulk actions and pagination.
 336       *
 337       * @since 5.3.0
 338       *
 339       * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'.
 340       */
 341  	protected function extra_tablenav( $which ) {
 342          ?>
 343          <div class="alignleft actions">
 344          <?php
 345          if ( 'top' === $which ) {
 346              ob_start();
 347  
 348              /**
 349               * Fires before the Filter button on the MS sites list table.
 350               *
 351               * @since 5.3.0
 352               *
 353               * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'.
 354               */
 355              do_action( 'restrict_manage_sites', $which );
 356  
 357              $output = ob_get_clean();
 358  
 359              if ( ! empty( $output ) ) {
 360                  echo $output;
 361                  submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'site-query-submit' ) );
 362              }
 363          }
 364          ?>
 365          </div>
 366          <?php
 367          /**
 368           * Fires immediately following the closing "actions" div in the tablenav for the
 369           * MS sites list table.
 370           *
 371           * @since 5.3.0
 372           *
 373           * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'.
 374           */
 375          do_action( 'manage_sites_extra_tablenav', $which );
 376      }
 377  
 378      /**
 379       * Gets an array of column titles keyed by their column name.
 380       *
 381       * @since 3.1.0
 382       *
 383       * @return string[] Array of column titles keyed by their column name.
 384       */
 385  	public function get_columns() {
 386          $sites_columns = array(
 387              'cb'          => '<input type="checkbox" />',
 388              'blogname'    => __( 'URL' ),
 389              'lastupdated' => __( 'Last Updated' ),
 390              'registered'  => _x( 'Registered', 'site' ),
 391              'users'       => __( 'Users' ),
 392          );
 393  
 394          if ( has_filter( 'wpmublogsaction' ) ) {
 395              $sites_columns['plugins'] = __( 'Actions' );
 396          }
 397  
 398          /**
 399           * Filters the displayed site columns in Sites list table.
 400           *
 401           * @since MU (3.0.0)
 402           *
 403           * @param string[] $sites_columns An array of displayed site columns. Default 'cb',
 404           *                               'blogname', 'lastupdated', 'registered', 'users'.
 405           */
 406          return apply_filters( 'wpmu_blogs_columns', $sites_columns );
 407      }
 408  
 409      /**
 410       * Gets an array of sortable columns.
 411       *
 412       * @since 3.1.0
 413       *
 414       * @return array<string, mixed> An array of sortable columns.
 415       */
 416  	protected function get_sortable_columns() {
 417  
 418          if ( is_subdomain_install() ) {
 419              $blogname_abbr         = __( 'Domain' );
 420              $blogname_orderby_text = __( 'Table ordered by Site Domain Name.' );
 421          } else {
 422              $blogname_abbr         = __( 'Path' );
 423              $blogname_orderby_text = __( 'Table ordered by Site Path.' );
 424          }
 425  
 426          return array(
 427              'blogname'    => array( 'blogname', false, $blogname_abbr, $blogname_orderby_text ),
 428              'lastupdated' => array( 'lastupdated', true, __( 'Last Updated' ), __( 'Table ordered by Last Updated.' ) ),
 429              'registered'  => array( 'blog_id', true, _x( 'Registered', 'site' ), __( 'Table ordered by Site Registered Date.' ), 'desc' ),
 430          );
 431      }
 432  
 433      /**
 434       * Handles the checkbox column output.
 435       *
 436       * @since 4.3.0
 437       * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
 438       *
 439       * @param array $item Current site.
 440       */
 441  	public function column_cb( $item ) {
 442          // Restores the more descriptive, specific name for use within this method.
 443          $blog = $item;
 444  
 445          if ( ! is_main_site( $blog['blog_id'] ) ) :
 446              $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
 447              ?>
 448              <input type="checkbox" id="blog_<?php echo $blog['blog_id']; ?>" name="allblogs[]" value="<?php echo esc_attr( $blog['blog_id'] ); ?>" />
 449              <label for="blog_<?php echo $blog['blog_id']; ?>">
 450                  <span class="screen-reader-text">
 451                  <?php
 452                  /* translators: %s: Site URL. */
 453                  printf( __( 'Select %s' ), $blogname );
 454                  ?>
 455                  </span>
 456              </label>
 457              <?php
 458          endif;
 459      }
 460  
 461      /**
 462       * Handles the ID column output.
 463       *
 464       * @since 4.4.0
 465       *
 466       * @param array $blog Current site.
 467       */
 468  	public function column_id( $blog ) {
 469          echo $blog['blog_id'];
 470      }
 471  
 472      /**
 473       * Handles the site name column output.
 474       *
 475       * @since 4.3.0
 476       *
 477       * @global string $mode List table view mode.
 478       *
 479       * @param array $blog Current site.
 480       */
 481  	public function column_blogname( $blog ) {
 482          global $mode;
 483  
 484          $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
 485  
 486          ?>
 487          <strong>
 488              <?php
 489              printf(
 490                  '<a href="%1$s" class="edit">%2$s</a>',
 491                  esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ),
 492                  $blogname
 493              );
 494  
 495              $this->site_states( $blog );
 496              ?>
 497          </strong>
 498          <?php
 499          if ( 'list' !== $mode ) {
 500              switch_to_blog( $blog['blog_id'] );
 501              echo '<p>';
 502              printf(
 503                  /* translators: 1: Site title, 2: Site tagline. */
 504                  __( '%1$s &#8211; %2$s' ),
 505                  get_option( 'blogname' ),
 506                  '<em>' . get_option( 'blogdescription' ) . '</em>'
 507              );
 508              echo '</p>';
 509              restore_current_blog();
 510          }
 511      }
 512  
 513      /**
 514       * Handles the lastupdated column output.
 515       *
 516       * @since 4.3.0
 517       *
 518       * @global string $mode List table view mode.
 519       *
 520       * @param array $blog Current site.
 521       */
 522  	public function column_lastupdated( $blog ) {
 523          global $mode;
 524  
 525          if ( 'list' === $mode ) {
 526              $date = __( 'Y/m/d' );
 527          } else {
 528              $date = __( 'Y/m/d g:i:s a' );
 529          }
 530  
 531          if ( '0000-00-00 00:00:00' === $blog['last_updated'] ) {
 532              _e( 'Never' );
 533          } else {
 534              echo mysql2date( $date, $blog['last_updated'] );
 535          }
 536      }
 537  
 538      /**
 539       * Handles the registered column output.
 540       *
 541       * @since 4.3.0
 542       *
 543       * @global string $mode List table view mode.
 544       *
 545       * @param array $blog Current site.
 546       */
 547  	public function column_registered( $blog ) {
 548          global $mode;
 549  
 550          if ( 'list' === $mode ) {
 551              $date = __( 'Y/m/d' );
 552          } else {
 553              $date = __( 'Y/m/d g:i:s a' );
 554          }
 555  
 556          if ( '0000-00-00 00:00:00' === $blog['registered'] ) {
 557              echo '&#x2014;';
 558          } else {
 559              echo mysql2date( $date, $blog['registered'] );
 560          }
 561      }
 562  
 563      /**
 564       * Handles the users column output.
 565       *
 566       * @since 4.3.0
 567       *
 568       * @param array $blog Current site.
 569       */
 570  	public function column_users( $blog ) {
 571          $user_count = wp_cache_get( $blog['blog_id'] . '_user_count', 'blog-details' );
 572          if ( ! $user_count ) {
 573              $blog_users = new WP_User_Query(
 574                  array(
 575                      'blog_id'     => $blog['blog_id'],
 576                      'fields'      => 'ID',
 577                      'number'      => 1,
 578                      'count_total' => true,
 579                  )
 580              );
 581              $user_count = $blog_users->get_total();
 582              wp_cache_set( $blog['blog_id'] . '_user_count', $user_count, 'blog-details', 12 * HOUR_IN_SECONDS );
 583          }
 584  
 585          printf(
 586              '<a href="%1$s">%2$s</a>',
 587              esc_url( network_admin_url( 'site-users.php?id=' . $blog['blog_id'] ) ),
 588              number_format_i18n( $user_count )
 589          );
 590      }
 591  
 592      /**
 593       * Handles the plugins column output.
 594       *
 595       * @since 4.3.0
 596       *
 597       * @param array $blog Current site.
 598       */
 599  	public function column_plugins( $blog ) {
 600          if ( has_filter( 'wpmublogsaction' ) ) {
 601              /**
 602               * Fires inside the auxiliary 'Actions' column of the Sites list table.
 603               *
 604               * By default this column is hidden unless something is hooked to the action.
 605               *
 606               * @since MU (3.0.0)
 607               *
 608               * @param int $blog_id The site ID.
 609               */
 610              do_action( 'wpmublogsaction', $blog['blog_id'] );
 611          }
 612      }
 613  
 614      /**
 615       * Handles output for the default column.
 616       *
 617       * @since 4.3.0
 618       * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
 619       *
 620       * @param array  $item        Current site.
 621       * @param string $column_name Current column name.
 622       */
 623  	public function column_default( $item, $column_name ) {
 624          // Restores the more descriptive, specific name for use within this method.
 625          $blog = $item;
 626  
 627          /**
 628           * Fires for each registered custom column in the Sites list table.
 629           *
 630           * @since 3.1.0
 631           *
 632           * @param string $column_name The name of the column to display.
 633           * @param int    $blog_id     The site ID.
 634           */
 635          do_action( 'manage_sites_custom_column', $column_name, $blog['blog_id'] );
 636      }
 637  
 638      /**
 639       * Generates the list table rows.
 640       *
 641       * @since 3.1.0
 642       */
 643  	public function display_rows() {
 644          foreach ( $this->items as $blog ) {
 645              $blog  = $blog->to_array();
 646              $class = '';
 647              reset( $this->status_list );
 648  
 649              foreach ( $this->status_list as $status => $col ) {
 650                  if ( '1' === $blog[ $status ] ) {
 651                      $class = " class='{$col[0]}'";
 652                  }
 653              }
 654  
 655              echo "<tr{$class}>";
 656  
 657              $this->single_row_columns( $blog );
 658  
 659              echo '</tr>';
 660          }
 661      }
 662  
 663      /**
 664       * Determines whether to output comma-separated site states.
 665       *
 666       * @since 5.3.0
 667       *
 668       * @param array<string, mixed> $site An array of site data.
 669       */
 670  	protected function site_states( $site ) {
 671          $site_states = array();
 672  
 673          // $site is still an array, so get the object.
 674          $_site = WP_Site::get_instance( $site['blog_id'] );
 675  
 676          if ( is_main_site( $_site->id ) ) {
 677              $site_states['main'] = __( 'Main' );
 678          }
 679  
 680          reset( $this->status_list );
 681  
 682          $site_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
 683          foreach ( $this->status_list as $status => $col ) {
 684              if ( '1' === $_site->{$status} && $site_status !== $status ) {
 685                  $site_states[ $col[0] ] = $col[1];
 686              }
 687          }
 688  
 689          /**
 690           * Filters the default site display states for items in the Sites list table.
 691           *
 692           * @since 5.3.0
 693           *
 694           * @param string[] $site_states An array of site states. Default 'Main',
 695           *                              'Archived', 'Mature', 'Spam', 'Flagged for Deletion'.
 696           * @param WP_Site  $site        The current site object.
 697           */
 698          $site_states = apply_filters( 'display_site_states', $site_states, $_site );
 699  
 700          if ( ! empty( $site_states ) ) {
 701              $state_count = count( $site_states );
 702              $separator   = wp_get_list_item_separator();
 703  
 704              $i = 0;
 705  
 706              echo ' &mdash; ';
 707  
 708              foreach ( $site_states as $state ) {
 709                  ++$i;
 710  
 711                  $suffix = ( $i < $state_count ) ? $separator : '';
 712  
 713                  echo "<span class='post-state'>{$state}{$suffix}</span>";
 714              }
 715          }
 716      }
 717  
 718      /**
 719       * Gets the name of the default primary column.
 720       *
 721       * @since 4.3.0
 722       *
 723       * @return string Name of the default primary column, in this case, 'blogname'.
 724       */
 725  	protected function get_default_primary_column_name() {
 726          return 'blogname';
 727      }
 728  
 729      /**
 730       * Generates and displays row action links.
 731       *
 732       * @since 4.3.0
 733       * @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
 734       *
 735       * @param array  $item        Site being acted upon.
 736       * @param string $column_name Current column name.
 737       * @param string $primary     Primary column name.
 738       * @return string Row actions output for sites in Multisite, or an empty string
 739       *                if the current column is not the primary column.
 740       */
 741  	protected function handle_row_actions( $item, $column_name, $primary ) {
 742          if ( $primary !== $column_name ) {
 743              return '';
 744          }
 745  
 746          // Restores the more descriptive, specific name for use within this method.
 747          $blog = $item;
 748  
 749          $blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
 750  
 751          // Preordered.
 752          $actions = array(
 753              'edit'       => '',
 754              'backend'    => '',
 755              'activate'   => '',
 756              'deactivate' => '',
 757              'archive'    => '',
 758              'unarchive'  => '',
 759              'spam'       => '',
 760              'unspam'     => '',
 761              'delete'     => '',
 762              'visit'      => '',
 763          );
 764  
 765          $actions['edit'] = sprintf(
 766              '<a href="%1$s">%2$s</a>',
 767              esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ),
 768              __( 'Edit' )
 769          );
 770  
 771          $actions['backend'] = sprintf(
 772              '<a href="%1$s" class="edit">%2$s</a>',
 773              esc_url( get_admin_url( $blog['blog_id'] ) ),
 774              __( 'Dashboard' )
 775          );
 776  
 777          if ( ! is_main_site( $blog['blog_id'] ) ) {
 778              if ( '1' === $blog['deleted'] ) {
 779                  $actions['activate'] = sprintf(
 780                      '<a href="%1$s">%2$s</a>',
 781                      esc_url(
 782                          wp_nonce_url(
 783                              network_admin_url( 'sites.php?action=confirm&amp;action2=activateblog&amp;id=' . $blog['blog_id'] ),
 784                              'activateblog_' . $blog['blog_id']
 785                          )
 786                      ),
 787                      _x( 'Remove Deletion Flag', 'site' )
 788                  );
 789              } else {
 790                  $actions['deactivate'] = sprintf(
 791                      '<a href="%1$s">%2$s</a>',
 792                      esc_url(
 793                          wp_nonce_url(
 794                              network_admin_url( 'sites.php?action=confirm&amp;action2=deactivateblog&amp;id=' . $blog['blog_id'] ),
 795                              'deactivateblog_' . $blog['blog_id']
 796                          )
 797                      ),
 798                      __( 'Flag for Deletion' )
 799                  );
 800              }
 801  
 802              if ( '1' === $blog['archived'] ) {
 803                  $actions['unarchive'] = sprintf(
 804                      '<a href="%1$s">%2$s</a>',
 805                      esc_url(
 806                          wp_nonce_url(
 807                              network_admin_url( 'sites.php?action=confirm&amp;action2=unarchiveblog&amp;id=' . $blog['blog_id'] ),
 808                              'unarchiveblog_' . $blog['blog_id']
 809                          )
 810                      ),
 811                      __( 'Unarchive' )
 812                  );
 813              } else {
 814                  $actions['archive'] = sprintf(
 815                      '<a href="%1$s">%2$s</a>',
 816                      esc_url(
 817                          wp_nonce_url(
 818                              network_admin_url( 'sites.php?action=confirm&amp;action2=archiveblog&amp;id=' . $blog['blog_id'] ),
 819                              'archiveblog_' . $blog['blog_id']
 820                          )
 821                      ),
 822                      _x( 'Archive', 'verb; site' )
 823                  );
 824              }
 825  
 826              if ( '1' === $blog['spam'] ) {
 827                  $actions['unspam'] = sprintf(
 828                      '<a href="%1$s">%2$s</a>',
 829                      esc_url(
 830                          wp_nonce_url(
 831                              network_admin_url( 'sites.php?action=confirm&amp;action2=unspamblog&amp;id=' . $blog['blog_id'] ),
 832                              'unspamblog_' . $blog['blog_id']
 833                          )
 834                      ),
 835                      _x( 'Not Spam', 'site' )
 836                  );
 837              } else {
 838                  $actions['spam'] = sprintf(
 839                      '<a href="%1$s">%2$s</a>',
 840                      esc_url(
 841                          wp_nonce_url(
 842                              network_admin_url( 'sites.php?action=confirm&amp;action2=spamblog&amp;id=' . $blog['blog_id'] ),
 843                              'spamblog_' . $blog['blog_id']
 844                          )
 845                      ),
 846                      _x( 'Spam', 'site' )
 847                  );
 848              }
 849  
 850              if ( current_user_can( 'delete_site', $blog['blog_id'] ) ) {
 851                  $actions['delete'] = sprintf(
 852                      '<a href="%1$s">%2$s</a>',
 853                      esc_url(
 854                          wp_nonce_url(
 855                              network_admin_url( 'sites.php?action=confirm&amp;action2=deleteblog&amp;id=' . $blog['blog_id'] ),
 856                              'deleteblog_' . $blog['blog_id']
 857                          )
 858                      ),
 859                      __( 'Delete Permanently' )
 860                  );
 861              }
 862          }
 863  
 864          $actions['visit'] = sprintf(
 865              '<a href="%1$s" rel="bookmark">%2$s</a>',
 866              esc_url( get_home_url( $blog['blog_id'], '/' ) ),
 867              __( 'Visit' )
 868          );
 869  
 870          /**
 871           * Filters the action links displayed for each site in the Sites list table.
 872           *
 873           * The 'Edit', 'Dashboard', 'Delete Permanently', and 'Visit' links are displayed by
 874           * default for each site. The site's status determines whether to show the
 875           * 'Remove Deletion Flag' or 'Flag for Deletion' link, 'Unarchive' or 'Archive' links, and
 876           * 'Not Spam' or 'Spam' link for each site.
 877           *
 878           * @since 3.1.0
 879           *
 880           * @param string[] $actions  An array of action links to be displayed.
 881           * @param int      $blog_id  The site ID.
 882           * @param string   $blogname Site path, formatted depending on whether it is a sub-domain
 883           *                           or subdirectory multisite installation.
 884           */
 885          $actions = apply_filters( 'manage_sites_action_links', array_filter( $actions ), $blog['blog_id'], $blogname );
 886  
 887          return $this->row_actions( $actions );
 888      }
 889  
 890      /**
 891       * Returns a clean label for the primary (URL) column's row header `aria-label`.
 892       *
 893       * Provides screen readers with just the site title as the row header name,
 894       * preventing them from computing the name from the full cell content.
 895       *
 896       * @since 7.1.0
 897       *
 898       * @param array $blog The current site properties array.
 899       * @return string The site title, or the site URL (domain + path) if the title is empty.
 900       */
 901  	protected function get_primary_column_aria_label( $blog ) {
 902          $blog_name = html_entity_decode( (string) get_blog_option( $blog['blog_id'], 'blogname', '' ), ENT_QUOTES, get_bloginfo( 'charset' ) );
 903          $blog_name = wp_strip_all_tags( $blog_name );
 904  
 905          // Fall back to the blog URL and path if the blog name is empty.
 906          return '' !== $blog_name ? $blog_name : untrailingslashit( $blog['domain'] . $blog['path'] );
 907      }
 908  }


Generated : Sun Oct 25 08:20:35 2026 Cross-referenced by PHPXref