wp/wp-admin/includes/class-wp-list-table.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     1 <?php
       
     2 /**
       
     3  * Base class for displaying a list of items in an ajaxified HTML table.
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage List_Table
       
     7  * @since 3.1.0
       
     8  */
       
     9 
       
    10 /**
       
    11  * Base class for displaying a list of items in an ajaxified HTML table.
       
    12  *
       
    13  * @package WordPress
       
    14  * @subpackage List_Table
       
    15  * @since 3.1.0
       
    16  * @access private
       
    17  */
       
    18 class WP_List_Table {
       
    19 
       
    20 	/**
       
    21 	 * The current list of items
       
    22 	 *
       
    23 	 * @since 3.1.0
       
    24 	 * @var array
       
    25 	 * @access protected
       
    26 	 */
       
    27 	var $items;
       
    28 
       
    29 	/**
       
    30 	 * Various information about the current table
       
    31 	 *
       
    32 	 * @since 3.1.0
       
    33 	 * @var array
       
    34 	 * @access private
       
    35 	 */
       
    36 	var $_args;
       
    37 
       
    38 	/**
       
    39 	 * Various information needed for displaying the pagination
       
    40 	 *
       
    41 	 * @since 3.1.0
       
    42 	 * @var array
       
    43 	 * @access private
       
    44 	 */
       
    45 	var $_pagination_args = array();
       
    46 
       
    47 	/**
       
    48 	 * The current screen
       
    49 	 *
       
    50 	 * @since 3.1.0
       
    51 	 * @var object
       
    52 	 * @access protected
       
    53 	 */
       
    54 	var $screen;
       
    55 
       
    56 	/**
       
    57 	 * Cached bulk actions
       
    58 	 *
       
    59 	 * @since 3.1.0
       
    60 	 * @var array
       
    61 	 * @access private
       
    62 	 */
       
    63 	var $_actions;
       
    64 
       
    65 	/**
       
    66 	 * Cached pagination output
       
    67 	 *
       
    68 	 * @since 3.1.0
       
    69 	 * @var string
       
    70 	 * @access private
       
    71 	 */
       
    72 	var $_pagination;
       
    73 
       
    74 	/**
       
    75 	 * Constructor. The child class should call this constructor from its own constructor
       
    76 	 *
       
    77 	 * @param array $args An associative array with information about the current table
       
    78 	 * @access protected
       
    79 	 */
       
    80 	function __construct( $args = array() ) {
       
    81 		$args = wp_parse_args( $args, array(
       
    82 			'plural' => '',
       
    83 			'singular' => '',
       
    84 			'ajax' => false,
       
    85 			'screen' => null,
       
    86 		) );
       
    87 
       
    88 		$this->screen = convert_to_screen( $args['screen'] );
       
    89 
       
    90 		add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
       
    91 
       
    92 		if ( !$args['plural'] )
       
    93 			$args['plural'] = $this->screen->base;
       
    94 
       
    95 		$args['plural'] = sanitize_key( $args['plural'] );
       
    96 		$args['singular'] = sanitize_key( $args['singular'] );
       
    97 
       
    98 		$this->_args = $args;
       
    99 
       
   100 		if ( $args['ajax'] ) {
       
   101 			// wp_enqueue_script( 'list-table' );
       
   102 			add_action( 'admin_footer', array( $this, '_js_vars' ) );
       
   103 		}
       
   104 	}
       
   105 
       
   106 	/**
       
   107 	 * Checks the current user's permissions
       
   108 	 * @uses wp_die()
       
   109 	 *
       
   110 	 * @since 3.1.0
       
   111 	 * @access public
       
   112 	 * @abstract
       
   113 	 */
       
   114 	function ajax_user_can() {
       
   115 		die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
       
   116 	}
       
   117 
       
   118 	/**
       
   119 	 * Prepares the list of items for displaying.
       
   120 	 * @uses WP_List_Table::set_pagination_args()
       
   121 	 *
       
   122 	 * @since 3.1.0
       
   123 	 * @access public
       
   124 	 * @abstract
       
   125 	 */
       
   126 	function prepare_items() {
       
   127 		die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
       
   128 	}
       
   129 
       
   130 	/**
       
   131 	 * An internal method that sets all the necessary pagination arguments
       
   132 	 *
       
   133 	 * @param array $args An associative array with information about the pagination
       
   134 	 * @access protected
       
   135 	 */
       
   136 	function set_pagination_args( $args ) {
       
   137 		$args = wp_parse_args( $args, array(
       
   138 			'total_items' => 0,
       
   139 			'total_pages' => 0,
       
   140 			'per_page' => 0,
       
   141 		) );
       
   142 
       
   143 		if ( !$args['total_pages'] && $args['per_page'] > 0 )
       
   144 			$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
       
   145 
       
   146 		// redirect if page number is invalid and headers are not already sent
       
   147 		if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
       
   148 			wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
       
   149 			exit;
       
   150 		}
       
   151 
       
   152 		$this->_pagination_args = $args;
       
   153 	}
       
   154 
       
   155 	/**
       
   156 	 * Access the pagination args
       
   157 	 *
       
   158 	 * @since 3.1.0
       
   159 	 * @access public
       
   160 	 *
       
   161 	 * @param string $key
       
   162 	 * @return array
       
   163 	 */
       
   164 	function get_pagination_arg( $key ) {
       
   165 		if ( 'page' == $key )
       
   166 			return $this->get_pagenum();
       
   167 
       
   168 		if ( isset( $this->_pagination_args[$key] ) )
       
   169 			return $this->_pagination_args[$key];
       
   170 	}
       
   171 
       
   172 	/**
       
   173 	 * Whether the table has items to display or not
       
   174 	 *
       
   175 	 * @since 3.1.0
       
   176 	 * @access public
       
   177 	 *
       
   178 	 * @return bool
       
   179 	 */
       
   180 	function has_items() {
       
   181 		return !empty( $this->items );
       
   182 	}
       
   183 
       
   184 	/**
       
   185 	 * Message to be displayed when there are no items
       
   186 	 *
       
   187 	 * @since 3.1.0
       
   188 	 * @access public
       
   189 	 */
       
   190 	function no_items() {
       
   191 		_e( 'No items found.' );
       
   192 	}
       
   193 
       
   194 	/**
       
   195 	 * Display the search box.
       
   196 	 *
       
   197 	 * @since 3.1.0
       
   198 	 * @access public
       
   199 	 *
       
   200 	 * @param string $text The search button text
       
   201 	 * @param string $input_id The search input id
       
   202 	 */
       
   203 	function search_box( $text, $input_id ) {
       
   204 		if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
       
   205 			return;
       
   206 
       
   207 		$input_id = $input_id . '-search-input';
       
   208 
       
   209 		if ( ! empty( $_REQUEST['orderby'] ) )
       
   210 			echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
       
   211 		if ( ! empty( $_REQUEST['order'] ) )
       
   212 			echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
       
   213 		if ( ! empty( $_REQUEST['post_mime_type'] ) )
       
   214 			echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
       
   215 		if ( ! empty( $_REQUEST['detached'] ) )
       
   216 			echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
       
   217 ?>
       
   218 <p class="search-box">
       
   219 	<label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
       
   220 	<input type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
       
   221 	<?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
       
   222 </p>
       
   223 <?php
       
   224 	}
       
   225 
       
   226 	/**
       
   227 	 * Get an associative array ( id => link ) with the list
       
   228 	 * of views available on this table.
       
   229 	 *
       
   230 	 * @since 3.1.0
       
   231 	 * @access protected
       
   232 	 *
       
   233 	 * @return array
       
   234 	 */
       
   235 	function get_views() {
       
   236 		return array();
       
   237 	}
       
   238 
       
   239 	/**
       
   240 	 * Display the list of views available on this table.
       
   241 	 *
       
   242 	 * @since 3.1.0
       
   243 	 * @access public
       
   244 	 */
       
   245 	function views() {
       
   246 		$views = $this->get_views();
       
   247 		$views = apply_filters( 'views_' . $this->screen->id, $views );
       
   248 
       
   249 		if ( empty( $views ) )
       
   250 			return;
       
   251 
       
   252 		echo "<ul class='subsubsub'>\n";
       
   253 		foreach ( $views as $class => $view ) {
       
   254 			$views[ $class ] = "\t<li class='$class'>$view";
       
   255 		}
       
   256 		echo implode( " |</li>\n", $views ) . "</li>\n";
       
   257 		echo "</ul>";
       
   258 	}
       
   259 
       
   260 	/**
       
   261 	 * Get an associative array ( option_name => option_title ) with the list
       
   262 	 * of bulk actions available on this table.
       
   263 	 *
       
   264 	 * @since 3.1.0
       
   265 	 * @access protected
       
   266 	 *
       
   267 	 * @return array
       
   268 	 */
       
   269 	function get_bulk_actions() {
       
   270 		return array();
       
   271 	}
       
   272 
       
   273 	/**
       
   274 	 * Display the bulk actions dropdown.
       
   275 	 *
       
   276 	 * @since 3.1.0
       
   277 	 * @access public
       
   278 	 */
       
   279 	function bulk_actions() {
       
   280 		if ( is_null( $this->_actions ) ) {
       
   281 			$no_new_actions = $this->_actions = $this->get_bulk_actions();
       
   282 			// This filter can currently only be used to remove actions.
       
   283 			$this->_actions = apply_filters( 'bulk_actions-' . $this->screen->id, $this->_actions );
       
   284 			$this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
       
   285 			$two = '';
       
   286 		} else {
       
   287 			$two = '2';
       
   288 		}
       
   289 
       
   290 		if ( empty( $this->_actions ) )
       
   291 			return;
       
   292 
       
   293 		echo "<select name='action$two'>\n";
       
   294 		echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
       
   295 
       
   296 		foreach ( $this->_actions as $name => $title ) {
       
   297 			$class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
       
   298 
       
   299 			echo "\t<option value='$name'$class>$title</option>\n";
       
   300 		}
       
   301 
       
   302 		echo "</select>\n";
       
   303 
       
   304 		submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) );
       
   305 		echo "\n";
       
   306 	}
       
   307 
       
   308 	/**
       
   309 	 * Get the current action selected from the bulk actions dropdown.
       
   310 	 *
       
   311 	 * @since 3.1.0
       
   312 	 * @access public
       
   313 	 *
       
   314 	 * @return string|bool The action name or False if no action was selected
       
   315 	 */
       
   316 	function current_action() {
       
   317 		if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
       
   318 			return $_REQUEST['action'];
       
   319 
       
   320 		if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
       
   321 			return $_REQUEST['action2'];
       
   322 
       
   323 		return false;
       
   324 	}
       
   325 
       
   326 	/**
       
   327 	 * Generate row actions div
       
   328 	 *
       
   329 	 * @since 3.1.0
       
   330 	 * @access protected
       
   331 	 *
       
   332 	 * @param array $actions The list of actions
       
   333 	 * @param bool $always_visible Whether the actions should be always visible
       
   334 	 * @return string
       
   335 	 */
       
   336 	function row_actions( $actions, $always_visible = false ) {
       
   337 		$action_count = count( $actions );
       
   338 		$i = 0;
       
   339 
       
   340 		if ( !$action_count )
       
   341 			return '';
       
   342 
       
   343 		$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
       
   344 		foreach ( $actions as $action => $link ) {
       
   345 			++$i;
       
   346 			( $i == $action_count ) ? $sep = '' : $sep = ' | ';
       
   347 			$out .= "<span class='$action'>$link$sep</span>";
       
   348 		}
       
   349 		$out .= '</div>';
       
   350 
       
   351 		return $out;
       
   352 	}
       
   353 
       
   354 	/**
       
   355 	 * Display a monthly dropdown for filtering items
       
   356 	 *
       
   357 	 * @since 3.1.0
       
   358 	 * @access protected
       
   359 	 */
       
   360 	function months_dropdown( $post_type ) {
       
   361 		global $wpdb, $wp_locale;
       
   362 
       
   363 		$months = $wpdb->get_results( $wpdb->prepare( "
       
   364 			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
       
   365 			FROM $wpdb->posts
       
   366 			WHERE post_type = %s
       
   367 			ORDER BY post_date DESC
       
   368 		", $post_type ) );
       
   369 
       
   370 		/**
       
   371 		 * Filter the months dropdown results.
       
   372 		 *
       
   373 		 * @since 3.7.0
       
   374 		 *
       
   375 		 * @param object $months    The months dropdown query results.
       
   376 		 * @param string $post_type The post type.
       
   377 		 */
       
   378 		$months = apply_filters( 'months_dropdown_results', $months, $post_type );
       
   379 
       
   380 		$month_count = count( $months );
       
   381 
       
   382 		if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
       
   383 			return;
       
   384 
       
   385 		$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
       
   386 ?>
       
   387 		<select name='m'>
       
   388 			<option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
       
   389 <?php
       
   390 		foreach ( $months as $arc_row ) {
       
   391 			if ( 0 == $arc_row->year )
       
   392 				continue;
       
   393 
       
   394 			$month = zeroise( $arc_row->month, 2 );
       
   395 			$year = $arc_row->year;
       
   396 
       
   397 			printf( "<option %s value='%s'>%s</option>\n",
       
   398 				selected( $m, $year . $month, false ),
       
   399 				esc_attr( $arc_row->year . $month ),
       
   400 				/* translators: 1: month name, 2: 4-digit year */
       
   401 				sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
       
   402 			);
       
   403 		}
       
   404 ?>
       
   405 		</select>
       
   406 <?php
       
   407 	}
       
   408 
       
   409 	/**
       
   410 	 * Display a view switcher
       
   411 	 *
       
   412 	 * @since 3.1.0
       
   413 	 * @access protected
       
   414 	 */
       
   415 	function view_switcher( $current_mode ) {
       
   416 		$modes = array(
       
   417 			'list'    => __( 'List View' ),
       
   418 			'excerpt' => __( 'Excerpt View' )
       
   419 		);
       
   420 
       
   421 ?>
       
   422 		<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
       
   423 		<div class="view-switch">
       
   424 <?php
       
   425 			foreach ( $modes as $mode => $title ) {
       
   426 				$class = ( $current_mode == $mode ) ? 'class="current"' : '';
       
   427 				echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
       
   428 			}
       
   429 		?>
       
   430 		</div>
       
   431 <?php
       
   432 	}
       
   433 
       
   434 	/**
       
   435 	 * Display a comment count bubble
       
   436 	 *
       
   437 	 * @since 3.1.0
       
   438 	 * @access protected
       
   439 	 *
       
   440 	 * @param int $post_id
       
   441 	 * @param int $pending_comments
       
   442 	 */
       
   443 	function comments_bubble( $post_id, $pending_comments ) {
       
   444 		$pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
       
   445 
       
   446 		if ( $pending_comments )
       
   447 			echo '<strong>';
       
   448 
       
   449 		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>";
       
   450 
       
   451 		if ( $pending_comments )
       
   452 			echo '</strong>';
       
   453 	}
       
   454 
       
   455 	/**
       
   456 	 * Get the current page number
       
   457 	 *
       
   458 	 * @since 3.1.0
       
   459 	 * @access protected
       
   460 	 *
       
   461 	 * @return int
       
   462 	 */
       
   463 	function get_pagenum() {
       
   464 		$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
       
   465 
       
   466 		if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
       
   467 			$pagenum = $this->_pagination_args['total_pages'];
       
   468 
       
   469 		return max( 1, $pagenum );
       
   470 	}
       
   471 
       
   472 	/**
       
   473 	 * Get number of items to display on a single page
       
   474 	 *
       
   475 	 * @since 3.1.0
       
   476 	 * @access protected
       
   477 	 *
       
   478 	 * @return int
       
   479 	 */
       
   480 	function get_items_per_page( $option, $default = 20 ) {
       
   481 		$per_page = (int) get_user_option( $option );
       
   482 		if ( empty( $per_page ) || $per_page < 1 )
       
   483 			$per_page = $default;
       
   484 
       
   485 		return (int) apply_filters( $option, $per_page );
       
   486 	}
       
   487 
       
   488 	/**
       
   489 	 * Display the pagination.
       
   490 	 *
       
   491 	 * @since 3.1.0
       
   492 	 * @access protected
       
   493 	 */
       
   494 	function pagination( $which ) {
       
   495 		if ( empty( $this->_pagination_args ) )
       
   496 			return;
       
   497 
       
   498 		extract( $this->_pagination_args, EXTR_SKIP );
       
   499 
       
   500 		$output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
       
   501 
       
   502 		$current = $this->get_pagenum();
       
   503 
       
   504 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
       
   505 
       
   506 		$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
       
   507 
       
   508 		$page_links = array();
       
   509 
       
   510 		$disable_first = $disable_last = '';
       
   511 		if ( $current == 1 )
       
   512 			$disable_first = ' disabled';
       
   513 		if ( $current == $total_pages )
       
   514 			$disable_last = ' disabled';
       
   515 
       
   516 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
       
   517 			'first-page' . $disable_first,
       
   518 			esc_attr__( 'Go to the first page' ),
       
   519 			esc_url( remove_query_arg( 'paged', $current_url ) ),
       
   520 			'&laquo;'
       
   521 		);
       
   522 
       
   523 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
       
   524 			'prev-page' . $disable_first,
       
   525 			esc_attr__( 'Go to the previous page' ),
       
   526 			esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
       
   527 			'&lsaquo;'
       
   528 		);
       
   529 
       
   530 		if ( 'bottom' == $which )
       
   531 			$html_current_page = $current;
       
   532 		else
       
   533 			$html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='paged' value='%s' size='%d' />",
       
   534 				esc_attr__( 'Current page' ),
       
   535 				$current,
       
   536 				strlen( $total_pages )
       
   537 			);
       
   538 
       
   539 		$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
       
   540 		$page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
       
   541 
       
   542 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
       
   543 			'next-page' . $disable_last,
       
   544 			esc_attr__( 'Go to the next page' ),
       
   545 			esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
       
   546 			'&rsaquo;'
       
   547 		);
       
   548 
       
   549 		$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
       
   550 			'last-page' . $disable_last,
       
   551 			esc_attr__( 'Go to the last page' ),
       
   552 			esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
       
   553 			'&raquo;'
       
   554 		);
       
   555 
       
   556 		$pagination_links_class = 'pagination-links';
       
   557 		if ( ! empty( $infinite_scroll ) )
       
   558 			$pagination_links_class = ' hide-if-js';
       
   559 		$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
       
   560 
       
   561 		if ( $total_pages )
       
   562 			$page_class = $total_pages < 2 ? ' one-page' : '';
       
   563 		else
       
   564 			$page_class = ' no-pages';
       
   565 
       
   566 		$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
       
   567 
       
   568 		echo $this->_pagination;
       
   569 	}
       
   570 
       
   571 	/**
       
   572 	 * Get a list of columns. The format is:
       
   573 	 * 'internal-name' => 'Title'
       
   574 	 *
       
   575 	 * @since 3.1.0
       
   576 	 * @access protected
       
   577 	 * @abstract
       
   578 	 *
       
   579 	 * @return array
       
   580 	 */
       
   581 	function get_columns() {
       
   582 		die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
       
   583 	}
       
   584 
       
   585 	/**
       
   586 	 * Get a list of sortable columns. The format is:
       
   587 	 * 'internal-name' => 'orderby'
       
   588 	 * or
       
   589 	 * 'internal-name' => array( 'orderby', true )
       
   590 	 *
       
   591 	 * The second format will make the initial sorting order be descending
       
   592 	 *
       
   593 	 * @since 3.1.0
       
   594 	 * @access protected
       
   595 	 *
       
   596 	 * @return array
       
   597 	 */
       
   598 	function get_sortable_columns() {
       
   599 		return array();
       
   600 	}
       
   601 
       
   602 	/**
       
   603 	 * Get a list of all, hidden and sortable columns, with filter applied
       
   604 	 *
       
   605 	 * @since 3.1.0
       
   606 	 * @access protected
       
   607 	 *
       
   608 	 * @return array
       
   609 	 */
       
   610 	function get_column_info() {
       
   611 		if ( isset( $this->_column_headers ) )
       
   612 			return $this->_column_headers;
       
   613 
       
   614 		$columns = get_column_headers( $this->screen );
       
   615 		$hidden = get_hidden_columns( $this->screen );
       
   616 
       
   617 		$_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $this->get_sortable_columns() );
       
   618 
       
   619 		$sortable = array();
       
   620 		foreach ( $_sortable as $id => $data ) {
       
   621 			if ( empty( $data ) )
       
   622 				continue;
       
   623 
       
   624 			$data = (array) $data;
       
   625 			if ( !isset( $data[1] ) )
       
   626 				$data[1] = false;
       
   627 
       
   628 			$sortable[$id] = $data;
       
   629 		}
       
   630 
       
   631 		$this->_column_headers = array( $columns, $hidden, $sortable );
       
   632 
       
   633 		return $this->_column_headers;
       
   634 	}
       
   635 
       
   636 	/**
       
   637 	 * Return number of visible columns
       
   638 	 *
       
   639 	 * @since 3.1.0
       
   640 	 * @access public
       
   641 	 *
       
   642 	 * @return int
       
   643 	 */
       
   644 	function get_column_count() {
       
   645 		list ( $columns, $hidden ) = $this->get_column_info();
       
   646 		$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
       
   647 		return count( $columns ) - count( $hidden );
       
   648 	}
       
   649 
       
   650 	/**
       
   651 	 * Print column headers, accounting for hidden and sortable columns.
       
   652 	 *
       
   653 	 * @since 3.1.0
       
   654 	 * @access protected
       
   655 	 *
       
   656 	 * @param bool $with_id Whether to set the id attribute or not
       
   657 	 */
       
   658 	function print_column_headers( $with_id = true ) {
       
   659 		list( $columns, $hidden, $sortable ) = $this->get_column_info();
       
   660 
       
   661 		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
       
   662 		$current_url = remove_query_arg( 'paged', $current_url );
       
   663 
       
   664 		if ( isset( $_GET['orderby'] ) )
       
   665 			$current_orderby = $_GET['orderby'];
       
   666 		else
       
   667 			$current_orderby = '';
       
   668 
       
   669 		if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
       
   670 			$current_order = 'desc';
       
   671 		else
       
   672 			$current_order = 'asc';
       
   673 
       
   674 		if ( ! empty( $columns['cb'] ) ) {
       
   675 			static $cb_counter = 1;
       
   676 			$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
       
   677 				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
       
   678 			$cb_counter++;
       
   679 		}
       
   680 
       
   681 		foreach ( $columns as $column_key => $column_display_name ) {
       
   682 			$class = array( 'manage-column', "column-$column_key" );
       
   683 
       
   684 			$style = '';
       
   685 			if ( in_array( $column_key, $hidden ) )
       
   686 				$style = 'display:none;';
       
   687 
       
   688 			$style = ' style="' . $style . '"';
       
   689 
       
   690 			if ( 'cb' == $column_key )
       
   691 				$class[] = 'check-column';
       
   692 			elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
       
   693 				$class[] = 'num';
       
   694 
       
   695 			if ( isset( $sortable[$column_key] ) ) {
       
   696 				list( $orderby, $desc_first ) = $sortable[$column_key];
       
   697 
       
   698 				if ( $current_orderby == $orderby ) {
       
   699 					$order = 'asc' == $current_order ? 'desc' : 'asc';
       
   700 					$class[] = 'sorted';
       
   701 					$class[] = $current_order;
       
   702 				} else {
       
   703 					$order = $desc_first ? 'desc' : 'asc';
       
   704 					$class[] = 'sortable';
       
   705 					$class[] = $desc_first ? 'asc' : 'desc';
       
   706 				}
       
   707 
       
   708 				$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>';
       
   709 			}
       
   710 
       
   711 			$id = $with_id ? "id='$column_key'" : '';
       
   712 
       
   713 			if ( !empty( $class ) )
       
   714 				$class = "class='" . join( ' ', $class ) . "'";
       
   715 
       
   716 			echo "<th scope='col' $id $class $style>$column_display_name</th>";
       
   717 		}
       
   718 	}
       
   719 
       
   720 	/**
       
   721 	 * Display the table
       
   722 	 *
       
   723 	 * @since 3.1.0
       
   724 	 * @access public
       
   725 	 */
       
   726 	function display() {
       
   727 		extract( $this->_args );
       
   728 
       
   729 		$this->display_tablenav( 'top' );
       
   730 
       
   731 ?>
       
   732 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
       
   733 	<thead>
       
   734 	<tr>
       
   735 		<?php $this->print_column_headers(); ?>
       
   736 	</tr>
       
   737 	</thead>
       
   738 
       
   739 	<tfoot>
       
   740 	<tr>
       
   741 		<?php $this->print_column_headers( false ); ?>
       
   742 	</tr>
       
   743 	</tfoot>
       
   744 
       
   745 	<tbody id="the-list"<?php if ( $singular ) echo " data-wp-lists='list:$singular'"; ?>>
       
   746 		<?php $this->display_rows_or_placeholder(); ?>
       
   747 	</tbody>
       
   748 </table>
       
   749 <?php
       
   750 		$this->display_tablenav( 'bottom' );
       
   751 	}
       
   752 
       
   753 	/**
       
   754 	 * Get a list of CSS classes for the <table> tag
       
   755 	 *
       
   756 	 * @since 3.1.0
       
   757 	 * @access protected
       
   758 	 *
       
   759 	 * @return array
       
   760 	 */
       
   761 	function get_table_classes() {
       
   762 		return array( 'widefat', 'fixed', $this->_args['plural'] );
       
   763 	}
       
   764 
       
   765 	/**
       
   766 	 * Generate the table navigation above or below the table
       
   767 	 *
       
   768 	 * @since 3.1.0
       
   769 	 * @access protected
       
   770 	 */
       
   771 	function display_tablenav( $which ) {
       
   772 		if ( 'top' == $which )
       
   773 			wp_nonce_field( 'bulk-' . $this->_args['plural'] );
       
   774 ?>
       
   775 	<div class="tablenav <?php echo esc_attr( $which ); ?>">
       
   776 
       
   777 		<div class="alignleft actions bulkactions">
       
   778 			<?php $this->bulk_actions(); ?>
       
   779 		</div>
       
   780 <?php
       
   781 		$this->extra_tablenav( $which );
       
   782 		$this->pagination( $which );
       
   783 ?>
       
   784 
       
   785 		<br class="clear" />
       
   786 	</div>
       
   787 <?php
       
   788 	}
       
   789 
       
   790 	/**
       
   791 	 * Extra controls to be displayed between bulk actions and pagination
       
   792 	 *
       
   793 	 * @since 3.1.0
       
   794 	 * @access protected
       
   795 	 */
       
   796 	function extra_tablenav( $which ) {}
       
   797 
       
   798 	/**
       
   799 	 * Generate the <tbody> part of the table
       
   800 	 *
       
   801 	 * @since 3.1.0
       
   802 	 * @access protected
       
   803 	 */
       
   804 	function display_rows_or_placeholder() {
       
   805 		if ( $this->has_items() ) {
       
   806 			$this->display_rows();
       
   807 		} else {
       
   808 			list( $columns, $hidden ) = $this->get_column_info();
       
   809 			echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
       
   810 			$this->no_items();
       
   811 			echo '</td></tr>';
       
   812 		}
       
   813 	}
       
   814 
       
   815 	/**
       
   816 	 * Generate the table rows
       
   817 	 *
       
   818 	 * @since 3.1.0
       
   819 	 * @access protected
       
   820 	 */
       
   821 	function display_rows() {
       
   822 		foreach ( $this->items as $item )
       
   823 			$this->single_row( $item );
       
   824 	}
       
   825 
       
   826 	/**
       
   827 	 * Generates content for a single row of the table
       
   828 	 *
       
   829 	 * @since 3.1.0
       
   830 	 * @access protected
       
   831 	 *
       
   832 	 * @param object $item The current item
       
   833 	 */
       
   834 	function single_row( $item ) {
       
   835 		static $row_class = '';
       
   836 		$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
       
   837 
       
   838 		echo '<tr' . $row_class . '>';
       
   839 		$this->single_row_columns( $item );
       
   840 		echo '</tr>';
       
   841 	}
       
   842 
       
   843 	/**
       
   844 	 * Generates the columns for a single row of the table
       
   845 	 *
       
   846 	 * @since 3.1.0
       
   847 	 * @access protected
       
   848 	 *
       
   849 	 * @param object $item The current item
       
   850 	 */
       
   851 	function single_row_columns( $item ) {
       
   852 		list( $columns, $hidden ) = $this->get_column_info();
       
   853 
       
   854 		foreach ( $columns as $column_name => $column_display_name ) {
       
   855 			$class = "class='$column_name column-$column_name'";
       
   856 
       
   857 			$style = '';
       
   858 			if ( in_array( $column_name, $hidden ) )
       
   859 				$style = ' style="display:none;"';
       
   860 
       
   861 			$attributes = "$class$style";
       
   862 
       
   863 			if ( 'cb' == $column_name ) {
       
   864 				echo '<th scope="row" class="check-column">';
       
   865 				echo $this->column_cb( $item );
       
   866 				echo '</th>';
       
   867 			}
       
   868 			elseif ( method_exists( $this, 'column_' . $column_name ) ) {
       
   869 				echo "<td $attributes>";
       
   870 				echo call_user_func( array( $this, 'column_' . $column_name ), $item );
       
   871 				echo "</td>";
       
   872 			}
       
   873 			else {
       
   874 				echo "<td $attributes>";
       
   875 				echo $this->column_default( $item, $column_name );
       
   876 				echo "</td>";
       
   877 			}
       
   878 		}
       
   879 	}
       
   880 
       
   881 	/**
       
   882 	 * Handle an incoming ajax request (called from admin-ajax.php)
       
   883 	 *
       
   884 	 * @since 3.1.0
       
   885 	 * @access public
       
   886 	 */
       
   887 	function ajax_response() {
       
   888 		$this->prepare_items();
       
   889 
       
   890 		extract( $this->_args );
       
   891 		extract( $this->_pagination_args, EXTR_SKIP );
       
   892 
       
   893 		ob_start();
       
   894 		if ( ! empty( $_REQUEST['no_placeholder'] ) )
       
   895 			$this->display_rows();
       
   896 		else
       
   897 			$this->display_rows_or_placeholder();
       
   898 
       
   899 		$rows = ob_get_clean();
       
   900 
       
   901 		$response = array( 'rows' => $rows );
       
   902 
       
   903 		if ( isset( $total_items ) )
       
   904 			$response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
       
   905 
       
   906 		if ( isset( $total_pages ) ) {
       
   907 			$response['total_pages'] = $total_pages;
       
   908 			$response['total_pages_i18n'] = number_format_i18n( $total_pages );
       
   909 		}
       
   910 
       
   911 		die( json_encode( $response ) );
       
   912 	}
       
   913 
       
   914 	/**
       
   915 	 * Send required variables to JavaScript land
       
   916 	 *
       
   917 	 * @access private
       
   918 	 */
       
   919 	function _js_vars() {
       
   920 		$args = array(
       
   921 			'class'  => get_class( $this ),
       
   922 			'screen' => array(
       
   923 				'id'   => $this->screen->id,
       
   924 				'base' => $this->screen->base,
       
   925 			)
       
   926 		);
       
   927 
       
   928 		printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
       
   929 	}
       
   930 }