[ 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  	public function views() {
 336          $views = $this->get_views();
 337  
 338          /** This filter is documented in wp-admin/includes/class-wp-list-table.php */
 339          $views = apply_filters( "views_{$this->screen->id}", $views );
 340  
 341          $this->screen->render_screen_reader_content( 'heading_views' );
 342  
 343          printf(
 344              /* translators: %s: https://wordpress.org/plugins/ */
 345              '<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>',
 346              __( 'https://wordpress.org/plugins/' )
 347          );
 348          ?>
 349  <div class="wp-filter">
 350      <ul class="filter-links">
 351          <?php
 352          if ( ! empty( $views ) ) {
 353              foreach ( $views as $class => $view ) {
 354                  $views[ $class ] = "\t<li class='$class'>$view";
 355              }
 356              echo implode( " </li>\n", $views ) . "</li>\n";
 357          }
 358          ?>
 359      </ul>
 360  
 361          <?php install_search_form(); ?>
 362  </div>
 363          <?php
 364      }
 365  
 366      /**
 367       * Displays the plugin install table.
 368       *
 369       * Overrides the parent display() method to provide a different container.
 370       *
 371       * @since 4.0.0
 372       */
 373  	public function display() {
 374          $singular = $this->_args['singular'];
 375  
 376          $data_attr = '';
 377  
 378          if ( $singular ) {
 379              $data_attr = " data-wp-lists='list:$singular'";
 380          }
 381  
 382          $this->display_tablenav( 'top' );
 383  
 384          ?>
 385  <div class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
 386          <?php
 387          $this->screen->render_screen_reader_content( 'heading_list' );
 388          ?>
 389      <div id="the-list"<?php echo $data_attr; ?>>
 390          <?php $this->display_rows_or_placeholder(); ?>
 391      </div>
 392  </div>
 393          <?php
 394          $this->display_tablenav( 'bottom' );
 395      }
 396  
 397      /**
 398       * @global string $tab
 399       *
 400       * @param string $which
 401       */
 402  	protected function display_tablenav( $which ) {
 403          if ( 'featured' === $GLOBALS['tab'] ) {
 404              return;
 405          }
 406  
 407          if ( 'top' === $which ) {
 408              wp_referer_field();
 409              ?>
 410              <div class="tablenav top">
 411                  <div class="alignleft actions">
 412                      <?php
 413                      /**
 414                       * Fires before the Plugin Install table header pagination is displayed.
 415                       *
 416                       * @since 2.7.0
 417                       */
 418                      do_action( 'install_plugins_table_header' );
 419                      ?>
 420                  </div>
 421                  <?php $this->pagination( $which ); ?>
 422                  <br class="clear" />
 423              </div>
 424          <?php } else { ?>
 425              <div class="tablenav bottom">
 426                  <?php $this->pagination( $which ); ?>
 427                  <br class="clear" />
 428              </div>
 429              <?php
 430          }
 431      }
 432  
 433      /**
 434       * @return array
 435       */
 436  	protected function get_table_classes() {
 437          return array( 'widefat', $this->_args['plural'] );
 438      }
 439  
 440      /**
 441       * @return string[] Array of column titles keyed by their column name.
 442       */
 443  	public function get_columns() {
 444          return array();
 445      }
 446  
 447      /**
 448       * @param object $plugin_a
 449       * @param object $plugin_b
 450       * @return int
 451       */
 452  	private function order_callback( $plugin_a, $plugin_b ) {
 453          $orderby = $this->orderby;
 454          if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
 455              return 0;
 456          }
 457  
 458          $a = $plugin_a->$orderby;
 459          $b = $plugin_b->$orderby;
 460  
 461          if ( $a === $b ) {
 462              return 0;
 463          }
 464  
 465          if ( 'DESC' === $this->order ) {
 466              return ( $a < $b ) ? 1 : -1;
 467          } else {
 468              return ( $a < $b ) ? -1 : 1;
 469          }
 470      }
 471  
 472      /**
 473       * Generates the list table rows.
 474       *
 475       * @since 3.1.0
 476       */
 477  	public function display_rows() {
 478          $plugins_allowedtags = array(
 479              'a'       => array(
 480                  'href'   => array(),
 481                  'title'  => array(),
 482                  'target' => array(),
 483              ),
 484              'abbr'    => array( 'title' => array() ),
 485              'acronym' => array( 'title' => array() ),
 486              'code'    => array(),
 487              'pre'     => array(),
 488              'em'      => array(),
 489              'strong'  => array(),
 490              'ul'      => array(),
 491              'ol'      => array(),
 492              'li'      => array(),
 493              'p'       => array(),
 494              'br'      => array(),
 495          );
 496  
 497          $plugins_group_titles = array(
 498              'Performance' => _x( 'Performance', 'Plugin installer group title' ),
 499              'Social'      => _x( 'Social', 'Plugin installer group title' ),
 500              'Tools'       => _x( 'Tools', 'Plugin installer group title' ),
 501          );
 502  
 503          $group = null;
 504  
 505          foreach ( (array) $this->items as $plugin ) {
 506              if ( is_object( $plugin ) ) {
 507                  $plugin = (array) $plugin;
 508              }
 509  
 510              // Display the group heading if there is one.
 511              if ( isset( $plugin['group'] ) && $plugin['group'] !== $group ) {
 512                  if ( isset( $this->groups[ $plugin['group'] ] ) ) {
 513                      $group_name = $this->groups[ $plugin['group'] ];
 514                      if ( isset( $plugins_group_titles[ $group_name ] ) ) {
 515                          $group_name = $plugins_group_titles[ $group_name ];
 516                      }
 517                  } else {
 518                      $group_name = $plugin['group'];
 519                  }
 520  
 521                  // Starting a new group, close off the divs of the last one.
 522                  if ( ! empty( $group ) ) {
 523                      echo '</div></div>';
 524                  }
 525  
 526                  echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
 527                  // Needs an extra wrapping div for nth-child selectors to work.
 528                  echo '<div class="plugin-items">';
 529  
 530                  $group = $plugin['group'];
 531              }
 532  
 533              $title = wp_kses( $plugin['name'], $plugins_allowedtags );
 534  
 535              // Remove any HTML from the description.
 536              $description = strip_tags( $plugin['short_description'] );
 537  
 538              /**
 539               * Filters the plugin card description on the Add Plugins screen.
 540               *
 541               * @since 6.0.0
 542               *
 543               * @param string $description Plugin card description.
 544               * @param array  $plugin      An array of plugin data. See {@see plugins_api()}
 545               *                            for the list of possible values.
 546               */
 547              $description = apply_filters( 'plugin_install_description', $description, $plugin );
 548  
 549              $version = wp_kses( $plugin['version'], $plugins_allowedtags );
 550  
 551              $name = strip_tags( $title . ' ' . $version );
 552  
 553              $author = wp_kses( $plugin['author'], $plugins_allowedtags );
 554              if ( ! empty( $author ) ) {
 555                  /* translators: %s: Plugin author. */
 556                  $author = ' <cite>' . sprintf( __( 'By %s' ), $author ) . '</cite>';
 557              }
 558  
 559              $requires_php = isset( $plugin['requires_php'] ) ? $plugin['requires_php'] : null;
 560              $requires_wp  = isset( $plugin['requires'] ) ? $plugin['requires'] : null;
 561  
 562              $compatible_php = is_php_version_compatible( $requires_php );
 563              $compatible_wp  = is_wp_version_compatible( $requires_wp );
 564              $tested_wp      = ( empty( $plugin['tested'] ) || version_compare( get_bloginfo( 'version' ), $plugin['tested'], '<=' ) );
 565  
 566              $action_links = array();
 567  
 568              $action_links[] = wp_get_plugin_action_button( $name, $plugin, $compatible_php, $compatible_wp );
 569  
 570              $details_link = self_admin_url(
 571                  'plugin-install.php?tab=plugin-information&amp;plugin=' . $plugin['slug'] .
 572                  '&amp;TB_iframe=true&amp;width=600&amp;height=550'
 573              );
 574  
 575              $action_links[] = sprintf(
 576                  '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
 577                  esc_url( $details_link ),
 578                  /* translators: %s: Plugin name and version. */
 579                  esc_attr( sprintf( __( 'More information about %s' ), $name ) ),
 580                  esc_attr( $name ),
 581                  __( 'More Details' )
 582              );
 583  
 584              if ( ! empty( $plugin['icons']['svg'] ) ) {
 585                  $plugin_icon_url = $plugin['icons']['svg'];
 586              } elseif ( ! empty( $plugin['icons']['2x'] ) ) {
 587                  $plugin_icon_url = $plugin['icons']['2x'];
 588              } elseif ( ! empty( $plugin['icons']['1x'] ) ) {
 589                  $plugin_icon_url = $plugin['icons']['1x'];
 590              } else {
 591                  $plugin_icon_url = $plugin['icons']['default'];
 592              }
 593  
 594              /**
 595               * Filters the install action links for a plugin.
 596               *
 597               * @since 2.7.0
 598               *
 599               * @param string[] $action_links An array of plugin action links.
 600               *                               Defaults are links to Details and Install Now.
 601               * @param array    $plugin       An array of plugin data. See {@see plugins_api()}
 602               *                               for the list of possible values.
 603               */
 604              $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin );
 605  
 606              $last_updated_timestamp = strtotime( $plugin['last_updated'] );
 607              ?>
 608          <div class="plugin-card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
 609              <?php
 610              if ( ! $compatible_php || ! $compatible_wp ) {
 611                  $incompatible_notice_message = '';
 612                  if ( ! $compatible_php && ! $compatible_wp ) {
 613                      $incompatible_notice_message .= __( 'This plugin does not work with your versions of WordPress and PHP.' );
 614                      if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
 615                          $incompatible_notice_message .= sprintf(
 616                              /* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */
 617                              ' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ),
 618                              self_admin_url( 'update-core.php' ),
 619                              esc_url( wp_get_update_php_url() )
 620                          );
 621                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 622                      } elseif ( current_user_can( 'update_core' ) ) {
 623                          $incompatible_notice_message .= sprintf(
 624                              /* translators: %s: URL to WordPress Updates screen. */
 625                              ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
 626                              self_admin_url( 'update-core.php' )
 627                          );
 628                      } elseif ( current_user_can( 'update_php' ) ) {
 629                          $incompatible_notice_message .= sprintf(
 630                              /* translators: %s: URL to Update PHP page. */
 631                              ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
 632                              esc_url( wp_get_update_php_url() )
 633                          );
 634                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 635                      }
 636                  } elseif ( ! $compatible_wp ) {
 637                      $incompatible_notice_message .= __( 'This plugin does not work with your version of WordPress.' );
 638                      if ( current_user_can( 'update_core' ) ) {
 639                          $incompatible_notice_message .= sprintf(
 640                              /* translators: %s: URL to WordPress Updates screen. */
 641                              ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
 642                              self_admin_url( 'update-core.php' )
 643                          );
 644                      }
 645                  } elseif ( ! $compatible_php ) {
 646                      $incompatible_notice_message .= __( 'This plugin does not work with your version of PHP.' );
 647                      if ( current_user_can( 'update_php' ) ) {
 648                          $incompatible_notice_message .= sprintf(
 649                              /* translators: %s: URL to Update PHP page. */
 650                              ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
 651                              esc_url( wp_get_update_php_url() )
 652                          );
 653                          $incompatible_notice_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false );
 654                      }
 655                  }
 656  
 657                  wp_admin_notice(
 658                      $incompatible_notice_message,
 659                      array(
 660                          'type'               => 'error',
 661                          'additional_classes' => array( 'notice-alt', 'inline' ),
 662                      )
 663                  );
 664              }
 665              ?>
 666              <div class="plugin-card-top">
 667                  <div class="name column-name">
 668                      <h3>
 669                          <a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
 670                          <?php echo $title; ?>
 671                          <img src="<?php echo esc_url( $plugin_icon_url ); ?>" class="plugin-icon" alt="" />
 672                          </a>
 673                      </h3>
 674                  </div>
 675                  <div class="action-links">
 676                      <?php
 677                      if ( $action_links ) {
 678                          echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
 679                      }
 680                      ?>
 681                  </div>
 682                  <div class="desc column-description">
 683                      <p><?php echo $description; ?></p>
 684                      <p class="authors"><?php echo $author; ?></p>
 685                  </div>
 686              </div>
 687              <?php
 688              $dependencies_notice = $this->get_dependencies_notice( $plugin );
 689              if ( ! empty( $dependencies_notice ) ) {
 690                  echo $dependencies_notice;
 691              }
 692              ?>
 693              <div class="plugin-card-bottom">
 694                  <div class="vers column-rating">
 695                      <?php
 696                      wp_star_rating(
 697                          array(
 698                              'rating' => $plugin['rating'],
 699                              'type'   => 'percent',
 700                              'number' => $plugin['num_ratings'],
 701                          )
 702                      );
 703                      ?>
 704                      <span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?>)</span>
 705                  </div>
 706                  <div class="column-updated">
 707                      <strong><?php _e( 'Last Updated:' ); ?></strong>
 708                      <?php
 709                          /* translators: %s: Human-readable time difference. */
 710                          printf( __( '%s ago' ), human_time_diff( $last_updated_timestamp ) );
 711                      ?>
 712                  </div>
 713                  <div class="column-downloaded">
 714                      <?php
 715                      if ( $plugin['active_installs'] >= 1000000 ) {
 716                          $active_installs_millions = floor( $plugin['active_installs'] / 1000000 );
 717                          $active_installs_text     = sprintf(
 718                              /* translators: %s: Number of millions. */
 719                              _nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations' ),
 720                              number_format_i18n( $active_installs_millions )
 721                          );
 722                      } elseif ( 0 === $plugin['active_installs'] ) {
 723                          $active_installs_text = _x( 'Less Than 10', 'Active plugin installations' );
 724                      } else {
 725                          $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
 726                      }
 727                      /* translators: %s: Number of installations. */
 728                      printf( __( '%s Active Installations' ), $active_installs_text );
 729                      ?>
 730                  </div>
 731                  <div class="column-compatibility">
 732                      <?php
 733                      if ( ! $tested_wp ) {
 734                          echo '<span class="compatibility-untested">' . __( 'Untested with your version of WordPress' ) . '</span>';
 735                      } elseif ( ! $compatible_wp ) {
 736                          echo '<span class="compatibility-incompatible">' . __( '<strong>Incompatible</strong> with your version of WordPress' ) . '</span>';
 737                      } else {
 738                          echo '<span class="compatibility-compatible">' . __( '<strong>Compatible</strong> with your version of WordPress' ) . '</span>';
 739                      }
 740                      ?>
 741                  </div>
 742              </div>
 743          </div>
 744              <?php
 745          }
 746  
 747          // Close off the group divs of the last one.
 748          if ( ! empty( $group ) ) {
 749              echo '</div></div>';
 750          }
 751      }
 752  
 753      /**
 754       * Returns a notice containing a list of dependencies required by the plugin.
 755       *
 756       * @since 6.5.0
 757       *
 758       * @param array  $plugin_data An array of plugin data. See {@see plugins_api()}
 759       *                            for the list of possible values.
 760       * @return string A notice containing a list of dependencies required by the plugin,
 761       *                or an empty string if none is required.
 762       */
 763  	protected function get_dependencies_notice( $plugin_data ) {
 764          if ( empty( $plugin_data['requires_plugins'] ) ) {
 765              return '';
 766          }
 767  
 768          $no_name_markup  = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span></div>';
 769          $has_name_markup = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span> %s</div>';
 770  
 771          $dependencies_list = '';
 772          foreach ( $plugin_data['requires_plugins'] as $dependency ) {
 773              $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $dependency );
 774  
 775              if (
 776                  false !== $dependency_data &&
 777                  ! empty( $dependency_data['name'] ) &&
 778                  ! empty( $dependency_data['slug'] ) &&
 779                  ! empty( $dependency_data['version'] )
 780              ) {
 781                  $more_details_link  = $this->get_more_details_link( $dependency_data['name'], $dependency_data['slug'] );
 782                  $dependencies_list .= sprintf( $has_name_markup, esc_html( $dependency_data['name'] ), $more_details_link );
 783                  continue;
 784              }
 785  
 786              $result = plugins_api( 'plugin_information', array( 'slug' => $dependency ) );
 787  
 788              if ( ! empty( $result->name ) ) {
 789                  $more_details_link  = $this->get_more_details_link( $result->name, $result->slug );
 790                  $dependencies_list .= sprintf( $has_name_markup, esc_html( $result->name ), $more_details_link );
 791                  continue;
 792              }
 793  
 794              $dependencies_list .= sprintf( $no_name_markup, esc_html( $dependency ) );
 795          }
 796  
 797          $dependencies_notice = sprintf(
 798              '<div class="plugin-dependencies notice notice-alt notice-info inline"><p class="plugin-dependencies-explainer-text">%s</p> %s</div>',
 799              '<strong>' . __( 'Additional plugins are required' ) . '</strong>',
 800              $dependencies_list
 801          );
 802  
 803          return $dependencies_notice;
 804      }
 805  
 806      /**
 807       * Creates a 'More details' link for the plugin.
 808       *
 809       * @since 6.5.0
 810       *
 811       * @param string $name The plugin's name.
 812       * @param string $slug The plugin's slug.
 813       * @return string The 'More details' link for the plugin.
 814       */
 815  	protected function get_more_details_link( $name, $slug ) {
 816          $url = add_query_arg(
 817              array(
 818                  'tab'       => 'plugin-information',
 819                  'plugin'    => $slug,
 820                  'TB_iframe' => 'true',
 821                  'width'     => '600',
 822                  'height'    => '550',
 823              ),
 824              network_admin_url( 'plugin-install.php' )
 825          );
 826  
 827          $more_details_link = sprintf(
 828              '<a href="%1$s" class="more-details-link thickbox open-plugin-details-modal" aria-label="%2$s" data-title="%3$s">%4$s</a>',
 829              esc_url( $url ),
 830              /* translators: %s: Plugin name. */
 831              sprintf( __( 'More information about %s' ), esc_html( $name ) ),
 832              esc_attr( $name ),
 833              __( 'More Details' )
 834          );
 835  
 836          return $more_details_link;
 837      }
 838  }


Generated : Thu Aug 14 08:20:01 2025 Cross-referenced by PHPXref