WP_Plugin_Install_List_Table

Core class used to implement displaying plugins to install in a list table.

Defined (1)

The class is defined in the following location(s).

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