home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / includes / class-wp-posts-list-table.php < prev    next >
Encoding:
PHP Script  |  2017-10-04  |  51.5 KB  |  1,773 lines

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