home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / includes / class-wp-plugin-install-list-table.php < prev    next >
Encoding:
PHP Script  |  2017-10-18  |  18.9 KB  |  635 lines

  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.  * @access private
  15.  *
  16.  * @see WP_List_Table
  17.  */
  18. class WP_Plugin_Install_List_Table extends WP_List_Table {
  19.  
  20.     public $order = 'ASC';
  21.     public $orderby = null;
  22.     public $groups = array();
  23.  
  24.     private $error;
  25.  
  26.     /**
  27.      *
  28.      * @return bool
  29.      */
  30.     public function ajax_user_can() {
  31.         return current_user_can('install_plugins');
  32.     }
  33.  
  34.     /**
  35.      * Return the list of known plugins.
  36.      *
  37.      * Uses the transient data from the updates API to determine the known
  38.      * installed plugins.
  39.      *
  40.      * @since 4.9.0
  41.      * @access protected
  42.      *
  43.      * @return array
  44.      */
  45.     protected function get_installed_plugins() {
  46.         $plugins = array();
  47.  
  48.         $plugin_info = get_site_transient( 'update_plugins' );
  49.         if ( isset( $plugin_info->no_update ) ) {
  50.             foreach ( $plugin_info->no_update as $plugin ) {
  51.                 $plugin->upgrade          = false;
  52.                 $plugins[ $plugin->slug ] = $plugin;
  53.             }
  54.         }
  55.  
  56.         if ( isset( $plugin_info->response ) ) {
  57.             foreach ( $plugin_info->response as $plugin ) {
  58.                 $plugin->upgrade          = true;
  59.                 $plugins[ $plugin->slug ] = $plugin;
  60.             }
  61.         }
  62.  
  63.         return $plugins;
  64.     }
  65.  
  66.     /**
  67.      * Return a list of slugs of installed plugins, if known.
  68.      *
  69.      * Uses the transient data from the updates API to determine the slugs of
  70.      * known installed plugins. This might be better elsewhere, perhaps even
  71.      * within get_plugins().
  72.      *
  73.      * @since 4.0.0
  74.      *
  75.      * @return array
  76.      */
  77.     protected function get_installed_plugin_slugs() {
  78.         return array_keys( $this->get_installed_plugins() );
  79.     }
  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.         include( ABSPATH . 'wp-admin/includes/plugin-install.php' );
  91.  
  92.         global $tabs, $tab, $paged, $type, $term;
  93.  
  94.         wp_reset_vars( array( 'tab' ) );
  95.  
  96.         $paged = $this->get_pagenum();
  97.  
  98.         $per_page = 30;
  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.         if ( $tab === 'beta' || false !== strpos( get_bloginfo( 'version' ), '-' ) ) {
  107.             $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' );
  108.         }
  109.         $tabs['featured']    = _x( 'Featured', 'Plugin Installer' );
  110.         $tabs['popular']     = _x( 'Popular', 'Plugin Installer' );
  111.         $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' );
  112.         $tabs['favorites']   = _x( 'Favorites', 'Plugin Installer' );
  113.         if ( current_user_can( 'upload_plugins' ) ) {
  114.             // No longer a real tab. Here for filter compatibility.
  115.             // Gets skipped in get_views().
  116.             $tabs['upload'] = __( 'Upload Plugin' );
  117.         }
  118.  
  119.         $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item.
  120.  
  121.         /**
  122.          * Filters the tabs shown on the Plugin Install screen.
  123.          *
  124.          * @since 2.7.0
  125.          *
  126.          * @param array $tabs The tabs shown on the Plugin Install screen. Defaults include 'featured', 'popular',
  127.          *                    'recommended', 'favorites', and 'upload'.
  128.          */
  129.         $tabs = apply_filters( 'install_plugins_tabs', $tabs );
  130.  
  131.         /**
  132.          * Filters tabs not associated with a menu item on the Plugin Install screen.
  133.          *
  134.          * @since 2.7.0
  135.          *
  136.          * @param array $nonmenu_tabs The tabs that don't have a Menu item on the Plugin Install screen.
  137.          */
  138.         $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs );
  139.  
  140.         // If a non-valid menu tab has been selected, And it's not a non-menu action.
  141.         if ( empty( $tab ) || ( !isset( $tabs[ $tab ] ) && !in_array( $tab, (array) $nonmenu_tabs ) ) )
  142.             $tab = key( $tabs );
  143.  
  144.         $installed_plugins = $this->get_installed_plugins();
  145.  
  146.         $args = array(
  147.             'page' => $paged,
  148.             'per_page' => $per_page,
  149.             'fields' => array(
  150.                 'last_updated' => true,
  151.                 'icons' => true,
  152.                 'active_installs' => true
  153.             ),
  154.             // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results.
  155.             'locale' => get_user_locale(),
  156.             'installed_plugins' => array_keys( $installed_plugins ),
  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.                 $args['fields']['group'] = true;
  180.                 $this->orderby = 'group';
  181.                 // No break!
  182.             case 'popular':
  183.             case 'new':
  184.             case 'beta':
  185.             case 'recommended':
  186.                 $args['browse'] = $tab;
  187.                 break;
  188.  
  189.             case 'favorites':
  190.                 $action = 'save_wporg_username_' . get_current_user_id();
  191.                 if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action ) ) {
  192.                     $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' );
  193.                     update_user_meta( get_current_user_id(), 'wporg_favorites', $user );
  194.                 } else {
  195.                     $user = get_user_option( 'wporg_favorites' );
  196.                 }
  197.                 if ( $user )
  198.                     $args['user'] = $user;
  199.                 else
  200.                     $args = false;
  201.  
  202.                 add_action( 'install_plugins_favorites', 'install_plugins_favorites_form', 9, 0 );
  203.                 break;
  204.  
  205.             default:
  206.                 $args = false;
  207.                 break;
  208.         }
  209.  
  210.         /**
  211.          * Filters API request arguments for each Plugin Install screen tab.
  212.          *
  213.          * The dynamic portion of the hook name, `$tab`, refers to the plugin install tabs.
  214.          * Default tabs include 'featured', 'popular', 'recommended', 'favorites', and 'upload'.
  215.          *
  216.          * @since 3.7.0
  217.          *
  218.          * @param array|bool $args Plugin Install API arguments.
  219.          */
  220.         $args = apply_filters( "install_plugins_table_api_args_{$tab}", $args );
  221.  
  222.         if ( !$args )
  223.             return;
  224.  
  225.         $api = plugins_api( 'query_plugins', $args );
  226.  
  227.         if ( is_wp_error( $api ) ) {
  228.             $this->error = $api;
  229.             return;
  230.         }
  231.  
  232.         $this->items = $api->plugins;
  233.  
  234.         if ( $this->orderby ) {
  235.             uasort( $this->items, array( $this, 'order_callback' ) );
  236.         }
  237.  
  238.         $this->set_pagination_args( array(
  239.             'total_items' => $api->info['results'],
  240.             'per_page' => $args['per_page'],
  241.         ) );
  242.  
  243.         if ( isset( $api->info['groups'] ) ) {
  244.             $this->groups = $api->info['groups'];
  245.         }
  246.  
  247.         if ( $installed_plugins ) {
  248.             $js_plugins = array_fill_keys(
  249.                 array( 'all', 'search', 'active', 'inactive', 'recently_activated', 'mustuse', 'dropins' ),
  250.                 array()
  251.             );
  252.  
  253.             $js_plugins['all'] = array_values( wp_list_pluck( $installed_plugins, 'plugin' ) );
  254.             $upgrade_plugins   = wp_filter_object_list( $installed_plugins, array( 'upgrade' => true ), 'and', 'plugin' );
  255.  
  256.             if ( $upgrade_plugins ) {
  257.                 $js_plugins['upgrade'] = array_values( $upgrade_plugins );
  258.             }
  259.  
  260.             wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
  261.                 'plugins' => $js_plugins,
  262.                 'totals'  => wp_get_update_data(),
  263.             ) );
  264.         }
  265.     }
  266.  
  267.     /**
  268.      */
  269.     public function no_items() {
  270.         if ( isset( $this->error ) ) { ?>
  271.             <div class="inline error"><p><?php echo $this->error->get_error_message(); ?></p>
  272.                 <p class="hide-if-no-js"><button class="button try-again"><?php _e( 'Try Again' ); ?></button></p>
  273.             </div>
  274.         <?php } else { ?>
  275.             <div class="no-plugin-results"><?php _e( 'No plugins found. Try a different search.' ); ?></div>
  276.         <?php
  277.         }
  278.     }
  279.  
  280.     /**
  281.      *
  282.      * @global array $tabs
  283.      * @global string $tab
  284.      *
  285.      * @return array
  286.      */
  287.     protected function get_views() {
  288.         global $tabs, $tab;
  289.  
  290.         $display_tabs = array();
  291.         foreach ( (array) $tabs as $action => $text ) {
  292.             $current_link_attributes = ( $action === $tab ) ? ' class="current" aria-current="page"' : '';
  293.             $href = self_admin_url('plugin-install.php?tab=' . $action);
  294.             $display_tabs['plugin-install-'.$action] = "<a href='$href'$current_link_attributes>$text</a>";
  295.         }
  296.         // No longer a real tab.
  297.         unset( $display_tabs['plugin-install-upload'] );
  298.  
  299.         return $display_tabs;
  300.     }
  301.  
  302.     /**
  303.      * Override parent views so we can use the filter bar display.
  304.      */
  305.     public function views() {
  306.         $views = $this->get_views();
  307.  
  308.         /** This filter is documented in wp-admin/inclues/class-wp-list-table.php */
  309.         $views = apply_filters( "views_{$this->screen->id}", $views );
  310.  
  311.         $this->screen->render_screen_reader_content( 'heading_views' );
  312. ?>
  313. <div class="wp-filter">
  314.     <ul class="filter-links">
  315.         <?php
  316.         if ( ! empty( $views ) ) {
  317.             foreach ( $views as $class => $view ) {
  318.                 $views[ $class ] = "\t<li class='$class'>$view";
  319.             }
  320.             echo implode( " </li>\n", $views ) . "</li>\n";
  321.         }
  322.         ?>
  323.     </ul>
  324.  
  325.     <?php install_search_form(); ?>
  326. </div>
  327. <?php
  328.     }
  329.  
  330.     /**
  331.      * Override the parent display() so we can provide a different container.
  332.      */
  333.     public function display() {
  334.         $singular = $this->_args['singular'];
  335.  
  336.         $data_attr = '';
  337.  
  338.         if ( $singular ) {
  339.             $data_attr = " data-wp-lists='list:$singular'";
  340.         }
  341.  
  342.         $this->display_tablenav( 'top' );
  343.  
  344. ?>
  345. <div class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
  346. <?php
  347.     $this->screen->render_screen_reader_content( 'heading_list' );
  348. ?>
  349.     <div id="the-list"<?php echo $data_attr; ?>>
  350.         <?php $this->display_rows_or_placeholder(); ?>
  351.     </div>
  352. </div>
  353. <?php
  354.         $this->display_tablenav( 'bottom' );
  355.     }
  356.  
  357.     /**
  358.      * @global string $tab
  359.      *
  360.      * @param string $which
  361.      */
  362.     protected function display_tablenav( $which ) {
  363.         if ( $GLOBALS['tab'] === 'featured' ) {
  364.             return;
  365.         }
  366.  
  367.         if ( 'top' === $which ) {
  368.             wp_referer_field();
  369.         ?>
  370.             <div class="tablenav top">
  371.                 <div class="alignleft actions">
  372.                     <?php
  373.                     /**
  374.                      * Fires before the Plugin Install table header pagination is displayed.
  375.                      *
  376.                      * @since 2.7.0
  377.                      */
  378.                     do_action( 'install_plugins_table_header' ); ?>
  379.                 </div>
  380.                 <?php $this->pagination( $which ); ?>
  381.                 <br class="clear" />
  382.             </div>
  383.         <?php } else { ?>
  384.             <div class="tablenav bottom">
  385.                 <?php $this->pagination( $which ); ?>
  386.                 <br class="clear" />
  387.             </div>
  388.         <?php
  389.         }
  390.     }
  391.  
  392.     /**
  393.      * @return array
  394.      */
  395.     protected function get_table_classes() {
  396.         return array( 'widefat', $this->_args['plural'] );
  397.     }
  398.  
  399.     /**
  400.      * @return array
  401.      */
  402.     public function get_columns() {
  403.         return array();
  404.     }
  405.  
  406.     /**
  407.      * @param object $plugin_a
  408.      * @param object $plugin_b
  409.      * @return int
  410.      */
  411.     private function order_callback( $plugin_a, $plugin_b ) {
  412.         $orderby = $this->orderby;
  413.         if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) {
  414.             return 0;
  415.         }
  416.  
  417.         $a = $plugin_a->$orderby;
  418.         $b = $plugin_b->$orderby;
  419.  
  420.         if ( $a == $b ) {
  421.             return 0;
  422.         }
  423.  
  424.         if ( 'DESC' === $this->order ) {
  425.             return ( $a < $b ) ? 1 : -1;
  426.         } else {
  427.             return ( $a < $b ) ? -1 : 1;
  428.         }
  429.     }
  430.  
  431.     public function display_rows() {
  432.         $plugins_allowedtags = array(
  433.             'a' => array( 'href' => array(),'title' => array(), 'target' => array() ),
  434.             'abbr' => array( 'title' => array() ),'acronym' => array( 'title' => array() ),
  435.             'code' => array(), 'pre' => array(), 'em' => array(),'strong' => array(),
  436.             'ul' => array(), 'ol' => array(), 'li' => array(), 'p' => array(), 'br' => array()
  437.         );
  438.  
  439.         $plugins_group_titles = array(
  440.             'Performance' => _x( 'Performance', 'Plugin installer group title' ),
  441.             'Social'      => _x( 'Social',      'Plugin installer group title' ),
  442.             'Tools'       => _x( 'Tools',       'Plugin installer group title' ),
  443.         );
  444.  
  445.         $group = null;
  446.  
  447.         foreach ( (array) $this->items as $plugin ) {
  448.             if ( is_object( $plugin ) ) {
  449.                 $plugin = (array) $plugin;
  450.             }
  451.  
  452.             // Display the group heading if there is one
  453.             if ( isset( $plugin['group'] ) && $plugin['group'] != $group ) {
  454.                 if ( isset( $this->groups[ $plugin['group'] ] ) ) {
  455.                     $group_name = $this->groups[ $plugin['group'] ];
  456.                     if ( isset( $plugins_group_titles[ $group_name ] ) ) {
  457.                         $group_name = $plugins_group_titles[ $group_name ];
  458.                     }
  459.                 } else {
  460.                     $group_name = $plugin['group'];
  461.                 }
  462.  
  463.                 // Starting a new group, close off the divs of the last one
  464.                 if ( ! empty( $group ) ) {
  465.                     echo '</div></div>';
  466.                 }
  467.  
  468.                 echo '<div class="plugin-group"><h3>' . esc_html( $group_name ) . '</h3>';
  469.                 // needs an extra wrapping div for nth-child selectors to work
  470.                 echo '<div class="plugin-items">';
  471.  
  472.                 $group = $plugin['group'];
  473.             }
  474.             $title = wp_kses( $plugin['name'], $plugins_allowedtags );
  475.  
  476.             // Remove any HTML from the description.
  477.             $description = strip_tags( $plugin['short_description'] );
  478.             $version = wp_kses( $plugin['version'], $plugins_allowedtags );
  479.  
  480.             $name = strip_tags( $title . ' ' . $version );
  481.  
  482.             $author = wp_kses( $plugin['author'], $plugins_allowedtags );
  483.             if ( ! empty( $author ) ) {
  484.                 $author = ' <cite>' . sprintf( __( 'By %s' ), $author ) . '</cite>';
  485.             }
  486.  
  487.             $action_links = array();
  488.  
  489.             if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
  490.                 $status = install_plugin_install_status( $plugin );
  491.  
  492.                 switch ( $status['status'] ) {
  493.                     case 'install':
  494.                         if ( $status['url'] ) {
  495.                             /* translators: 1: Plugin name and version. */
  496.                             $action_links[] = '<a class="install-now button" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Install %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Install Now' ) . '</a>';
  497.                         }
  498.                         break;
  499.  
  500.                     case 'update_available':
  501.                         if ( $status['url'] ) {
  502.                             /* translators: 1: Plugin name and version */
  503.                             $action_links[] = '<a class="update-now button aria-button-if-js" data-plugin="' . esc_attr( $status['file'] ) . '" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Update %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Update Now' ) . '</a>';
  504.                         }
  505.                         break;
  506.  
  507.                     case 'latest_installed':
  508.                     case 'newer_installed':
  509.                         if ( is_plugin_active( $status['file'] ) ) {
  510.                             $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Active', 'plugin' ) . '</button>';
  511.                         } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) {
  512.                             $button_text  = __( 'Activate' );
  513.                             /* translators: %s: Plugin name */
  514.                             $button_label = _x( 'Activate %s', 'plugin' );
  515.                             $activate_url = add_query_arg( array(
  516.                                 '_wpnonce'    => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
  517.                                 'action'      => 'activate',
  518.                                 'plugin'      => $status['file'],
  519.                             ), network_admin_url( 'plugins.php' ) );
  520.  
  521.                             if ( is_network_admin() ) {
  522.                                 $button_text  = __( 'Network Activate' );
  523.                                 /* translators: %s: Plugin name */
  524.                                 $button_label = _x( 'Network Activate %s', 'plugin' );
  525.                                 $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
  526.                             }
  527.  
  528.                             $action_links[] = sprintf(
  529.                                 '<a href="%1$s" class="button activate-now" aria-label="%2$s">%3$s</a>',
  530.                                 esc_url( $activate_url ),
  531.                                 esc_attr( sprintf( $button_label, $plugin['name'] ) ),
  532.                                 $button_text
  533.                             );
  534.                         } else {
  535.                             $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Installed', 'plugin' ) . '</button>';
  536.                         }
  537.                         break;
  538.                 }
  539.             }
  540.  
  541.             $details_link   = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin['slug'] .
  542.                                 '&TB_iframe=true&width=600&height=550' );
  543.  
  544.             /* translators: 1: Plugin name and version. */
  545.             $action_links[] = '<a href="' . esc_url( $details_link ) . '" class="thickbox open-plugin-details-modal" aria-label="' . esc_attr( sprintf( __( 'More information about %s' ), $name ) ) . '" data-title="' . esc_attr( $name ) . '">' . __( 'More Details' ) . '</a>';
  546.  
  547.             if ( !empty( $plugin['icons']['svg'] ) ) {
  548.                 $plugin_icon_url = $plugin['icons']['svg'];
  549.             } elseif ( !empty( $plugin['icons']['2x'] ) ) {
  550.                 $plugin_icon_url = $plugin['icons']['2x'];
  551.             } elseif ( !empty( $plugin['icons']['1x'] ) ) {
  552.                 $plugin_icon_url = $plugin['icons']['1x'];
  553.             } else {
  554.                 $plugin_icon_url = $plugin['icons']['default'];
  555.             }
  556.  
  557.             /**
  558.              * Filters the install action links for a plugin.
  559.              *
  560.              * @since 2.7.0
  561.              *
  562.              * @param array $action_links An array of plugin action hyperlinks. Defaults are links to Details and Install Now.
  563.              * @param array $plugin       The plugin currently being listed.
  564.              */
  565.             $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin );
  566.  
  567.             $last_updated_timestamp = strtotime( $plugin['last_updated'] );
  568.         ?>
  569.         <div class="plugin-card plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
  570.             <div class="plugin-card-top">
  571.                 <div class="name column-name">
  572.                     <h3>
  573.                         <a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
  574.                         <?php echo $title; ?>
  575.                         <img src="<?php echo esc_attr( $plugin_icon_url ) ?>" class="plugin-icon" alt="">
  576.                         </a>
  577.                     </h3>
  578.                 </div>
  579.                 <div class="action-links">
  580.                     <?php
  581.                         if ( $action_links ) {
  582.                             echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
  583.                         }
  584.                     ?>
  585.                 </div>
  586.                 <div class="desc column-description">
  587.                     <p><?php echo $description; ?></p>
  588.                     <p class="authors"><?php echo $author; ?></p>
  589.                 </div>
  590.             </div>
  591.             <div class="plugin-card-bottom">
  592.                 <div class="vers column-rating">
  593.                     <?php wp_star_rating( array( 'rating' => $plugin['rating'], 'type' => 'percent', 'number' => $plugin['num_ratings'] ) ); ?>
  594.                     <span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?>)</span>
  595.                 </div>
  596.                 <div class="column-updated">
  597.                     <strong><?php _e( 'Last Updated:' ); ?></strong> <?php printf( __( '%s ago' ), human_time_diff( $last_updated_timestamp ) ); ?>
  598.                 </div>
  599.                 <div class="column-downloaded">
  600.                     <?php
  601.                     if ( $plugin['active_installs'] >= 1000000 ) {
  602.                         $active_installs_text = _x( '1+ Million', 'Active plugin installations' );
  603.                     } elseif ( 0 == $plugin['active_installs'] ) {
  604.                         $active_installs_text = _x( 'Less Than 10', 'Active plugin installations' );
  605.                     } else {
  606.                         $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
  607.                     }
  608.                     printf( __( '%s Active Installations' ), $active_installs_text );
  609.                     ?>
  610.                 </div>
  611.                 <div class="column-compatibility">
  612.                     <?php
  613.                     $wp_version = get_bloginfo( 'version' );
  614.  
  615.                     if ( ! empty( $plugin['tested'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['tested'] ) ), $plugin['tested'], '>' ) ) {
  616.                         echo '<span class="compatibility-untested">' . __( 'Untested with your version of WordPress' ) . '</span>';
  617.                     } elseif ( ! empty( $plugin['requires'] ) && version_compare( substr( $wp_version, 0, strlen( $plugin['requires'] ) ), $plugin['requires'], '<' ) ) {
  618.                         echo '<span class="compatibility-incompatible">' . __( '<strong>Incompatible</strong> with your version of WordPress' ) . '</span>';
  619.                     } else {
  620.                         echo '<span class="compatibility-compatible">' . __( '<strong>Compatible</strong> with your version of WordPress' ) . '</span>';
  621.                     }
  622.                     ?>
  623.                 </div>
  624.             </div>
  625.         </div>
  626.         <?php
  627.         }
  628.  
  629.         // Close off the group divs of the last one
  630.         if ( ! empty( $group ) ) {
  631.             echo '</div></div>';
  632.         }
  633.     }
  634. }
  635.