wp/wp-admin/includes/class-wp-list-table.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     1 <?php
     1 <?php
       
     2 /**
       
     3  * Administration API: WP_List_Table class
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage List_Table
       
     7  * @since 3.1.0
       
     8  */
       
     9 
     2 /**
    10 /**
     3  * Base class for displaying a list of items in an ajaxified HTML table.
    11  * Base class for displaying a list of items in an ajaxified HTML table.
     4  *
    12  *
     5  * @since 3.1.0
    13  * @since 3.1.0
     6  * @access private
    14  * @access private
     7  *
       
     8  * @package WordPress
       
     9  * @subpackage List_Table
       
    10  */
    15  */
    11 class WP_List_Table {
    16 class WP_List_Table {
    12 
    17 
    13 	/**
    18 	/**
    14 	 * The current list of items
    19 	 * The current list of items.
    15 	 *
    20 	 *
    16 	 * @since 3.1.0
    21 	 * @since 3.1.0
    17 	 * @var array
    22 	 * @var array
    18 	 * @access public
       
    19 	 */
    23 	 */
    20 	public $items;
    24 	public $items;
    21 
    25 
    22 	/**
    26 	/**
    23 	 * Various information about the current table
    27 	 * Various information about the current table.
    24 	 *
    28 	 *
    25 	 * @since 3.1.0
    29 	 * @since 3.1.0
    26 	 * @var array
    30 	 * @var array
    27 	 * @access protected
       
    28 	 */
    31 	 */
    29 	protected $_args;
    32 	protected $_args;
    30 
    33 
    31 	/**
    34 	/**
    32 	 * Various information needed for displaying the pagination
    35 	 * Various information needed for displaying the pagination.
    33 	 *
    36 	 *
    34 	 * @since 3.1.0
    37 	 * @since 3.1.0
    35 	 * @var array
    38 	 * @var array
    36 	 */
    39 	 */
    37 	protected $_pagination_args = array();
    40 	protected $_pagination_args = array();
    38 
    41 
    39 	/**
    42 	/**
    40 	 * The current screen
    43 	 * The current screen.
    41 	 *
    44 	 *
    42 	 * @since 3.1.0
    45 	 * @since 3.1.0
    43 	 * @var object
    46 	 * @var object
    44 	 * @access protected
       
    45 	 */
    47 	 */
    46 	protected $screen;
    48 	protected $screen;
    47 
    49 
    48 	/**
    50 	/**
    49 	 * Cached bulk actions
    51 	 * Cached bulk actions.
    50 	 *
    52 	 *
    51 	 * @since 3.1.0
    53 	 * @since 3.1.0
    52 	 * @var array
    54 	 * @var array
    53 	 * @access private
       
    54 	 */
    55 	 */
    55 	private $_actions;
    56 	private $_actions;
    56 
    57 
    57 	/**
    58 	/**
    58 	 * Cached pagination output
    59 	 * Cached pagination output.
    59 	 *
    60 	 *
    60 	 * @since 3.1.0
    61 	 * @since 3.1.0
    61 	 * @var string
    62 	 * @var string
    62 	 * @access private
       
    63 	 */
    63 	 */
    64 	private $_pagination;
    64 	private $_pagination;
    65 
    65 
    66 	/**
    66 	/**
    67 	 * The view switcher modes.
    67 	 * The view switcher modes.
    68 	 *
    68 	 *
    69 	 * @since 4.1.0
    69 	 * @since 4.1.0
    70 	 * @var array
    70 	 * @var array
    71 	 * @access protected
       
    72 	 */
    71 	 */
    73 	protected $modes = array();
    72 	protected $modes = array();
    74 
    73 
    75 	/**
    74 	/**
    76 	 * Stores the value returned by ->get_column_info()
    75 	 * Stores the value returned by ->get_column_info().
    77 	 *
    76 	 *
       
    77 	 * @since 4.1.0
    78 	 * @var array
    78 	 * @var array
    79 	 */
    79 	 */
    80 	protected $_column_headers;
    80 	protected $_column_headers;
    81 
    81 
       
    82 	/**
       
    83 	 * {@internal Missing Summary}
       
    84 	 *
       
    85 	 * @var array
       
    86 	 */
    82 	protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
    87 	protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
    83 
    88 
       
    89 	/**
       
    90 	 * {@internal Missing Summary}
       
    91 	 *
       
    92 	 * @var array
       
    93 	 */
    84 	protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
    94 	protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
    85 		'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
    95 		'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
    86 		'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
    96 		'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
    87 		'single_row_columns' );
    97 		'single_row_columns' );
    88 
    98 
    91 	 *
   101 	 *
    92 	 * The child class should call this constructor from its own constructor to override
   102 	 * The child class should call this constructor from its own constructor to override
    93 	 * the default $args.
   103 	 * the default $args.
    94 	 *
   104 	 *
    95 	 * @since 3.1.0
   105 	 * @since 3.1.0
    96 	 * @access public
       
    97 	 *
   106 	 *
    98 	 * @param array|string $args {
   107 	 * @param array|string $args {
    99 	 *     Array or string of arguments.
   108 	 *     Array or string of arguments.
   100 	 *
   109 	 *
   101 	 *     @type string $plural   Plural value used for labels and the objects being listed.
   110 	 *     @type string $plural   Plural value used for labels and the objects being listed.
   102 	 *                            This affects things such as CSS class-names and nonces used
   111 	 *                            This affects things such as CSS class-names and nonces used
   103 	 *                            in the list table, e.g. 'posts'. Default empty.
   112 	 *                            in the list table, e.g. 'posts'. Default empty.
   104 	 *     @type string $singular Singular label for an object being listed, e.g. 'post'.
   113 	 *     @type string $singular Singular label for an object being listed, e.g. 'post'.
   105 	 *                            Default empty
   114 	 *                            Default empty
   106 	 *     @type bool   $ajax     Whether the list table supports AJAX. This includes loading
   115 	 *     @type bool   $ajax     Whether the list table supports Ajax. This includes loading
   107 	 *                            and sorting data, for example. If true, the class will call
   116 	 *                            and sorting data, for example. If true, the class will call
   108 	 *                            the {@see _js_vars()} method in the footer to provide variables
   117 	 *                            the _js_vars() method in the footer to provide variables
   109 	 *                            to any scripts handling AJAX events. Default false.
   118 	 *                            to any scripts handling Ajax events. Default false.
   110 	 *     @type string $screen   String containing the hook name used to determine the current
   119 	 *     @type string $screen   String containing the hook name used to determine the current
   111 	 *                            screen. If left null, the current screen will be automatically set.
   120 	 *                            screen. If left null, the current screen will be automatically set.
   112 	 *                            Default null.
   121 	 *                            Default null.
   113 	 * }
   122 	 * }
   114 	 */
   123 	 */
   144 			);
   153 			);
   145 		}
   154 		}
   146 	}
   155 	}
   147 
   156 
   148 	/**
   157 	/**
   149 	 * Make private properties readable for backwards compatibility.
   158 	 * Make private properties readable for backward compatibility.
   150 	 *
   159 	 *
   151 	 * @since 4.0.0
   160 	 * @since 4.0.0
   152 	 * @access public
       
   153 	 *
   161 	 *
   154 	 * @param string $name Property to get.
   162 	 * @param string $name Property to get.
   155 	 * @return mixed Property.
   163 	 * @return mixed Property.
   156 	 */
   164 	 */
   157 	public function __get( $name ) {
   165 	public function __get( $name ) {
   159 			return $this->$name;
   167 			return $this->$name;
   160 		}
   168 		}
   161 	}
   169 	}
   162 
   170 
   163 	/**
   171 	/**
   164 	 * Make private properties settable for backwards compatibility.
   172 	 * Make private properties settable for backward compatibility.
   165 	 *
   173 	 *
   166 	 * @since 4.0.0
   174 	 * @since 4.0.0
   167 	 * @access public
       
   168 	 *
   175 	 *
   169 	 * @param string $name  Property to check if set.
   176 	 * @param string $name  Property to check if set.
   170 	 * @param mixed  $value Property value.
   177 	 * @param mixed  $value Property value.
   171 	 * @return mixed Newly-set property.
   178 	 * @return mixed Newly-set property.
   172 	 */
   179 	 */
   175 			return $this->$name = $value;
   182 			return $this->$name = $value;
   176 		}
   183 		}
   177 	}
   184 	}
   178 
   185 
   179 	/**
   186 	/**
   180 	 * Make private properties checkable for backwards compatibility.
   187 	 * Make private properties checkable for backward compatibility.
   181 	 *
   188 	 *
   182 	 * @since 4.0.0
   189 	 * @since 4.0.0
   183 	 * @access public
       
   184 	 *
   190 	 *
   185 	 * @param string $name Property to check if set.
   191 	 * @param string $name Property to check if set.
   186 	 * @return bool Whether the property is set.
   192 	 * @return bool Whether the property is set.
   187 	 */
   193 	 */
   188 	public function __isset( $name ) {
   194 	public function __isset( $name ) {
   190 			return isset( $this->$name );
   196 			return isset( $this->$name );
   191 		}
   197 		}
   192 	}
   198 	}
   193 
   199 
   194 	/**
   200 	/**
   195 	 * Make private properties un-settable for backwards compatibility.
   201 	 * Make private properties un-settable for backward compatibility.
   196 	 *
   202 	 *
   197 	 * @since 4.0.0
   203 	 * @since 4.0.0
   198 	 * @access public
       
   199 	 *
   204 	 *
   200 	 * @param string $name Property to unset.
   205 	 * @param string $name Property to unset.
   201 	 */
   206 	 */
   202 	public function __unset( $name ) {
   207 	public function __unset( $name ) {
   203 		if ( in_array( $name, $this->compat_fields ) ) {
   208 		if ( in_array( $name, $this->compat_fields ) ) {
   204 			unset( $this->$name );
   209 			unset( $this->$name );
   205 		}
   210 		}
   206 	}
   211 	}
   207 
   212 
   208 	/**
   213 	/**
   209 	 * Make private/protected methods readable for backwards compatibility.
   214 	 * Make private/protected methods readable for backward compatibility.
   210 	 *
   215 	 *
   211 	 * @since 4.0.0
   216 	 * @since 4.0.0
   212 	 * @access public
       
   213 	 *
   217 	 *
   214 	 * @param callable $name      Method to call.
   218 	 * @param callable $name      Method to call.
   215 	 * @param array    $arguments Arguments to pass when calling.
   219 	 * @param array    $arguments Arguments to pass when calling.
   216 	 * @return mixed|bool Return value of the callback, false otherwise.
   220 	 * @return mixed|bool Return value of the callback, false otherwise.
   217 	 */
   221 	 */
   224 
   228 
   225 	/**
   229 	/**
   226 	 * Checks the current user's permissions
   230 	 * Checks the current user's permissions
   227 	 *
   231 	 *
   228 	 * @since 3.1.0
   232 	 * @since 3.1.0
   229 	 * @access public
       
   230 	 * @abstract
   233 	 * @abstract
   231 	 */
   234 	 */
   232 	public function ajax_user_can() {
   235 	public function ajax_user_can() {
   233 		die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
   236 		die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
   234 	}
   237 	}
   236 	/**
   239 	/**
   237 	 * Prepares the list of items for displaying.
   240 	 * Prepares the list of items for displaying.
   238 	 * @uses WP_List_Table::set_pagination_args()
   241 	 * @uses WP_List_Table::set_pagination_args()
   239 	 *
   242 	 *
   240 	 * @since 3.1.0
   243 	 * @since 3.1.0
   241 	 * @access public
       
   242 	 * @abstract
   244 	 * @abstract
   243 	 */
   245 	 */
   244 	public function prepare_items() {
   246 	public function prepare_items() {
   245 		die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
   247 		die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
   246 	}
   248 	}
   247 
   249 
   248 	/**
   250 	/**
   249 	 * An internal method that sets all the necessary pagination arguments
   251 	 * An internal method that sets all the necessary pagination arguments
   250 	 *
   252 	 *
   251 	 * @param array $args An associative array with information about the pagination
   253 	 * @since 3.1.0
   252 	 * @access protected
   254 	 *
       
   255 	 * @param array|string $args Array or string of arguments with information about the pagination.
   253 	 */
   256 	 */
   254 	protected function set_pagination_args( $args ) {
   257 	protected function set_pagination_args( $args ) {
   255 		$args = wp_parse_args( $args, array(
   258 		$args = wp_parse_args( $args, array(
   256 			'total_items' => 0,
   259 			'total_items' => 0,
   257 			'total_pages' => 0,
   260 			'total_pages' => 0,
   260 
   263 
   261 		if ( !$args['total_pages'] && $args['per_page'] > 0 )
   264 		if ( !$args['total_pages'] && $args['per_page'] > 0 )
   262 			$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
   265 			$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
   263 
   266 
   264 		// Redirect if page number is invalid and headers are not already sent.
   267 		// Redirect if page number is invalid and headers are not already sent.
   265 		if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
   268 		if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
   266 			wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
   269 			wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
   267 			exit;
   270 			exit;
   268 		}
   271 		}
   269 
   272 
   270 		$this->_pagination_args = $args;
   273 		$this->_pagination_args = $args;
   272 
   275 
   273 	/**
   276 	/**
   274 	 * Access the pagination args.
   277 	 * Access the pagination args.
   275 	 *
   278 	 *
   276 	 * @since 3.1.0
   279 	 * @since 3.1.0
   277 	 * @access public
       
   278 	 *
   280 	 *
   279 	 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
   281 	 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
   280 	 *                    'total_pages', 'per_page', or 'infinite_scroll'.
   282 	 *                    'total_pages', 'per_page', or 'infinite_scroll'.
   281 	 * @return int Number of items that correspond to the given pagination argument.
   283 	 * @return int Number of items that correspond to the given pagination argument.
   282 	 */
   284 	 */
   283 	public function get_pagination_arg( $key ) {
   285 	public function get_pagination_arg( $key ) {
   284 		if ( 'page' == $key )
   286 		if ( 'page' === $key ) {
   285 			return $this->get_pagenum();
   287 			return $this->get_pagenum();
   286 
   288 		}
   287 		if ( isset( $this->_pagination_args[$key] ) )
   289 
       
   290 		if ( isset( $this->_pagination_args[$key] ) ) {
   288 			return $this->_pagination_args[$key];
   291 			return $this->_pagination_args[$key];
       
   292 		}
   289 	}
   293 	}
   290 
   294 
   291 	/**
   295 	/**
   292 	 * Whether the table has items to display or not
   296 	 * Whether the table has items to display or not
   293 	 *
   297 	 *
   294 	 * @since 3.1.0
   298 	 * @since 3.1.0
   295 	 * @access public
       
   296 	 *
   299 	 *
   297 	 * @return bool
   300 	 * @return bool
   298 	 */
   301 	 */
   299 	public function has_items() {
   302 	public function has_items() {
   300 		return !empty( $this->items );
   303 		return !empty( $this->items );
   302 
   305 
   303 	/**
   306 	/**
   304 	 * Message to be displayed when there are no items
   307 	 * Message to be displayed when there are no items
   305 	 *
   308 	 *
   306 	 * @since 3.1.0
   309 	 * @since 3.1.0
   307 	 * @access public
       
   308 	 */
   310 	 */
   309 	public function no_items() {
   311 	public function no_items() {
   310 		_e( 'No items found.' );
   312 		_e( 'No items found.' );
   311 	}
   313 	}
   312 
   314 
   313 	/**
   315 	/**
   314 	 * Display the search box.
   316 	 * Displays the search box.
   315 	 *
   317 	 *
   316 	 * @since 3.1.0
   318 	 * @since 3.1.0
   317 	 * @access public
   319 	 *
   318 	 *
   320 	 * @param string $text     The 'submit' button label.
   319 	 * @param string $text The search button text
   321 	 * @param string $input_id ID attribute value for the search input field.
   320 	 * @param string $input_id The search input id
       
   321 	 */
   322 	 */
   322 	public function search_box( $text, $input_id ) {
   323 	public function search_box( $text, $input_id ) {
   323 		if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
   324 		if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
   324 			return;
   325 			return;
   325 
   326 
   333 			echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
   334 			echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
   334 		if ( ! empty( $_REQUEST['detached'] ) )
   335 		if ( ! empty( $_REQUEST['detached'] ) )
   335 			echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
   336 			echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
   336 ?>
   337 ?>
   337 <p class="search-box">
   338 <p class="search-box">
   338 	<label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
   339 	<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
   339 	<input type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
   340 	<input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
   340 	<?php submit_button( $text, 'button', '', false, array('id' => 'search-submit') ); ?>
   341 	<?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
   341 </p>
   342 </p>
   342 <?php
   343 <?php
   343 	}
   344 	}
   344 
   345 
   345 	/**
   346 	/**
   346 	 * Get an associative array ( id => link ) with the list
   347 	 * Get an associative array ( id => link ) with the list
   347 	 * of views available on this table.
   348 	 * of views available on this table.
   348 	 *
   349 	 *
   349 	 * @since 3.1.0
   350 	 * @since 3.1.0
   350 	 * @access protected
       
   351 	 *
   351 	 *
   352 	 * @return array
   352 	 * @return array
   353 	 */
   353 	 */
   354 	protected function get_views() {
   354 	protected function get_views() {
   355 		return array();
   355 		return array();
   357 
   357 
   358 	/**
   358 	/**
   359 	 * Display the list of views available on this table.
   359 	 * Display the list of views available on this table.
   360 	 *
   360 	 *
   361 	 * @since 3.1.0
   361 	 * @since 3.1.0
   362 	 * @access public
       
   363 	 */
   362 	 */
   364 	public function views() {
   363 	public function views() {
   365 		$views = $this->get_views();
   364 		$views = $this->get_views();
   366 		/**
   365 		/**
   367 		 * Filter the list of available list table views.
   366 		 * Filters the list of available list table views.
   368 		 *
   367 		 *
   369 		 * The dynamic portion of the hook name, `$this->screen->id`, refers
   368 		 * The dynamic portion of the hook name, `$this->screen->id`, refers
   370 		 * to the ID of the current screen, usually a string.
   369 		 * to the ID of the current screen, usually a string.
   371 		 *
   370 		 *
   372 		 * @since 3.5.0
   371 		 * @since 3.5.0
   376 		$views = apply_filters( "views_{$this->screen->id}", $views );
   375 		$views = apply_filters( "views_{$this->screen->id}", $views );
   377 
   376 
   378 		if ( empty( $views ) )
   377 		if ( empty( $views ) )
   379 			return;
   378 			return;
   380 
   379 
       
   380 		$this->screen->render_screen_reader_content( 'heading_views' );
       
   381 
   381 		echo "<ul class='subsubsub'>\n";
   382 		echo "<ul class='subsubsub'>\n";
   382 		foreach ( $views as $class => $view ) {
   383 		foreach ( $views as $class => $view ) {
   383 			$views[ $class ] = "\t<li class='$class'>$view";
   384 			$views[ $class ] = "\t<li class='$class'>$view";
   384 		}
   385 		}
   385 		echo implode( " |</li>\n", $views ) . "</li>\n";
   386 		echo implode( " |</li>\n", $views ) . "</li>\n";
   389 	/**
   390 	/**
   390 	 * Get an associative array ( option_name => option_title ) with the list
   391 	 * Get an associative array ( option_name => option_title ) with the list
   391 	 * of bulk actions available on this table.
   392 	 * of bulk actions available on this table.
   392 	 *
   393 	 *
   393 	 * @since 3.1.0
   394 	 * @since 3.1.0
   394 	 * @access protected
       
   395 	 *
   395 	 *
   396 	 * @return array
   396 	 * @return array
   397 	 */
   397 	 */
   398 	protected function get_bulk_actions() {
   398 	protected function get_bulk_actions() {
   399 		return array();
   399 		return array();
   401 
   401 
   402 	/**
   402 	/**
   403 	 * Display the bulk actions dropdown.
   403 	 * Display the bulk actions dropdown.
   404 	 *
   404 	 *
   405 	 * @since 3.1.0
   405 	 * @since 3.1.0
   406 	 * @access protected
       
   407 	 *
   406 	 *
   408 	 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
   407 	 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
   409 	 *                      This is designated as optional for backwards-compatibility.
   408 	 *                      This is designated as optional for backward compatibility.
   410 	 */
   409 	 */
   411 	protected function bulk_actions( $which = '' ) {
   410 	protected function bulk_actions( $which = '' ) {
   412 		if ( is_null( $this->_actions ) ) {
   411 		if ( is_null( $this->_actions ) ) {
   413 			$no_new_actions = $this->_actions = $this->get_bulk_actions();
   412 			$this->_actions = $this->get_bulk_actions();
   414 			/**
   413 			/**
   415 			 * Filter the list table Bulk Actions drop-down.
   414 			 * Filters the list table Bulk Actions drop-down.
   416 			 *
   415 			 *
   417 			 * The dynamic portion of the hook name, `$this->screen->id`, refers
   416 			 * The dynamic portion of the hook name, `$this->screen->id`, refers
   418 			 * to the ID of the current screen, usually a string.
   417 			 * to the ID of the current screen, usually a string.
   419 			 *
   418 			 *
   420 			 * This filter can currently only be used to remove bulk actions.
   419 			 * This filter can currently only be used to remove bulk actions.
   422 			 * @since 3.5.0
   421 			 * @since 3.5.0
   423 			 *
   422 			 *
   424 			 * @param array $actions An array of the available bulk actions.
   423 			 * @param array $actions An array of the available bulk actions.
   425 			 */
   424 			 */
   426 			$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
   425 			$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
   427 			$this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
       
   428 			$two = '';
   426 			$two = '';
   429 		} else {
   427 		} else {
   430 			$two = '2';
   428 			$two = '2';
   431 		}
   429 		}
   432 
   430 
   433 		if ( empty( $this->_actions ) )
   431 		if ( empty( $this->_actions ) )
   434 			return;
   432 			return;
   435 
   433 
   436 		echo "<label for='bulk-action-selector-" . esc_attr( $which ) . "' class='screen-reader-text'>" . __( 'Select bulk action' ) . "</label>";
   434 		echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
   437 		echo "<select name='action$two' id='bulk-action-selector-" . esc_attr( $which ) . "'>\n";
   435 		echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
   438 		echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
   436 		echo '<option value="-1">' . __( 'Bulk Actions' ) . "</option>\n";
   439 
   437 
   440 		foreach ( $this->_actions as $name => $title ) {
   438 		foreach ( $this->_actions as $name => $title ) {
   441 			$class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
   439 			$class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
   442 
   440 
   443 			echo "\t<option value='$name'$class>$title</option>\n";
   441 			echo "\t" . '<option value="' . $name . '"' . $class . '>' . $title . "</option>\n";
   444 		}
   442 		}
   445 
   443 
   446 		echo "</select>\n";
   444 		echo "</select>\n";
   447 
   445 
   448 		submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
   446 		submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
   451 
   449 
   452 	/**
   450 	/**
   453 	 * Get the current action selected from the bulk actions dropdown.
   451 	 * Get the current action selected from the bulk actions dropdown.
   454 	 *
   452 	 *
   455 	 * @since 3.1.0
   453 	 * @since 3.1.0
   456 	 * @access public
   454 	 *
   457 	 *
   455 	 * @return string|false The action name or False if no action was selected
   458 	 * @return string|bool The action name or False if no action was selected
       
   459 	 */
   456 	 */
   460 	public function current_action() {
   457 	public function current_action() {
   461 		if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) )
   458 		if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) )
   462 			return false;
   459 			return false;
   463 
   460 
   472 
   469 
   473 	/**
   470 	/**
   474 	 * Generate row actions div
   471 	 * Generate row actions div
   475 	 *
   472 	 *
   476 	 * @since 3.1.0
   473 	 * @since 3.1.0
   477 	 * @access protected
       
   478 	 *
   474 	 *
   479 	 * @param array $actions The list of actions
   475 	 * @param array $actions The list of actions
   480 	 * @param bool $always_visible Whether the actions should be always visible
   476 	 * @param bool $always_visible Whether the actions should be always visible
   481 	 * @return string
   477 	 * @return string
   482 	 */
   478 	 */
   493 			( $i == $action_count ) ? $sep = '' : $sep = ' | ';
   489 			( $i == $action_count ) ? $sep = '' : $sep = ' | ';
   494 			$out .= "<span class='$action'>$link$sep</span>";
   490 			$out .= "<span class='$action'>$link$sep</span>";
   495 		}
   491 		}
   496 		$out .= '</div>';
   492 		$out .= '</div>';
   497 
   493 
       
   494 		$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
       
   495 
   498 		return $out;
   496 		return $out;
   499 	}
   497 	}
   500 
   498 
   501 	/**
   499 	/**
   502 	 * Display a monthly dropdown for filtering items
   500 	 * Display a monthly dropdown for filtering items
   503 	 *
   501 	 *
   504 	 * @since 3.1.0
   502 	 * @since 3.1.0
   505 	 * @access protected
   503 	 *
       
   504 	 * @global wpdb      $wpdb
       
   505 	 * @global WP_Locale $wp_locale
   506 	 *
   506 	 *
   507 	 * @param string $post_type
   507 	 * @param string $post_type
   508 	 */
   508 	 */
   509 	protected function months_dropdown( $post_type ) {
   509 	protected function months_dropdown( $post_type ) {
   510 		global $wpdb, $wp_locale;
   510 		global $wpdb, $wp_locale;
   511 
   511 
   512 		/**
   512 		/**
   513 		 * Filter whether to remove the 'Months' drop-down from the post list table.
   513 		 * Filters whether to remove the 'Months' drop-down from the post list table.
   514 		 *
   514 		 *
   515 		 * @since 4.2.0
   515 		 * @since 4.2.0
   516 		 *
   516 		 *
   517 		 * @param bool   $disable   Whether to disable the drop-down. Default false.
   517 		 * @param bool   $disable   Whether to disable the drop-down. Default false.
   518 		 * @param string $post_type The post type.
   518 		 * @param string $post_type The post type.
   519 		 */
   519 		 */
   520 		if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
   520 		if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
   521 			return;
   521 			return;
   522 		}
   522 		}
   523 
   523 
       
   524 		$extra_checks = "AND post_status != 'auto-draft'";
       
   525 		if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
       
   526 			$extra_checks .= " AND post_status != 'trash'";
       
   527 		} elseif ( isset( $_GET['post_status'] ) ) {
       
   528 			$extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
       
   529 		}
       
   530 
   524 		$months = $wpdb->get_results( $wpdb->prepare( "
   531 		$months = $wpdb->get_results( $wpdb->prepare( "
   525 			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
   532 			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
   526 			FROM $wpdb->posts
   533 			FROM $wpdb->posts
   527 			WHERE post_type = %s
   534 			WHERE post_type = %s
       
   535 			$extra_checks
   528 			ORDER BY post_date DESC
   536 			ORDER BY post_date DESC
   529 		", $post_type ) );
   537 		", $post_type ) );
   530 
   538 
   531 		/**
   539 		/**
   532 		 * Filter the 'Months' drop-down results.
   540 		 * Filters the 'Months' drop-down results.
   533 		 *
   541 		 *
   534 		 * @since 3.7.0
   542 		 * @since 3.7.0
   535 		 *
   543 		 *
   536 		 * @param object $months    The months drop-down query results.
   544 		 * @param object $months    The months drop-down query results.
   537 		 * @param string $post_type The post type.
   545 		 * @param string $post_type The post type.
   570 
   578 
   571 	/**
   579 	/**
   572 	 * Display a view switcher
   580 	 * Display a view switcher
   573 	 *
   581 	 *
   574 	 * @since 3.1.0
   582 	 * @since 3.1.0
   575 	 * @access protected
       
   576 	 *
   583 	 *
   577 	 * @param string $current_mode
   584 	 * @param string $current_mode
   578 	 */
   585 	 */
   579 	protected function view_switcher( $current_mode ) {
   586 	protected function view_switcher( $current_mode ) {
   580 ?>
   587 ?>
   581 		<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
   588 		<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
   582 		<div class="view-switch">
   589 		<div class="view-switch">
   583 <?php
   590 <?php
   584 			foreach ( $this->modes as $mode => $title ) {
   591 			foreach ( $this->modes as $mode => $title ) {
   585 				$classes = array( 'view-' . $mode );
   592 				$classes = array( 'view-' . $mode );
   586 				if ( $current_mode == $mode )
   593 				if ( $current_mode === $mode )
   587 					$classes[] = 'current';
   594 					$classes[] = 'current';
   588 				printf(
   595 				printf(
   589 					"<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
   596 					"<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
   590 					esc_url( add_query_arg( 'mode', $mode ) ),
   597 					esc_url( add_query_arg( 'mode', $mode ) ),
   591 					implode( ' ', $classes ),
   598 					implode( ' ', $classes ),
   599 
   606 
   600 	/**
   607 	/**
   601 	 * Display a comment count bubble
   608 	 * Display a comment count bubble
   602 	 *
   609 	 *
   603 	 * @since 3.1.0
   610 	 * @since 3.1.0
   604 	 * @access protected
       
   605 	 *
   611 	 *
   606 	 * @param int $post_id          The post ID.
   612 	 * @param int $post_id          The post ID.
   607 	 * @param int $pending_comments Number of pending comments.
   613 	 * @param int $pending_comments Number of pending comments.
   608 	 */
   614 	 */
   609 	protected function comments_bubble( $post_id, $pending_comments ) {
   615 	protected function comments_bubble( $post_id, $pending_comments ) {
   610 		$pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
   616 		$approved_comments = get_comments_number();
   611 
   617 
   612 		if ( $pending_comments )
   618 		$approved_comments_number = number_format_i18n( $approved_comments );
   613 			echo '<strong>';
   619 		$pending_comments_number = number_format_i18n( $pending_comments );
   614 
   620 
   615 		echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>";
   621 		$approved_only_phrase = sprintf( _n( '%s comment', '%s comments', $approved_comments ), $approved_comments_number );
   616 
   622 		$approved_phrase = sprintf( _n( '%s approved comment', '%s approved comments', $approved_comments ), $approved_comments_number );
   617 		if ( $pending_comments )
   623 		$pending_phrase = sprintf( _n( '%s pending comment', '%s pending comments', $pending_comments ), $pending_comments_number );
   618 			echo '</strong>';
   624 
       
   625 		// No comments at all.
       
   626 		if ( ! $approved_comments && ! $pending_comments ) {
       
   627 			printf( '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">%s</span>',
       
   628 				__( 'No comments' )
       
   629 			);
       
   630 		// Approved comments have different display depending on some conditions.
       
   631 		} elseif ( $approved_comments ) {
       
   632 			printf( '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
       
   633 				esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'approved' ), admin_url( 'edit-comments.php' ) ) ),
       
   634 				$approved_comments_number,
       
   635 				$pending_comments ? $approved_phrase : $approved_only_phrase
       
   636 			);
       
   637 		} else {
       
   638 			printf( '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
       
   639 				$approved_comments_number,
       
   640 				$pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
       
   641 			);
       
   642 		}
       
   643 
       
   644 		if ( $pending_comments ) {
       
   645 			printf( '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
       
   646 				esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'moderated' ), admin_url( 'edit-comments.php' ) ) ),
       
   647 				$pending_comments_number,
       
   648 				$pending_phrase
       
   649 			);
       
   650 		} else {
       
   651 			printf( '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
       
   652 				$pending_comments_number,
       
   653 				$approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
       
   654 			);
       
   655 		}
   619 	}
   656 	}
   620 
   657 
   621 	/**
   658 	/**
   622 	 * Get the current page number
   659 	 * Get the current page number
   623 	 *
   660 	 *
   624 	 * @since 3.1.0
   661 	 * @since 3.1.0
   625 	 * @access public
       
   626 	 *
   662 	 *
   627 	 * @return int
   663 	 * @return int
   628 	 */
   664 	 */
   629 	public function get_pagenum() {
   665 	public function get_pagenum() {
   630 		$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
   666 		$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
   631 
   667 
   632 		if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
   668 		if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
   633 			$pagenum = $this->_pagination_args['total_pages'];
   669 			$pagenum = $this->_pagination_args['total_pages'];
   634 
   670 
   635 		return max( 1, $pagenum );
   671 		return max( 1, $pagenum );
   636 	}
   672 	}
   637 
   673 
   638 	/**
   674 	/**
   639 	 * Get number of items to display on a single page
   675 	 * Get number of items to display on a single page
   640 	 *
   676 	 *
   641 	 * @since 3.1.0
   677 	 * @since 3.1.0
   642 	 * @access protected
       
   643 	 *
   678 	 *
   644 	 * @param string $option
   679 	 * @param string $option
   645 	 * @param int    $default
   680 	 * @param int    $default
   646 	 * @return int
   681 	 * @return int
   647 	 */
   682 	 */
   649 		$per_page = (int) get_user_option( $option );
   684 		$per_page = (int) get_user_option( $option );
   650 		if ( empty( $per_page ) || $per_page < 1 )
   685 		if ( empty( $per_page ) || $per_page < 1 )
   651 			$per_page = $default;
   686 			$per_page = $default;
   652 
   687 
   653 		/**
   688 		/**
   654 		 * Filter the number of items to be displayed on each page of the list table.
   689 		 * Filters the number of items to be displayed on each page of the list table.
   655 		 *
   690 		 *
   656 		 * The dynamic hook name, $option, refers to the `per_page` option depending
   691 		 * The dynamic hook name, $option, refers to the `per_page` option depending
   657 		 * on the type of list table in use. Possible values include: 'edit_comments_per_page',
   692 		 * on the type of list table in use. Possible values include: 'edit_comments_per_page',
   658 		 * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
   693 		 * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
   659 		 * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
   694 		 * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
   661 		 *
   696 		 *
   662 		 * @since 2.9.0
   697 		 * @since 2.9.0
   663 		 *
   698 		 *
   664 		 * @param int $per_page Number of items to be displayed. Default 20.
   699 		 * @param int $per_page Number of items to be displayed. Default 20.
   665 		 */
   700 		 */
   666 		return (int) apply_filters( $option, $per_page );
   701 		return (int) apply_filters( "{$option}", $per_page );
   667 	}
   702 	}
   668 
   703 
   669 	/**
   704 	/**
   670 	 * Display the pagination.
   705 	 * Display the pagination.
   671 	 *
   706 	 *
   672 	 * @since 3.1.0
   707 	 * @since 3.1.0
   673 	 * @access protected
       
   674 	 *
   708 	 *
   675 	 * @param string $which
   709 	 * @param string $which
   676 	 */
   710 	 */
   677 	protected function pagination( $which ) {
   711 	protected function pagination( $which ) {
   678 		if ( empty( $this->_pagination_args ) ) {
   712 		if ( empty( $this->_pagination_args ) ) {
   684 		$infinite_scroll = false;
   718 		$infinite_scroll = false;
   685 		if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
   719 		if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
   686 			$infinite_scroll = $this->_pagination_args['infinite_scroll'];
   720 			$infinite_scroll = $this->_pagination_args['infinite_scroll'];
   687 		}
   721 		}
   688 
   722 
   689 		$output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
   723 		if ( 'top' === $which && $total_pages > 1 ) {
       
   724 			$this->screen->render_screen_reader_content( 'heading_pagination' );
       
   725 		}
       
   726 
       
   727 		$output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
   690 
   728 
   691 		$current = $this->get_pagenum();
   729 		$current = $this->get_pagenum();
       
   730 		$removable_query_args = wp_removable_query_args();
   692 
   731 
   693 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   732 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   694 
   733 
   695 		$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
   734 		$current_url = remove_query_arg( $removable_query_args, $current_url );
   696 
   735 
   697 		$page_links = array();
   736 		$page_links = array();
   698 
   737 
   699 		$disable_first = $disable_last = '';
   738 		$total_pages_before = '<span class="paging-input">';
   700 		if ( $current == 1 ) {
   739 		$total_pages_after  = '</span></span>';
   701 			$disable_first = ' disabled';
   740 
   702 		}
   741 		$disable_first = $disable_last = $disable_prev = $disable_next = false;
   703 		if ( $current == $total_pages ) {
   742 
   704 			$disable_last = ' disabled';
   743  		if ( $current == 1 ) {
   705 		}
   744 			$disable_first = true;
   706 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
   745 			$disable_prev = true;
   707 			'first-page' . $disable_first,
   746  		}
   708 			esc_attr__( 'Go to the first page' ),
   747 		if ( $current == 2 ) {
   709 			esc_url( remove_query_arg( 'paged', $current_url ) ),
   748 			$disable_first = true;
   710 			'&laquo;'
   749 		}
   711 		);
   750  		if ( $current == $total_pages ) {
   712 
   751 			$disable_last = true;
   713 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
   752 			$disable_next = true;
   714 			'prev-page' . $disable_first,
   753  		}
   715 			esc_attr__( 'Go to the previous page' ),
   754 		if ( $current == $total_pages - 1 ) {
   716 			esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
   755 			$disable_last = true;
   717 			'&lsaquo;'
   756 		}
   718 		);
   757 
   719 
   758 		if ( $disable_first ) {
   720 		if ( 'bottom' == $which ) {
   759 			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
   721 			$html_current_page = $current;
       
   722 		} else {
   760 		} else {
   723 			$html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' title='%s' type='text' name='paged' value='%s' size='%d' />",
   761 			$page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
   724 				'<label for="current-page-selector" class="screen-reader-text">' . __( 'Select Page' ) . '</label>',
   762 				esc_url( remove_query_arg( 'paged', $current_url ) ),
   725 				esc_attr__( 'Current page' ),
   763 				__( 'First page' ),
       
   764 				'&laquo;'
       
   765 			);
       
   766 		}
       
   767 
       
   768 		if ( $disable_prev ) {
       
   769 			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
       
   770 		} else {
       
   771 			$page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
       
   772 				esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
       
   773 				__( 'Previous page' ),
       
   774 				'&lsaquo;'
       
   775 			);
       
   776 		}
       
   777 
       
   778 		if ( 'bottom' === $which ) {
       
   779 			$html_current_page  = $current;
       
   780 			$total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
       
   781 		} else {
       
   782 			$html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
       
   783 				'<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
   726 				$current,
   784 				$current,
   727 				strlen( $total_pages )
   785 				strlen( $total_pages )
   728 			);
   786 			);
   729 		}
   787 		}
   730 		$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
   788 		$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
   731 		$page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
   789 		$page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
   732 
   790 
   733 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
   791 		if ( $disable_next ) {
   734 			'next-page' . $disable_last,
   792 			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
   735 			esc_attr__( 'Go to the next page' ),
   793 		} else {
   736 			esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
   794 			$page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
   737 			'&rsaquo;'
   795 				esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
   738 		);
   796 				__( 'Next page' ),
   739 
   797 				'&rsaquo;'
   740 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
   798 			);
   741 			'last-page' . $disable_last,
   799 		}
   742 			esc_attr__( 'Go to the last page' ),
   800 
   743 			esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
   801 		if ( $disable_last ) {
   744 			'&raquo;'
   802 			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
   745 		);
   803 		} else {
       
   804 			$page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
       
   805 				esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
       
   806 				__( 'Last page' ),
       
   807 				'&raquo;'
       
   808 			);
       
   809 		}
   746 
   810 
   747 		$pagination_links_class = 'pagination-links';
   811 		$pagination_links_class = 'pagination-links';
   748 		if ( ! empty( $infinite_scroll ) ) {
   812 		if ( ! empty( $infinite_scroll ) ) {
   749 			$pagination_links_class = ' hide-if-js';
   813 			$pagination_links_class .= ' hide-if-js';
   750 		}
   814 		}
   751 		$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
   815 		$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
   752 
   816 
   753 		if ( $total_pages ) {
   817 		if ( $total_pages ) {
   754 			$page_class = $total_pages < 2 ? ' one-page' : '';
   818 			$page_class = $total_pages < 2 ? ' one-page' : '';
   763 	/**
   827 	/**
   764 	 * Get a list of columns. The format is:
   828 	 * Get a list of columns. The format is:
   765 	 * 'internal-name' => 'Title'
   829 	 * 'internal-name' => 'Title'
   766 	 *
   830 	 *
   767 	 * @since 3.1.0
   831 	 * @since 3.1.0
   768 	 * @access public
       
   769 	 * @abstract
   832 	 * @abstract
   770 	 *
   833 	 *
   771 	 * @return array
   834 	 * @return array
   772 	 */
   835 	 */
   773 	public function get_columns() {
   836 	public function get_columns() {
   781 	 * 'internal-name' => array( 'orderby', true )
   844 	 * 'internal-name' => array( 'orderby', true )
   782 	 *
   845 	 *
   783 	 * The second format will make the initial sorting order be descending
   846 	 * The second format will make the initial sorting order be descending
   784 	 *
   847 	 *
   785 	 * @since 3.1.0
   848 	 * @since 3.1.0
   786 	 * @access protected
       
   787 	 *
   849 	 *
   788 	 * @return array
   850 	 * @return array
   789 	 */
   851 	 */
   790 	protected function get_sortable_columns() {
   852 	protected function get_sortable_columns() {
   791 		return array();
   853 		return array();
   792 	}
   854 	}
   793 
   855 
   794 	/**
   856 	/**
       
   857 	 * Gets the name of the default primary column.
       
   858 	 *
       
   859 	 * @since 4.3.0
       
   860 	 *
       
   861 	 * @return string Name of the default primary column, in this case, an empty string.
       
   862 	 */
       
   863 	protected function get_default_primary_column_name() {
       
   864 		$columns = $this->get_columns();
       
   865 		$column = '';
       
   866 
       
   867 		if ( empty( $columns ) ) {
       
   868 			return $column;
       
   869 		}
       
   870 
       
   871 		// We need a primary defined so responsive views show something,
       
   872 		// so let's fall back to the first non-checkbox column.
       
   873 		foreach ( $columns as $col => $column_name ) {
       
   874 			if ( 'cb' === $col ) {
       
   875 				continue;
       
   876 			}
       
   877 
       
   878 			$column = $col;
       
   879 			break;
       
   880 		}
       
   881 
       
   882 		return $column;
       
   883 	}
       
   884 
       
   885 	/**
       
   886 	 * Public wrapper for WP_List_Table::get_default_primary_column_name().
       
   887 	 *
       
   888 	 * @since 4.4.0
       
   889 	 *
       
   890 	 * @return string Name of the default primary column.
       
   891 	 */
       
   892 	public function get_primary_column() {
       
   893 		return $this->get_primary_column_name();
       
   894 	}
       
   895 
       
   896 	/**
       
   897 	 * Gets the name of the primary column.
       
   898 	 *
       
   899 	 * @since 4.3.0
       
   900 	 *
       
   901 	 * @return string The name of the primary column.
       
   902 	 */
       
   903 	protected function get_primary_column_name() {
       
   904 		$columns = get_column_headers( $this->screen );
       
   905 		$default = $this->get_default_primary_column_name();
       
   906 
       
   907 		// If the primary column doesn't exist fall back to the
       
   908 		// first non-checkbox column.
       
   909 		if ( ! isset( $columns[ $default ] ) ) {
       
   910 			$default = WP_List_Table::get_default_primary_column_name();
       
   911 		}
       
   912 
       
   913 		/**
       
   914 		 * Filters the name of the primary column for the current list table.
       
   915 		 *
       
   916 		 * @since 4.3.0
       
   917 		 *
       
   918 		 * @param string $default Column name default for the specific list table, e.g. 'name'.
       
   919 		 * @param string $context Screen ID for specific list table, e.g. 'plugins'.
       
   920 		 */
       
   921 		$column  = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
       
   922 
       
   923 		if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
       
   924 			$column = $default;
       
   925 		}
       
   926 
       
   927 		return $column;
       
   928 	}
       
   929 
       
   930 	/**
   795 	 * Get a list of all, hidden and sortable columns, with filter applied
   931 	 * Get a list of all, hidden and sortable columns, with filter applied
   796 	 *
   932 	 *
   797 	 * @since 3.1.0
   933 	 * @since 3.1.0
   798 	 * @access protected
       
   799 	 *
   934 	 *
   800 	 * @return array
   935 	 * @return array
   801 	 */
   936 	 */
   802 	protected function get_column_info() {
   937 	protected function get_column_info() {
   803 		if ( isset( $this->_column_headers ) )
   938 		// $_column_headers is already set / cached
   804 			return $this->_column_headers;
   939 		if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
       
   940 			// Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
       
   941 			// In 4.3, we added a fourth argument for primary column.
       
   942 			$column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
       
   943 			foreach ( $this->_column_headers as $key => $value ) {
       
   944 				$column_headers[ $key ] = $value;
       
   945 			}
       
   946 
       
   947 			return $column_headers;
       
   948 		}
   805 
   949 
   806 		$columns = get_column_headers( $this->screen );
   950 		$columns = get_column_headers( $this->screen );
   807 		$hidden = get_hidden_columns( $this->screen );
   951 		$hidden = get_hidden_columns( $this->screen );
   808 
   952 
   809 		$sortable_columns = $this->get_sortable_columns();
   953 		$sortable_columns = $this->get_sortable_columns();
   810 		/**
   954 		/**
   811 		 * Filter the list table sortable columns for a specific screen.
   955 		 * Filters the list table sortable columns for a specific screen.
   812 		 *
   956 		 *
   813 		 * The dynamic portion of the hook name, `$this->screen->id`, refers
   957 		 * The dynamic portion of the hook name, `$this->screen->id`, refers
   814 		 * to the ID of the current screen, usually a string.
   958 		 * to the ID of the current screen, usually a string.
   815 		 *
   959 		 *
   816 		 * @since 3.5.0
   960 		 * @since 3.5.0
   829 				$data[1] = false;
   973 				$data[1] = false;
   830 
   974 
   831 			$sortable[$id] = $data;
   975 			$sortable[$id] = $data;
   832 		}
   976 		}
   833 
   977 
   834 		$this->_column_headers = array( $columns, $hidden, $sortable );
   978 		$primary = $this->get_primary_column_name();
       
   979 		$this->_column_headers = array( $columns, $hidden, $sortable, $primary );
   835 
   980 
   836 		return $this->_column_headers;
   981 		return $this->_column_headers;
   837 	}
   982 	}
   838 
   983 
   839 	/**
   984 	/**
   840 	 * Return number of visible columns
   985 	 * Return number of visible columns
   841 	 *
   986 	 *
   842 	 * @since 3.1.0
   987 	 * @since 3.1.0
   843 	 * @access public
       
   844 	 *
   988 	 *
   845 	 * @return int
   989 	 * @return int
   846 	 */
   990 	 */
   847 	public function get_column_count() {
   991 	public function get_column_count() {
   848 		list ( $columns, $hidden ) = $this->get_column_info();
   992 		list ( $columns, $hidden ) = $this->get_column_info();
   852 
   996 
   853 	/**
   997 	/**
   854 	 * Print column headers, accounting for hidden and sortable columns.
   998 	 * Print column headers, accounting for hidden and sortable columns.
   855 	 *
   999 	 *
   856 	 * @since 3.1.0
  1000 	 * @since 3.1.0
   857 	 * @access public
  1001 	 *
       
  1002 	 * @staticvar int $cb_counter
   858 	 *
  1003 	 *
   859 	 * @param bool $with_id Whether to set the id attribute or not
  1004 	 * @param bool $with_id Whether to set the id attribute or not
   860 	 */
  1005 	 */
   861 	public function print_column_headers( $with_id = true ) {
  1006 	public function print_column_headers( $with_id = true ) {
   862 		list( $columns, $hidden, $sortable ) = $this->get_column_info();
  1007 		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
   863 
  1008 
   864 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
  1009 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   865 		$current_url = remove_query_arg( 'paged', $current_url );
  1010 		$current_url = remove_query_arg( 'paged', $current_url );
   866 
  1011 
   867 		if ( isset( $_GET['orderby'] ) )
  1012 		if ( isset( $_GET['orderby'] ) ) {
   868 			$current_orderby = $_GET['orderby'];
  1013 			$current_orderby = $_GET['orderby'];
   869 		else
  1014 		} else {
   870 			$current_orderby = '';
  1015 			$current_orderby = '';
   871 
  1016 		}
   872 		if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
  1017 
       
  1018 		if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
   873 			$current_order = 'desc';
  1019 			$current_order = 'desc';
   874 		else
  1020 		} else {
   875 			$current_order = 'asc';
  1021 			$current_order = 'asc';
       
  1022 		}
   876 
  1023 
   877 		if ( ! empty( $columns['cb'] ) ) {
  1024 		if ( ! empty( $columns['cb'] ) ) {
   878 			static $cb_counter = 1;
  1025 			static $cb_counter = 1;
   879 			$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
  1026 			$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
   880 				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
  1027 				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
   882 		}
  1029 		}
   883 
  1030 
   884 		foreach ( $columns as $column_key => $column_display_name ) {
  1031 		foreach ( $columns as $column_key => $column_display_name ) {
   885 			$class = array( 'manage-column', "column-$column_key" );
  1032 			$class = array( 'manage-column', "column-$column_key" );
   886 
  1033 
   887 			$style = '';
  1034 			if ( in_array( $column_key, $hidden ) ) {
   888 			if ( in_array( $column_key, $hidden ) )
  1035 				$class[] = 'hidden';
   889 				$style = 'display:none;';
  1036 			}
   890 
  1037 
   891 			$style = ' style="' . $style . '"';
  1038 			if ( 'cb' === $column_key )
   892 
       
   893 			if ( 'cb' == $column_key )
       
   894 				$class[] = 'check-column';
  1039 				$class[] = 'check-column';
   895 			elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
  1040 			elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
   896 				$class[] = 'num';
  1041 				$class[] = 'num';
   897 
  1042 
       
  1043 			if ( $column_key === $primary ) {
       
  1044 				$class[] = 'column-primary';
       
  1045 			}
       
  1046 
   898 			if ( isset( $sortable[$column_key] ) ) {
  1047 			if ( isset( $sortable[$column_key] ) ) {
   899 				list( $orderby, $desc_first ) = $sortable[$column_key];
  1048 				list( $orderby, $desc_first ) = $sortable[$column_key];
   900 
  1049 
   901 				if ( $current_orderby == $orderby ) {
  1050 				if ( $current_orderby === $orderby ) {
   902 					$order = 'asc' == $current_order ? 'desc' : 'asc';
  1051 					$order = 'asc' === $current_order ? 'desc' : 'asc';
   903 					$class[] = 'sorted';
  1052 					$class[] = 'sorted';
   904 					$class[] = $current_order;
  1053 					$class[] = $current_order;
   905 				} else {
  1054 				} else {
   906 					$order = $desc_first ? 'desc' : 'asc';
  1055 					$order = $desc_first ? 'desc' : 'asc';
   907 					$class[] = 'sortable';
  1056 					$class[] = 'sortable';
   909 				}
  1058 				}
   910 
  1059 
   911 				$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
  1060 				$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
   912 			}
  1061 			}
   913 
  1062 
       
  1063 			$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
       
  1064 			$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
   914 			$id = $with_id ? "id='$column_key'" : '';
  1065 			$id = $with_id ? "id='$column_key'" : '';
   915 
  1066 
   916 			if ( !empty( $class ) )
  1067 			if ( !empty( $class ) )
   917 				$class = "class='" . join( ' ', $class ) . "'";
  1068 				$class = "class='" . join( ' ', $class ) . "'";
   918 
  1069 
   919 			echo "<th scope='col' $id $class $style>$column_display_name</th>";
  1070 			echo "<$tag $scope $id $class>$column_display_name</$tag>";
   920 		}
  1071 		}
   921 	}
  1072 	}
   922 
  1073 
   923 	/**
  1074 	/**
   924 	 * Display the table
  1075 	 * Display the table
   925 	 *
  1076 	 *
   926 	 * @since 3.1.0
  1077 	 * @since 3.1.0
   927 	 * @access public
       
   928 	 */
  1078 	 */
   929 	public function display() {
  1079 	public function display() {
   930 		$singular = $this->_args['singular'];
  1080 		$singular = $this->_args['singular'];
   931 
  1081 
   932 		$this->display_tablenav( 'top' );
  1082 		$this->display_tablenav( 'top' );
   933 
  1083 
       
  1084 		$this->screen->render_screen_reader_content( 'heading_list' );
   934 ?>
  1085 ?>
   935 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
  1086 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
   936 	<thead>
  1087 	<thead>
   937 	<tr>
  1088 	<tr>
   938 		<?php $this->print_column_headers(); ?>
  1089 		<?php $this->print_column_headers(); ?>
   956 <?php
  1107 <?php
   957 		$this->display_tablenav( 'bottom' );
  1108 		$this->display_tablenav( 'bottom' );
   958 	}
  1109 	}
   959 
  1110 
   960 	/**
  1111 	/**
   961 	 * Get a list of CSS classes for the list table table tag.
  1112 	 * Get a list of CSS classes for the WP_List_Table table tag.
   962 	 *
  1113 	 *
   963 	 * @since 3.1.0
  1114 	 * @since 3.1.0
   964 	 * @access protected
       
   965 	 *
  1115 	 *
   966 	 * @return array List of CSS classes for the table tag.
  1116 	 * @return array List of CSS classes for the table tag.
   967 	 */
  1117 	 */
   968 	protected function get_table_classes() {
  1118 	protected function get_table_classes() {
   969 		return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
  1119 		return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
   971 
  1121 
   972 	/**
  1122 	/**
   973 	 * Generate the table navigation above or below the table
  1123 	 * Generate the table navigation above or below the table
   974 	 *
  1124 	 *
   975 	 * @since 3.1.0
  1125 	 * @since 3.1.0
   976 	 * @access protected
       
   977 	 * @param string $which
  1126 	 * @param string $which
   978 	 */
  1127 	 */
   979 	protected function display_tablenav( $which ) {
  1128 	protected function display_tablenav( $which ) {
   980 		if ( 'top' == $which )
  1129 		if ( 'top' === $which ) {
   981 			wp_nonce_field( 'bulk-' . $this->_args['plural'] );
  1130 			wp_nonce_field( 'bulk-' . $this->_args['plural'] );
   982 ?>
  1131 		}
       
  1132 		?>
   983 	<div class="tablenav <?php echo esc_attr( $which ); ?>">
  1133 	<div class="tablenav <?php echo esc_attr( $which ); ?>">
   984 
  1134 
       
  1135 		<?php if ( $this->has_items() ): ?>
   985 		<div class="alignleft actions bulkactions">
  1136 		<div class="alignleft actions bulkactions">
   986 			<?php $this->bulk_actions( $which ); ?>
  1137 			<?php $this->bulk_actions( $which ); ?>
   987 		</div>
  1138 		</div>
   988 <?php
  1139 		<?php endif;
   989 		$this->extra_tablenav( $which );
  1140 		$this->extra_tablenav( $which );
   990 		$this->pagination( $which );
  1141 		$this->pagination( $which );
   991 ?>
  1142 ?>
   992 
  1143 
   993 		<br class="clear" />
  1144 		<br class="clear" />
   997 
  1148 
   998 	/**
  1149 	/**
   999 	 * Extra controls to be displayed between bulk actions and pagination
  1150 	 * Extra controls to be displayed between bulk actions and pagination
  1000 	 *
  1151 	 *
  1001 	 * @since 3.1.0
  1152 	 * @since 3.1.0
  1002 	 * @access protected
       
  1003 	 *
  1153 	 *
  1004 	 * @param string $which
  1154 	 * @param string $which
  1005 	 */
  1155 	 */
  1006 	protected function extra_tablenav( $which ) {}
  1156 	protected function extra_tablenav( $which ) {}
  1007 
  1157 
  1008 	/**
  1158 	/**
  1009 	 * Generate the tbody element for the list table.
  1159 	 * Generate the tbody element for the list table.
  1010 	 *
  1160 	 *
  1011 	 * @since 3.1.0
  1161 	 * @since 3.1.0
  1012 	 * @access public
       
  1013 	 */
  1162 	 */
  1014 	public function display_rows_or_placeholder() {
  1163 	public function display_rows_or_placeholder() {
  1015 		if ( $this->has_items() ) {
  1164 		if ( $this->has_items() ) {
  1016 			$this->display_rows();
  1165 			$this->display_rows();
  1017 		} else {
  1166 		} else {
  1023 
  1172 
  1024 	/**
  1173 	/**
  1025 	 * Generate the table rows
  1174 	 * Generate the table rows
  1026 	 *
  1175 	 *
  1027 	 * @since 3.1.0
  1176 	 * @since 3.1.0
  1028 	 * @access public
       
  1029 	 */
  1177 	 */
  1030 	public function display_rows() {
  1178 	public function display_rows() {
  1031 		foreach ( $this->items as $item )
  1179 		foreach ( $this->items as $item )
  1032 			$this->single_row( $item );
  1180 			$this->single_row( $item );
  1033 	}
  1181 	}
  1034 
  1182 
  1035 	/**
  1183 	/**
  1036 	 * Generates content for a single row of the table
  1184 	 * Generates content for a single row of the table
  1037 	 *
  1185 	 *
  1038 	 * @since 3.1.0
  1186 	 * @since 3.1.0
  1039 	 * @access public
       
  1040 	 *
  1187 	 *
  1041 	 * @param object $item The current item
  1188 	 * @param object $item The current item
  1042 	 */
  1189 	 */
  1043 	public function single_row( $item ) {
  1190 	public function single_row( $item ) {
  1044 		echo '<tr>';
  1191 		echo '<tr>';
  1045 		$this->single_row_columns( $item );
  1192 		$this->single_row_columns( $item );
  1046 		echo '</tr>';
  1193 		echo '</tr>';
  1047 	}
  1194 	}
  1048 
  1195 
       
  1196 	/**
       
  1197 	 *
       
  1198 	 * @param object $item
       
  1199 	 * @param string $column_name
       
  1200 	 */
  1049 	protected function column_default( $item, $column_name ) {}
  1201 	protected function column_default( $item, $column_name ) {}
  1050 
  1202 
       
  1203 	/**
       
  1204 	 *
       
  1205 	 * @param object $item
       
  1206 	 */
  1051 	protected function column_cb( $item ) {}
  1207 	protected function column_cb( $item ) {}
  1052 
  1208 
  1053 	/**
  1209 	/**
  1054 	 * Generates the columns for a single row of the table
  1210 	 * Generates the columns for a single row of the table
  1055 	 *
  1211 	 *
  1056 	 * @since 3.1.0
  1212 	 * @since 3.1.0
  1057 	 * @access protected
       
  1058 	 *
  1213 	 *
  1059 	 * @param object $item The current item
  1214 	 * @param object $item The current item
  1060 	 */
  1215 	 */
  1061 	protected function single_row_columns( $item ) {
  1216 	protected function single_row_columns( $item ) {
  1062 		list( $columns, $hidden ) = $this->get_column_info();
  1217 		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
  1063 
  1218 
  1064 		foreach ( $columns as $column_name => $column_display_name ) {
  1219 		foreach ( $columns as $column_name => $column_display_name ) {
  1065 			$class = "class='$column_name column-$column_name'";
  1220 			$classes = "$column_name column-$column_name";
  1066 
  1221 			if ( $primary === $column_name ) {
  1067 			$style = '';
  1222 				$classes .= ' has-row-actions column-primary';
  1068 			if ( in_array( $column_name, $hidden ) )
  1223 			}
  1069 				$style = ' style="display:none;"';
  1224 
  1070 
  1225 			if ( in_array( $column_name, $hidden ) ) {
  1071 			$attributes = "$class$style";
  1226 				$classes .= ' hidden';
  1072 
  1227 			}
  1073 			if ( 'cb' == $column_name ) {
  1228 
       
  1229 			// Comments column uses HTML in the display name with screen reader text.
       
  1230 			// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
       
  1231 			$data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
       
  1232 
       
  1233 			$attributes = "class='$classes' $data";
       
  1234 
       
  1235 			if ( 'cb' === $column_name ) {
  1074 				echo '<th scope="row" class="check-column">';
  1236 				echo '<th scope="row" class="check-column">';
  1075 				echo $this->column_cb( $item );
  1237 				echo $this->column_cb( $item );
  1076 				echo '</th>';
  1238 				echo '</th>';
  1077 			}
  1239 			} elseif ( method_exists( $this, '_column_' . $column_name ) ) {
  1078 			elseif ( method_exists( $this, 'column_' . $column_name ) ) {
  1240 				echo call_user_func(
       
  1241 					array( $this, '_column_' . $column_name ),
       
  1242 					$item,
       
  1243 					$classes,
       
  1244 					$data,
       
  1245 					$primary
       
  1246 				);
       
  1247 			} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
  1079 				echo "<td $attributes>";
  1248 				echo "<td $attributes>";
  1080 				echo call_user_func( array( $this, 'column_' . $column_name ), $item );
  1249 				echo call_user_func( array( $this, 'column_' . $column_name ), $item );
       
  1250 				echo $this->handle_row_actions( $item, $column_name, $primary );
       
  1251 				echo "</td>";
       
  1252 			} else {
       
  1253 				echo "<td $attributes>";
       
  1254 				echo $this->column_default( $item, $column_name );
       
  1255 				echo $this->handle_row_actions( $item, $column_name, $primary );
  1081 				echo "</td>";
  1256 				echo "</td>";
  1082 			}
  1257 			}
  1083 			else {
  1258 		}
  1084 				echo "<td $attributes>";
  1259 	}
  1085 				echo $this->column_default( $item, $column_name );
  1260 
  1086 				echo "</td>";
  1261 	/**
  1087 			}
  1262 	 * Generates and display row actions links for the list table.
  1088 		}
  1263 	 *
  1089 	}
  1264 	 * @since 4.3.0
       
  1265 	 *
       
  1266 	 * @param object $item        The item being acted upon.
       
  1267 	 * @param string $column_name Current column name.
       
  1268 	 * @param string $primary     Primary column name.
       
  1269 	 * @return string The row actions HTML, or an empty string if the current column is the primary column.
       
  1270 	 */
       
  1271 	protected function handle_row_actions( $item, $column_name, $primary ) {
       
  1272 		return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
       
  1273  	}
  1090 
  1274 
  1091 	/**
  1275 	/**
  1092 	 * Handle an incoming ajax request (called from admin-ajax.php)
  1276 	 * Handle an incoming ajax request (called from admin-ajax.php)
  1093 	 *
  1277 	 *
  1094 	 * @since 3.1.0
  1278 	 * @since 3.1.0
  1095 	 * @access public
       
  1096 	 */
  1279 	 */
  1097 	public function ajax_response() {
  1280 	public function ajax_response() {
  1098 		$this->prepare_items();
  1281 		$this->prepare_items();
  1099 
  1282 
  1100 		ob_start();
  1283 		ob_start();
  1108 
  1291 
  1109 		$response = array( 'rows' => $rows );
  1292 		$response = array( 'rows' => $rows );
  1110 
  1293 
  1111 		if ( isset( $this->_pagination_args['total_items'] ) ) {
  1294 		if ( isset( $this->_pagination_args['total_items'] ) ) {
  1112 			$response['total_items_i18n'] = sprintf(
  1295 			$response['total_items_i18n'] = sprintf(
  1113 				_n( '1 item', '%s items', $this->_pagination_args['total_items'] ),
  1296 				_n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
  1114 				number_format_i18n( $this->_pagination_args['total_items'] )
  1297 				number_format_i18n( $this->_pagination_args['total_items'] )
  1115 			);
  1298 			);
  1116 		}
  1299 		}
  1117 		if ( isset( $this->_pagination_args['total_pages'] ) ) {
  1300 		if ( isset( $this->_pagination_args['total_pages'] ) ) {
  1118 			$response['total_pages'] = $this->_pagination_args['total_pages'];
  1301 			$response['total_pages'] = $this->_pagination_args['total_pages'];
  1123 	}
  1306 	}
  1124 
  1307 
  1125 	/**
  1308 	/**
  1126 	 * Send required variables to JavaScript land
  1309 	 * Send required variables to JavaScript land
  1127 	 *
  1310 	 *
  1128 	 * @access public
       
  1129 	 */
  1311 	 */
  1130 	public function _js_vars() {
  1312 	public function _js_vars() {
  1131 		$args = array(
  1313 		$args = array(
  1132 			'class'  => get_class( $this ),
  1314 			'class'  => get_class( $this ),
  1133 			'screen' => array(
  1315 			'screen' => array(