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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. *
  21. * @return bool
  22. */
  23. public function ajax_user_can() {
  24. return current_user_can( 'install_themes' );
  25. }
  26. /**
  27. *
  28. * @global array $tabs
  29. * @global string $tab
  30. * @global int $paged
  31. * @global string $type
  32. * @global array $theme_field_defaults
  33. */
  34. public function prepare_items() {
  35. include( ABSPATH . 'wp-admin/includes/theme-install.php' );
  36. global $tabs, $tab, $paged, $type, $theme_field_defaults;
  37. wp_reset_vars( array( 'tab' ) );
  38. $search_terms = array();
  39. $search_string = '';
  40. if ( ! empty( $_REQUEST['s'] ) ){
  41. $search_string = strtolower( wp_unslash( $_REQUEST['s'] ) );
  42. $search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) );
  43. }
  44. if ( ! empty( $_REQUEST['features'] ) )
  45. $this->features = $_REQUEST['features'];
  46. $paged = $this->get_pagenum();
  47. $per_page = 36;
  48. // These are the tabs which are shown on the page,
  49. $tabs = array();
  50. $tabs['dashboard'] = __( 'Search' );
  51. if ( 'search' === $tab )
  52. $tabs['search'] = __( 'Search Results' );
  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 array $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. $args = array( 'page' => $paged, 'per_page' => $per_page, 'fields' => $theme_field_defaults );
  74. switch ( $tab ) {
  75. case 'search':
  76. $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
  77. switch ( $type ) {
  78. case 'tag':
  79. $args['tag'] = array_map( 'sanitize_key', $search_terms );
  80. break;
  81. case 'term':
  82. $args['search'] = $search_string;
  83. break;
  84. case 'author':
  85. $args['author'] = $search_string;
  86. break;
  87. }
  88. if ( ! empty( $this->features ) ) {
  89. $args['tag'] = $this->features;
  90. $_REQUEST['s'] = implode( ',', $this->features );
  91. $_REQUEST['type'] = 'tag';
  92. }
  93. add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 );
  94. break;
  95. case 'featured':
  96. // case 'popular':
  97. case 'new':
  98. case 'updated':
  99. $args['browse'] = $tab;
  100. break;
  101. default:
  102. $args = false;
  103. break;
  104. }
  105. /**
  106. * Filters API request arguments for each Install Themes screen tab.
  107. *
  108. * The dynamic portion of the hook name, `$tab`, refers to the theme install
  109. * tabs. Default tabs are 'dashboard', 'search', 'upload', 'featured',
  110. * 'new', and 'updated'.
  111. *
  112. * @since 3.7.0
  113. *
  114. * @param array $args An array of themes API arguments.
  115. */
  116. $args = apply_filters( "install_themes_table_api_args_{$tab}", $args );
  117. if ( ! $args )
  118. return;
  119. $api = themes_api( 'query_themes', $args );
  120. if ( is_wp_error( $api ) )
  121. wp_die( $api->get_error_message() . '</p> <p><a href="#" onclick="document.location.reload(); return false;">' . __( 'Try again' ) . '</a>' );
  122. $this->items = $api->themes;
  123. $this->set_pagination_args( array(
  124. 'total_items' => $api->info['results'],
  125. 'per_page' => $args['per_page'],
  126. 'infinite_scroll' => true,
  127. ) );
  128. }
  129. /**
  130. */
  131. public function no_items() {
  132. _e( 'No themes match your request.' );
  133. }
  134. /**
  135. *
  136. * @global array $tabs
  137. * @global string $tab
  138. * @return array
  139. */
  140. protected function get_views() {
  141. global $tabs, $tab;
  142. $display_tabs = array();
  143. foreach ( (array) $tabs as $action => $text ) {
  144. $current_link_attributes = ( $action === $tab ) ? ' class="current" aria-current="page"' : '';
  145. $href = self_admin_url('theme-install.php?tab=' . $action);
  146. $display_tabs['theme-install-'.$action] = "<a href='$href'$current_link_attributes>$text</a>";
  147. }
  148. return $display_tabs;
  149. }
  150. /**
  151. */
  152. public function display() {
  153. wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' );
  154. ?>
  155. <div class="tablenav top themes">
  156. <div class="alignleft actions">
  157. <?php
  158. /**
  159. * Fires in the Install Themes list table header.
  160. *
  161. * @since 2.8.0
  162. */
  163. do_action( 'install_themes_table_header' );
  164. ?>
  165. </div>
  166. <?php $this->pagination( 'top' ); ?>
  167. <br class="clear" />
  168. </div>
  169. <div id="availablethemes">
  170. <?php $this->display_rows_or_placeholder(); ?>
  171. </div>
  172. <?php
  173. $this->tablenav( 'bottom' );
  174. }
  175. /**
  176. */
  177. public function display_rows() {
  178. $themes = $this->items;
  179. foreach ( $themes as $theme ) {
  180. ?>
  181. <div class="available-theme installable-theme"><?php
  182. $this->single_row( $theme );
  183. ?></div>
  184. <?php } // end foreach $theme_names
  185. $this->theme_installer();
  186. }
  187. /**
  188. * Prints a theme from the WordPress.org API.
  189. *
  190. * @since 3.1.0
  191. *
  192. * @global array $themes_allowedtags
  193. *
  194. * @param object $theme {
  195. * An object that contains theme data returned by the WordPress.org API.
  196. *
  197. * @type string $name Theme name, e.g. 'Twenty Seventeen'.
  198. * @type string $slug Theme slug, e.g. 'twentyseventeen'.
  199. * @type string $version Theme version, e.g. '1.1'.
  200. * @type string $author Theme author username, e.g. 'melchoyce'.
  201. * @type string $preview_url Preview URL, e.g. 'http://2017.wordpress.net/'.
  202. * @type string $screenshot_url Screenshot URL, e.g. 'https://wordpress.org/themes/twentyseventeen/'.
  203. * @type float $rating Rating score.
  204. * @type int $num_ratings The number of ratings.
  205. * @type string $homepage Theme homepage, e.g. 'https://wordpress.org/themes/twentyseventeen/'.
  206. * @type string $description Theme description.
  207. * @type string $download_link Theme ZIP download URL.
  208. * }
  209. */
  210. public function single_row( $theme ) {
  211. global $themes_allowedtags;
  212. if ( empty( $theme ) )
  213. return;
  214. $name = wp_kses( $theme->name, $themes_allowedtags );
  215. $author = wp_kses( $theme->author, $themes_allowedtags );
  216. $preview_title = sprintf( __('Preview &#8220;%s&#8221;'), $name );
  217. $preview_url = add_query_arg( array(
  218. 'tab' => 'theme-information',
  219. 'theme' => $theme->slug,
  220. ), self_admin_url( 'theme-install.php' ) );
  221. $actions = array();
  222. $install_url = add_query_arg( array(
  223. 'action' => 'install-theme',
  224. 'theme' => $theme->slug,
  225. ), self_admin_url( 'update.php' ) );
  226. $update_url = add_query_arg( array(
  227. 'action' => 'upgrade-theme',
  228. 'theme' => $theme->slug,
  229. ), self_admin_url( 'update.php' ) );
  230. $status = $this->_get_theme_status( $theme );
  231. switch ( $status ) {
  232. case 'update_available':
  233. $actions[] = '<a class="install-now" href="' . esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ) . '">' . __( 'Update' ) . '</a>';
  234. break;
  235. case 'newer_installed':
  236. case 'latest_installed':
  237. $actions[] = '<span class="install-now" title="' . esc_attr__( 'This theme is already installed and is up to date' ) . '">' . _x( 'Installed', 'theme' ) . '</span>';
  238. break;
  239. case 'install':
  240. default:
  241. $actions[] = '<a class="install-now" href="' . esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Install %s' ), $name ) ) . '">' . __( 'Install Now' ) . '</a>';
  242. break;
  243. }
  244. $actions[] = '<a class="install-theme-preview" href="' . esc_url( $preview_url ) . '" title="' . esc_attr( sprintf( __( 'Preview %s' ), $name ) ) . '">' . __( 'Preview' ) . '</a>';
  245. /**
  246. * Filters the install action links for a theme in the Install Themes list table.
  247. *
  248. * @since 3.4.0
  249. *
  250. * @param array $actions An array of theme action hyperlinks. Defaults are
  251. * links to Install Now, Preview, and Details.
  252. * @param WP_Theme $theme Theme object.
  253. */
  254. $actions = apply_filters( 'theme_install_actions', $actions, $theme );
  255. ?>
  256. <a class="screenshot install-theme-preview" href="<?php echo esc_url( $preview_url ); ?>" title="<?php echo esc_attr( $preview_title ); ?>">
  257. <img src="<?php echo esc_url( $theme->screenshot_url ); ?>" width="150" alt="" />
  258. </a>
  259. <h3><?php echo $name; ?></h3>
  260. <div class="theme-author"><?php printf( __( 'By %s' ), $author ); ?></div>
  261. <div class="action-links">
  262. <ul>
  263. <?php foreach ( $actions as $action ): ?>
  264. <li><?php echo $action; ?></li>
  265. <?php endforeach; ?>
  266. <li class="hide-if-no-js"><a href="#" class="theme-detail"><?php _e('Details') ?></a></li>
  267. </ul>
  268. </div>
  269. <?php
  270. $this->install_theme_info( $theme );
  271. }
  272. /**
  273. * Prints the wrapper for the theme installer.
  274. */
  275. public function theme_installer() {
  276. ?>
  277. <div id="theme-installer" class="wp-full-overlay expanded">
  278. <div class="wp-full-overlay-sidebar">
  279. <div class="wp-full-overlay-header">
  280. <a href="#" class="close-full-overlay button"><?php _e( 'Close' ); ?></a>
  281. <span class="theme-install"></span>
  282. </div>
  283. <div class="wp-full-overlay-sidebar-content">
  284. <div class="install-theme-info"></div>
  285. </div>
  286. <div class="wp-full-overlay-footer">
  287. <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>">
  288. <span class="collapse-sidebar-arrow"></span>
  289. <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
  290. </button>
  291. </div>
  292. </div>
  293. <div class="wp-full-overlay-main"></div>
  294. </div>
  295. <?php
  296. }
  297. /**
  298. * Prints the wrapper for the theme installer with a provided theme's data.
  299. * Used to make the theme installer work for no-js.
  300. *
  301. * @param object $theme - A WordPress.org Theme API object.
  302. */
  303. public function theme_installer_single( $theme ) {
  304. ?>
  305. <div id="theme-installer" class="wp-full-overlay single-theme">
  306. <div class="wp-full-overlay-sidebar">
  307. <?php $this->install_theme_info( $theme ); ?>
  308. </div>
  309. <div class="wp-full-overlay-main">
  310. <iframe src="<?php echo esc_url( $theme->preview_url ); ?>"></iframe>
  311. </div>
  312. </div>
  313. <?php
  314. }
  315. /**
  316. * Prints the info for a theme (to be used in the theme installer modal).
  317. *
  318. * @global array $themes_allowedtags
  319. *
  320. * @param object $theme - A WordPress.org Theme API object.
  321. */
  322. public function install_theme_info( $theme ) {
  323. global $themes_allowedtags;
  324. if ( empty( $theme ) )
  325. return;
  326. $name = wp_kses( $theme->name, $themes_allowedtags );
  327. $author = wp_kses( $theme->author, $themes_allowedtags );
  328. $install_url = add_query_arg( array(
  329. 'action' => 'install-theme',
  330. 'theme' => $theme->slug,
  331. ), self_admin_url( 'update.php' ) );
  332. $update_url = add_query_arg( array(
  333. 'action' => 'upgrade-theme',
  334. 'theme' => $theme->slug,
  335. ), self_admin_url( 'update.php' ) );
  336. $status = $this->_get_theme_status( $theme );
  337. ?>
  338. <div class="install-theme-info"><?php
  339. switch ( $status ) {
  340. case 'update_available':
  341. echo '<a class="theme-install button button-primary" href="' . esc_url( wp_nonce_url( $update_url, 'upgrade-theme_' . $theme->slug ) ) . '" title="' . esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ) . '">' . __( 'Update' ) . '</a>';
  342. break;
  343. case 'newer_installed':
  344. case 'latest_installed':
  345. echo '<span class="theme-install" title="' . esc_attr__( 'This theme is already installed and is up to date' ) . '">' . _x( 'Installed', 'theme' ) . '</span>';
  346. break;
  347. case 'install':
  348. default:
  349. echo '<a class="theme-install button button-primary" href="' . esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ) . '">' . __( 'Install' ) . '</a>';
  350. break;
  351. } ?>
  352. <h3 class="theme-name"><?php echo $name; ?></h3>
  353. <span class="theme-by"><?php printf( __( 'By %s' ), $author ); ?></span>
  354. <?php if ( isset( $theme->screenshot_url ) ): ?>
  355. <img class="theme-screenshot" src="<?php echo esc_url( $theme->screenshot_url ); ?>" alt="" />
  356. <?php endif; ?>
  357. <div class="theme-details">
  358. <?php wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings ) ); ?>
  359. <div class="theme-version">
  360. <strong><?php _e('Version:') ?> </strong>
  361. <?php echo wp_kses( $theme->version, $themes_allowedtags ); ?>
  362. </div>
  363. <div class="theme-description">
  364. <?php echo wp_kses( $theme->description, $themes_allowedtags ); ?>
  365. </div>
  366. </div>
  367. <input class="theme-preview-url" type="hidden" value="<?php echo esc_url( $theme->preview_url ); ?>" />
  368. </div>
  369. <?php
  370. }
  371. /**
  372. * Send required variables to JavaScript land
  373. *
  374. * @since 3.4.0
  375. *
  376. * @global string $tab Current tab within Themes->Install screen
  377. * @global string $type Type of search.
  378. *
  379. * @param array $extra_args Unused.
  380. */
  381. public function _js_vars( $extra_args = array() ) {
  382. global $tab, $type;
  383. parent::_js_vars( compact( 'tab', 'type' ) );
  384. }
  385. /**
  386. * Check to see if the theme is already installed.
  387. *
  388. * @since 3.4.0
  389. *
  390. * @param object $theme - A WordPress.org Theme API object.
  391. * @return string Theme status.
  392. */
  393. private function _get_theme_status( $theme ) {
  394. $status = 'install';
  395. $installed_theme = wp_get_theme( $theme->slug );
  396. if ( $installed_theme->exists() ) {
  397. if ( version_compare( $installed_theme->get('Version'), $theme->version, '=' ) )
  398. $status = 'latest_installed';
  399. elseif ( version_compare( $installed_theme->get('Version'), $theme->version, '>' ) )
  400. $status = 'newer_installed';
  401. else
  402. $status = 'update_available';
  403. }
  404. return $status;
  405. }
  406. }