class-wp-theme-install-list-table.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <?php
  2. /**
  3. * List Table API: WP_Theme_Install_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying themes to install in a list table.
  11. *
  12. * @since 3.1.0
  13. * @access private
  14. *
  15. * @see WP_Themes_List_Table
  16. */
  17. class WP_Theme_Install_List_Table extends WP_Themes_List_Table {
  18. public $features = array();
  19. /**
  20. * @return bool
  21. */
  22. public function ajax_user_can() {
  23. return current_user_can( 'install_themes' );
  24. }
  25. /**
  26. * @global array $tabs
  27. * @global string $tab
  28. * @global int $paged
  29. * @global string $type
  30. * @global array $theme_field_defaults
  31. */
  32. public function prepare_items() {
  33. include( ABSPATH . 'wp-admin/includes/theme-install.php' );
  34. global $tabs, $tab, $paged, $type, $theme_field_defaults;
  35. wp_reset_vars( array( 'tab' ) );
  36. $search_terms = array();
  37. $search_string = '';
  38. if ( ! empty( $_REQUEST['s'] ) ) {
  39. $search_string = strtolower( wp_unslash( $_REQUEST['s'] ) );
  40. $search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) );
  41. }
  42. if ( ! empty( $_REQUEST['features'] ) ) {
  43. $this->features = $_REQUEST['features'];
  44. }
  45. $paged = $this->get_pagenum();
  46. $per_page = 36;
  47. // These are the tabs which are shown on the page,
  48. $tabs = array();
  49. $tabs['dashboard'] = __( 'Search' );
  50. if ( 'search' === $tab ) {
  51. $tabs['search'] = __( 'Search Results' );
  52. }
  53. $tabs['upload'] = __( 'Upload' );
  54. $tabs['featured'] = _x( 'Featured', 'themes' );
  55. //$tabs['popular'] = _x( 'Popular', 'themes' );
  56. $tabs['new'] = _x( 'Latest', 'themes' );
  57. $tabs['updated'] = _x( 'Recently Updated', 'themes' );
  58. $nonmenu_tabs = array( 'theme-information' ); // Valid actions to perform which do not have a Menu item.
  59. /** This filter is documented in wp-admin/theme-install.php */
  60. $tabs = apply_filters( 'install_themes_tabs', $tabs );
  61. /**
  62. * Filters tabs not associated with a menu item on the Install Themes screen.
  63. *
  64. * @since 2.8.0
  65. *
  66. * @param string[] $nonmenu_tabs The tabs that don't have a menu item on
  67. * the Install Themes screen.
  68. */
  69. $nonmenu_tabs = apply_filters( 'install_themes_nonmenu_tabs', $nonmenu_tabs );
  70. // If a non-valid menu tab has been selected, And it's not a non-menu action.
  71. if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs ) ) ) {
  72. $tab = key( $tabs );
  73. }
  74. $args = array(
  75. 'page' => $paged,
  76. 'per_page' => $per_page,
  77. 'fields' => $theme_field_defaults,
  78. );
  79. switch ( $tab ) {
  80. case 'search':
  81. $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
  82. switch ( $type ) {
  83. case 'tag':
  84. $args['tag'] = array_map( 'sanitize_key', $search_terms );
  85. break;
  86. case 'term':
  87. $args['search'] = $search_string;
  88. break;
  89. case 'author':
  90. $args['author'] = $search_string;
  91. break;
  92. }
  93. if ( ! empty( $this->features ) ) {
  94. $args['tag'] = $this->features;
  95. $_REQUEST['s'] = implode( ',', $this->features );
  96. $_REQUEST['type'] = 'tag';
  97. }
  98. add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 );
  99. break;
  100. case 'featured':
  101. // case 'popular':
  102. case 'new':
  103. case 'updated':
  104. $args['browse'] = $tab;
  105. break;
  106. default:
  107. $args = false;
  108. break;
  109. }
  110. /**
  111. * Filters API request arguments for each Install Themes screen tab.
  112. *
  113. * The dynamic portion of the hook name, `$tab`, refers to the theme install
  114. * tabs. Default tabs are 'dashboard', 'search', 'upload', 'featured',
  115. * 'new', and 'updated'.
  116. *
  117. * @since 3.7.0
  118. *
  119. * @param array $args An array of themes API arguments.
  120. */
  121. $args = apply_filters( "install_themes_table_api_args_{$tab}", $args );
  122. if ( ! $args ) {
  123. return;
  124. }
  125. $api = themes_api( 'query_themes', $args );
  126. if ( is_wp_error( $api ) ) {
  127. wp_die( $api->get_error_message() . '</p> <p><a href="#" onclick="document.location.reload(); return false;">' . __( 'Try Again' ) . '</a>' );
  128. }
  129. $this->items = $api->themes;
  130. $this->set_pagination_args(
  131. array(
  132. 'total_items' => $api->info['results'],
  133. 'per_page' => $args['per_page'],
  134. 'infinite_scroll' => true,
  135. )
  136. );
  137. }
  138. /**
  139. */
  140. public function no_items() {
  141. _e( 'No themes match your request.' );
  142. }
  143. /**
  144. * @global array $tabs
  145. * @global string $tab
  146. * @return array
  147. */
  148. protected function get_views() {
  149. global $tabs, $tab;
  150. $display_tabs = array();
  151. foreach ( (array) $tabs as $action => $text ) {
  152. $current_link_attributes = ( $action === $tab ) ? ' class="current" aria-current="page"' : '';
  153. $href = self_admin_url( 'theme-install.php?tab=' . $action );
  154. $display_tabs[ 'theme-install-' . $action ] = "<a href='$href'$current_link_attributes>$text</a>";
  155. }
  156. return $display_tabs;
  157. }
  158. /**
  159. * Displays the theme install table.
  160. *
  161. * Overrides the parent display() method to provide a different container.
  162. *
  163. * @since 3.1.0
  164. */
  165. public function display() {
  166. wp_nonce_field( 'fetch-list-' . get_class( $this ), '_ajax_fetch_list_nonce' );
  167. ?>
  168. <div class="tablenav top themes">
  169. <div class="alignleft actions">
  170. <?php
  171. /**
  172. * Fires in the Install Themes list table header.
  173. *
  174. * @since 2.8.0
  175. */
  176. do_action( 'install_themes_table_header' );
  177. ?>
  178. </div>
  179. <?php $this->pagination( 'top' ); ?>
  180. <br class="clear" />
  181. </div>
  182. <div id="availablethemes">
  183. <?php $this->display_rows_or_placeholder(); ?>
  184. </div>
  185. <?php
  186. $this->tablenav( 'bottom' );
  187. }
  188. /**
  189. */
  190. public function display_rows() {
  191. $themes = $this->items;
  192. foreach ( $themes as $theme ) {
  193. ?>
  194. <div class="available-theme installable-theme">
  195. <?php
  196. $this->single_row( $theme );
  197. ?>
  198. </div>
  199. <?php
  200. } // end foreach $theme_names
  201. $this->theme_installer();
  202. }
  203. /**
  204. * Prints a theme from the WordPress.org API.
  205. *
  206. * @since 3.1.0
  207. *
  208. * @global array $themes_allowedtags
  209. *
  210. * @param object $theme {
  211. * An object that contains theme data returned by the WordPress.org API.
  212. *
  213. * @type string $name Theme name, e.g. 'Twenty Twenty'.
  214. * @type string $slug Theme slug, e.g. 'twentytwenty'.
  215. * @type string $version Theme version, e.g. '1.1'.
  216. * @type string $author Theme author username, e.g. 'melchoyce'.
  217. * @type string $preview_url Preview URL, e.g. 'http://2020.wordpress.net/'.
  218. * @type string $screenshot_url Screenshot URL, e.g. 'https://wordpress.org/themes/twentytwenty/'.
  219. * @type float $rating Rating score.
  220. * @type int $num_ratings The number of ratings.
  221. * @type string $homepage Theme homepage, e.g. 'https://wordpress.org/themes/twentytwenty/'.
  222. * @type string $description Theme description.
  223. * @type string $download_link Theme ZIP download URL.
  224. * }
  225. */
  226. public function single_row( $theme ) {
  227. global $themes_allowedtags;
  228. if ( empty( $theme ) ) {
  229. return;
  230. }
  231. $name = wp_kses( $theme->name, $themes_allowedtags );
  232. $author = wp_kses( $theme->author, $themes_allowedtags );
  233. /* translators: %s: Theme name. */
  234. $preview_title = sprintf( __( 'Preview &#8220;%s&#8221;' ), $name );
  235. $preview_url = add_query_arg(
  236. array(
  237. 'tab' => 'theme-information',
  238. 'theme' => $theme->slug,
  239. ),
  240. self_admin_url( 'theme-install.php' )
  241. );
  242. $actions = array();
  243. $install_url = add_query_arg(
  244. array(
  245. 'action' => 'install-theme',
  246. 'theme' => $theme->slug,
  247. ),
  248. self_admin_url( 'update.php' )
  249. );
  250. $update_url = add_query_arg(
  251. array(
  252. 'action' => 'upgrade-theme',
  253. 'theme' => $theme->slug,
  254. ),
  255. self_admin_url( 'update.php' )
  256. );
  257. $status = $this->_get_theme_status( $theme );
  258. switch ( $status ) {
  259. case 'update_available':
  260. $actions[] = sprintf(
  261. '<a class="install-now" href="%s" title="%s">%s</a>',
  262. esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ),
  263. /* translators: %s: Theme version. */
  264. esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ),
  265. __( 'Update' )
  266. );
  267. break;
  268. case 'newer_installed':
  269. case 'latest_installed':
  270. $actions[] = sprintf(
  271. '<span class="install-now" title="%s">%s</span>',
  272. esc_attr__( 'This theme is already installed and is up to date' ),
  273. _x( 'Installed', 'theme' )
  274. );
  275. break;
  276. case 'install':
  277. default:
  278. $actions[] = sprintf(
  279. '<a class="install-now" href="%s" title="%s">%s</a>',
  280. esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ),
  281. /* translators: %s: Theme name. */
  282. esc_attr( sprintf( __( 'Install %s' ), $name ) ),
  283. __( 'Install Now' )
  284. );
  285. break;
  286. }
  287. $actions[] = sprintf(
  288. '<a class="install-theme-preview" href="%s" title="%s">%s</a>',
  289. esc_url( $preview_url ),
  290. /* translators: %s: Theme name. */
  291. esc_attr( sprintf( __( 'Preview %s' ), $name ) ),
  292. __( 'Preview' )
  293. );
  294. /**
  295. * Filters the install action links for a theme in the Install Themes list table.
  296. *
  297. * @since 3.4.0
  298. *
  299. * @param string[] $actions An array of theme action links. Defaults are
  300. * links to Install Now, Preview, and Details.
  301. * @param WP_Theme $theme Theme object.
  302. */
  303. $actions = apply_filters( 'theme_install_actions', $actions, $theme );
  304. ?>
  305. <a class="screenshot install-theme-preview" href="<?php echo esc_url( $preview_url ); ?>" title="<?php echo esc_attr( $preview_title ); ?>">
  306. <img src="<?php echo esc_url( $theme->screenshot_url ); ?>" width="150" alt="" />
  307. </a>
  308. <h3><?php echo $name; ?></h3>
  309. <div class="theme-author">
  310. <?php
  311. /* translators: %s: Theme author. */
  312. printf( __( 'By %s' ), $author );
  313. ?>
  314. </div>
  315. <div class="action-links">
  316. <ul>
  317. <?php foreach ( $actions as $action ) : ?>
  318. <li><?php echo $action; ?></li>
  319. <?php endforeach; ?>
  320. <li class="hide-if-no-js"><a href="#" class="theme-detail"><?php _e( 'Details' ); ?></a></li>
  321. </ul>
  322. </div>
  323. <?php
  324. $this->install_theme_info( $theme );
  325. }
  326. /**
  327. * Prints the wrapper for the theme installer.
  328. */
  329. public function theme_installer() {
  330. ?>
  331. <div id="theme-installer" class="wp-full-overlay expanded">
  332. <div class="wp-full-overlay-sidebar">
  333. <div class="wp-full-overlay-header">
  334. <a href="#" class="close-full-overlay button"><?php _e( 'Close' ); ?></a>
  335. <span class="theme-install"></span>
  336. </div>
  337. <div class="wp-full-overlay-sidebar-content">
  338. <div class="install-theme-info"></div>
  339. </div>
  340. <div class="wp-full-overlay-footer">
  341. <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>">
  342. <span class="collapse-sidebar-arrow"></span>
  343. <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
  344. </button>
  345. </div>
  346. </div>
  347. <div class="wp-full-overlay-main"></div>
  348. </div>
  349. <?php
  350. }
  351. /**
  352. * Prints the wrapper for the theme installer with a provided theme's data.
  353. * Used to make the theme installer work for no-js.
  354. *
  355. * @param object $theme - A WordPress.org Theme API object.
  356. */
  357. public function theme_installer_single( $theme ) {
  358. ?>
  359. <div id="theme-installer" class="wp-full-overlay single-theme">
  360. <div class="wp-full-overlay-sidebar">
  361. <?php $this->install_theme_info( $theme ); ?>
  362. </div>
  363. <div class="wp-full-overlay-main">
  364. <iframe src="<?php echo esc_url( $theme->preview_url ); ?>"></iframe>
  365. </div>
  366. </div>
  367. <?php
  368. }
  369. /**
  370. * Prints the info for a theme (to be used in the theme installer modal).
  371. *
  372. * @global array $themes_allowedtags
  373. *
  374. * @param object $theme - A WordPress.org Theme API object.
  375. */
  376. public function install_theme_info( $theme ) {
  377. global $themes_allowedtags;
  378. if ( empty( $theme ) ) {
  379. return;
  380. }
  381. $name = wp_kses( $theme->name, $themes_allowedtags );
  382. $author = wp_kses( $theme->author, $themes_allowedtags );
  383. $install_url = add_query_arg(
  384. array(
  385. 'action' => 'install-theme',
  386. 'theme' => $theme->slug,
  387. ),
  388. self_admin_url( 'update.php' )
  389. );
  390. $update_url = add_query_arg(
  391. array(
  392. 'action' => 'upgrade-theme',
  393. 'theme' => $theme->slug,
  394. ),
  395. self_admin_url( 'update.php' )
  396. );
  397. $status = $this->_get_theme_status( $theme );
  398. ?>
  399. <div class="install-theme-info">
  400. <?php
  401. switch ( $status ) {
  402. case 'update_available':
  403. printf(
  404. '<a class="theme-install button button-primary" href="%s" title="%s">%s</a>',
  405. esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ),
  406. /* translators: %s: Theme version. */
  407. esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ),
  408. __( 'Update' )
  409. );
  410. break;
  411. case 'newer_installed':
  412. case 'latest_installed':
  413. printf(
  414. '<span class="theme-install" title="%s">%s</span>',
  415. esc_attr__( 'This theme is already installed and is up to date' ),
  416. _x( 'Installed', 'theme' )
  417. );
  418. break;
  419. case 'install':
  420. default:
  421. printf(
  422. '<a class="theme-install button button-primary" href="%s">%s</a>',
  423. esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ),
  424. __( 'Install' )
  425. );
  426. break;
  427. }
  428. ?>
  429. <h3 class="theme-name"><?php echo $name; ?></h3>
  430. <span class="theme-by">
  431. <?php
  432. /* translators: %s: Theme author. */
  433. printf( __( 'By %s' ), $author );
  434. ?>
  435. </span>
  436. <?php if ( isset( $theme->screenshot_url ) ) : ?>
  437. <img class="theme-screenshot" src="<?php echo esc_url( $theme->screenshot_url ); ?>" alt="" />
  438. <?php endif; ?>
  439. <div class="theme-details">
  440. <?php
  441. wp_star_rating(
  442. array(
  443. 'rating' => $theme->rating,
  444. 'type' => 'percent',
  445. 'number' => $theme->num_ratings,
  446. )
  447. );
  448. ?>
  449. <div class="theme-version">
  450. <strong><?php _e( 'Version:' ); ?> </strong>
  451. <?php echo wp_kses( $theme->version, $themes_allowedtags ); ?>
  452. </div>
  453. <div class="theme-description">
  454. <?php echo wp_kses( $theme->description, $themes_allowedtags ); ?>
  455. </div>
  456. </div>
  457. <input class="theme-preview-url" type="hidden" value="<?php echo esc_url( $theme->preview_url ); ?>" />
  458. </div>
  459. <?php
  460. }
  461. /**
  462. * Send required variables to JavaScript land
  463. *
  464. * @since 3.4.0
  465. *
  466. * @global string $tab Current tab within Themes->Install screen
  467. * @global string $type Type of search.
  468. *
  469. * @param array $extra_args Unused.
  470. */
  471. public function _js_vars( $extra_args = array() ) {
  472. global $tab, $type;
  473. parent::_js_vars( compact( 'tab', 'type' ) );
  474. }
  475. /**
  476. * Check to see if the theme is already installed.
  477. *
  478. * @since 3.4.0
  479. *
  480. * @param object $theme - A WordPress.org Theme API object.
  481. * @return string Theme status.
  482. */
  483. private function _get_theme_status( $theme ) {
  484. $status = 'install';
  485. $installed_theme = wp_get_theme( $theme->slug );
  486. if ( $installed_theme->exists() ) {
  487. if ( version_compare( $installed_theme->get( 'Version' ), $theme->version, '=' ) ) {
  488. $status = 'latest_installed';
  489. } elseif ( version_compare( $installed_theme->get( 'Version' ), $theme->version, '>' ) ) {
  490. $status = 'newer_installed';
  491. } else {
  492. $status = 'update_available';
  493. }
  494. }
  495. return $status;
  496. }
  497. }