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