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


Generated : Fri May 23 08:20:01 2025 Cross-referenced by PHPXref