[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-wp-plugin-install-list-table.php (source)

   1  <?php
   2  /**
   3   * List Table API: WP_Plugin_Install_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 plugins to install in a list table.
  12   *
  13   * @since 3.1.0
  14   *
  15   * @see WP_List_Table
  16   */
  17  class WP_Plugin_Install_List_Table extends WP_List_Table {
  18  
  19      public $order   = 'ASC';
  20      public $orderby = null;
  21      public $groups  = array();
  22  
  23      private $error;
  24  
  25      /**
  26       * @return bool
  27       */
  28  	public function ajax_user_can() {
  29          return current_user_can( 'install_plugins' );
  30      }
  31  
  32      /**
  33       * Returns the list of known plugins.
  34       *
  35       * Uses the transient data from the updates API to determine the known
  36       * installed plugins.
  37       *
  38       * @since 4.9.0
  39       *
  40       * @return array
  41       */
  42  	protected function get_installed_plugins() {
  43          $plugins = array();
  44  
  45          $plugin_info = get_site_transient( 'update_plugins' );
  46          if ( isset( $plugin_info->no_update ) ) {
  47              foreach ( $plugin_info->no_update as $plugin ) {
  48                  if ( isset( $plugin->slug ) ) {
  49                      $plugin->upgrade          = false;
  50                      $plugins[ $plugin->slug ] = $plugin;
  51                  }
  52              }
  53          }
  54  
  55          if ( isset( $plugin_info->response ) ) {
  56              foreach ( $plugin_info->response as $plugin ) {
  57                  if ( isset( $plugin->slug ) ) {
  58                      $plugin->upgrade          = true;
  59                      $plugins[ $plugin->slug ] = $plugin;
  60                  }
  61              }
  62          }
  63  
  64          return $plugins;
  65      }
  66  
  67      /**
  68       * Returns a list of slugs of installed plugins, if known.
  69       *
  70       * Uses the transient data from the updates API to determine the slugs of
  71       * known installed plugins. This might be better elsewhere, perhaps even
  72       * within get_plugins().
  73       *
  74       * @since 4.0.0
  75       *
  76       * @return array
  77       */
  78  	protected function get_installed_plugin_slugs() {
  79          return array_keys( $this->get_installed_plugins() );
  80      }
  81  
  82      /**
  83       * @global array  $tabs
  84       * @global string $tab
  85       * @global int    $paged
  86       * @global string $type
  87       * @global string $term
  88       */
  89  	public function prepare_items() {
  90          require_once  ABSPATH . 'wp-admin/includes/plugin-install.php';
  91  
  92          global $tabs, $tab, $paged, $type, $term;
  93  
  94          $tab = ! empty( $_REQUEST['tab'] ) ? sanitize_text_field( $_REQUEST['tab'] ) : '';
  95  
  96          $paged = $this->get_pagenum();
  97  
  98          $per_page = 36;
  99  
 100          // These are the tabs which are shown on the page.
 101          $tabs = array();
 102  
 103          if ( 'search' === $tab ) {
 104              $tabs['search'] = __( 'Search Results' );
 105          }
 106  
 107          if ( 'beta' === $tab || str_contains( get_bloginfo( 'version' ), '-' ) ) {
 108              $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' );
 109          }
 110  
 111          $tabs['featured']    = _x( 'Featured', 'Plugin Installer' );
 112          $tabs['popular']     = _x( 'Popular', 'Plugin Installer' );
 113          $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' );
 114          $tabs['favorites']   = _x( 'Favorites', 'Plugin Installer' );
 115  
 116          if ( current_user_can( 'upload_plugins' ) ) {
 117              /*
 118               * No longer a real tab. Here for filter compatibility.
 119               * Gets skipped in get_views().
 120               */
 121              $tabs['upload'] = __( 'Upload Plugin' );
 122          }
 123  
 124          $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item.
 125  
 126          /**
 127           * Filters the tabs shown on the Add Plugins screen.
 128           *
 129           * @since 2.7.0
 130           *
 131           * @param string[] $tabs The tabs shown on the Add Plugins screen. Defaults include
 132           *                       'featured', 'popular', 'recommended', 'favorites', and 'upload'.
 133           */
 134          $tabs = apply_filters( 'install_plugins_tabs', $tabs );
 135  
 136          /**
 137           * Filters tabs not associated with a menu item on the Add Plugins screen.
 138           *
 139           * @since 2.7.0
 140           *
 141           * @param string[] $nonmenu_tabs The tabs that don't have a menu item on the Add Plugins screen.
 142           */
 143          $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs );
 144  
 145          // If a non-valid menu tab has been selected, And it's not a non-menu action.
 146          if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs, true ) ) ) {
 147              $tab = key( $tabs );
 148          }
 149  
 150          $installed_plugins = $this->get_installed_plugins();
 151  
 152          $args = array(
 153              'page'     => $paged,
 154              'per_page' => $per_page,
 155              // Send the locale to the API so it can provide context-sensitive results.
 156              'locale'   => get_user_locale(),
 157          );
 158  
 159          switch ( $tab ) {
 160              case 'search':
 161                  $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
 162                  $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : '';
 163  
 164                  switch ( $type ) {
 165                      case 'tag':
 166                          $args['tag'] = sanitize_title_with_dashes( $term );
 167                          break;
 168                      case 'term':
 169                          $args['search'] = $term;
 170                          break;
 171                      case 'author':
 172                          $args['author'] = $term;
 173                          break;
 174                  }
 175  
 176                  break;
 177  
 178              case 'featured':
 179              case 'popular':
 180              case 'new':
 181              case 'beta':
 182                  $args['browse'] = $tab;
 183                  break;
 184              case 'recommended':
 185                  $args['browse'] = $tab;
 186                  // Include the list of installed plugins so we can get relevant results.
 187                  $args['installed_plugins'] = array_keys( $installed_plugins );
 188                  break;
 189  
 190              case 'favorites':
 191                  $action = 'save_wporg_username_' . get_current_user_id();
 192                  if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action ) ) {
 193                      $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' );
 194  
 195                      // If the save url parameter is passed with a falsey value, don't save the favorite user.
 196                      if ( ! isset( $_GET['save'] ) || $_GET['save'] ) {
 197                          update_user_meta( get_current_user_id(), 'wporg_favorites', $user );
 198                      }
 199                  } else {
 200                      $user = get_user_option( 'wporg_favorites' );
 201                  }
 202                  if ( $user ) {
 203                      $args['user'] = $user;
 204                  } else {
 205                      $args = false;
 206                  }
 207  
 208                  add_action( 'install_plugins_favorites', 'install_plugins_favorites_form', 9, 0 );
 209                  break;
 210  
 211              default:
 212                  $args = false;
 213                  break;
 214          }
 215  
 216          /**
 217           * Filters API request arguments for each Add Plugins screen tab.
 218           *
 219           * The dynamic portion of the hook name, `$tab`, refers to the plugin install tabs.
 220           *
 221           * Possible hook names include:
 222           *
 223           *  - `install_plugins_table_api_args_favorites`
 224           *  - `install_plugins_table_api_args_featured`
 225           *  - `install_plugins_table_api_args_popular`
 226           *  - `install_plugins_table_api_args_recommended`
 227           *  - `install_plugins_table_api_args_upload`
 228           *  - `install_plugins_table_api_args_search`
 229           *  - `install_plugins_table_api_args_beta`
 230           *
 231           * @since 3.7.0
 232           *
 233           * @param array|false $args Plugin install API arguments.
 234           */
 235          $args = apply_filters( "install_plugins_table_api_args_{$tab}", $args );
 236  
 237          if ( ! $args ) {
 238              return;
 239          }
 240  
 241          $api = plugins_api( 'query_plugins', $args );
 242  
 243          if ( is_wp_error( $api ) ) {
 244              $this->error = $api;
 245              return;
 246          }
 247  
 248          $this->items = $api->plugins;
 249  
 250          if ( $this->orderby ) {
 251              uasort( $this->items, array( $this, 'order_callback' ) );
 252          }
 253  
 254          $this->set_pagination_args(
 255              array(
 256                  'total_items' => $api->info['results'],
 257                  'per_page'    => $args['per_page'],
 258              )
 259          );
 260  
 261          if ( isset( $api->info['groups'] ) ) {
 262              $this->groups = $api->info['groups'];
 263          }
 264  
 265          if ( $installed_plugins ) {
 266              $js_plugins = array_fill_keys(
 267                  array( 'all', 'search', 'active', 'inactive', 'recently_activated', 'mustuse', 'dropins' ),
 268                  array()
 269              );
 270  
 271              $js_plugins['all'] = array_values( wp_list_pluck( $installed_plugins, 'plugin' ) );
 272              $upgrade_plugins   = wp_filter_object_list( $installed_plugins, array( 'upgrade' => true ), 'and', 'plugin' );
 273  
 274              if ( $upgrade_plugins ) {
 275                  $js_plugins['upgrade'] = array_values( $upgrade_plugins );
 276              }
 277  
 278              wp_localize_script(
 279                  'updates',
 280                  '_wpUpdatesItemCounts',
 281                  array(
 282                      'plugins' => $js_plugins,
 283                      'totals'  => wp_get_update_data(),
 284                  )
 285              );
 286          }
 287      }
 288  
 289      /**
 290       */
 291  	public function no_items() {
 292          if ( isset( $this->error ) ) {
 293              $error_message  = '<p>' . $this->error->get_error_message() . '</p>';
 294              $error_message .= '<p class="hide-if-no-js"><button class="button try-again">' . __( 'Try Again' ) . '</button></p>';
 295              wp_admin_notice(
 296                  $error_message,
 297                  array(
 298                      'additional_classes' => array( 'inline', 'error' ),
 299                      'paragraph_wrap'     => false,
 300                  )
 301              );
 302              ?>
 303          <?php } else { ?>
 304              <div class="no-plugin-results"><?php _e( 'No plugins found. Try a different search.' ); ?></div>
 305              <?php
 306          }
 307      }
 308  
 309      /**
 310       * @global array $tabs
 311       * @global string $tab
 312       *
 313       * @return array
 314       */
 315  	protected function get_views() {
 316          global $tabs, $tab;
 317  
 318          $display_tabs = array();
 319          foreach ( (array) $tabs as $action => $text ) {
 320              $display_tabs[ 'plugin-install-' . $action ] = array(
 321                  'url'     => self_admin_url( 'plugin-install.php?tab=' . $action ),
 322                  'label'   => $text,
 323                  'current' => $action === $tab,
 324              );
 325          }
 326          // No longer a real tab.
 327          unset( $display_tabs['plugin-install-upload'] );
 328  
 329          return $this->get_views_links( $display_tabs );
 330      }
 331  
 332      /**
 333       * Overrides parent views so we can use the filter bar display.
 334       *
 335       * @global string $tab The current tab.
 336       */
 337  	public function views() {
 338          global $tab;
 339  
 340          $views = $this->get_views();
 341  
 342          /** This filter is documented in wp-admin/includes/class-wp-list-table.php */
 343          $views = apply_filters( "views_{$this->screen->id}", $views );
 344  
 345          $this->screen->render_screen_reader_content( 'heading_views' );
 346  
 347          printf(
 348              /* translators: %s: https://wordpress.org/plugins/ */
 349              '<p>' . __( 'Plugins extend and expand the functionality of WordPress. You may install plugins from the <a href="%s">WordPress Plugin Directory</a> right on this page, or upload a plugin in .zip format by clicking the button above.' ) . '</p>',
 350              __( 'https://wordpress.org/plugins/' )
 351          );
 352          ?>
 353  <div class="wp-filter">
 354      <ul class="filter-links">
 355          <?php
 356          if ( ! empty( $views ) ) {
 357              foreach ( $views as $class => $view ) {
 358                  $views[ $class ] = "\t<li class='$class'>$view";
 359              }
 360              echo implode( " </li>\n", $views ) . "</li>\n";
 361          }
 362          ?>
 363      </ul>
 364  
 365          <?php
 366          if ( 'favorites' !== $tab ) {
 367              install_search_form();
 368          }
 369          ?>
 370  </div>
 371          <?php
 372      }
 373  
 374      /**
 375       * Displays the plugin install table.
 376       *
 377       * Overrides the parent display() method to provide a different container.
 378       *
 379       * @since 4.0.0
 380       */
 381  	public function display() {
 382          $singular = $this->_args['singular'];
 383  
 384          $data_attr = '';
 385  
 386          if ( $singular ) {
 387              $data_attr = " data-wp-lists='list:$singular'";
 388          }
 389  
 390          $this->display_tablenav( 'top' );
 391  
 392          ?>
 393  <div class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
 394          <?php
 395          $this->screen->render_screen_reader_content( 'heading_list' );
 396          ?>
 397      <div id="the-list"<?php echo $data_attr; ?>>
 398          <?php $this->display_rows_or_placeholder(); ?>
 399      </div>
 400  </div>
 401          <?php
 402          $this->display_tablenav( 'bottom' );
 403      }
 404  
 405      /**
 406       * @global string $tab
 407       *
 408       * @param string $which
 409       */
 410  	protected function display_tablenav( $which ) {
 411          if ( 'featured' === $GLOBALS['tab'] ) {
 412              return;
 413          }
 414  
 415          if ( 'top' === $which ) {
 416              wp_referer_field();
 417              ?>
 418              <div class="tablenav top">
 419                  <div class="alignleft actions">
 420                      <?php
 421                      /**
 422                       * Fires before the Plugin Install table header pagination is displayed.
 423                       *
 424                       * @since 2.7.0
 425                       */
 426                      do_action( 'install_plugins_table_header' );
 427                      ?>
 428                  </div>
 429                  <?php $this->pagination( $which ); ?>
 430                  <br class="clear" />
 431              </div>
 432          <?php } else { ?>
 433              <div class="tablenav bottom">
 434                  <?php $this->pagination( $which ); ?>
 435                  <br class="clear" />
 436              </div>
 437              <?php
 438          }
 439      }
 440  
 441      /**
 442       * @return array
 443       */
 444  	protected function get_table_classes() {
 445          return array( 'widefat', $this->_args['plural'] );
 446      }
 447  
 448      /**
 449       * @return string[] Array of column titles keyed by their column name.
 450       */
 451  	public function get_columns() {
 452          return array();
 453      }
 454  
 455      /**
 456       * @param object $plugin_a
 457       * @param object $plugin_b
 458       * @return int
 459       */
 460  	private function order_callback( $plugin_a, $plugin_b ) {
 461          $orderby = $this->orderby;
 462          if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
 463              return 0;
 464          }
 465  
 466          $a = $plugin_a->$orderby;
 467          $b = $plugin_b->$orderby;
 468  
 469          return 'DESC' === $this->order ?
 470              $b <=> $a :
 471              $a <=> $b;
 472      }
 473  
 474      /**
 475       * Generates the list table rows.
 476       *
 477       * @since 3.1.0
 478       */
 479  	public function display_rows() {
 480          $plugins_allowedtags = array(
 481              'a'       => array(
 482                  'href'   => array(),
 483                  'title'  => array(),
 484                  'target' => array(),
 485              ),
 486              'abbr'    => array( 'title' => array() ),
 487              'acronym' => array( 'title' => array() ),
 488              'code'    => array(),
 489              'pre'     => array(),
 490              'em'      => array(),
 491              'strong'  => array(),
 492              'ul'      => array(),
 493              'ol'      => array(),
 494              'li'      => array(),
 495              'p'       => array(),
 496              'br'      => array(),
 497          );
 498  
 499          $plugins_group_titles = array(
 500              'Performance' => _x( 'Performance', 'Plugin installer group title' ),
 501              'Social'      => _x( 'Social', 'Plugin installer group title' ),
 502              'Tools'       => _x( 'Tools', 'Plugin installer group title' ),
 503          );
 504  
 505          $group = null;
 506  
 507          foreach ( (array) $this->items as $plugin ) {
 508              if ( is_object( $plugin ) ) {
 509                  $plugin = (array) $plugin;
 510              }
 511  
 512              // Display the group heading if there is one.
 513              if ( isset( $plugin['group'] ) && $plugin['group'] !== $group ) {
 514                  if ( isset( $this->groups[ $plugin['group'] ] ) ) {
 515                      $group_name = $this->groups[ $plugin['group'] ];
 516                      if ( isset( $plugins_group_titles[ $group_name ] ) ) {
 517                          $group_name = $plugins_group_titles[ $group_name ];
 518                      }
 519                  } else {
 520                      $group_name = $plugin['group'];
 521                  }
 522  
 523                  // Starting a new group, close off the divs of the last one.
 524                  if ( ! empty( $group ) ) {
 525                      echo '</div></div>';
 526                  }
 527  
 528                  echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
 529                  // Needs an extra wrapping div for nth-child selectors to work.
 530                  echo '<div class="plugin-items">';
 531  
 532                  $group = $plugin['group'];
 533              }
 534  
 535              $title = wp_kses( $plugin['name'], $plugins_allowedtags );
 536  
 537              // Remove any HTML from the description.
 538              $description = strip_tags( $plugin['short_description'] );
 539  
 540              /**
 541               * Filters the plugin card description on the Add Plugins screen.
 542               *
 543               * @since 6.0.0
 544               *
 545               * @param string $description Plugin card description.
 546               * @param array  $plugin      An array of plugin data. See {@see plugins_api()}
 547               *                            for the list of possible values.
 548               */
 549              $description = apply_filters( 'plugin_install_description', $description, $plugin );
 550  
 551              $version = wp_kses( $plugin['version'], $plugins_allowedtags );
 552  
 553              $name = strip_tags( $title . ' ' . $version );
 554  
 555              $author = wp_kses( $plugin['author'], $plugins_allowedtags );
 556              if ( ! empty( $author ) ) {
 557                  /* translators: %s: Plugin author. */
 558                  $author = ' <cite>' . sprintf( __( 'By %s' ), $author ) . '</cite>';
 559              }
 560  
 561              $requires_php = $plugin['requires_php'] ?? null;
 562              $requires_wp  = $plugin['requires'] ?? null;
 563  
 564              $compatible_php = is_php_version_compatible( $requires_php );
 565              $compatible_wp  = is_wp_version_compatible( $requires_wp );
 566              $tested_wp      = ( empty( $plugin['tested'] ) || version_compare( get_bloginfo( 'version' ), $plugin['tested'], '<=' ) );
 567  
 568              $action_links = array();
 569  
 570              $action_links[] = wp_get_plugin_action_button( $name, $plugin, $compatible_php, $compatible_wp );
 571  
 572              $details_link = self_admin_url(
 573                  'plugin-install.php?tab=plugin-information&amp;plugin=' . $plugin['slug'] .
 574                  '&amp;TB_iframe=true&amp;width=600&amp;height=550'
 575              );
 576  
 577              $action_links[] = sprintf(
 578                  '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
 579                  esc_url( $details_link ),
 580                  /* translators: %s: Plugin name and version. */
 581                  esc_attr( sprintf( __( 'More information about %s' ), $name ) ),
 582                  esc_attr( $name ),
 583                  __( 'More Details' )
 584              );
 585  
 586              if ( ! empty( $plugin['icons']['svg'] ) ) {
 587                  $plugin_icon_url = $plugin['icons']['svg'];
 588              } elseif ( ! empty( $plugin['icons']['2x'] ) ) {
 589                  $plugin_icon_url = $plugin['icons']['2x'];
 590              } elseif ( ! empty( $plugin['icons']['1x'] ) ) {
 591                  $plugin_icon_url = $plugin['icons']['1x'];
 592              } else {
 593                  $plugin_icon_url = $plugin['icons']['default'];
 594              }
 595  
 596              /**
 597               * Filters the install action links for a plugin.
 598               *
 599               * @since 2.7.0
 600               *
 601               * @param string[] $action_links An array of plugin action links.
 602               *                               Defaults are links to Details and Install Now.
 603               * @param array    $plugin       An array of plugin data. See {@see plugins_api()}
 604               *                               for the list of possible values.
 605               */
 606              $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin );
 607  
 608              $last_updated_timestamp = strtotime( $plugin['last_updated'] );
 609              ?>
 610          <div class="plugin-card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
 611              <?php
 612              if ( ! $compatible_php || ! $compatible_wp ) {
 613                  $incompatible_notice_message = '';
 614                  if ( ! $compatible_php && ! $compatible_wp ) {
 615                      $incompatible_notice_message .= __( 'This plugin does not work with your versions of WordPress and PHP.' );
 616                      if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
 617                          $incompatible_notice_message .= sprintf(
 618                              /* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */
 619                              ' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ),
 620                              self_admin_url( 'update-core.php' ),
 621                              esc_url( wp_get_update_php_url() )
 622                          );
 623                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 624                      } elseif ( current_user_can( 'update_core' ) ) {
 625                          $incompatible_notice_message .= sprintf(
 626                              /* translators: %s: URL to WordPress Updates screen. */
 627                              ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
 628                              self_admin_url( 'update-core.php' )
 629                          );
 630                      } elseif ( current_user_can( 'update_php' ) ) {
 631                          $incompatible_notice_message .= sprintf(
 632                              /* translators: %s: URL to Update PHP page. */
 633                              ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
 634                              esc_url( wp_get_update_php_url() )
 635                          );
 636                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 637                      }
 638                  } elseif ( ! $compatible_wp ) {
 639                      $incompatible_notice_message .= __( 'This plugin does not work with your version of WordPress.' );
 640                      if ( current_user_can( 'update_core' ) ) {
 641                          $incompatible_notice_message .= sprintf(
 642                              /* translators: %s: URL to WordPress Updates screen. */
 643                              ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
 644                              self_admin_url( 'update-core.php' )
 645                          );
 646                      }
 647                  } elseif ( ! $compatible_php ) {
 648                      $incompatible_notice_message .= __( 'This plugin does not work with your version of PHP.' );
 649                      if ( current_user_can( 'update_php' ) ) {
 650                          $incompatible_notice_message .= sprintf(
 651                              /* translators: %s: URL to Update PHP page. */
 652                              ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
 653                              esc_url( wp_get_update_php_url() )
 654                          );
 655                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 656                      }
 657                  }
 658  
 659                  wp_admin_notice(
 660                      $incompatible_notice_message,
 661                      array(
 662                          'type'               => 'error',
 663                          'additional_classes' => array( 'notice-alt', 'inline' ),
 664                      )
 665                  );
 666              }
 667              ?>
 668              <div class="plugin-card-top">
 669                  <div class="name column-name">
 670                      <h3>
 671                          <a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
 672                          <?php echo $title; ?>
 673                          <img src="<?php echo esc_url( $plugin_icon_url ); ?>" class="plugin-icon" alt="" />
 674                          </a>
 675                      </h3>
 676                  </div>
 677                  <div class="action-links">
 678                      <?php
 679                      if ( $action_links ) {
 680                          echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
 681                      }
 682                      ?>
 683                  </div>
 684                  <div class="desc column-description">
 685                      <p><?php echo $description; ?></p>
 686                      <p class="authors"><?php echo $author; ?></p>
 687                  </div>
 688              </div>
 689              <?php
 690              $dependencies_notice = $this->get_dependencies_notice( $plugin );
 691              if ( ! empty( $dependencies_notice ) ) {
 692                  echo $dependencies_notice;
 693              }
 694              ?>
 695              <div class="plugin-card-bottom">
 696                  <div class="vers column-rating">
 697                      <?php
 698                      wp_star_rating(
 699                          array(
 700                              'rating' => $plugin['rating'],
 701                              'type'   => 'percent',
 702                              'number' => $plugin['num_ratings'],
 703                          )
 704                      );
 705                      ?>
 706                      <span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?>)</span>
 707                  </div>
 708                  <div class="column-updated">
 709                      <strong><?php _e( 'Last Updated:' ); ?></strong>
 710                      <?php
 711                          /* translators: %s: Human-readable time difference. */
 712                          printf( __( '%s ago' ), human_time_diff( $last_updated_timestamp ) );
 713                      ?>
 714                  </div>
 715                  <div class="column-downloaded">
 716                      <?php
 717                      if ( $plugin['active_installs'] >= 1000000 ) {
 718                          $active_installs_millions = floor( $plugin['active_installs'] / 1000000 );
 719                          $active_installs_text     = sprintf(
 720                              /* translators: %s: Number of millions. */
 721                              _nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations' ),
 722                              number_format_i18n( $active_installs_millions )
 723                          );
 724                      } elseif ( 0 === $plugin['active_installs'] ) {
 725                          $active_installs_text = _x( 'Less Than 10', 'Active plugin installations' );
 726                      } else {
 727                          $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
 728                      }
 729                      /* translators: %s: Number of installations. */
 730                      printf( __( '%s Active Installations' ), $active_installs_text );
 731                      ?>
 732                  </div>
 733                  <div class="column-compatibility">
 734                      <?php
 735                      if ( ! $tested_wp ) {
 736                          echo '<span class="compatibility-untested">' . __( 'Untested with your version of WordPress' ) . '</span>';
 737                      } elseif ( ! $compatible_wp ) {
 738                          echo '<span class="compatibility-incompatible">' . __( '<strong>Incompatible</strong> with your version of WordPress' ) . '</span>';
 739                      } else {
 740                          echo '<span class="compatibility-compatible">' . __( '<strong>Compatible</strong> with your version of WordPress' ) . '</span>';
 741                      }
 742                      ?>
 743                  </div>
 744              </div>
 745          </div>
 746              <?php
 747          }
 748  
 749          // Close off the group divs of the last one.
 750          if ( ! empty( $group ) ) {
 751              echo '</div></div>';
 752          }
 753      }
 754  
 755      /**
 756       * Returns a notice containing a list of dependencies required by the plugin.
 757       *
 758       * @since 6.5.0
 759       *
 760       * @param array  $plugin_data An array of plugin data. See {@see plugins_api()}
 761       *                            for the list of possible values.
 762       * @return string A notice containing a list of dependencies required by the plugin,
 763       *                or an empty string if none is required.
 764       */
 765  	protected function get_dependencies_notice( $plugin_data ) {
 766          if ( empty( $plugin_data['requires_plugins'] ) ) {
 767              return '';
 768          }
 769  
 770          $no_name_markup  = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span></div>';
 771          $has_name_markup = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span> %s</div>';
 772  
 773          $dependencies_list = '';
 774          foreach ( $plugin_data['requires_plugins'] as $dependency ) {
 775              $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $dependency );
 776  
 777              if (
 778                  false !== $dependency_data &&
 779                  ! empty( $dependency_data['name'] ) &&
 780                  ! empty( $dependency_data['slug'] ) &&
 781                  ! empty( $dependency_data['version'] )
 782              ) {
 783                  $more_details_link  = $this->get_more_details_link( $dependency_data['name'], $dependency_data['slug'] );
 784                  $dependencies_list .= sprintf( $has_name_markup, esc_html( $dependency_data['name'] ), $more_details_link );
 785                  continue;
 786              }
 787  
 788              $result = plugins_api( 'plugin_information', array( 'slug' => $dependency ) );
 789  
 790              if ( ! empty( $result->name ) ) {
 791                  $more_details_link  = $this->get_more_details_link( $result->name, $result->slug );
 792                  $dependencies_list .= sprintf( $has_name_markup, esc_html( $result->name ), $more_details_link );
 793                  continue;
 794              }
 795  
 796              $dependencies_list .= sprintf( $no_name_markup, esc_html( $dependency ) );
 797          }
 798  
 799          $dependencies_notice = sprintf(
 800              '<div class="plugin-dependencies notice notice-alt notice-info inline"><p class="plugin-dependencies-explainer-text">%s</p> %s</div>',
 801              '<strong>' . __( 'Additional plugins are required' ) . '</strong>',
 802              $dependencies_list
 803          );
 804  
 805          return $dependencies_notice;
 806      }
 807  
 808      /**
 809       * Creates a 'More details' link for the plugin.
 810       *
 811       * @since 6.5.0
 812       *
 813       * @param string $name The plugin's name.
 814       * @param string $slug The plugin's slug.
 815       * @return string The 'More details' link for the plugin.
 816       */
 817  	protected function get_more_details_link( $name, $slug ) {
 818          $url = add_query_arg(
 819              array(
 820                  'tab'       => 'plugin-information',
 821                  'plugin'    => $slug,
 822                  'TB_iframe' => 'true',
 823                  'width'     => '600',
 824                  'height'    => '550',
 825              ),
 826              network_admin_url( 'plugin-install.php' )
 827          );
 828  
 829          $more_details_link = sprintf(
 830              '<a href="%1$s" class="more-details-link thickbox open-plugin-details-modal" aria-label="%2$s" data-title="%3$s">%4$s</a>',
 831              esc_url( $url ),
 832              /* translators: %s: Plugin name. */
 833              sprintf( __( 'More information about %s' ), esc_html( $name ) ),
 834              esc_attr( $name ),
 835              __( 'More Details' )
 836          );
 837  
 838          return $more_details_link;
 839      }
 840  }


Generated : Tue Jun 16 08:20:09 2026 Cross-referenced by PHPXref