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