[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * List Table API: WP_MS_Themes_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 themes 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_Themes_List_Table extends WP_List_Table {
  18  
  19      public $site_id;
  20      public $is_site_themes;
  21  
  22      private $has_items;
  23  
  24      /**
  25       * Whether to show the auto-updates UI.
  26       *
  27       * @since 5.5.0
  28       *
  29       * @var bool True if auto-updates UI is to be shown, false otherwise.
  30       */
  31      protected $show_autoupdates = true;
  32  
  33      /**
  34       * Constructor.
  35       *
  36       * @since 3.1.0
  37       *
  38       * @see WP_List_Table::__construct() for more information on default arguments.
  39       *
  40       * @global string $status The current theme status.
  41       * @global int    $page   The current page number.
  42       *
  43       * @param array $args An associative array of arguments.
  44       */
  45  	public function __construct( $args = array() ) {
  46          global $status, $page;
  47  
  48          parent::__construct(
  49              array(
  50                  'plural' => 'themes',
  51                  'screen' => $args['screen'] ?? null,
  52              )
  53          );
  54  
  55          $status = $_REQUEST['theme_status'] ?? 'all';
  56          if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken', 'auto-update-enabled', 'auto-update-disabled' ), true ) ) {
  57              $status = 'all';
  58          }
  59  
  60          $page = $this->get_pagenum();
  61  
  62          $this->is_site_themes = 'site-themes-network' === $this->screen->id;
  63  
  64          if ( $this->is_site_themes ) {
  65              $this->site_id = isset( $_REQUEST['id'] ) ? (int) $_REQUEST['id'] : 0;
  66          }
  67  
  68          $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'theme' ) &&
  69              ! $this->is_site_themes && current_user_can( 'update_themes' );
  70      }
  71  
  72      /**
  73       * Gets the list of CSS classes for the table tag.
  74       *
  75       * @return string[] The list of CSS classes.
  76       */
  77  	protected function get_table_classes() {
  78          // @todo Remove and add CSS for .themes.
  79          return array( 'widefat', 'plugins' );
  80      }
  81  
  82      /**
  83       * Checks if the current user has permissions to perform AJAX actions.
  84       *
  85       * @return bool True if the current user has permissions, false otherwise.
  86       */
  87  	public function ajax_user_can() {
  88          if ( $this->is_site_themes ) {
  89              return current_user_can( 'manage_sites' );
  90          } else {
  91              return current_user_can( 'manage_network_themes' );
  92          }
  93      }
  94  
  95      /**
  96       * Prepares the themes list for display.
  97       *
  98       * @global string             $status  The current theme status.
  99       * @global array<string, int> $totals  An array of theme counts for each status.
 100       * @global int                $page    The current page number.
 101       * @global string             $orderby The column to order the themes list by.
 102       * @global string             $order   The order of the themes list (ASC or DESC).
 103       * @global string             $s       The search string.
 104       */
 105  	public function prepare_items() {
 106          global $status, $totals, $page, $orderby, $order, $s;
 107  
 108          $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : '';
 109          $order   = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : '';
 110          $s       = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '';
 111  
 112          $themes = array(
 113              /**
 114               * Filters the full array of WP_Theme objects to list in the Multisite
 115               * themes list table.
 116               *
 117               * @since 3.1.0
 118               *
 119               * @param WP_Theme[] $all Array of WP_Theme objects to display in the list table.
 120               */
 121              'all'      => apply_filters( 'all_themes', wp_get_themes() ),
 122              'search'   => array(),
 123              'enabled'  => array(),
 124              'disabled' => array(),
 125              'upgrade'  => array(),
 126              'broken'   => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ),
 127          );
 128  
 129          if ( $this->show_autoupdates ) {
 130              $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
 131  
 132              $themes['auto-update-enabled']  = array();
 133              $themes['auto-update-disabled'] = array();
 134          }
 135  
 136          if ( $this->is_site_themes ) {
 137              $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' );
 138              $allowed_where   = 'site';
 139          } else {
 140              $themes_per_page = $this->get_items_per_page( 'themes_network_per_page' );
 141              $allowed_where   = 'network';
 142          }
 143  
 144          $current      = get_site_transient( 'update_themes' );
 145          $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current;
 146  
 147          foreach ( (array) $themes['all'] as $key => $theme ) {
 148              if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) {
 149                  unset( $themes['all'][ $key ] );
 150                  continue;
 151              }
 152  
 153              if ( $maybe_update && isset( $current->response[ $key ] ) ) {
 154                  $themes['all'][ $key ]->update = true;
 155                  $themes['upgrade'][ $key ]     = $themes['all'][ $key ];
 156              }
 157  
 158              $filter                    = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled';
 159              $themes[ $filter ][ $key ] = $themes['all'][ $key ];
 160  
 161              $theme_data = array(
 162                  'update_supported' => $theme->update_supported ?? true,
 163              );
 164  
 165              // Extra info if known. array_merge() ensures $theme_data has precedence if keys collide.
 166              if ( isset( $current->response[ $key ] ) ) {
 167                  $theme_data = array_merge( (array) $current->response[ $key ], $theme_data );
 168              } elseif ( isset( $current->no_update[ $key ] ) ) {
 169                  $theme_data = array_merge( (array) $current->no_update[ $key ], $theme_data );
 170              } else {
 171                  $theme_data['update_supported'] = false;
 172              }
 173  
 174              $theme->update_supported = $theme_data['update_supported'];
 175  
 176              /*
 177               * Create the expected payload for the auto_update_theme filter, this is the same data
 178               * as contained within $updates or $no_updates but used when the Theme is not known.
 179               */
 180              $filter_payload = array(
 181                  'theme'        => $key,
 182                  'new_version'  => '',
 183                  'url'          => '',
 184                  'package'      => '',
 185                  'requires'     => '',
 186                  'requires_php' => '',
 187              );
 188  
 189              $filter_payload = (object) array_merge( $filter_payload, array_intersect_key( $theme_data, $filter_payload ) );
 190  
 191              $auto_update_forced = wp_is_auto_update_forced_for_item( 'theme', null, $filter_payload );
 192  
 193              if ( ! is_null( $auto_update_forced ) ) {
 194                  $theme->auto_update_forced = $auto_update_forced;
 195              }
 196  
 197              if ( $this->show_autoupdates ) {
 198                  $enabled = in_array( $key, $auto_updates, true ) && $theme->update_supported;
 199                  if ( isset( $theme->auto_update_forced ) ) {
 200                      $enabled = (bool) $theme->auto_update_forced;
 201                  }
 202  
 203                  if ( $enabled ) {
 204                      $themes['auto-update-enabled'][ $key ] = $theme;
 205                  } else {
 206                      $themes['auto-update-disabled'][ $key ] = $theme;
 207                  }
 208              }
 209          }
 210  
 211          if ( $s ) {
 212              $status           = 'search';
 213              $themes['search'] = array_filter( array_merge( $themes['all'], $themes['broken'] ), array( $this, '_search_callback' ) );
 214          }
 215  
 216          $totals    = array();
 217          $js_themes = array();
 218          foreach ( $themes as $type => $list ) {
 219              $totals[ $type ]    = count( $list );
 220              $js_themes[ $type ] = array_keys( $list );
 221          }
 222  
 223          if ( empty( $themes[ $status ] ) && ! in_array( $status, array( 'all', 'search' ), true ) ) {
 224              $status = 'all';
 225          }
 226  
 227          $this->items = $themes[ $status ];
 228          WP_Theme::sort_by_name( $this->items );
 229  
 230          $this->has_items = ! empty( $themes['all'] );
 231          $total_this_page = $totals[ $status ];
 232  
 233          wp_localize_script(
 234              'updates',
 235              '_wpUpdatesItemCounts',
 236              array(
 237                  'themes' => $js_themes,
 238                  'totals' => wp_get_update_data(),
 239              )
 240          );
 241  
 242          if ( $orderby ) {
 243              $orderby = ucfirst( $orderby );
 244              $order   = strtoupper( $order );
 245  
 246              if ( 'Name' === $orderby ) {
 247                  if ( 'ASC' === $order ) {
 248                      $this->items = array_reverse( $this->items );
 249                  }
 250              } else {
 251                  uasort( $this->items, array( $this, '_order_callback' ) );
 252              }
 253          }
 254  
 255          $start = ( $page - 1 ) * $themes_per_page;
 256  
 257          if ( $total_this_page > $themes_per_page ) {
 258              $this->items = array_slice( $this->items, $start, $themes_per_page, true );
 259          }
 260  
 261          $this->set_pagination_args(
 262              array(
 263                  'total_items' => $total_this_page,
 264                  'per_page'    => $themes_per_page,
 265              )
 266          );
 267      }
 268  
 269      /**
 270       * Filters a theme by the search term.
 271       *
 272       * @param WP_Theme $theme The WP_Theme object to check.
 273       * @return bool True if the theme matches the search term, false otherwise.
 274       */
 275  	public function _search_callback( $theme ) {
 276          static $term = null;
 277          if ( is_null( $term ) ) {
 278              $term = wp_unslash( $_REQUEST['s'] );
 279          }
 280  
 281          foreach ( array( 'Name', 'Description', 'Author', 'Author', 'AuthorURI' ) as $field ) {
 282              // Don't mark up; Do translate.
 283              if ( false !== stripos( $theme->display( $field, false, true ), $term ) ) {
 284                  return true;
 285              }
 286          }
 287  
 288          if ( false !== stripos( $theme->get_stylesheet(), $term ) ) {
 289              return true;
 290          }
 291  
 292          if ( false !== stripos( $theme->get_template(), $term ) ) {
 293              return true;
 294          }
 295  
 296          return false;
 297      }
 298  
 299      // Not used by any core columns.
 300      /**
 301       * Compares the order of two themes by a specific field.
 302       *
 303       * @global string $orderby The column to order the themes list by.
 304       * @global string $order   The order of the themes list (ASC or DESC).
 305       *
 306       * @param WP_Theme $theme_a The first theme to compare.
 307       * @param WP_Theme $theme_b The second theme to compare.
 308       * @return int 0 if equal, -1 if the first is less than the second, 1 if more.
 309       */
 310  	public function _order_callback( $theme_a, $theme_b ) {
 311          global $orderby, $order;
 312  
 313          $a = $theme_a[ $orderby ];
 314          $b = $theme_b[ $orderby ];
 315  
 316          return 'DESC' === $order ?
 317              $b <=> $a :
 318              $a <=> $b;
 319      }
 320  
 321      /**
 322       * Displays the message when there are no items to list.
 323       */
 324  	public function no_items() {
 325          if ( $this->has_items ) {
 326              _e( 'No themes found.' );
 327          } else {
 328              _e( 'No themes are currently available.' );
 329          }
 330      }
 331  
 332      /**
 333       * Gets the list of columns for the list table.
 334       *
 335       * @return array<string, string> Array of column titles keyed by their column name.
 336       */
 337  	public function get_columns() {
 338          $columns = array(
 339              'cb'          => '<input type="checkbox" />',
 340              'name'        => __( 'Theme' ),
 341              'description' => __( 'Description' ),
 342          );
 343  
 344          if ( $this->show_autoupdates ) {
 345              $columns['auto-updates'] = __( 'Automatic Updates' );
 346          }
 347  
 348          return $columns;
 349      }
 350  
 351      /**
 352       * Gets the list of sortable columns for the list table.
 353       *
 354       * @return array<string, array<int, mixed>> An array of sortable columns.
 355       */
 356  	protected function get_sortable_columns() {
 357          return array(
 358              'name' => array( 'name', false, __( 'Theme' ), __( 'Table ordered by Theme Name.' ), 'asc' ),
 359          );
 360      }
 361  
 362      /**
 363       * Gets the name of the primary column.
 364       *
 365       * @since 4.3.0
 366       *
 367       * @return string Unalterable name of the primary column name, in this case, 'name'.
 368       */
 369  	protected function get_primary_column_name() {
 370          return 'name';
 371      }
 372  
 373      /**
 374       * Gets the list of views (statuses) for the list table.
 375       *
 376       * @global array<string, int> $totals An array of theme counts for each status.
 377       * @global string             $status The current theme status.
 378       *
 379       * @return array<string, string> The list of views.
 380       */
 381  	protected function get_views() {
 382          global $totals, $status;
 383  
 384          $status_links = array();
 385          foreach ( $totals as $type => $count ) {
 386              if ( ! $count ) {
 387                  continue;
 388              }
 389  
 390              switch ( $type ) {
 391                  case 'all':
 392                      /* translators: %s: Number of themes. */
 393                      $text = _nx(
 394                          'All <span class="count">(%s)</span>',
 395                          'All <span class="count">(%s)</span>',
 396                          $count,
 397                          'themes'
 398                      );
 399                      break;
 400                  case 'enabled':
 401                      /* translators: %s: Number of themes. */
 402                      $text = _nx(
 403                          'Enabled <span class="count">(%s)</span>',
 404                          'Enabled <span class="count">(%s)</span>',
 405                          $count,
 406                          'themes'
 407                      );
 408                      break;
 409                  case 'disabled':
 410                      /* translators: %s: Number of themes. */
 411                      $text = _nx(
 412                          'Disabled <span class="count">(%s)</span>',
 413                          'Disabled <span class="count">(%s)</span>',
 414                          $count,
 415                          'themes'
 416                      );
 417                      break;
 418                  case 'upgrade':
 419                      /* translators: %s: Number of themes. */
 420                      $text = _nx(
 421                          'Update Available <span class="count">(%s)</span>',
 422                          'Update Available <span class="count">(%s)</span>',
 423                          $count,
 424                          'themes'
 425                      );
 426                      break;
 427                  case 'broken':
 428                      /* translators: %s: Number of themes. */
 429                      $text = _nx(
 430                          'Broken <span class="count">(%s)</span>',
 431                          'Broken <span class="count">(%s)</span>',
 432                          $count,
 433                          'themes'
 434                      );
 435                      break;
 436                  case 'auto-update-enabled':
 437                      /* translators: %s: Number of themes. */
 438                      $text = _n(
 439                          'Auto-updates Enabled <span class="count">(%s)</span>',
 440                          'Auto-updates Enabled <span class="count">(%s)</span>',
 441                          $count
 442                      );
 443                      break;
 444                  case 'auto-update-disabled':
 445                      /* translators: %s: Number of themes. */
 446                      $text = _n(
 447                          'Auto-updates Disabled <span class="count">(%s)</span>',
 448                          'Auto-updates Disabled <span class="count">(%s)</span>',
 449                          $count
 450                      );
 451                      break;
 452              }
 453  
 454              if ( $this->is_site_themes ) {
 455                  $url = 'site-themes.php?id=' . $this->site_id;
 456              } else {
 457                  $url = 'themes.php';
 458              }
 459  
 460              if ( 'search' !== $type ) {
 461                  $status_links[ $type ] = array(
 462                      'url'     => esc_url( add_query_arg( 'theme_status', $type, $url ) ),
 463                      'label'   => sprintf( $text, number_format_i18n( $count ) ),
 464                      'current' => $type === $status,
 465                  );
 466              }
 467          }
 468  
 469          return $this->get_views_links( $status_links );
 470      }
 471  
 472      /**
 473       * Gets the list of bulk actions for the list table.
 474       *
 475       * @global string $status The current theme status.
 476       *
 477       * @return array<string, string> The list of bulk actions.
 478       */
 479  	protected function get_bulk_actions() {
 480          global $status;
 481  
 482          $actions = array();
 483          if ( 'enabled' !== $status ) {
 484              $actions['enable-selected'] = $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' );
 485          }
 486          if ( 'disabled' !== $status ) {
 487              $actions['disable-selected'] = $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' );
 488          }
 489          if ( ! $this->is_site_themes ) {
 490              if ( current_user_can( 'update_themes' ) ) {
 491                  $actions['update-selected'] = __( 'Update' );
 492              }
 493              if ( current_user_can( 'delete_themes' ) ) {
 494                  $actions['delete-selected'] = __( 'Delete' );
 495              }
 496          }
 497  
 498          if ( $this->show_autoupdates ) {
 499              if ( 'auto-update-enabled' !== $status ) {
 500                  $actions['enable-auto-update-selected'] = __( 'Enable Auto-updates' );
 501              }
 502  
 503              if ( 'auto-update-disabled' !== $status ) {
 504                  $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' );
 505              }
 506          }
 507  
 508          return $actions;
 509      }
 510  
 511      /**
 512       * Generates the list table rows.
 513       *
 514       * @since 3.1.0
 515       */
 516  	public function display_rows() {
 517          foreach ( $this->items as $theme ) {
 518              $this->single_row( $theme );
 519          }
 520      }
 521  
 522      /**
 523       * Handles the checkbox column output.
 524       *
 525       * @since 4.3.0
 526       * @since 5.9.0 Renamed `$theme` to `$item` to match parent class for PHP 8 named parameter support.
 527       *
 528       * @param WP_Theme $item The current WP_Theme object.
 529       */
 530  	public function column_cb( $item ) {
 531          // Restores the more descriptive, specific name for use within this method.
 532          $theme = $item;
 533  
 534          $checkbox_id = 'checkbox_' . md5( $theme->get( 'Name' ) );
 535          ?>
 536          <input type="checkbox" name="checked[]" value="<?php echo esc_attr( $theme->get_stylesheet() ); ?>" id="<?php echo $checkbox_id; ?>" />
 537          <label for="<?php echo $checkbox_id; ?>" >
 538              <span class="screen-reader-text">
 539              <?php
 540              printf(
 541                  /* translators: Hidden accessibility text. %s: Theme name */
 542                  __( 'Select %s' ),
 543                  $theme->display( 'Name' )
 544              );
 545              ?>
 546              </span>
 547          </label>
 548          <?php
 549      }
 550  
 551      /**
 552       * Handles the name column output.
 553       *
 554       * @since 4.3.0
 555       *
 556       * @global string $status The current theme status.
 557       * @global int    $page   The current page number.
 558       * @global string $s      The search string.
 559       *
 560       * @param WP_Theme $theme The current WP_Theme object.
 561       */
 562  	public function column_name( $theme ) {
 563          global $status, $page, $s;
 564  
 565          $context = $status;
 566  
 567          if ( $this->is_site_themes ) {
 568              $url     = "site-themes.php?id={$this->site_id}&amp;";
 569              $allowed = $theme->is_allowed( 'site', $this->site_id );
 570          } else {
 571              $url     = 'themes.php?';
 572              $allowed = $theme->is_allowed( 'network' );
 573          }
 574  
 575          // Pre-order.
 576          $actions = array(
 577              'enable'  => '',
 578              'disable' => '',
 579              'delete'  => '',
 580          );
 581  
 582          $stylesheet = $theme->get_stylesheet();
 583          $theme_key  = urlencode( $stylesheet );
 584  
 585          if ( ! $allowed ) {
 586              if ( ! $theme->errors() ) {
 587                  $url = add_query_arg(
 588                      array(
 589                          'action' => 'enable',
 590                          'theme'  => $theme_key,
 591                          'paged'  => $page,
 592                          's'      => $s,
 593                      ),
 594                      $url
 595                  );
 596  
 597                  if ( $this->is_site_themes ) {
 598                      /* translators: %s: Theme name. */
 599                      $aria_label = sprintf( __( 'Enable %s' ), $theme->display( 'Name' ) );
 600                  } else {
 601                      /* translators: %s: Theme name. */
 602                      $aria_label = sprintf( __( 'Network Enable %s' ), $theme->display( 'Name' ) );
 603                  }
 604  
 605                  $actions['enable'] = sprintf(
 606                      '<a href="%s" class="edit" aria-label="%s">%s</a>',
 607                      esc_url( wp_nonce_url( $url, 'enable-theme_' . $stylesheet ) ),
 608                      esc_attr( $aria_label ),
 609                      ( $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ) )
 610                  );
 611              }
 612          } else {
 613              $url = add_query_arg(
 614                  array(
 615                      'action' => 'disable',
 616                      'theme'  => $theme_key,
 617                      'paged'  => $page,
 618                      's'      => $s,
 619                  ),
 620                  $url
 621              );
 622  
 623              if ( $this->is_site_themes ) {
 624                  /* translators: %s: Theme name. */
 625                  $aria_label = sprintf( __( 'Disable %s' ), $theme->display( 'Name' ) );
 626              } else {
 627                  /* translators: %s: Theme name. */
 628                  $aria_label = sprintf( __( 'Network Disable %s' ), $theme->display( 'Name' ) );
 629              }
 630  
 631              $actions['disable'] = sprintf(
 632                  '<a href="%s" aria-label="%s">%s</a>',
 633                  esc_url( wp_nonce_url( $url, 'disable-theme_' . $stylesheet ) ),
 634                  esc_attr( $aria_label ),
 635                  ( $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ) )
 636              );
 637          }
 638  
 639          if ( ! $allowed && ! $this->is_site_themes
 640              && current_user_can( 'delete_themes' )
 641              && get_option( 'stylesheet' ) !== $stylesheet
 642              && get_option( 'template' ) !== $stylesheet
 643          ) {
 644              $url = add_query_arg(
 645                  array(
 646                      'action'       => 'delete-selected',
 647                      'checked[]'    => $theme_key,
 648                      'theme_status' => $context,
 649                      'paged'        => $page,
 650                      's'            => $s,
 651                  ),
 652                  'themes.php'
 653              );
 654  
 655              /* translators: %s: Theme name. */
 656              $aria_label = sprintf( _x( 'Delete %s', 'theme' ), $theme->display( 'Name' ) );
 657  
 658              $actions['delete'] = sprintf(
 659                  '<a href="%s" class="delete" aria-label="%s">%s</a>',
 660                  esc_url( wp_nonce_url( $url, 'bulk-themes' ) ),
 661                  esc_attr( $aria_label ),
 662                  __( 'Delete' )
 663              );
 664          }
 665          /**
 666           * Filters the action links displayed for each theme in the Multisite
 667           * themes list table.
 668           *
 669           * The action links displayed are determined by the theme's status, and
 670           * which Multisite themes list table is being displayed - the Network
 671           * themes list table (themes.php), which displays all installed themes,
 672           * or the Site themes list table (site-themes.php), which displays the
 673           * non-network enabled themes when editing a site in the Network admin.
 674           *
 675           * The default action links for the Network themes list table include
 676           * 'Network Enable', 'Network Disable', and 'Delete'.
 677           *
 678           * The default action links for the Site themes list table include
 679           * 'Enable', and 'Disable'.
 680           *
 681           * @since 2.8.0
 682           *
 683           * @param string[] $actions An array of action links.
 684           * @param WP_Theme $theme   The current WP_Theme object.
 685           * @param string   $context Status of the theme, one of 'all', 'enabled', or 'disabled'.
 686           */
 687          $actions = apply_filters( 'theme_action_links', array_filter( $actions ), $theme, $context );
 688  
 689          /**
 690           * Filters the action links of a specific theme in the Multisite themes
 691           * list table.
 692           *
 693           * The dynamic portion of the hook name, `$stylesheet`, refers to the
 694           * directory name of the theme, which in most cases is synonymous
 695           * with the template name.
 696           *
 697           * @since 3.1.0
 698           *
 699           * @param string[] $actions An array of action links.
 700           * @param WP_Theme $theme   The current WP_Theme object.
 701           * @param string   $context Status of the theme, one of 'all', 'enabled', or 'disabled'.
 702           */
 703          $actions = apply_filters( "theme_action_links_{$stylesheet}", $actions, $theme, $context );
 704  
 705          echo $this->row_actions( $actions, true );
 706      }
 707  
 708      /**
 709       * Handles the description column output.
 710       *
 711       * @since 4.3.0
 712       *
 713       * @global string             $status The current theme status.
 714       * @global array<string, int> $totals An array of theme counts for each status.
 715       *
 716       * @param WP_Theme $theme The current WP_Theme object.
 717       */
 718  	public function column_description( $theme ) {
 719          global $status, $totals;
 720  
 721          if ( $theme->errors() ) {
 722              $pre = 'broken' === $status ? '<strong class="error-message">' . __( 'Broken Theme:' ) . '</strong> ' : '';
 723              wp_admin_notice(
 724                  $pre . $theme->errors()->get_error_message(),
 725                  array(
 726                      'type'               => 'error',
 727                      'additional_classes' => 'inline',
 728                  )
 729              );
 730          }
 731  
 732          if ( $this->is_site_themes ) {
 733              $allowed = $theme->is_allowed( 'site', $this->site_id );
 734          } else {
 735              $allowed = $theme->is_allowed( 'network' );
 736          }
 737  
 738          $class = ! $allowed ? 'inactive' : 'active';
 739          if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) ) {
 740              $class .= ' update';
 741          }
 742  
 743          echo "<div class='theme-description'><p>" . $theme->display( 'Description' ) . "</p></div>
 744              <div class='$class second theme-version-author-uri'>";
 745  
 746          $stylesheet = $theme->get_stylesheet();
 747          $theme_meta = array();
 748  
 749          if ( $theme->get( 'Version' ) ) {
 750              /* translators: %s: Theme version. */
 751              $theme_meta[] = sprintf( __( 'Version %s' ), $theme->display( 'Version' ) );
 752          }
 753  
 754          /* translators: %s: Theme author. */
 755          $theme_meta[] = sprintf( __( 'By %s' ), $theme->display( 'Author' ) );
 756  
 757          if ( $theme->get( 'ThemeURI' ) ) {
 758              /* translators: %s: Theme name. */
 759              $aria_label = sprintf( __( 'Visit theme site for %s' ), $theme->display( 'Name' ) );
 760  
 761              $theme_meta[] = sprintf(
 762                  '<a href="%s" aria-label="%s">%s</a>',
 763                  $theme->display( 'ThemeURI' ),
 764                  esc_attr( $aria_label ),
 765                  __( 'Visit Theme Site' )
 766              );
 767          }
 768  
 769          if ( $theme->parent() ) {
 770              $theme_meta[] = sprintf(
 771                  /* translators: %s: Theme name. */
 772                  __( 'Child theme of %s' ),
 773                  '<strong>' . $theme->parent()->display( 'Name' ) . '</strong>'
 774              );
 775          }
 776  
 777          /**
 778           * Filters the array of row meta for each theme in the Multisite themes
 779           * list table.
 780           *
 781           * @since 3.1.0
 782           *
 783           * @param string[] $theme_meta An array of the theme's metadata, including
 784           *                             the version, author, and theme URI.
 785           * @param string   $stylesheet Directory name of the theme.
 786           * @param WP_Theme $theme      WP_Theme object.
 787           * @param string   $status     Status of the theme.
 788           */
 789          $theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $stylesheet, $theme, $status );
 790  
 791          echo implode( ' | ', $theme_meta );
 792  
 793          echo '</div>';
 794      }
 795  
 796      /**
 797       * Handles the auto-updates column output.
 798       *
 799       * @since 5.5.0
 800       *
 801       * @global string $status The current theme status.
 802       * @global int    $page   The current page number.
 803       *
 804       * @param WP_Theme $theme The current WP_Theme object.
 805       */
 806  	public function column_autoupdates( $theme ) {
 807          global $status, $page;
 808  
 809          static $auto_updates, $available_updates;
 810  
 811          if ( ! $auto_updates ) {
 812              $auto_updates = (array) get_site_option( 'auto_update_themes', array() );
 813          }
 814          if ( ! $available_updates ) {
 815              $available_updates = get_site_transient( 'update_themes' );
 816          }
 817  
 818          $stylesheet = $theme->get_stylesheet();
 819  
 820          if ( isset( $theme->auto_update_forced ) ) {
 821              if ( $theme->auto_update_forced ) {
 822                  // Forced on.
 823                  $text = __( 'Auto-updates enabled' );
 824              } else {
 825                  $text = __( 'Auto-updates disabled' );
 826              }
 827              $action     = 'unavailable';
 828              $time_class = ' hidden';
 829          } elseif ( empty( $theme->update_supported ) ) {
 830              $text       = '';
 831              $action     = 'unavailable';
 832              $time_class = ' hidden';
 833          } elseif ( in_array( $stylesheet, $auto_updates, true ) ) {
 834              $text       = __( 'Disable auto-updates' );
 835              $action     = 'disable';
 836              $time_class = '';
 837          } else {
 838              $text       = __( 'Enable auto-updates' );
 839              $action     = 'enable';
 840              $time_class = ' hidden';
 841          }
 842  
 843          $query_args = array(
 844              'action'       => "{$action}-auto-update",
 845              'theme'        => $stylesheet,
 846              'paged'        => $page,
 847              'theme_status' => $status,
 848          );
 849  
 850          $url = add_query_arg( $query_args, 'themes.php' );
 851  
 852          if ( 'unavailable' === $action ) {
 853              $html[] = '<span class="label">' . $text . '</span>';
 854          } else {
 855              $html[] = sprintf(
 856                  '<a href="%s" class="toggle-auto-update aria-button-if-js" data-wp-action="%s">',
 857                  wp_nonce_url( $url, 'updates' ),
 858                  $action
 859              );
 860  
 861              $html[] = '<span class="dashicons dashicons-update spin hidden" aria-hidden="true"></span>';
 862              $html[] = '<span class="label">' . $text . '</span>';
 863              $html[] = '</a>';
 864  
 865          }
 866  
 867          if ( isset( $available_updates->response[ $stylesheet ] ) ) {
 868              $html[] = sprintf(
 869                  '<div class="auto-update-time%s">%s</div>',
 870                  $time_class,
 871                  wp_get_auto_update_message()
 872              );
 873          }
 874  
 875          $html = implode( '', $html );
 876  
 877          /**
 878           * Filters the HTML of the auto-updates setting for each theme in the Themes list table.
 879           *
 880           * @since 5.5.0
 881           *
 882           * @param string   $html       The HTML for theme's auto-update setting, including
 883           *                             toggle auto-update action link and time to next update.
 884           * @param string   $stylesheet Directory name of the theme.
 885           * @param WP_Theme $theme      WP_Theme object.
 886           */
 887          echo apply_filters( 'theme_auto_update_setting_html', $html, $stylesheet, $theme );
 888  
 889          wp_admin_notice(
 890              '',
 891              array(
 892                  'type'               => 'error',
 893                  'additional_classes' => array( 'notice-alt', 'inline', 'hidden' ),
 894              )
 895          );
 896      }
 897  
 898      /**
 899       * Handles default column output.
 900       *
 901       * @since 4.3.0
 902       * @since 5.9.0 Renamed `$theme` to `$item` to match parent class for PHP 8 named parameter support.
 903       *
 904       * @param WP_Theme $item        The current WP_Theme object.
 905       * @param string   $column_name The current column name.
 906       */
 907  	public function column_default( $item, $column_name ) {
 908          // Restores the more descriptive, specific name for use within this method.
 909          $theme = $item;
 910  
 911          $stylesheet = $theme->get_stylesheet();
 912  
 913          /**
 914           * Fires inside each custom column of the Multisite themes list table.
 915           *
 916           * @since 3.1.0
 917           *
 918           * @param string   $column_name Name of the column.
 919           * @param string   $stylesheet  Directory name of the theme.
 920           * @param WP_Theme $theme       Current WP_Theme object.
 921           */
 922          do_action( 'manage_themes_custom_column', $column_name, $stylesheet, $theme );
 923      }
 924  
 925      /**
 926       * Handles the output for a single table row.
 927       *
 928       * @since 4.3.0
 929       *
 930       * @param WP_Theme $item The current WP_Theme object.
 931       */
 932  	public function single_row_columns( $item ) {
 933          list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
 934  
 935          foreach ( $columns as $column_name => $column_display_name ) {
 936              $extra_classes = '';
 937              if ( in_array( $column_name, $hidden, true ) ) {
 938                  $extra_classes .= ' hidden';
 939              }
 940  
 941              switch ( $column_name ) {
 942                  case 'cb':
 943                      echo '<th scope="row" class="check-column">';
 944  
 945                      $this->column_cb( $item );
 946  
 947                      echo '</th>';
 948                      break;
 949  
 950                  case 'name':
 951                      $active_theme_label = '';
 952  
 953                      /* The presence of the site_id property means that this is a subsite view and a label for the active theme needs to be added */
 954                      if ( ! empty( $this->site_id ) ) {
 955                          $stylesheet = get_blog_option( $this->site_id, 'stylesheet' );
 956                          $template   = get_blog_option( $this->site_id, 'template' );
 957  
 958                          /* Add a label for the active template */
 959                          if ( $item->get_template() === $template ) {
 960                              $active_theme_label = ' &mdash; ' . __( 'Active Theme' );
 961                          }
 962  
 963                          /* In case this is a child theme, label it properly */
 964                          if ( $stylesheet !== $template && $item->get_stylesheet() === $stylesheet ) {
 965                              $active_theme_label = ' &mdash; ' . __( 'Active Child Theme' );
 966                          }
 967                      }
 968  
 969                      echo "<td class='theme-title column-primary{$extra_classes}'><strong>" . $item->display( 'Name' ) . $active_theme_label . '</strong>';
 970  
 971                      $this->column_name( $item );
 972  
 973                      echo '</td>';
 974                      break;
 975  
 976                  case 'description':
 977                      echo "<td class='column-description desc{$extra_classes}'>";
 978  
 979                      $this->column_description( $item );
 980  
 981                      echo '</td>';
 982                      break;
 983  
 984                  case 'auto-updates':
 985                      echo "<td class='column-auto-updates{$extra_classes}'>";
 986  
 987                      $this->column_autoupdates( $item );
 988  
 989                      echo '</td>';
 990                      break;
 991                  default:
 992                      echo "<td class='$column_name column-$column_name{$extra_classes}'>";
 993  
 994                      $this->column_default( $item, $column_name );
 995  
 996                      echo '</td>';
 997                      break;
 998              }
 999          }
1000      }
1001  
1002      /**
1003       * Handles the output for a single table row.
1004       *
1005       * @global string             $status The current theme status.
1006       * @global array<string, int> $totals An array of theme counts for each status.
1007       *
1008       * @param WP_Theme $theme The current WP_Theme object.
1009       */
1010  	public function single_row( $theme ) {
1011          global $status, $totals;
1012  
1013          if ( $this->is_site_themes ) {
1014              $allowed = $theme->is_allowed( 'site', $this->site_id );
1015          } else {
1016              $allowed = $theme->is_allowed( 'network' );
1017          }
1018  
1019          $stylesheet = $theme->get_stylesheet();
1020  
1021          $class = ! $allowed ? 'inactive' : 'active';
1022          if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) ) {
1023              $class .= ' update';
1024          }
1025  
1026          printf(
1027              '<tr class="%s" data-slug="%s">',
1028              esc_attr( $class ),
1029              esc_attr( $stylesheet )
1030          );
1031  
1032          $this->single_row_columns( $theme );
1033  
1034          echo '</tr>';
1035  
1036          if ( $this->is_site_themes ) {
1037              remove_action( "after_theme_row_$stylesheet", 'wp_theme_update_row' );
1038          }
1039  
1040          /**
1041           * Fires after each row in the Multisite themes list table.
1042           *
1043           * @since 3.1.0
1044           *
1045           * @param string   $stylesheet Directory name of the theme.
1046           * @param WP_Theme $theme      Current WP_Theme object.
1047           * @param string   $status     Status of the theme.
1048           */
1049          do_action( 'after_theme_row', $stylesheet, $theme, $status );
1050  
1051          /**
1052           * Fires after each specific row in the Multisite themes list table.
1053           *
1054           * The dynamic portion of the hook name, `$stylesheet`, refers to the
1055           * directory name of the theme, most often synonymous with the template
1056           * name of the theme.
1057           *
1058           * @since 3.5.0
1059           *
1060           * @param string   $stylesheet Directory name of the theme.
1061           * @param WP_Theme $theme      Current WP_Theme object.
1062           * @param string   $status     Status of the theme.
1063           */
1064          do_action( "after_theme_row_{$stylesheet}", $stylesheet, $theme, $status );
1065      }
1066  }


Generated : Tue Apr 21 08:20:12 2026 Cross-referenced by PHPXref