/wp-admin/includes/class-wp-posts-list-table.php

  1. <?php 
  2. /** 
  3. * List Table API: WP_Posts_List_Table class 
  4. * 
  5. * @package WordPress 
  6. * @subpackage Administration 
  7. * @since 3.1.0 
  8. */ 
  9.  
  10. /** 
  11. * Core class used to implement displaying posts in a list table. 
  12. * 
  13. * @since 3.1.0 
  14. * @access private 
  15. * 
  16. * @see WP_List_Table 
  17. */ 
  18. class WP_Posts_List_Table extends WP_List_Table { 
  19.  
  20. /** 
  21. * Whether the items should be displayed hierarchically or linearly. 
  22. * 
  23. * @since 3.1.0 
  24. * @var bool 
  25. * @access protected 
  26. */ 
  27. protected $hierarchical_display; 
  28.  
  29. /** 
  30. * Holds the number of pending comments for each post. 
  31. * 
  32. * @since 3.1.0 
  33. * @var array 
  34. * @access protected 
  35. */ 
  36. protected $comment_pending_count; 
  37.  
  38. /** 
  39. * Holds the number of posts for this user. 
  40. * 
  41. * @since 3.1.0 
  42. * @var int 
  43. * @access private 
  44. */ 
  45. private $user_posts_count; 
  46.  
  47. /** 
  48. * Holds the number of posts which are sticky. 
  49. * 
  50. * @since 3.1.0 
  51. * @var int 
  52. * @access private 
  53. */ 
  54. private $sticky_posts_count = 0; 
  55.  
  56. private $is_trash; 
  57.  
  58. /** 
  59. * Current level for output. 
  60. * 
  61. * @since 4.3.0 
  62. * @access protected 
  63. * @var int 
  64. */ 
  65. protected $current_level = 0; 
  66.  
  67. /** 
  68. * Constructor. 
  69. * 
  70. * @since 3.1.0 
  71. * @access public 
  72. * 
  73. * @see WP_List_Table::__construct() for more information on default arguments. 
  74. * 
  75. * @global WP_Post_Type $post_type_object 
  76. * @global wpdb $wpdb 
  77. * 
  78. * @param array $args An associative array of arguments. 
  79. */ 
  80. public function __construct( $args = array() ) { 
  81. global $post_type_object, $wpdb; 
  82.  
  83. parent::__construct( array( 
  84. 'plural' => 'posts',  
  85. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,  
  86. ) ); 
  87.  
  88. $post_type = $this->screen->post_type; 
  89. $post_type_object = get_post_type_object( $post_type ); 
  90.  
  91. $exclude_states = get_post_stati( array( 
  92. 'show_in_admin_all_list' => false,  
  93. ) ); 
  94. $this->user_posts_count = intval( $wpdb->get_var( $wpdb->prepare( " 
  95. SELECT COUNT( 1 ) 
  96. FROM $wpdb->posts 
  97. WHERE post_type = %s 
  98. AND post_status NOT IN ( '" . implode( "', '", $exclude_states ) . "' ) 
  99. AND post_author = %d 
  100. ", $post_type, get_current_user_id() ) ) ); 
  101.  
  102. if ( $this->user_posts_count && ! current_user_can( $post_type_object->cap->edit_others_posts ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['all_posts'] ) && empty( $_REQUEST['author'] ) && empty( $_REQUEST['show_sticky'] ) ) { 
  103. $_GET['author'] = get_current_user_id(); 
  104.  
  105. if ( 'post' === $post_type && $sticky_posts = get_option( 'sticky_posts' ) ) { 
  106. $sticky_posts = implode( ', ', array_map( 'absint', (array) $sticky_posts ) ); 
  107. $this->sticky_posts_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( 1 ) FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('trash', 'auto-draft') AND ID IN ($sticky_posts)", $post_type ) ); 
  108.  
  109. /** 
  110. * Sets whether the table layout should be hierarchical or not. 
  111. * 
  112. * @since 4.2.0 
  113. * 
  114. * @param bool $display Whether the table layout should be hierarchical. 
  115. */ 
  116. public function set_hierarchical_display( $display ) { 
  117. $this->hierarchical_display = $display; 
  118.  
  119. /** 
  120. * 
  121. * @return bool 
  122. */ 
  123. public function ajax_user_can() { 
  124. return current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_posts ); 
  125.  
  126. /** 
  127. * 
  128. * @global array $avail_post_stati 
  129. * @global WP_Query $wp_query 
  130. * @global int $per_page 
  131. * @global string $mode 
  132. */ 
  133. public function prepare_items() { 
  134. global $avail_post_stati, $wp_query, $per_page, $mode; 
  135.  
  136. // is going to call wp() 
  137. $avail_post_stati = wp_edit_posts_query(); 
  138.  
  139. $this->set_hierarchical_display( is_post_type_hierarchical( $this->screen->post_type ) && 'menu_order title' === $wp_query->query['orderby'] ); 
  140.  
  141. $post_type = $this->screen->post_type; 
  142. $per_page = $this->get_items_per_page( 'edit_' . $post_type . '_per_page' ); 
  143.  
  144. /** This filter is documented in wp-admin/includes/post.php */ 
  145. $per_page = apply_filters( 'edit_posts_per_page', $per_page, $post_type ); 
  146.  
  147. if ( $this->hierarchical_display ) { 
  148. $total_items = $wp_query->post_count; 
  149. } elseif ( $wp_query->found_posts || $this->get_pagenum() === 1 ) { 
  150. $total_items = $wp_query->found_posts; 
  151. } else { 
  152. $post_counts = (array) wp_count_posts( $post_type, 'readable' ); 
  153.  
  154. if ( isset( $_REQUEST['post_status'] ) && in_array( $_REQUEST['post_status'] , $avail_post_stati ) ) { 
  155. $total_items = $post_counts[ $_REQUEST['post_status'] ]; 
  156. } elseif ( isset( $_REQUEST['show_sticky'] ) && $_REQUEST['show_sticky'] ) { 
  157. $total_items = $this->sticky_posts_count; 
  158. } elseif ( isset( $_GET['author'] ) && $_GET['author'] == get_current_user_id() ) { 
  159. $total_items = $this->user_posts_count; 
  160. } else { 
  161. $total_items = array_sum( $post_counts ); 
  162.  
  163. // Subtract post types that are not included in the admin all list. 
  164. foreach ( get_post_stati( array( 'show_in_admin_all_list' => false ) ) as $state ) { 
  165. $total_items -= $post_counts[ $state ]; 
  166.  
  167. if ( ! empty( $_REQUEST['mode'] ) ) { 
  168. $mode = $_REQUEST['mode'] === 'excerpt' ? 'excerpt' : 'list'; 
  169. set_user_setting( 'posts_list_mode', $mode ); 
  170. } else { 
  171. $mode = get_user_setting( 'posts_list_mode', 'list' ); 
  172.  
  173. $this->is_trash = isset( $_REQUEST['post_status'] ) && $_REQUEST['post_status'] === 'trash'; 
  174.  
  175. $this->set_pagination_args( array( 
  176. 'total_items' => $total_items,  
  177. 'per_page' => $per_page 
  178. ) ); 
  179.  
  180. /** 
  181. * 
  182. * @return bool 
  183. */ 
  184. public function has_items() { 
  185. return have_posts(); 
  186.  
  187. /** 
  188. * @access public 
  189. */ 
  190. public function no_items() { 
  191. if ( isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'] ) 
  192. echo get_post_type_object( $this->screen->post_type )->labels->not_found_in_trash; 
  193. else 
  194. echo get_post_type_object( $this->screen->post_type )->labels->not_found; 
  195.  
  196. /** 
  197. * Determine if the current view is the "All" view. 
  198. * 
  199. * @since 4.2.0 
  200. * 
  201. * @return bool Whether the current view is the "All" view. 
  202. */ 
  203. protected function is_base_request() { 
  204. $vars = $_GET; 
  205. unset( $vars['paged'] ); 
  206.  
  207. if ( empty( $vars ) ) { 
  208. return true; 
  209. } elseif ( 1 === count( $vars ) && ! empty( $vars['post_type'] ) ) { 
  210. return $this->screen->post_type === $vars['post_type']; 
  211.  
  212. return 1 === count( $vars ) && ! empty( $vars['mode'] ); 
  213.  
  214. /** 
  215. * Helper to create links to edit.php with params. 
  216. * 
  217. * @since 4.4.0 
  218. * @access protected 
  219. * 
  220. * @param array $args URL parameters for the link. 
  221. * @param string $label Link text. 
  222. * @param string $class Optional. Class attribute. Default empty string. 
  223. * @return string The formatted link string. 
  224. */ 
  225. protected function get_edit_link( $args, $label, $class = '' ) { 
  226. $url = add_query_arg( $args, 'edit.php' ); 
  227.  
  228. $class_html = ''; 
  229. if ( ! empty( $class ) ) { 
  230. $class_html = sprintf( 
  231. ' class="%s"',  
  232. esc_attr( $class ) 
  233. ); 
  234.  
  235. return sprintf( 
  236. '<a href="%s"%s>%s</a>',  
  237. esc_url( $url ),  
  238. $class_html,  
  239. $label 
  240. ); 
  241.  
  242. /** 
  243. * 
  244. * @global array $locked_post_status This seems to be deprecated. 
  245. * @global array $avail_post_stati 
  246. * @return array 
  247. */ 
  248. protected function get_views() { 
  249. global $locked_post_status, $avail_post_stati; 
  250.  
  251. $post_type = $this->screen->post_type; 
  252.  
  253. if ( !empty($locked_post_status) ) 
  254. return array(); 
  255.  
  256. $status_links = array(); 
  257. $num_posts = wp_count_posts( $post_type, 'readable' ); 
  258. $total_posts = array_sum( (array) $num_posts ); 
  259. $class = ''; 
  260.  
  261. $current_user_id = get_current_user_id(); 
  262. $all_args = array( 'post_type' => $post_type ); 
  263. $mine = ''; 
  264.  
  265. // Subtract post types that are not included in the admin all list. 
  266. foreach ( get_post_stati( array( 'show_in_admin_all_list' => false ) ) as $state ) { 
  267. $total_posts -= $num_posts->$state; 
  268.  
  269. if ( $this->user_posts_count && $this->user_posts_count !== $total_posts ) { 
  270. if ( isset( $_GET['author'] ) && ( $_GET['author'] == $current_user_id ) ) { 
  271. $class = 'current'; 
  272.  
  273. $mine_args = array( 
  274. 'post_type' => $post_type,  
  275. 'author' => $current_user_id 
  276. ); 
  277.  
  278. $mine_inner_html = sprintf( 
  279. _nx( 
  280. 'Mine <span class="count">(%s)</span>',  
  281. 'Mine <span class="count">(%s)</span>',  
  282. $this->user_posts_count,  
  283. 'posts' 
  284. ),  
  285. number_format_i18n( $this->user_posts_count ) 
  286. ); 
  287.  
  288. $mine = $this->get_edit_link( $mine_args, $mine_inner_html, $class ); 
  289.  
  290. $all_args['all_posts'] = 1; 
  291. $class = ''; 
  292.  
  293. if ( empty( $class ) && ( $this->is_base_request() || isset( $_REQUEST['all_posts'] ) ) ) { 
  294. $class = 'current'; 
  295.  
  296. $all_inner_html = sprintf( 
  297. _nx( 
  298. 'All <span class="count">(%s)</span>',  
  299. 'All <span class="count">(%s)</span>',  
  300. $total_posts,  
  301. 'posts' 
  302. ),  
  303. number_format_i18n( $total_posts ) 
  304. ); 
  305.  
  306. $status_links['all'] = $this->get_edit_link( $all_args, $all_inner_html, $class ); 
  307. if ( $mine ) { 
  308. $status_links['mine'] = $mine; 
  309.  
  310. foreach ( get_post_stati(array('show_in_admin_status_list' => true), 'objects') as $status ) { 
  311. $class = ''; 
  312.  
  313. $status_name = $status->name; 
  314.  
  315. if ( ! in_array( $status_name, $avail_post_stati ) || empty( $num_posts->$status_name ) ) { 
  316. continue; 
  317.  
  318. if ( isset($_REQUEST['post_status']) && $status_name === $_REQUEST['post_status'] ) { 
  319. $class = 'current'; 
  320.  
  321. $status_args = array( 
  322. 'post_status' => $status_name,  
  323. 'post_type' => $post_type,  
  324. ); 
  325.  
  326. $status_label = sprintf( 
  327. translate_nooped_plural( $status->label_count, $num_posts->$status_name ),  
  328. number_format_i18n( $num_posts->$status_name ) 
  329. ); 
  330.  
  331. $status_links[ $status_name ] = $this->get_edit_link( $status_args, $status_label, $class ); 
  332.  
  333. if ( ! empty( $this->sticky_posts_count ) ) { 
  334. $class = ! empty( $_REQUEST['show_sticky'] ) ? 'current' : ''; 
  335.  
  336. $sticky_args = array( 
  337. 'post_type' => $post_type,  
  338. 'show_sticky' => 1 
  339. ); 
  340.  
  341. $sticky_inner_html = sprintf( 
  342. _nx( 
  343. 'Sticky <span class="count">(%s)</span>',  
  344. 'Sticky <span class="count">(%s)</span>',  
  345. $this->sticky_posts_count,  
  346. 'posts' 
  347. ),  
  348. number_format_i18n( $this->sticky_posts_count ) 
  349. ); 
  350.  
  351. $sticky_link = array( 
  352. 'sticky' => $this->get_edit_link( $sticky_args, $sticky_inner_html, $class ) 
  353. ); 
  354.  
  355. // Sticky comes after Publish, or if not listed, after All. 
  356. $split = 1 + array_search( ( isset( $status_links['publish'] ) ? 'publish' : 'all' ), array_keys( $status_links ) ); 
  357. $status_links = array_merge( array_slice( $status_links, 0, $split ), $sticky_link, array_slice( $status_links, $split ) ); 
  358.  
  359. return $status_links; 
  360.  
  361. /** 
  362. * 
  363. * @return array 
  364. */ 
  365. protected function get_bulk_actions() { 
  366. $actions = array(); 
  367. $post_type_obj = get_post_type_object( $this->screen->post_type ); 
  368.  
  369. if ( current_user_can( $post_type_obj->cap->edit_posts ) ) { 
  370. if ( $this->is_trash ) { 
  371. $actions['untrash'] = __( 'Restore' ); 
  372. } else { 
  373. $actions['edit'] = __( 'Edit' ); 
  374.  
  375. if ( current_user_can( $post_type_obj->cap->delete_posts ) ) { 
  376. if ( $this->is_trash || ! EMPTY_TRASH_DAYS ) { 
  377. $actions['delete'] = __( 'Delete Permanently' ); 
  378. } else { 
  379. $actions['trash'] = __( 'Move to Trash' ); 
  380.  
  381. return $actions; 
  382.  
  383. /** 
  384. * Displays a categories drop-down for filtering on the Posts list table. 
  385. * 
  386. * @since 4.6.0 
  387. * @access protected 
  388. * 
  389. * @global int $cat Currently selected category. 
  390. * 
  391. * @param string $post_type Post type slug. 
  392. */ 
  393. protected function categories_dropdown( $post_type ) { 
  394. global $cat; 
  395.  
  396. /** 
  397. * Filters whether to remove the 'Categories' drop-down from the post list table. 
  398. * 
  399. * @since 4.6.0 
  400. * 
  401. * @param bool $disable Whether to disable the categories drop-down. Default false. 
  402. * @param string $post_type Post type slug. 
  403. */ 
  404. if ( false !== apply_filters( 'disable_categories_dropdown', false, $post_type ) ) { 
  405. return; 
  406.  
  407. if ( is_object_in_taxonomy( $post_type, 'category' ) ) { 
  408. $dropdown_options = array( 
  409. 'show_option_all' => get_taxonomy( 'category' )->labels->all_items,  
  410. 'hide_empty' => 0,  
  411. 'hierarchical' => 1,  
  412. 'show_count' => 0,  
  413. 'orderby' => 'name',  
  414. 'selected' => $cat 
  415. ); 
  416.  
  417. echo '<label class="screen-reader-text" for="cat">' . __( 'Filter by category' ) . '</label>'; 
  418. wp_dropdown_categories( $dropdown_options ); 
  419.  
  420. /** 
  421. * @param string $which 
  422. */ 
  423. protected function extra_tablenav( $which ) { 
  424. ?> 
  425. <div class="alignleft actions"> 
  426. <?php 
  427. if ( 'top' === $which && !is_singular() ) { 
  428. ob_start(); 
  429.  
  430. $this->months_dropdown( $this->screen->post_type ); 
  431. $this->categories_dropdown( $this->screen->post_type ); 
  432.  
  433. /** 
  434. * Fires before the Filter button on the Posts and Pages list tables. 
  435. * 
  436. * The Filter button allows sorting by date and/or category on the 
  437. * Posts list table, and sorting by date on the Pages list table. 
  438. * 
  439. * @since 2.1.0 
  440. * @since 4.4.0 The `$post_type` parameter was added. 
  441. * @since 4.6.0 The `$which` parameter was added. 
  442. * 
  443. * @param string $post_type The post type slug. 
  444. * @param string $which The location of the extra table nav markup: 
  445. * 'top' or 'bottom'. 
  446. */ 
  447. do_action( 'restrict_manage_posts', $this->screen->post_type, $which ); 
  448.  
  449. $output = ob_get_clean(); 
  450.  
  451. if ( ! empty( $output ) ) { 
  452. echo $output; 
  453. submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) ); 
  454.  
  455. if ( $this->is_trash && current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_others_posts ) ) { 
  456. submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false ); 
  457. ?> 
  458. </div> 
  459. <?php 
  460. /** 
  461. * Fires immediately following the closing "actions" div in the tablenav for the posts 
  462. * list table. 
  463. * 
  464. * @since 4.4.0 
  465. * 
  466. * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. 
  467. */ 
  468. do_action( 'manage_posts_extra_tablenav', $which ); 
  469.  
  470. /** 
  471. * 
  472. * @return string 
  473. */ 
  474. public function current_action() { 
  475. if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) 
  476. return 'delete_all'; 
  477.  
  478. return parent::current_action(); 
  479.  
  480. /** 
  481. * 
  482. * @return array 
  483. */ 
  484. protected function get_table_classes() { 
  485. return array( 'widefat', 'fixed', 'striped', is_post_type_hierarchical( $this->screen->post_type ) ? 'pages' : 'posts' ); 
  486.  
  487. /** 
  488. * 
  489. * @return array 
  490. */ 
  491. public function get_columns() { 
  492. $post_type = $this->screen->post_type; 
  493.  
  494. $posts_columns = array(); 
  495.  
  496. $posts_columns['cb'] = '<input type="checkbox" />'; 
  497.  
  498. /** translators: manage posts column name */ 
  499. $posts_columns['title'] = _x( 'Title', 'column name' ); 
  500.  
  501. if ( post_type_supports( $post_type, 'author' ) ) { 
  502. $posts_columns['author'] = __( 'Author' ); 
  503.  
  504. $taxonomies = get_object_taxonomies( $post_type, 'objects' ); 
  505. $taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' ); 
  506.  
  507. /** 
  508. * Filters the taxonomy columns in the Posts list table. 
  509. * 
  510. * The dynamic portion of the hook name, `$post_type`, refers to the post 
  511. * type slug. 
  512. * 
  513. * @since 3.5.0 
  514. * 
  515. * @param array $taxonomies Array of taxonomies to show columns for. 
  516. * @param string $post_type The post type. 
  517. */ 
  518. $taxonomies = apply_filters( "manage_taxonomies_for_{$post_type}_columns", $taxonomies, $post_type ); 
  519. $taxonomies = array_filter( $taxonomies, 'taxonomy_exists' ); 
  520.  
  521. foreach ( $taxonomies as $taxonomy ) { 
  522. if ( 'category' === $taxonomy ) 
  523. $column_key = 'categories'; 
  524. elseif ( 'post_tag' === $taxonomy ) 
  525. $column_key = 'tags'; 
  526. else 
  527. $column_key = 'taxonomy-' . $taxonomy; 
  528.  
  529. $posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name; 
  530.  
  531. $post_status = !empty( $_REQUEST['post_status'] ) ? $_REQUEST['post_status'] : 'all'; 
  532. if ( post_type_supports( $post_type, 'comments' ) && !in_array( $post_status, array( 'pending', 'draft', 'future' ) ) ) 
  533. $posts_columns['comments'] = '<span class="vers comment-grey-bubble" title="' . esc_attr__( 'Comments' ) . '"><span class="screen-reader-text">' . __( 'Comments' ) . '</span></span>'; 
  534.  
  535. $posts_columns['date'] = __( 'Date' ); 
  536.  
  537. if ( 'page' === $post_type ) { 
  538.  
  539. /** 
  540. * Filters the columns displayed in the Pages list table. 
  541. * 
  542. * @since 2.5.0 
  543. * 
  544. * @param array $post_columns An array of column names. 
  545. */ 
  546. $posts_columns = apply_filters( 'manage_pages_columns', $posts_columns ); 
  547. } else { 
  548.  
  549. /** 
  550. * Filters the columns displayed in the Posts list table. 
  551. * 
  552. * @since 1.5.0 
  553. * 
  554. * @param array $posts_columns An array of column names. 
  555. * @param string $post_type The post type slug. 
  556. */ 
  557. $posts_columns = apply_filters( 'manage_posts_columns', $posts_columns, $post_type ); 
  558.  
  559. /** 
  560. * Filters the columns displayed in the Posts list table for a specific post type. 
  561. * 
  562. * The dynamic portion of the hook name, `$post_type`, refers to the post type slug. 
  563. * 
  564. * @since 3.0.0 
  565. * 
  566. * @param array $post_columns An array of column names. 
  567. */ 
  568. return apply_filters( "manage_{$post_type}_posts_columns", $posts_columns ); 
  569.  
  570. /** 
  571. * 
  572. * @return array 
  573. */ 
  574. protected function get_sortable_columns() { 
  575. return array( 
  576. 'title' => 'title',  
  577. 'parent' => 'parent',  
  578. 'comments' => 'comment_count',  
  579. 'date' => array( 'date', true ) 
  580. ); 
  581.  
  582. /** 
  583. * @global WP_Query $wp_query 
  584. * @global int $per_page 
  585. * @param array $posts 
  586. * @param int $level 
  587. */ 
  588. public function display_rows( $posts = array(), $level = 0 ) { 
  589. global $wp_query, $per_page; 
  590.  
  591. if ( empty( $posts ) ) 
  592. $posts = $wp_query->posts; 
  593.  
  594. add_filter( 'the_title', 'esc_html' ); 
  595.  
  596. if ( $this->hierarchical_display ) { 
  597. $this->_display_rows_hierarchical( $posts, $this->get_pagenum(), $per_page ); 
  598. } else { 
  599. $this->_display_rows( $posts, $level ); 
  600.  
  601. /** 
  602. * @param array $posts 
  603. * @param int $level 
  604. */ 
  605. private function _display_rows( $posts, $level = 0 ) { 
  606. // Create array of post IDs. 
  607. $post_ids = array(); 
  608.  
  609. foreach ( $posts as $a_post ) 
  610. $post_ids[] = $a_post->ID; 
  611.  
  612. $this->comment_pending_count = get_pending_comments_num( $post_ids ); 
  613.  
  614. foreach ( $posts as $post ) 
  615. $this->single_row( $post, $level ); 
  616.  
  617. /** 
  618. * @global wpdb $wpdb 
  619. * @global WP_Post $post 
  620. * @param array $pages 
  621. * @param int $pagenum 
  622. * @param int $per_page 
  623. */ 
  624. private function _display_rows_hierarchical( $pages, $pagenum = 1, $per_page = 20 ) { 
  625. global $wpdb; 
  626.  
  627. $level = 0; 
  628.  
  629. if ( ! $pages ) { 
  630. $pages = get_pages( array( 'sort_column' => 'menu_order' ) ); 
  631.  
  632. if ( ! $pages ) 
  633. return; 
  634.  
  635. /** 
  636. * Arrange pages into two parts: top level pages and children_pages 
  637. * children_pages is two dimensional array, eg. 
  638. * children_pages[10][] contains all sub-pages whose parent is 10. 
  639. * It only takes O( N ) to arrange this and it takes O( 1 ) for subsequent lookup operations 
  640. * If searching, ignore hierarchy and treat everything as top level 
  641. */ 
  642. if ( empty( $_REQUEST['s'] ) ) { 
  643.  
  644. $top_level_pages = array(); 
  645. $children_pages = array(); 
  646.  
  647. foreach ( $pages as $page ) { 
  648.  
  649. // Catch and repair bad pages. 
  650. if ( $page->post_parent == $page->ID ) { 
  651. $page->post_parent = 0; 
  652. $wpdb->update( $wpdb->posts, array( 'post_parent' => 0 ), array( 'ID' => $page->ID ) ); 
  653. clean_post_cache( $page ); 
  654.  
  655. if ( 0 == $page->post_parent ) 
  656. $top_level_pages[] = $page; 
  657. else 
  658. $children_pages[ $page->post_parent ][] = $page; 
  659.  
  660. $pages = &$top_level_pages; 
  661.  
  662. $count = 0; 
  663. $start = ( $pagenum - 1 ) * $per_page; 
  664. $end = $start + $per_page; 
  665. $to_display = array(); 
  666.  
  667. foreach ( $pages as $page ) { 
  668. if ( $count >= $end ) 
  669. break; 
  670.  
  671. if ( $count >= $start ) { 
  672. $to_display[$page->ID] = $level; 
  673.  
  674. $count++; 
  675.  
  676. if ( isset( $children_pages ) ) 
  677. $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page, $to_display ); 
  678.  
  679. // If it is the last pagenum and there are orphaned pages, display them with paging as well. 
  680. if ( isset( $children_pages ) && $count < $end ) { 
  681. foreach ( $children_pages as $orphans ) { 
  682. foreach ( $orphans as $op ) { 
  683. if ( $count >= $end ) 
  684. break; 
  685.  
  686. if ( $count >= $start ) { 
  687. $to_display[$op->ID] = 0; 
  688.  
  689. $count++; 
  690.  
  691. $ids = array_keys( $to_display ); 
  692. _prime_post_caches( $ids ); 
  693.  
  694. if ( ! isset( $GLOBALS['post'] ) ) { 
  695. $GLOBALS['post'] = reset( $ids ); 
  696.  
  697. foreach ( $to_display as $page_id => $level ) { 
  698. echo "\t"; 
  699. $this->single_row( $page_id, $level ); 
  700.  
  701. /** 
  702. * Given a top level page ID, display the nested hierarchy of sub-pages 
  703. * together with paging support 
  704. * 
  705. * @since 3.1.0 (Standalone function exists since 2.6.0) 
  706. * @since 4.2.0 Added the `$to_display` parameter. 
  707. * 
  708. * @param array $children_pages 
  709. * @param int $count 
  710. * @param int $parent 
  711. * @param int $level 
  712. * @param int $pagenum 
  713. * @param int $per_page 
  714. * @param array $to_display List of pages to be displayed. Passed by reference. 
  715. */ 
  716. private function _page_rows( &$children_pages, &$count, $parent, $level, $pagenum, $per_page, &$to_display ) { 
  717. if ( ! isset( $children_pages[$parent] ) ) 
  718. return; 
  719.  
  720. $start = ( $pagenum - 1 ) * $per_page; 
  721. $end = $start + $per_page; 
  722.  
  723. foreach ( $children_pages[$parent] as $page ) { 
  724. if ( $count >= $end ) 
  725. break; 
  726.  
  727. // If the page starts in a subtree, print the parents. 
  728. if ( $count == $start && $page->post_parent > 0 ) { 
  729. $my_parents = array(); 
  730. $my_parent = $page->post_parent; 
  731. while ( $my_parent ) { 
  732. // Get the ID from the list or the attribute if my_parent is an object 
  733. $parent_id = $my_parent; 
  734. if ( is_object( $my_parent ) ) { 
  735. $parent_id = $my_parent->ID; 
  736.  
  737. $my_parent = get_post( $parent_id ); 
  738. $my_parents[] = $my_parent; 
  739. if ( !$my_parent->post_parent ) 
  740. break; 
  741. $my_parent = $my_parent->post_parent; 
  742. $num_parents = count( $my_parents ); 
  743. while ( $my_parent = array_pop( $my_parents ) ) { 
  744. $to_display[$my_parent->ID] = $level - $num_parents; 
  745. $num_parents--; 
  746.  
  747. if ( $count >= $start ) { 
  748. $to_display[$page->ID] = $level; 
  749.  
  750. $count++; 
  751.  
  752. $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page, $to_display ); 
  753.  
  754. unset( $children_pages[$parent] ); //required in order to keep track of orphans 
  755.  
  756. /** 
  757. * Handles the checkbox column output. 
  758. * 
  759. * @since 4.3.0 
  760. * @access public 
  761. * 
  762. * @param WP_Post $post The current WP_Post object. 
  763. */ 
  764. public function column_cb( $post ) { 
  765. if ( current_user_can( 'edit_post', $post->ID ) ): ?> 
  766. <label class="screen-reader-text" for="cb-select-<?php the_ID(); ?>"><?php 
  767. printf( __( 'Select %s' ), _draft_or_post_title() ); 
  768. ?></label> 
  769. <input id="cb-select-<?php the_ID(); ?>" type="checkbox" name="post[]" value="<?php the_ID(); ?>" /> 
  770. <div class="locked-indicator"> 
  771. <span class="locked-indicator-icon" aria-hidden="true"></span> 
  772. <span class="screen-reader-text"><?php 
  773. printf( 
  774. /** translators: %s: post title */ 
  775. __( '“%s” is locked' ),  
  776. _draft_or_post_title() 
  777. ); 
  778. ?></span> 
  779. </div> 
  780. <?php endif; 
  781.  
  782. /** 
  783. * @since 4.3.0 
  784. * @access protected 
  785. * 
  786. * @param WP_Post $post 
  787. * @param string $classes 
  788. * @param string $data 
  789. * @param string $primary 
  790. */ 
  791. protected function _column_title( $post, $classes, $data, $primary ) { 
  792. echo '<td class="' . $classes . ' page-title" ', $data, '>'; 
  793. echo $this->column_title( $post ); 
  794. echo $this->handle_row_actions( $post, 'title', $primary ); 
  795. echo '</td>'; 
  796.  
  797. /** 
  798. * Handles the title column output. 
  799. * 
  800. * @since 4.3.0 
  801. * @access public 
  802. * 
  803. * @global string $mode 
  804. * 
  805. * @param WP_Post $post The current WP_Post object. 
  806. */ 
  807. public function column_title( $post ) { 
  808. global $mode; 
  809.  
  810. if ( $this->hierarchical_display ) { 
  811. if ( 0 === $this->current_level && (int) $post->post_parent > 0 ) { 
  812. // Sent level 0 by accident, by default, or because we don't know the actual level. 
  813. $find_main_page = (int) $post->post_parent; 
  814. while ( $find_main_page > 0 ) { 
  815. $parent = get_post( $find_main_page ); 
  816.  
  817. if ( is_null( $parent ) ) { 
  818. break; 
  819.  
  820. $this->current_level++; 
  821. $find_main_page = (int) $parent->post_parent; 
  822.  
  823. if ( ! isset( $parent_name ) ) { 
  824. /** This filter is documented in wp-includes/post-template.php */ 
  825. $parent_name = apply_filters( 'the_title', $parent->post_title, $parent->ID ); 
  826.  
  827. $can_edit_post = current_user_can( 'edit_post', $post->ID ); 
  828.  
  829. if ( $can_edit_post && $post->post_status != 'trash' ) { 
  830. $lock_holder = wp_check_post_lock( $post->ID ); 
  831.  
  832. if ( $lock_holder ) { 
  833. $lock_holder = get_userdata( $lock_holder ); 
  834. $locked_avatar = get_avatar( $lock_holder->ID, 18 ); 
  835. $locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) ); 
  836. } else { 
  837. $locked_avatar = $locked_text = ''; 
  838.  
  839. echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n"; 
  840.  
  841. $pad = str_repeat( '— ', $this->current_level ); 
  842. echo "<strong>"; 
  843.  
  844. $format = get_post_format( $post->ID ); 
  845. if ( $format ) { 
  846. $label = get_post_format_string( $format ); 
  847.  
  848. $format_class = 'post-state-format post-format-icon post-format-' . $format; 
  849.  
  850. $format_args = array( 
  851. 'post_format' => $format,  
  852. 'post_type' => $post->post_type 
  853. ); 
  854.  
  855. echo $this->get_edit_link( $format_args, $label . ':', $format_class ); 
  856.  
  857. $title = _draft_or_post_title(); 
  858.  
  859. if ( $can_edit_post && $post->post_status != 'trash' ) { 
  860. printf( 
  861. '<a class="row-title" href="%s" aria-label="%s">%s%s</a>',  
  862. get_edit_post_link( $post->ID ),  
  863. /** translators: %s: post title */ 
  864. esc_attr( sprintf( __( '“%s” (Edit)' ), $title ) ),  
  865. $pad,  
  866. $title 
  867. ); 
  868. } else { 
  869. echo $pad . $title; 
  870. _post_states( $post ); 
  871.  
  872. if ( isset( $parent_name ) ) { 
  873. $post_type_object = get_post_type_object( $post->post_type ); 
  874. echo ' | ' . $post_type_object->labels->parent_item_colon . ' ' . esc_html( $parent_name ); 
  875. echo "</strong>\n"; 
  876.  
  877. if ( ! is_post_type_hierarchical( $this->screen->post_type ) && 'excerpt' === $mode && current_user_can( 'read_post', $post->ID ) ) { 
  878. echo esc_html( get_the_excerpt() ); 
  879.  
  880. get_inline_data( $post ); 
  881.  
  882. /** 
  883. * Handles the post date column output. 
  884. * 
  885. * @since 4.3.0 
  886. * @access public 
  887. * 
  888. * @global string $mode 
  889. * 
  890. * @param WP_Post $post The current WP_Post object. 
  891. */ 
  892. public function column_date( $post ) { 
  893. global $mode; 
  894.  
  895. if ( '0000-00-00 00:00:00' === $post->post_date ) { 
  896. $t_time = $h_time = __( 'Unpublished' ); 
  897. $time_diff = 0; 
  898. } else { 
  899. $t_time = get_the_time( __( 'Y/m/d g:i:s a' ) ); 
  900. $m_time = $post->post_date; 
  901. $time = get_post_time( 'G', true, $post ); 
  902.  
  903. $time_diff = time() - $time; 
  904.  
  905. if ( $time_diff > 0 && $time_diff < DAY_IN_SECONDS ) { 
  906. $h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) ); 
  907. } else { 
  908. $h_time = mysql2date( __( 'Y/m/d' ), $m_time ); 
  909.  
  910. if ( 'publish' === $post->post_status ) { 
  911. _e( 'Published' ); 
  912. } elseif ( 'future' === $post->post_status ) { 
  913. if ( $time_diff > 0 ) { 
  914. echo '<strong class="error-message">' . __( 'Missed schedule' ) . '</strong>'; 
  915. } else { 
  916. _e( 'Scheduled' ); 
  917. } else { 
  918. _e( 'Last Modified' ); 
  919. echo '<br />'; 
  920. if ( 'excerpt' === $mode ) { 
  921. /** 
  922. * Filters the published time of the post. 
  923. * 
  924. * If `$mode` equals 'excerpt', the published time and date are both displayed. 
  925. * If `$mode` equals 'list' (default), the publish date is displayed, with the 
  926. * time and date together available as an abbreviation definition. 
  927. * 
  928. * @since 2.5.1 
  929. * 
  930. * @param string $t_time The published time. 
  931. * @param WP_Post $post Post object. 
  932. * @param string $column_name The column name. 
  933. * @param string $mode The list display mode ('excerpt' or 'list'). 
  934. */ 
  935. echo apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode ); 
  936. } else { 
  937.  
  938. /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */ 
  939. echo '<abbr title="' . $t_time . '">' . apply_filters( 'post_date_column_time', $h_time, $post, 'date', $mode ) . '</abbr>'; 
  940.  
  941. /** 
  942. * Handles the comments column output. 
  943. * 
  944. * @since 4.3.0 
  945. * @access public 
  946. * 
  947. * @param WP_Post $post The current WP_Post object. 
  948. */ 
  949. public function column_comments( $post ) { 
  950. ?> 
  951. <div class="post-com-count-wrapper"> 
  952. <?php 
  953. $pending_comments = isset( $this->comment_pending_count[$post->ID] ) ? $this->comment_pending_count[$post->ID] : 0; 
  954.  
  955. $this->comments_bubble( $post->ID, $pending_comments ); 
  956. ?> 
  957. </div> 
  958. <?php 
  959.  
  960. /** 
  961. * Handles the post author column output. 
  962. * 
  963. * @since 4.3.0 
  964. * @access public 
  965. * 
  966. * @param WP_Post $post The current WP_Post object. 
  967. */ 
  968. public function column_author( $post ) { 
  969. $args = array( 
  970. 'post_type' => $post->post_type,  
  971. 'author' => get_the_author_meta( 'ID' ) 
  972. ); 
  973. echo $this->get_edit_link( $args, get_the_author() ); 
  974.  
  975. /** 
  976. * Handles the default column output. 
  977. * 
  978. * @since 4.3.0 
  979. * @access public 
  980. * 
  981. * @param WP_Post $post The current WP_Post object. 
  982. * @param string $column_name The current column name. 
  983. */ 
  984. public function column_default( $post, $column_name ) { 
  985. if ( 'categories' === $column_name ) { 
  986. $taxonomy = 'category'; 
  987. } elseif ( 'tags' === $column_name ) { 
  988. $taxonomy = 'post_tag'; 
  989. } elseif ( 0 === strpos( $column_name, 'taxonomy-' ) ) { 
  990. $taxonomy = substr( $column_name, 9 ); 
  991. } else { 
  992. $taxonomy = false; 
  993. if ( $taxonomy ) { 
  994. $taxonomy_object = get_taxonomy( $taxonomy ); 
  995. $terms = get_the_terms( $post->ID, $taxonomy ); 
  996. if ( is_array( $terms ) ) { 
  997. $out = array(); 
  998. foreach ( $terms as $t ) { 
  999. $posts_in_term_qv = array(); 
  1000. if ( 'post' != $post->post_type ) { 
  1001. $posts_in_term_qv['post_type'] = $post->post_type; 
  1002. if ( $taxonomy_object->query_var ) { 
  1003. $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug; 
  1004. } else { 
  1005. $posts_in_term_qv['taxonomy'] = $taxonomy; 
  1006. $posts_in_term_qv['term'] = $t->slug; 
  1007.  
  1008. $label = esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) ); 
  1009. $out[] = $this->get_edit_link( $posts_in_term_qv, $label ); 
  1010. /** translators: used between list items, there is a space after the comma */ 
  1011. echo join( __( ', ' ), $out ); 
  1012. } else { 
  1013. echo '<span aria-hidden="true">—</span><span class="screen-reader-text">' . $taxonomy_object->labels->no_terms . '</span>'; 
  1014. return; 
  1015.  
  1016. if ( is_post_type_hierarchical( $post->post_type ) ) { 
  1017.  
  1018. /** 
  1019. * Fires in each custom column on the Posts list table. 
  1020. * 
  1021. * This hook only fires if the current post type is hierarchical,  
  1022. * such as pages. 
  1023. * 
  1024. * @since 2.5.0 
  1025. * 
  1026. * @param string $column_name The name of the column to display. 
  1027. * @param int $post_id The current post ID. 
  1028. */ 
  1029. do_action( 'manage_pages_custom_column', $column_name, $post->ID ); 
  1030. } else { 
  1031.  
  1032. /** 
  1033. * Fires in each custom column in the Posts list table. 
  1034. * 
  1035. * This hook only fires if the current post type is non-hierarchical,  
  1036. * such as posts. 
  1037. * 
  1038. * @since 1.5.0 
  1039. * 
  1040. * @param string $column_name The name of the column to display. 
  1041. * @param int $post_id The current post ID. 
  1042. */ 
  1043. do_action( 'manage_posts_custom_column', $column_name, $post->ID ); 
  1044.  
  1045. /** 
  1046. * Fires for each custom column of a specific post type in the Posts list table. 
  1047. * 
  1048. * The dynamic portion of the hook name, `$post->post_type`, refers to the post type. 
  1049. * 
  1050. * @since 3.1.0 
  1051. * 
  1052. * @param string $column_name The name of the column to display. 
  1053. * @param int $post_id The current post ID. 
  1054. */ 
  1055. do_action( "manage_{$post->post_type}_posts_custom_column", $column_name, $post->ID ); 
  1056.  
  1057. /** 
  1058. * @global WP_Post $post 
  1059. * 
  1060. * @param int|WP_Post $post 
  1061. * @param int $level 
  1062. */ 
  1063. public function single_row( $post, $level = 0 ) { 
  1064. $global_post = get_post(); 
  1065.  
  1066. $post = get_post( $post ); 
  1067. $this->current_level = $level; 
  1068.  
  1069. $GLOBALS['post'] = $post; 
  1070. setup_postdata( $post ); 
  1071.  
  1072. $classes = 'iedit author-' . ( get_current_user_id() == $post->post_author ? 'self' : 'other' ); 
  1073.  
  1074. $lock_holder = wp_check_post_lock( $post->ID ); 
  1075. if ( $lock_holder ) { 
  1076. $classes .= ' wp-locked'; 
  1077.  
  1078. if ( $post->post_parent ) { 
  1079. $count = count( get_post_ancestors( $post->ID ) ); 
  1080. $classes .= ' level-'. $count; 
  1081. } else { 
  1082. $classes .= ' level-0'; 
  1083. ?> 
  1084. <tr id="post-<?php echo $post->ID; ?>" class="<?php echo implode( ' ', get_post_class( $classes, $post->ID ) ); ?>"> 
  1085. <?php $this->single_row_columns( $post ); ?> 
  1086. </tr> 
  1087. <?php 
  1088. $GLOBALS['post'] = $global_post; 
  1089.  
  1090. /** 
  1091. * Gets the name of the default primary column. 
  1092. * 
  1093. * @since 4.3.0 
  1094. * @access protected 
  1095. * 
  1096. * @return string Name of the default primary column, in this case, 'title'. 
  1097. */ 
  1098. protected function get_default_primary_column_name() { 
  1099. return 'title'; 
  1100.  
  1101. /** 
  1102. * Generates and displays row action links. 
  1103. * 
  1104. * @since 4.3.0 
  1105. * @access protected 
  1106. * 
  1107. * @param object $post Post being acted upon. 
  1108. * @param string $column_name Current column name. 
  1109. * @param string $primary Primary column name. 
  1110. * @return string Row actions output for posts. 
  1111. */ 
  1112. protected function handle_row_actions( $post, $column_name, $primary ) { 
  1113. if ( $primary !== $column_name ) { 
  1114. return ''; 
  1115.  
  1116. $post_type_object = get_post_type_object( $post->post_type ); 
  1117. $can_edit_post = current_user_can( 'edit_post', $post->ID ); 
  1118. $actions = array(); 
  1119. $title = _draft_or_post_title(); 
  1120.  
  1121. if ( $can_edit_post && 'trash' != $post->post_status ) { 
  1122. $actions['edit'] = sprintf( 
  1123. '<a href="%s" aria-label="%s">%s</a>',  
  1124. get_edit_post_link( $post->ID ),  
  1125. /** translators: %s: post title */ 
  1126. esc_attr( sprintf( __( 'Edit “%s”' ), $title ) ),  
  1127. __( 'Edit' ) 
  1128. ); 
  1129. $actions['inline hide-if-no-js'] = sprintf( 
  1130. '<a href="#" class="editinline" aria-label="%s">%s</a>',  
  1131. /** translators: %s: post title */ 
  1132. esc_attr( sprintf( __( 'Quick edit “%s” inline' ), $title ) ),  
  1133. __( 'Quick Edit' ) 
  1134. ); 
  1135.  
  1136. if ( current_user_can( 'delete_post', $post->ID ) ) { 
  1137. if ( 'trash' === $post->post_status ) { 
  1138. $actions['untrash'] = sprintf( 
  1139. '<a href="%s" aria-label="%s">%s</a>',  
  1140. wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ),  
  1141. /** translators: %s: post title */ 
  1142. esc_attr( sprintf( __( 'Restore “%s” from the Trash' ), $title ) ),  
  1143. __( 'Restore' ) 
  1144. ); 
  1145. } elseif ( EMPTY_TRASH_DAYS ) { 
  1146. $actions['trash'] = sprintf( 
  1147. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',  
  1148. get_delete_post_link( $post->ID ),  
  1149. /** translators: %s: post title */ 
  1150. esc_attr( sprintf( __( 'Move “%s” to the Trash' ), $title ) ),  
  1151. _x( 'Trash', 'verb' ) 
  1152. ); 
  1153. if ( 'trash' === $post->post_status || ! EMPTY_TRASH_DAYS ) { 
  1154. $actions['delete'] = sprintf( 
  1155. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',  
  1156. get_delete_post_link( $post->ID, '', true ),  
  1157. /** translators: %s: post title */ 
  1158. esc_attr( sprintf( __( 'Delete “%s” permanently' ), $title ) ),  
  1159. __( 'Delete Permanently' ) 
  1160. ); 
  1161.  
  1162. if ( is_post_type_viewable( $post_type_object ) ) { 
  1163. if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) { 
  1164. if ( $can_edit_post ) { 
  1165. $preview_link = get_preview_post_link( $post ); 
  1166. $actions['view'] = sprintf( 
  1167. '<a href="%s" rel="permalink" aria-label="%s">%s</a>',  
  1168. esc_url( $preview_link ),  
  1169. /** translators: %s: post title */ 
  1170. esc_attr( sprintf( __( 'Preview “%s”' ), $title ) ),  
  1171. __( 'Preview' ) 
  1172. ); 
  1173. } elseif ( 'trash' != $post->post_status ) { 
  1174. $actions['view'] = sprintf( 
  1175. '<a href="%s" rel="permalink" aria-label="%s">%s</a>',  
  1176. get_permalink( $post->ID ),  
  1177. /** translators: %s: post title */ 
  1178. esc_attr( sprintf( __( 'View “%s”' ), $title ) ),  
  1179. __( 'View' ) 
  1180. ); 
  1181.  
  1182. if ( is_post_type_hierarchical( $post->post_type ) ) { 
  1183.  
  1184. /** 
  1185. * Filters the array of row action links on the Pages list table. 
  1186. * 
  1187. * The filter is evaluated only for hierarchical post types. 
  1188. * 
  1189. * @since 2.8.0 
  1190. * 
  1191. * @param array $actions An array of row action links. Defaults are 
  1192. * 'Edit', 'Quick Edit', 'Restore, 'Trash',  
  1193. * 'Delete Permanently', 'Preview', and 'View'. 
  1194. * @param WP_Post $post The post object. 
  1195. */ 
  1196. $actions = apply_filters( 'page_row_actions', $actions, $post ); 
  1197. } else { 
  1198.  
  1199. /** 
  1200. * Filters the array of row action links on the Posts list table. 
  1201. * 
  1202. * The filter is evaluated only for non-hierarchical post types. 
  1203. * 
  1204. * @since 2.8.0 
  1205. * 
  1206. * @param array $actions An array of row action links. Defaults are 
  1207. * 'Edit', 'Quick Edit', 'Restore, 'Trash',  
  1208. * 'Delete Permanently', 'Preview', and 'View'. 
  1209. * @param WP_Post $post The post object. 
  1210. */ 
  1211. $actions = apply_filters( 'post_row_actions', $actions, $post ); 
  1212.  
  1213. return $this->row_actions( $actions ); 
  1214.  
  1215. /** 
  1216. * Outputs the hidden row displayed when inline editing 
  1217. * 
  1218. * @since 3.1.0 
  1219. * 
  1220. * @global string $mode 
  1221. */ 
  1222. public function inline_edit() { 
  1223. global $mode; 
  1224.  
  1225. $screen = $this->screen; 
  1226.  
  1227. $post = get_default_post_to_edit( $screen->post_type ); 
  1228. $post_type_object = get_post_type_object( $screen->post_type ); 
  1229.  
  1230. $taxonomy_names = get_object_taxonomies( $screen->post_type ); 
  1231. $hierarchical_taxonomies = array(); 
  1232. $flat_taxonomies = array(); 
  1233. foreach ( $taxonomy_names as $taxonomy_name ) { 
  1234.  
  1235. $taxonomy = get_taxonomy( $taxonomy_name ); 
  1236.  
  1237. $show_in_quick_edit = $taxonomy->show_in_quick_edit; 
  1238.  
  1239. /** 
  1240. * Filters whether the current taxonomy should be shown in the Quick Edit panel. 
  1241. * 
  1242. * @since 4.2.0 
  1243. * 
  1244. * @param bool $show_in_quick_edit Whether to show the current taxonomy in Quick Edit. 
  1245. * @param string $taxonomy_name Taxonomy name. 
  1246. * @param string $post_type Post type of current Quick Edit post. 
  1247. */ 
  1248. if ( ! apply_filters( 'quick_edit_show_taxonomy', $show_in_quick_edit, $taxonomy_name, $screen->post_type ) ) { 
  1249. continue; 
  1250.  
  1251. if ( $taxonomy->hierarchical ) 
  1252. $hierarchical_taxonomies[] = $taxonomy; 
  1253. else 
  1254. $flat_taxonomies[] = $taxonomy; 
  1255.  
  1256. $m = ( isset( $mode ) && 'excerpt' === $mode ) ? 'excerpt' : 'list'; 
  1257. $can_publish = current_user_can( $post_type_object->cap->publish_posts ); 
  1258. $core_columns = array( 'cb' => true, 'date' => true, 'title' => true, 'categories' => true, 'tags' => true, 'comments' => true, 'author' => true ); 
  1259.  
  1260. ?> 
  1261.  
  1262. <form method="get"><table style="display: none"><tbody id="inlineedit"> 
  1263. <?php 
  1264. $hclass = count( $hierarchical_taxonomies ) ? 'post' : 'page'; 
  1265. $bulk = 0; 
  1266. while ( $bulk < 2 ) { ?> 
  1267.  
  1268. <tr id="<?php echo $bulk ? 'bulk-edit' : 'inline-edit'; ?>" class="inline-edit-row inline-edit-row-<?php echo "$hclass inline-edit-" . $screen->post_type; 
  1269. echo $bulk ? " bulk-edit-row bulk-edit-row-$hclass bulk-edit-{$screen->post_type}" : " quick-edit-row quick-edit-row-$hclass inline-edit-{$screen->post_type}"; 
  1270. ?>" style="display: none"><td colspan="<?php echo $this->get_column_count(); ?>" class="colspanchange"> 
  1271.  
  1272. <fieldset class="inline-edit-col-left"> 
  1273. <legend class="inline-edit-legend"><?php echo $bulk ? __( 'Bulk Edit' ) : __( 'Quick Edit' ); ?></legend> 
  1274. <div class="inline-edit-col"> 
  1275. <?php 
  1276.  
  1277. if ( post_type_supports( $screen->post_type, 'title' ) ) : 
  1278. if ( $bulk ) : ?> 
  1279. <div id="bulk-title-div"> 
  1280. <div id="bulk-titles"></div> 
  1281. </div> 
  1282.  
  1283. <?php else : // $bulk ?> 
  1284.  
  1285. <label> 
  1286. <span class="title"><?php _e( 'Title' ); ?></span> 
  1287. <span class="input-text-wrap"><input type="text" name="post_title" class="ptitle" value="" /></span> 
  1288. </label> 
  1289.  
  1290. <label> 
  1291. <span class="title"><?php _e( 'Slug' ); ?></span> 
  1292. <span class="input-text-wrap"><input type="text" name="post_name" value="" /></span> 
  1293. </label> 
  1294.  
  1295. <?php endif; // $bulk 
  1296. endif; // post_type_supports title ?> 
  1297.  
  1298. <?php if ( !$bulk ) : ?> 
  1299. <fieldset class="inline-edit-date"> 
  1300. <legend><span class="title"><?php _e( 'Date' ); ?></span></legend> 
  1301. <?php touch_time( 1, 1, 0, 1 ); ?> 
  1302. </fieldset> 
  1303. <br class="clear" /> 
  1304. <?php endif; // $bulk 
  1305.  
  1306. if ( post_type_supports( $screen->post_type, 'author' ) ) : 
  1307. $authors_dropdown = ''; 
  1308.  
  1309. if ( is_super_admin() || current_user_can( $post_type_object->cap->edit_others_posts ) ) : 
  1310. $users_opt = array( 
  1311. 'hide_if_only_one_author' => false,  
  1312. 'who' => 'authors',  
  1313. 'name' => 'post_author',  
  1314. 'class'=> 'authors',  
  1315. 'multi' => 1,  
  1316. 'echo' => 0,  
  1317. 'show' => 'display_name_with_login',  
  1318. ); 
  1319. if ( $bulk ) 
  1320. $users_opt['show_option_none'] = __( '— No Change —' ); 
  1321.  
  1322. if ( $authors = wp_dropdown_users( $users_opt ) ) : 
  1323. $authors_dropdown = '<label class="inline-edit-author">'; 
  1324. $authors_dropdown .= '<span class="title">' . __( 'Author' ) . '</span>'; 
  1325. $authors_dropdown .= $authors; 
  1326. $authors_dropdown .= '</label>'; 
  1327. endif; 
  1328. endif; // authors 
  1329. ?> 
  1330.  
  1331. <?php if ( !$bulk ) echo $authors_dropdown; 
  1332. endif; // post_type_supports author 
  1333.  
  1334. if ( !$bulk && $can_publish ) : 
  1335. ?> 
  1336.  
  1337. <div class="inline-edit-group wp-clearfix"> 
  1338. <label class="alignleft"> 
  1339. <span class="title"><?php _e( 'Password' ); ?></span> 
  1340. <span class="input-text-wrap"><input type="text" name="post_password" class="inline-edit-password-input" value="" /></span> 
  1341. </label> 
  1342.  
  1343. <em class="alignleft inline-edit-or"> 
  1344. <?php 
  1345. /** translators: Between password field and private checkbox on post quick edit interface */ 
  1346. _e( '–OR–' ); 
  1347. ?> 
  1348. </em> 
  1349. <label class="alignleft inline-edit-private"> 
  1350. <input type="checkbox" name="keep_private" value="private" /> 
  1351. <span class="checkbox-title"><?php _e( 'Private' ); ?></span> 
  1352. </label> 
  1353. </div> 
  1354.  
  1355. <?php endif; ?> 
  1356.  
  1357. </div></fieldset> 
  1358.  
  1359. <?php if ( count( $hierarchical_taxonomies ) && !$bulk ) : ?> 
  1360.  
  1361. <fieldset class="inline-edit-col-center inline-edit-categories"><div class="inline-edit-col"> 
  1362.  
  1363. <?php foreach ( $hierarchical_taxonomies as $taxonomy ) : ?> 
  1364.  
  1365. <span class="title inline-edit-categories-label"><?php echo esc_html( $taxonomy->labels->name ) ?></span> 
  1366. <input type="hidden" name="<?php echo ( $taxonomy->name === 'category' ) ? 'post_category[]' : 'tax_input[' . esc_attr( $taxonomy->name ) . '][]'; ?>" value="0" /> 
  1367. <ul class="cat-checklist <?php echo esc_attr( $taxonomy->name )?>-checklist"> 
  1368. <?php wp_terms_checklist( null, array( 'taxonomy' => $taxonomy->name ) ) ?> 
  1369. </ul> 
  1370.  
  1371. <?php endforeach; //$hierarchical_taxonomies as $taxonomy ?> 
  1372.  
  1373. </div></fieldset> 
  1374.  
  1375. <?php endif; // count( $hierarchical_taxonomies ) && !$bulk ?> 
  1376.  
  1377. <fieldset class="inline-edit-col-right"><div class="inline-edit-col"> 
  1378.  
  1379. <?php 
  1380. if ( post_type_supports( $screen->post_type, 'author' ) && $bulk ) 
  1381. echo $authors_dropdown; 
  1382.  
  1383. if ( post_type_supports( $screen->post_type, 'page-attributes' ) ) : 
  1384.  
  1385. if ( $post_type_object->hierarchical ) : 
  1386. ?> 
  1387. <label> 
  1388. <span class="title"><?php _e( 'Parent' ); ?></span> 
  1389. <?php 
  1390. $dropdown_args = array( 
  1391. 'post_type' => $post_type_object->name,  
  1392. 'selected' => $post->post_parent,  
  1393. 'name' => 'post_parent',  
  1394. 'show_option_none' => __( 'Main Page (no parent)' ),  
  1395. 'option_none_value' => 0,  
  1396. 'sort_column' => 'menu_order, post_title',  
  1397. ); 
  1398.  
  1399. if ( $bulk ) 
  1400. $dropdown_args['show_option_no_change'] = __( '— No Change —' ); 
  1401.  
  1402. /** 
  1403. * Filters the arguments used to generate the Quick Edit page-parent drop-down. 
  1404. * 
  1405. * @since 2.7.0 
  1406. * 
  1407. * @see wp_dropdown_pages() 
  1408. * 
  1409. * @param array $dropdown_args An array of arguments. 
  1410. */ 
  1411. $dropdown_args = apply_filters( 'quick_edit_dropdown_pages_args', $dropdown_args ); 
  1412.  
  1413. wp_dropdown_pages( $dropdown_args ); 
  1414. ?> 
  1415. </label> 
  1416.  
  1417. <?php 
  1418. endif; // hierarchical 
  1419.  
  1420. if ( !$bulk ) : ?> 
  1421.  
  1422. <label> 
  1423. <span class="title"><?php _e( 'Order' ); ?></span> 
  1424. <span class="input-text-wrap"><input type="text" name="menu_order" class="inline-edit-menu-order-input" value="<?php echo $post->menu_order ?>" /></span> 
  1425. </label> 
  1426.  
  1427. <?php 
  1428. endif; // !$bulk 
  1429. endif; // page-attributes 
  1430. ?> 
  1431.  
  1432. <?php if ( 0 < count( get_page_templates( null, $screen->post_type ) ) ) : ?> 
  1433. <label> 
  1434. <span class="title"><?php _e( 'Template' ); ?></span> 
  1435. <select name="page_template"> 
  1436. <?php if ( $bulk ) : ?> 
  1437. <option value="-1"><?php _e( '— No Change —' ); ?></option> 
  1438. <?php endif; // $bulk ?> 
  1439. <?php 
  1440. /** This filter is documented in wp-admin/includes/meta-boxes.php */ 
  1441. $default_title = apply_filters( 'default_page_template_title', __( 'Default Template' ), 'quick-edit' ); 
  1442. ?> 
  1443. <option value="default"><?php echo esc_html( $default_title ); ?></option> 
  1444. <?php page_template_dropdown( '', $screen->post_type ) ?> 
  1445. </select> 
  1446. </label> 
  1447. <?php endif; ?> 
  1448.  
  1449. <?php if ( count( $flat_taxonomies ) && !$bulk ) : ?> 
  1450.  
  1451. <?php foreach ( $flat_taxonomies as $taxonomy ) : ?> 
  1452. <?php if ( current_user_can( $taxonomy->cap->assign_terms ) ) : 
  1453. $taxonomy_name = esc_attr( $taxonomy->name ); 
  1454.  
  1455. ?> 
  1456. <label class="inline-edit-tags"> 
  1457. <span class="title"><?php echo esc_html( $taxonomy->labels->name ) ?></span> 
  1458. <textarea data-wp-taxonomy="<?php echo $taxonomy_name; ?>" cols="22" rows="1" name="tax_input[<?php echo $taxonomy_name; ?>]" class="tax_input_<?php echo $taxonomy_name; ?>"></textarea> 
  1459. </label> 
  1460. <?php endif; ?> 
  1461.  
  1462. <?php endforeach; //$flat_taxonomies as $taxonomy ?> 
  1463.  
  1464. <?php endif; // count( $flat_taxonomies ) && !$bulk ?> 
  1465.  
  1466. <?php if ( post_type_supports( $screen->post_type, 'comments' ) || post_type_supports( $screen->post_type, 'trackbacks' ) ) : 
  1467. if ( $bulk ) : ?> 
  1468.  
  1469. <div class="inline-edit-group wp-clearfix"> 
  1470. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?> 
  1471. <label class="alignleft"> 
  1472. <span class="title"><?php _e( 'Comments' ); ?></span> 
  1473. <select name="comment_status"> 
  1474. <option value=""><?php _e( '— No Change —' ); ?></option> 
  1475. <option value="open"><?php _e( 'Allow' ); ?></option> 
  1476. <option value="closed"><?php _e( 'Do not allow' ); ?></option> 
  1477. </select> 
  1478. </label> 
  1479. <?php endif; if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?> 
  1480. <label class="alignright"> 
  1481. <span class="title"><?php _e( 'Pings' ); ?></span> 
  1482. <select name="ping_status"> 
  1483. <option value=""><?php _e( '— No Change —' ); ?></option> 
  1484. <option value="open"><?php _e( 'Allow' ); ?></option> 
  1485. <option value="closed"><?php _e( 'Do not allow' ); ?></option> 
  1486. </select> 
  1487. </label> 
  1488. <?php endif; ?> 
  1489. </div> 
  1490.  
  1491. <?php else : // $bulk ?> 
  1492.  
  1493. <div class="inline-edit-group wp-clearfix"> 
  1494. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?> 
  1495. <label class="alignleft"> 
  1496. <input type="checkbox" name="comment_status" value="open" /> 
  1497. <span class="checkbox-title"><?php _e( 'Allow Comments' ); ?></span> 
  1498. </label> 
  1499. <?php endif; if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?> 
  1500. <label class="alignleft"> 
  1501. <input type="checkbox" name="ping_status" value="open" /> 
  1502. <span class="checkbox-title"><?php _e( 'Allow Pings' ); ?></span> 
  1503. </label> 
  1504. <?php endif; ?> 
  1505. </div> 
  1506.  
  1507. <?php endif; // $bulk 
  1508. endif; // post_type_supports comments or pings ?> 
  1509.  
  1510. <div class="inline-edit-group wp-clearfix"> 
  1511. <label class="inline-edit-status alignleft"> 
  1512. <span class="title"><?php _e( 'Status' ); ?></span> 
  1513. <select name="_status"> 
  1514. <?php if ( $bulk ) : ?> 
  1515. <option value="-1"><?php _e( '— No Change —' ); ?></option> 
  1516. <?php endif; // $bulk ?> 
  1517. <?php if ( $can_publish ) : // Contributors only get "Unpublished" and "Pending Review" ?> 
  1518. <option value="publish"><?php _e( 'Published' ); ?></option> 
  1519. <option value="future"><?php _e( 'Scheduled' ); ?></option> 
  1520. <?php if ( $bulk ) : ?> 
  1521. <option value="private"><?php _e( 'Private' ) ?></option> 
  1522. <?php endif; // $bulk ?> 
  1523. <?php endif; ?> 
  1524. <option value="pending"><?php _e( 'Pending Review' ); ?></option> 
  1525. <option value="draft"><?php _e( 'Draft' ); ?></option> 
  1526. </select> 
  1527. </label> 
  1528.  
  1529. <?php if ( 'post' === $screen->post_type && $can_publish && current_user_can( $post_type_object->cap->edit_others_posts ) ) : ?> 
  1530.  
  1531. <?php if ( $bulk ) : ?> 
  1532.  
  1533. <label class="alignright"> 
  1534. <span class="title"><?php _e( 'Sticky' ); ?></span> 
  1535. <select name="sticky"> 
  1536. <option value="-1"><?php _e( '— No Change —' ); ?></option> 
  1537. <option value="sticky"><?php _e( 'Sticky' ); ?></option> 
  1538. <option value="unsticky"><?php _e( 'Not Sticky' ); ?></option> 
  1539. </select> 
  1540. </label> 
  1541.  
  1542. <?php else : // $bulk ?> 
  1543.  
  1544. <label class="alignleft"> 
  1545. <input type="checkbox" name="sticky" value="sticky" /> 
  1546. <span class="checkbox-title"><?php _e( 'Make this post sticky' ); ?></span> 
  1547. </label> 
  1548.  
  1549. <?php endif; // $bulk ?> 
  1550.  
  1551. <?php endif; // 'post' && $can_publish && current_user_can( 'edit_others_cap' ) ?> 
  1552.  
  1553. </div> 
  1554.  
  1555. <?php 
  1556.  
  1557. if ( $bulk && current_theme_supports( 'post-formats' ) && post_type_supports( $screen->post_type, 'post-formats' ) ) { 
  1558. $post_formats = get_theme_support( 'post-formats' ); 
  1559.  
  1560. ?> 
  1561. <label class="alignleft"> 
  1562. <span class="title"><?php _ex( 'Format', 'post format' ); ?></span> 
  1563. <select name="post_format"> 
  1564. <option value="-1"><?php _e( '— No Change —' ); ?></option> 
  1565. <option value="0"><?php echo get_post_format_string( 'standard' ); ?></option> 
  1566. <?php 
  1567. if ( is_array( $post_formats[0] ) ) { 
  1568. foreach ( $post_formats[0] as $format ) { 
  1569. ?> 
  1570. <option value="<?php echo esc_attr( $format ); ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></option> 
  1571. <?php 
  1572. ?> 
  1573. </select></label> 
  1574. <?php 
  1575.  
  1576.  
  1577. ?> 
  1578.  
  1579. </div></fieldset> 
  1580.  
  1581. <?php 
  1582. list( $columns ) = $this->get_column_info(); 
  1583.  
  1584. foreach ( $columns as $column_name => $column_display_name ) { 
  1585. if ( isset( $core_columns[$column_name] ) ) 
  1586. continue; 
  1587.  
  1588. if ( $bulk ) { 
  1589.  
  1590. /** 
  1591. * Fires once for each column in Bulk Edit mode. 
  1592. * 
  1593. * @since 2.7.0 
  1594. * 
  1595. * @param string $column_name Name of the column to edit. 
  1596. * @param WP_Post $post_type The post type slug. 
  1597. */ 
  1598. do_action( 'bulk_edit_custom_box', $column_name, $screen->post_type ); 
  1599. } else { 
  1600.  
  1601. /** 
  1602. * Fires once for each column in Quick Edit mode. 
  1603. * 
  1604. * @since 2.7.0 
  1605. * 
  1606. * @param string $column_name Name of the column to edit. 
  1607. * @param string $post_type The post type slug. 
  1608. */ 
  1609. do_action( 'quick_edit_custom_box', $column_name, $screen->post_type ); 
  1610.  
  1611. ?> 
  1612. <p class="submit inline-edit-save"> 
  1613. <button type="button" class="button cancel alignleft"><?php _e( 'Cancel' ); ?></button> 
  1614. <?php if ( ! $bulk ) { 
  1615. wp_nonce_field( 'inlineeditnonce', '_inline_edit', false ); 
  1616. ?> 
  1617. <button type="button" class="button button-primary save alignright"><?php _e( 'Update' ); ?></button> 
  1618. <span class="spinner"></span> 
  1619. <?php } else { 
  1620. submit_button( __( 'Update' ), 'primary alignright', 'bulk_edit', false ); 
  1621. } ?> 
  1622. <input type="hidden" name="post_view" value="<?php echo esc_attr( $m ); ?>" /> 
  1623. <input type="hidden" name="screen" value="<?php echo esc_attr( $screen->id ); ?>" /> 
  1624. <?php if ( ! $bulk && ! post_type_supports( $screen->post_type, 'author' ) ) { ?> 
  1625. <input type="hidden" name="post_author" value="<?php echo esc_attr( $post->post_author ); ?>" /> 
  1626. <?php } ?> 
  1627. <span class="error" style="display:none"></span> 
  1628. <br class="clear" /> 
  1629. </p> 
  1630. </td></tr> 
  1631. <?php 
  1632. $bulk++; 
  1633. ?> 
  1634. </tbody></table></form> 
  1635. <?php 
.