class-wp-themes-list-table.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. /**
  3. * List Table API: WP_Themes_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying installed themes in a list table.
  11. *
  12. * @since 3.1.0
  13. * @access private
  14. *
  15. * @see WP_List_Table
  16. */
  17. class WP_Themes_List_Table extends WP_List_Table {
  18. protected $search_terms = array();
  19. public $features = array();
  20. /**
  21. * Constructor.
  22. *
  23. * @since 3.1.0
  24. *
  25. * @see WP_List_Table::__construct() for more information on default arguments.
  26. *
  27. * @param array $args An associative array of arguments.
  28. */
  29. public function __construct( $args = array() ) {
  30. parent::__construct( array(
  31. 'ajax' => true,
  32. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
  33. ) );
  34. }
  35. /**
  36. *
  37. * @return bool
  38. */
  39. public function ajax_user_can() {
  40. // Do not check edit_theme_options here. Ajax calls for available themes require switch_themes.
  41. return current_user_can( 'switch_themes' );
  42. }
  43. /**
  44. */
  45. public function prepare_items() {
  46. $themes = wp_get_themes( array( 'allowed' => true ) );
  47. if ( ! empty( $_REQUEST['s'] ) )
  48. $this->search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', strtolower( wp_unslash( $_REQUEST['s'] ) ) ) ) ) );
  49. if ( ! empty( $_REQUEST['features'] ) )
  50. $this->features = $_REQUEST['features'];
  51. if ( $this->search_terms || $this->features ) {
  52. foreach ( $themes as $key => $theme ) {
  53. if ( ! $this->search_theme( $theme ) )
  54. unset( $themes[ $key ] );
  55. }
  56. }
  57. unset( $themes[ get_option( 'stylesheet' ) ] );
  58. WP_Theme::sort_by_name( $themes );
  59. $per_page = 36;
  60. $page = $this->get_pagenum();
  61. $start = ( $page - 1 ) * $per_page;
  62. $this->items = array_slice( $themes, $start, $per_page, true );
  63. $this->set_pagination_args( array(
  64. 'total_items' => count( $themes ),
  65. 'per_page' => $per_page,
  66. 'infinite_scroll' => true,
  67. ) );
  68. }
  69. /**
  70. */
  71. public function no_items() {
  72. if ( $this->search_terms || $this->features ) {
  73. _e( 'No items found.' );
  74. return;
  75. }
  76. $blog_id = get_current_blog_id();
  77. if ( is_multisite() ) {
  78. if ( current_user_can( 'install_themes' ) && current_user_can( 'manage_network_themes' ) ) {
  79. printf( __( 'You only have one theme enabled for this site right now. Visit the Network Admin to <a href="%1$s">enable</a> or <a href="%2$s">install</a> more themes.' ), network_admin_url( 'site-themes.php?id=' . $blog_id ), network_admin_url( 'theme-install.php' ) );
  80. return;
  81. } elseif ( current_user_can( 'manage_network_themes' ) ) {
  82. printf( __( 'You only have one theme enabled for this site right now. Visit the Network Admin to <a href="%1$s">enable</a> more themes.' ), network_admin_url( 'site-themes.php?id=' . $blog_id ) );
  83. return;
  84. }
  85. // Else, fallthrough. install_themes doesn't help if you can't enable it.
  86. } else {
  87. if ( current_user_can( 'install_themes' ) ) {
  88. printf( __( 'You only have one theme installed right now. Live a little! You can choose from over 1,000 free themes in the WordPress Theme Directory at any time: just click on the <a href="%s">Install Themes</a> tab above.' ), admin_url( 'theme-install.php' ) );
  89. return;
  90. }
  91. }
  92. // Fallthrough.
  93. printf( __( 'Only the current theme is available to you. Contact the %s administrator for information about accessing additional themes.' ), get_site_option( 'site_name' ) );
  94. }
  95. /**
  96. * @param string $which
  97. */
  98. public function tablenav( $which = 'top' ) {
  99. if ( $this->get_pagination_arg( 'total_pages' ) <= 1 )
  100. return;
  101. ?>
  102. <div class="tablenav themes <?php echo $which; ?>">
  103. <?php $this->pagination( $which ); ?>
  104. <span class="spinner"></span>
  105. <br class="clear" />
  106. </div>
  107. <?php
  108. }
  109. /**
  110. */
  111. public function display() {
  112. wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' );
  113. ?>
  114. <?php $this->tablenav( 'top' ); ?>
  115. <div id="availablethemes">
  116. <?php $this->display_rows_or_placeholder(); ?>
  117. </div>
  118. <?php $this->tablenav( 'bottom' ); ?>
  119. <?php
  120. }
  121. /**
  122. *
  123. * @return array
  124. */
  125. public function get_columns() {
  126. return array();
  127. }
  128. /**
  129. */
  130. public function display_rows_or_placeholder() {
  131. if ( $this->has_items() ) {
  132. $this->display_rows();
  133. } else {
  134. echo '<div class="no-items">';
  135. $this->no_items();
  136. echo '</div>';
  137. }
  138. }
  139. /**
  140. */
  141. public function display_rows() {
  142. $themes = $this->items;
  143. foreach ( $themes as $theme ):
  144. ?><div class="available-theme"><?php
  145. $template = $theme->get_template();
  146. $stylesheet = $theme->get_stylesheet();
  147. $title = $theme->display('Name');
  148. $version = $theme->display('Version');
  149. $author = $theme->display('Author');
  150. $activate_link = wp_nonce_url( "themes.php?action=activate&amp;template=" . urlencode( $template ) . "&amp;stylesheet=" . urlencode( $stylesheet ), 'switch-theme_' . $stylesheet );
  151. $actions = array();
  152. $actions['activate'] = '<a href="' . $activate_link . '" class="activatelink" title="'
  153. . esc_attr( sprintf( __( 'Activate &#8220;%s&#8221;' ), $title ) ) . '">' . __( 'Activate' ) . '</a>';
  154. if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
  155. $actions['preview'] .= '<a href="' . wp_customize_url( $stylesheet ) . '" class="load-customize hide-if-no-customize">'
  156. . __( 'Live Preview' ) . '</a>';
  157. }
  158. if ( ! is_multisite() && current_user_can( 'delete_themes' ) )
  159. $actions['delete'] = '<a class="submitdelete deletion" href="' . wp_nonce_url( 'themes.php?action=delete&amp;stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet )
  160. . '" onclick="' . "return confirm( '" . esc_js( sprintf( __( "You are about to delete this theme '%s'\n 'Cancel' to stop, 'OK' to delete." ), $title ) )
  161. . "' );" . '">' . __( 'Delete' ) . '</a>';
  162. /** This filter is documented in wp-admin/includes/class-wp-ms-themes-list-table.php */
  163. $actions = apply_filters( 'theme_action_links', $actions, $theme, 'all' );
  164. /** This filter is documented in wp-admin/includes/class-wp-ms-themes-list-table.php */
  165. $actions = apply_filters( "theme_action_links_$stylesheet", $actions, $theme, 'all' );
  166. $delete_action = isset( $actions['delete'] ) ? '<div class="delete-theme">' . $actions['delete'] . '</div>' : '';
  167. unset( $actions['delete'] );
  168. ?>
  169. <span class="screenshot hide-if-customize">
  170. <?php if ( $screenshot = $theme->get_screenshot() ) : ?>
  171. <img src="<?php echo esc_url( $screenshot ); ?>" alt="" />
  172. <?php endif; ?>
  173. </span>
  174. <a href="<?php echo wp_customize_url( $stylesheet ); ?>" class="screenshot load-customize hide-if-no-customize">
  175. <?php if ( $screenshot = $theme->get_screenshot() ) : ?>
  176. <img src="<?php echo esc_url( $screenshot ); ?>" alt="" />
  177. <?php endif; ?>
  178. </a>
  179. <h3><?php echo $title; ?></h3>
  180. <div class="theme-author"><?php printf( __( 'By %s' ), $author ); ?></div>
  181. <div class="action-links">
  182. <ul>
  183. <?php foreach ( $actions as $action ): ?>
  184. <li><?php echo $action; ?></li>
  185. <?php endforeach; ?>
  186. <li class="hide-if-no-js"><a href="#" class="theme-detail"><?php _e('Details') ?></a></li>
  187. </ul>
  188. <?php echo $delete_action; ?>
  189. <?php theme_update_available( $theme ); ?>
  190. </div>
  191. <div class="themedetaildiv hide-if-js">
  192. <p><strong><?php _e('Version:'); ?></strong> <?php echo $version; ?></p>
  193. <p><?php echo $theme->display('Description'); ?></p>
  194. <?php if ( $theme->parent() ) {
  195. printf( ' <p class="howto">' . __( 'This <a href="%1$s">child theme</a> requires its parent theme, %2$s.' ) . '</p>',
  196. __( 'https://codex.wordpress.org/Child_Themes' ),
  197. $theme->parent()->display( 'Name' ) );
  198. } ?>
  199. </div>
  200. </div>
  201. <?php
  202. endforeach;
  203. }
  204. /**
  205. * @param WP_Theme $theme
  206. * @return bool
  207. */
  208. public function search_theme( $theme ) {
  209. // Search the features
  210. foreach ( $this->features as $word ) {
  211. if ( ! in_array( $word, $theme->get('Tags') ) )
  212. return false;
  213. }
  214. // Match all phrases
  215. foreach ( $this->search_terms as $word ) {
  216. if ( in_array( $word, $theme->get('Tags') ) )
  217. continue;
  218. foreach ( array( 'Name', 'Description', 'Author', 'AuthorURI' ) as $header ) {
  219. // Don't mark up; Do translate.
  220. if ( false !== stripos( strip_tags( $theme->display( $header, false, true ) ), $word ) ) {
  221. continue 2;
  222. }
  223. }
  224. if ( false !== stripos( $theme->get_stylesheet(), $word ) )
  225. continue;
  226. if ( false !== stripos( $theme->get_template(), $word ) )
  227. continue;
  228. return false;
  229. }
  230. return true;
  231. }
  232. /**
  233. * Send required variables to JavaScript land
  234. *
  235. * @since 3.4.0
  236. *
  237. * @param array $extra_args
  238. */
  239. public function _js_vars( $extra_args = array() ) {
  240. $search_string = isset( $_REQUEST['s'] ) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : '';
  241. $args = array(
  242. 'search' => $search_string,
  243. 'features' => $this->features,
  244. 'paged' => $this->get_pagenum(),
  245. 'total_pages' => ! empty( $this->_pagination_args['total_pages'] ) ? $this->_pagination_args['total_pages'] : 1,
  246. );
  247. if ( is_array( $extra_args ) )
  248. $args = array_merge( $args, $extra_args );
  249. printf( "<script type='text/javascript'>var theme_list_args = %s;</script>\n", wp_json_encode( $args ) );
  250. parent::_js_vars();
  251. }
  252. }