1 <?php |
1 <?php |
|
2 /** |
|
3 * Administration API: WP_List_Table class |
|
4 * |
|
5 * @package WordPress |
|
6 * @subpackage List_Table |
|
7 * @since 3.1.0 |
|
8 */ |
|
9 |
2 /** |
10 /** |
3 * Base class for displaying a list of items in an ajaxified HTML table. |
11 * Base class for displaying a list of items in an ajaxified HTML table. |
4 * |
12 * |
5 * @since 3.1.0 |
13 * @since 3.1.0 |
6 * @access private |
14 * @access private |
7 * |
|
8 * @package WordPress |
|
9 * @subpackage List_Table |
|
10 */ |
15 */ |
11 class WP_List_Table { |
16 class WP_List_Table { |
12 |
17 |
13 /** |
18 /** |
14 * The current list of items |
19 * The current list of items. |
15 * |
20 * |
16 * @since 3.1.0 |
21 * @since 3.1.0 |
17 * @var array |
22 * @var array |
18 * @access public |
|
19 */ |
23 */ |
20 public $items; |
24 public $items; |
21 |
25 |
22 /** |
26 /** |
23 * Various information about the current table |
27 * Various information about the current table. |
24 * |
28 * |
25 * @since 3.1.0 |
29 * @since 3.1.0 |
26 * @var array |
30 * @var array |
27 * @access protected |
|
28 */ |
31 */ |
29 protected $_args; |
32 protected $_args; |
30 |
33 |
31 /** |
34 /** |
32 * Various information needed for displaying the pagination |
35 * Various information needed for displaying the pagination. |
33 * |
36 * |
34 * @since 3.1.0 |
37 * @since 3.1.0 |
35 * @var array |
38 * @var array |
36 */ |
39 */ |
37 protected $_pagination_args = array(); |
40 protected $_pagination_args = array(); |
38 |
41 |
39 /** |
42 /** |
40 * The current screen |
43 * The current screen. |
41 * |
44 * |
42 * @since 3.1.0 |
45 * @since 3.1.0 |
43 * @var object |
46 * @var object |
44 * @access protected |
|
45 */ |
47 */ |
46 protected $screen; |
48 protected $screen; |
47 |
49 |
48 /** |
50 /** |
49 * Cached bulk actions |
51 * Cached bulk actions. |
50 * |
52 * |
51 * @since 3.1.0 |
53 * @since 3.1.0 |
52 * @var array |
54 * @var array |
53 * @access private |
|
54 */ |
55 */ |
55 private $_actions; |
56 private $_actions; |
56 |
57 |
57 /** |
58 /** |
58 * Cached pagination output |
59 * Cached pagination output. |
59 * |
60 * |
60 * @since 3.1.0 |
61 * @since 3.1.0 |
61 * @var string |
62 * @var string |
62 * @access private |
|
63 */ |
63 */ |
64 private $_pagination; |
64 private $_pagination; |
65 |
65 |
66 /** |
66 /** |
67 * The view switcher modes. |
67 * The view switcher modes. |
68 * |
68 * |
69 * @since 4.1.0 |
69 * @since 4.1.0 |
70 * @var array |
70 * @var array |
71 * @access protected |
|
72 */ |
71 */ |
73 protected $modes = array(); |
72 protected $modes = array(); |
74 |
73 |
75 /** |
74 /** |
76 * Stores the value returned by ->get_column_info() |
75 * Stores the value returned by ->get_column_info(). |
77 * |
76 * |
|
77 * @since 4.1.0 |
78 * @var array |
78 * @var array |
79 */ |
79 */ |
80 protected $_column_headers; |
80 protected $_column_headers; |
81 |
81 |
|
82 /** |
|
83 * {@internal Missing Summary} |
|
84 * |
|
85 * @var array |
|
86 */ |
82 protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' ); |
87 protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' ); |
83 |
88 |
|
89 /** |
|
90 * {@internal Missing Summary} |
|
91 * |
|
92 * @var array |
|
93 */ |
84 protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions', |
94 protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions', |
85 'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination', |
95 'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination', |
86 'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav', |
96 'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav', |
87 'single_row_columns' ); |
97 'single_row_columns' ); |
88 |
98 |
91 * |
101 * |
92 * The child class should call this constructor from its own constructor to override |
102 * The child class should call this constructor from its own constructor to override |
93 * the default $args. |
103 * the default $args. |
94 * |
104 * |
95 * @since 3.1.0 |
105 * @since 3.1.0 |
96 * @access public |
|
97 * |
106 * |
98 * @param array|string $args { |
107 * @param array|string $args { |
99 * Array or string of arguments. |
108 * Array or string of arguments. |
100 * |
109 * |
101 * @type string $plural Plural value used for labels and the objects being listed. |
110 * @type string $plural Plural value used for labels and the objects being listed. |
102 * This affects things such as CSS class-names and nonces used |
111 * This affects things such as CSS class-names and nonces used |
103 * in the list table, e.g. 'posts'. Default empty. |
112 * in the list table, e.g. 'posts'. Default empty. |
104 * @type string $singular Singular label for an object being listed, e.g. 'post'. |
113 * @type string $singular Singular label for an object being listed, e.g. 'post'. |
105 * Default empty |
114 * Default empty |
106 * @type bool $ajax Whether the list table supports AJAX. This includes loading |
115 * @type bool $ajax Whether the list table supports Ajax. This includes loading |
107 * and sorting data, for example. If true, the class will call |
116 * and sorting data, for example. If true, the class will call |
108 * the {@see _js_vars()} method in the footer to provide variables |
117 * the _js_vars() method in the footer to provide variables |
109 * to any scripts handling AJAX events. Default false. |
118 * to any scripts handling Ajax events. Default false. |
110 * @type string $screen String containing the hook name used to determine the current |
119 * @type string $screen String containing the hook name used to determine the current |
111 * screen. If left null, the current screen will be automatically set. |
120 * screen. If left null, the current screen will be automatically set. |
112 * Default null. |
121 * Default null. |
113 * } |
122 * } |
114 */ |
123 */ |
422 * @since 3.5.0 |
421 * @since 3.5.0 |
423 * |
422 * |
424 * @param array $actions An array of the available bulk actions. |
423 * @param array $actions An array of the available bulk actions. |
425 */ |
424 */ |
426 $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); |
425 $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); |
427 $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions ); |
|
428 $two = ''; |
426 $two = ''; |
429 } else { |
427 } else { |
430 $two = '2'; |
428 $two = '2'; |
431 } |
429 } |
432 |
430 |
433 if ( empty( $this->_actions ) ) |
431 if ( empty( $this->_actions ) ) |
434 return; |
432 return; |
435 |
433 |
436 echo "<label for='bulk-action-selector-" . esc_attr( $which ) . "' class='screen-reader-text'>" . __( 'Select bulk action' ) . "</label>"; |
434 echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>'; |
437 echo "<select name='action$two' id='bulk-action-selector-" . esc_attr( $which ) . "'>\n"; |
435 echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n"; |
438 echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n"; |
436 echo '<option value="-1">' . __( 'Bulk Actions' ) . "</option>\n"; |
439 |
437 |
440 foreach ( $this->_actions as $name => $title ) { |
438 foreach ( $this->_actions as $name => $title ) { |
441 $class = 'edit' == $name ? ' class="hide-if-no-js"' : ''; |
439 $class = 'edit' === $name ? ' class="hide-if-no-js"' : ''; |
442 |
440 |
443 echo "\t<option value='$name'$class>$title</option>\n"; |
441 echo "\t" . '<option value="' . $name . '"' . $class . '>' . $title . "</option>\n"; |
444 } |
442 } |
445 |
443 |
446 echo "</select>\n"; |
444 echo "</select>\n"; |
447 |
445 |
448 submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) ); |
446 submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) ); |
493 ( $i == $action_count ) ? $sep = '' : $sep = ' | '; |
489 ( $i == $action_count ) ? $sep = '' : $sep = ' | '; |
494 $out .= "<span class='$action'>$link$sep</span>"; |
490 $out .= "<span class='$action'>$link$sep</span>"; |
495 } |
491 } |
496 $out .= '</div>'; |
492 $out .= '</div>'; |
497 |
493 |
|
494 $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>'; |
|
495 |
498 return $out; |
496 return $out; |
499 } |
497 } |
500 |
498 |
501 /** |
499 /** |
502 * Display a monthly dropdown for filtering items |
500 * Display a monthly dropdown for filtering items |
503 * |
501 * |
504 * @since 3.1.0 |
502 * @since 3.1.0 |
505 * @access protected |
503 * |
|
504 * @global wpdb $wpdb |
|
505 * @global WP_Locale $wp_locale |
506 * |
506 * |
507 * @param string $post_type |
507 * @param string $post_type |
508 */ |
508 */ |
509 protected function months_dropdown( $post_type ) { |
509 protected function months_dropdown( $post_type ) { |
510 global $wpdb, $wp_locale; |
510 global $wpdb, $wp_locale; |
511 |
511 |
512 /** |
512 /** |
513 * Filter whether to remove the 'Months' drop-down from the post list table. |
513 * Filters whether to remove the 'Months' drop-down from the post list table. |
514 * |
514 * |
515 * @since 4.2.0 |
515 * @since 4.2.0 |
516 * |
516 * |
517 * @param bool $disable Whether to disable the drop-down. Default false. |
517 * @param bool $disable Whether to disable the drop-down. Default false. |
518 * @param string $post_type The post type. |
518 * @param string $post_type The post type. |
519 */ |
519 */ |
520 if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) { |
520 if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) { |
521 return; |
521 return; |
522 } |
522 } |
523 |
523 |
|
524 $extra_checks = "AND post_status != 'auto-draft'"; |
|
525 if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) { |
|
526 $extra_checks .= " AND post_status != 'trash'"; |
|
527 } elseif ( isset( $_GET['post_status'] ) ) { |
|
528 $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] ); |
|
529 } |
|
530 |
524 $months = $wpdb->get_results( $wpdb->prepare( " |
531 $months = $wpdb->get_results( $wpdb->prepare( " |
525 SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month |
532 SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month |
526 FROM $wpdb->posts |
533 FROM $wpdb->posts |
527 WHERE post_type = %s |
534 WHERE post_type = %s |
|
535 $extra_checks |
528 ORDER BY post_date DESC |
536 ORDER BY post_date DESC |
529 ", $post_type ) ); |
537 ", $post_type ) ); |
530 |
538 |
531 /** |
539 /** |
532 * Filter the 'Months' drop-down results. |
540 * Filters the 'Months' drop-down results. |
533 * |
541 * |
534 * @since 3.7.0 |
542 * @since 3.7.0 |
535 * |
543 * |
536 * @param object $months The months drop-down query results. |
544 * @param object $months The months drop-down query results. |
537 * @param string $post_type The post type. |
545 * @param string $post_type The post type. |
570 |
578 |
571 /** |
579 /** |
572 * Display a view switcher |
580 * Display a view switcher |
573 * |
581 * |
574 * @since 3.1.0 |
582 * @since 3.1.0 |
575 * @access protected |
|
576 * |
583 * |
577 * @param string $current_mode |
584 * @param string $current_mode |
578 */ |
585 */ |
579 protected function view_switcher( $current_mode ) { |
586 protected function view_switcher( $current_mode ) { |
580 ?> |
587 ?> |
581 <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" /> |
588 <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" /> |
582 <div class="view-switch"> |
589 <div class="view-switch"> |
583 <?php |
590 <?php |
584 foreach ( $this->modes as $mode => $title ) { |
591 foreach ( $this->modes as $mode => $title ) { |
585 $classes = array( 'view-' . $mode ); |
592 $classes = array( 'view-' . $mode ); |
586 if ( $current_mode == $mode ) |
593 if ( $current_mode === $mode ) |
587 $classes[] = 'current'; |
594 $classes[] = 'current'; |
588 printf( |
595 printf( |
589 "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n", |
596 "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n", |
590 esc_url( add_query_arg( 'mode', $mode ) ), |
597 esc_url( add_query_arg( 'mode', $mode ) ), |
591 implode( ' ', $classes ), |
598 implode( ' ', $classes ), |
599 |
606 |
600 /** |
607 /** |
601 * Display a comment count bubble |
608 * Display a comment count bubble |
602 * |
609 * |
603 * @since 3.1.0 |
610 * @since 3.1.0 |
604 * @access protected |
|
605 * |
611 * |
606 * @param int $post_id The post ID. |
612 * @param int $post_id The post ID. |
607 * @param int $pending_comments Number of pending comments. |
613 * @param int $pending_comments Number of pending comments. |
608 */ |
614 */ |
609 protected function comments_bubble( $post_id, $pending_comments ) { |
615 protected function comments_bubble( $post_id, $pending_comments ) { |
610 $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) ); |
616 $approved_comments = get_comments_number(); |
611 |
617 |
612 if ( $pending_comments ) |
618 $approved_comments_number = number_format_i18n( $approved_comments ); |
613 echo '<strong>'; |
619 $pending_comments_number = number_format_i18n( $pending_comments ); |
614 |
620 |
615 echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>"; |
621 $approved_only_phrase = sprintf( _n( '%s comment', '%s comments', $approved_comments ), $approved_comments_number ); |
616 |
622 $approved_phrase = sprintf( _n( '%s approved comment', '%s approved comments', $approved_comments ), $approved_comments_number ); |
617 if ( $pending_comments ) |
623 $pending_phrase = sprintf( _n( '%s pending comment', '%s pending comments', $pending_comments ), $pending_comments_number ); |
618 echo '</strong>'; |
624 |
|
625 // No comments at all. |
|
626 if ( ! $approved_comments && ! $pending_comments ) { |
|
627 printf( '<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>', |
|
628 __( 'No comments' ) |
|
629 ); |
|
630 // Approved comments have different display depending on some conditions. |
|
631 } elseif ( $approved_comments ) { |
|
632 printf( '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>', |
|
633 esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'approved' ), admin_url( 'edit-comments.php' ) ) ), |
|
634 $approved_comments_number, |
|
635 $pending_comments ? $approved_phrase : $approved_only_phrase |
|
636 ); |
|
637 } else { |
|
638 printf( '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>', |
|
639 $approved_comments_number, |
|
640 $pending_comments ? __( 'No approved comments' ) : __( 'No comments' ) |
|
641 ); |
|
642 } |
|
643 |
|
644 if ( $pending_comments ) { |
|
645 printf( '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>', |
|
646 esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'moderated' ), admin_url( 'edit-comments.php' ) ) ), |
|
647 $pending_comments_number, |
|
648 $pending_phrase |
|
649 ); |
|
650 } else { |
|
651 printf( '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>', |
|
652 $pending_comments_number, |
|
653 $approved_comments ? __( 'No pending comments' ) : __( 'No comments' ) |
|
654 ); |
|
655 } |
619 } |
656 } |
620 |
657 |
621 /** |
658 /** |
622 * Get the current page number |
659 * Get the current page number |
623 * |
660 * |
624 * @since 3.1.0 |
661 * @since 3.1.0 |
625 * @access public |
|
626 * |
662 * |
627 * @return int |
663 * @return int |
628 */ |
664 */ |
629 public function get_pagenum() { |
665 public function get_pagenum() { |
630 $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; |
666 $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; |
631 |
667 |
632 if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) |
668 if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) |
633 $pagenum = $this->_pagination_args['total_pages']; |
669 $pagenum = $this->_pagination_args['total_pages']; |
634 |
670 |
635 return max( 1, $pagenum ); |
671 return max( 1, $pagenum ); |
636 } |
672 } |
637 |
673 |
638 /** |
674 /** |
639 * Get number of items to display on a single page |
675 * Get number of items to display on a single page |
640 * |
676 * |
641 * @since 3.1.0 |
677 * @since 3.1.0 |
642 * @access protected |
|
643 * |
678 * |
644 * @param string $option |
679 * @param string $option |
645 * @param int $default |
680 * @param int $default |
646 * @return int |
681 * @return int |
647 */ |
682 */ |
684 $infinite_scroll = false; |
718 $infinite_scroll = false; |
685 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) { |
719 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) { |
686 $infinite_scroll = $this->_pagination_args['infinite_scroll']; |
720 $infinite_scroll = $this->_pagination_args['infinite_scroll']; |
687 } |
721 } |
688 |
722 |
689 $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>'; |
723 if ( 'top' === $which && $total_pages > 1 ) { |
|
724 $this->screen->render_screen_reader_content( 'heading_pagination' ); |
|
725 } |
|
726 |
|
727 $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>'; |
690 |
728 |
691 $current = $this->get_pagenum(); |
729 $current = $this->get_pagenum(); |
|
730 $removable_query_args = wp_removable_query_args(); |
692 |
731 |
693 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); |
732 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); |
694 |
733 |
695 $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url ); |
734 $current_url = remove_query_arg( $removable_query_args, $current_url ); |
696 |
735 |
697 $page_links = array(); |
736 $page_links = array(); |
698 |
737 |
699 $disable_first = $disable_last = ''; |
738 $total_pages_before = '<span class="paging-input">'; |
700 if ( $current == 1 ) { |
739 $total_pages_after = '</span></span>'; |
701 $disable_first = ' disabled'; |
740 |
702 } |
741 $disable_first = $disable_last = $disable_prev = $disable_next = false; |
703 if ( $current == $total_pages ) { |
742 |
704 $disable_last = ' disabled'; |
743 if ( $current == 1 ) { |
705 } |
744 $disable_first = true; |
706 $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>", |
745 $disable_prev = true; |
707 'first-page' . $disable_first, |
746 } |
708 esc_attr__( 'Go to the first page' ), |
747 if ( $current == 2 ) { |
709 esc_url( remove_query_arg( 'paged', $current_url ) ), |
748 $disable_first = true; |
710 '«' |
749 } |
711 ); |
750 if ( $current == $total_pages ) { |
712 |
751 $disable_last = true; |
713 $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>", |
752 $disable_next = true; |
714 'prev-page' . $disable_first, |
753 } |
715 esc_attr__( 'Go to the previous page' ), |
754 if ( $current == $total_pages - 1 ) { |
716 esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ), |
755 $disable_last = true; |
717 '‹' |
756 } |
718 ); |
757 |
719 |
758 if ( $disable_first ) { |
720 if ( 'bottom' == $which ) { |
759 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">«</span>'; |
721 $html_current_page = $current; |
|
722 } else { |
760 } else { |
723 $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' title='%s' type='text' name='paged' value='%s' size='%d' />", |
761 $page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
724 '<label for="current-page-selector" class="screen-reader-text">' . __( 'Select Page' ) . '</label>', |
762 esc_url( remove_query_arg( 'paged', $current_url ) ), |
725 esc_attr__( 'Current page' ), |
763 __( 'First page' ), |
|
764 '«' |
|
765 ); |
|
766 } |
|
767 |
|
768 if ( $disable_prev ) { |
|
769 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">‹</span>'; |
|
770 } else { |
|
771 $page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
|
772 esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ), |
|
773 __( 'Previous page' ), |
|
774 '‹' |
|
775 ); |
|
776 } |
|
777 |
|
778 if ( 'bottom' === $which ) { |
|
779 $html_current_page = $current; |
|
780 $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">'; |
|
781 } else { |
|
782 $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>", |
|
783 '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>', |
726 $current, |
784 $current, |
727 strlen( $total_pages ) |
785 strlen( $total_pages ) |
728 ); |
786 ); |
729 } |
787 } |
730 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) ); |
788 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) ); |
731 $page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>'; |
789 $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after; |
732 |
790 |
733 $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>", |
791 if ( $disable_next ) { |
734 'next-page' . $disable_last, |
792 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">›</span>'; |
735 esc_attr__( 'Go to the next page' ), |
793 } else { |
736 esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ), |
794 $page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
737 '›' |
795 esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ), |
738 ); |
796 __( 'Next page' ), |
739 |
797 '›' |
740 $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>", |
798 ); |
741 'last-page' . $disable_last, |
799 } |
742 esc_attr__( 'Go to the last page' ), |
800 |
743 esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), |
801 if ( $disable_last ) { |
744 '»' |
802 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">»</span>'; |
745 ); |
803 } else { |
|
804 $page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>", |
|
805 esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), |
|
806 __( 'Last page' ), |
|
807 '»' |
|
808 ); |
|
809 } |
746 |
810 |
747 $pagination_links_class = 'pagination-links'; |
811 $pagination_links_class = 'pagination-links'; |
748 if ( ! empty( $infinite_scroll ) ) { |
812 if ( ! empty( $infinite_scroll ) ) { |
749 $pagination_links_class = ' hide-if-js'; |
813 $pagination_links_class .= ' hide-if-js'; |
750 } |
814 } |
751 $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>'; |
815 $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>'; |
752 |
816 |
753 if ( $total_pages ) { |
817 if ( $total_pages ) { |
754 $page_class = $total_pages < 2 ? ' one-page' : ''; |
818 $page_class = $total_pages < 2 ? ' one-page' : ''; |
781 * 'internal-name' => array( 'orderby', true ) |
844 * 'internal-name' => array( 'orderby', true ) |
782 * |
845 * |
783 * The second format will make the initial sorting order be descending |
846 * The second format will make the initial sorting order be descending |
784 * |
847 * |
785 * @since 3.1.0 |
848 * @since 3.1.0 |
786 * @access protected |
|
787 * |
849 * |
788 * @return array |
850 * @return array |
789 */ |
851 */ |
790 protected function get_sortable_columns() { |
852 protected function get_sortable_columns() { |
791 return array(); |
853 return array(); |
792 } |
854 } |
793 |
855 |
794 /** |
856 /** |
|
857 * Gets the name of the default primary column. |
|
858 * |
|
859 * @since 4.3.0 |
|
860 * |
|
861 * @return string Name of the default primary column, in this case, an empty string. |
|
862 */ |
|
863 protected function get_default_primary_column_name() { |
|
864 $columns = $this->get_columns(); |
|
865 $column = ''; |
|
866 |
|
867 if ( empty( $columns ) ) { |
|
868 return $column; |
|
869 } |
|
870 |
|
871 // We need a primary defined so responsive views show something, |
|
872 // so let's fall back to the first non-checkbox column. |
|
873 foreach ( $columns as $col => $column_name ) { |
|
874 if ( 'cb' === $col ) { |
|
875 continue; |
|
876 } |
|
877 |
|
878 $column = $col; |
|
879 break; |
|
880 } |
|
881 |
|
882 return $column; |
|
883 } |
|
884 |
|
885 /** |
|
886 * Public wrapper for WP_List_Table::get_default_primary_column_name(). |
|
887 * |
|
888 * @since 4.4.0 |
|
889 * |
|
890 * @return string Name of the default primary column. |
|
891 */ |
|
892 public function get_primary_column() { |
|
893 return $this->get_primary_column_name(); |
|
894 } |
|
895 |
|
896 /** |
|
897 * Gets the name of the primary column. |
|
898 * |
|
899 * @since 4.3.0 |
|
900 * |
|
901 * @return string The name of the primary column. |
|
902 */ |
|
903 protected function get_primary_column_name() { |
|
904 $columns = get_column_headers( $this->screen ); |
|
905 $default = $this->get_default_primary_column_name(); |
|
906 |
|
907 // If the primary column doesn't exist fall back to the |
|
908 // first non-checkbox column. |
|
909 if ( ! isset( $columns[ $default ] ) ) { |
|
910 $default = WP_List_Table::get_default_primary_column_name(); |
|
911 } |
|
912 |
|
913 /** |
|
914 * Filters the name of the primary column for the current list table. |
|
915 * |
|
916 * @since 4.3.0 |
|
917 * |
|
918 * @param string $default Column name default for the specific list table, e.g. 'name'. |
|
919 * @param string $context Screen ID for specific list table, e.g. 'plugins'. |
|
920 */ |
|
921 $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id ); |
|
922 |
|
923 if ( empty( $column ) || ! isset( $columns[ $column ] ) ) { |
|
924 $column = $default; |
|
925 } |
|
926 |
|
927 return $column; |
|
928 } |
|
929 |
|
930 /** |
795 * Get a list of all, hidden and sortable columns, with filter applied |
931 * Get a list of all, hidden and sortable columns, with filter applied |
796 * |
932 * |
797 * @since 3.1.0 |
933 * @since 3.1.0 |
798 * @access protected |
|
799 * |
934 * |
800 * @return array |
935 * @return array |
801 */ |
936 */ |
802 protected function get_column_info() { |
937 protected function get_column_info() { |
803 if ( isset( $this->_column_headers ) ) |
938 // $_column_headers is already set / cached |
804 return $this->_column_headers; |
939 if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) { |
|
940 // Back-compat for list tables that have been manually setting $_column_headers for horse reasons. |
|
941 // In 4.3, we added a fourth argument for primary column. |
|
942 $column_headers = array( array(), array(), array(), $this->get_primary_column_name() ); |
|
943 foreach ( $this->_column_headers as $key => $value ) { |
|
944 $column_headers[ $key ] = $value; |
|
945 } |
|
946 |
|
947 return $column_headers; |
|
948 } |
805 |
949 |
806 $columns = get_column_headers( $this->screen ); |
950 $columns = get_column_headers( $this->screen ); |
807 $hidden = get_hidden_columns( $this->screen ); |
951 $hidden = get_hidden_columns( $this->screen ); |
808 |
952 |
809 $sortable_columns = $this->get_sortable_columns(); |
953 $sortable_columns = $this->get_sortable_columns(); |
810 /** |
954 /** |
811 * Filter the list table sortable columns for a specific screen. |
955 * Filters the list table sortable columns for a specific screen. |
812 * |
956 * |
813 * The dynamic portion of the hook name, `$this->screen->id`, refers |
957 * The dynamic portion of the hook name, `$this->screen->id`, refers |
814 * to the ID of the current screen, usually a string. |
958 * to the ID of the current screen, usually a string. |
815 * |
959 * |
816 * @since 3.5.0 |
960 * @since 3.5.0 |
852 |
996 |
853 /** |
997 /** |
854 * Print column headers, accounting for hidden and sortable columns. |
998 * Print column headers, accounting for hidden and sortable columns. |
855 * |
999 * |
856 * @since 3.1.0 |
1000 * @since 3.1.0 |
857 * @access public |
1001 * |
|
1002 * @staticvar int $cb_counter |
858 * |
1003 * |
859 * @param bool $with_id Whether to set the id attribute or not |
1004 * @param bool $with_id Whether to set the id attribute or not |
860 */ |
1005 */ |
861 public function print_column_headers( $with_id = true ) { |
1006 public function print_column_headers( $with_id = true ) { |
862 list( $columns, $hidden, $sortable ) = $this->get_column_info(); |
1007 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); |
863 |
1008 |
864 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); |
1009 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); |
865 $current_url = remove_query_arg( 'paged', $current_url ); |
1010 $current_url = remove_query_arg( 'paged', $current_url ); |
866 |
1011 |
867 if ( isset( $_GET['orderby'] ) ) |
1012 if ( isset( $_GET['orderby'] ) ) { |
868 $current_orderby = $_GET['orderby']; |
1013 $current_orderby = $_GET['orderby']; |
869 else |
1014 } else { |
870 $current_orderby = ''; |
1015 $current_orderby = ''; |
871 |
1016 } |
872 if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) |
1017 |
|
1018 if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) { |
873 $current_order = 'desc'; |
1019 $current_order = 'desc'; |
874 else |
1020 } else { |
875 $current_order = 'asc'; |
1021 $current_order = 'asc'; |
|
1022 } |
876 |
1023 |
877 if ( ! empty( $columns['cb'] ) ) { |
1024 if ( ! empty( $columns['cb'] ) ) { |
878 static $cb_counter = 1; |
1025 static $cb_counter = 1; |
879 $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>' |
1026 $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>' |
880 . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />'; |
1027 . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />'; |
882 } |
1029 } |
883 |
1030 |
884 foreach ( $columns as $column_key => $column_display_name ) { |
1031 foreach ( $columns as $column_key => $column_display_name ) { |
885 $class = array( 'manage-column', "column-$column_key" ); |
1032 $class = array( 'manage-column', "column-$column_key" ); |
886 |
1033 |
887 $style = ''; |
1034 if ( in_array( $column_key, $hidden ) ) { |
888 if ( in_array( $column_key, $hidden ) ) |
1035 $class[] = 'hidden'; |
889 $style = 'display:none;'; |
1036 } |
890 |
1037 |
891 $style = ' style="' . $style . '"'; |
1038 if ( 'cb' === $column_key ) |
892 |
|
893 if ( 'cb' == $column_key ) |
|
894 $class[] = 'check-column'; |
1039 $class[] = 'check-column'; |
895 elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) |
1040 elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) |
896 $class[] = 'num'; |
1041 $class[] = 'num'; |
897 |
1042 |
|
1043 if ( $column_key === $primary ) { |
|
1044 $class[] = 'column-primary'; |
|
1045 } |
|
1046 |
898 if ( isset( $sortable[$column_key] ) ) { |
1047 if ( isset( $sortable[$column_key] ) ) { |
899 list( $orderby, $desc_first ) = $sortable[$column_key]; |
1048 list( $orderby, $desc_first ) = $sortable[$column_key]; |
900 |
1049 |
901 if ( $current_orderby == $orderby ) { |
1050 if ( $current_orderby === $orderby ) { |
902 $order = 'asc' == $current_order ? 'desc' : 'asc'; |
1051 $order = 'asc' === $current_order ? 'desc' : 'asc'; |
903 $class[] = 'sorted'; |
1052 $class[] = 'sorted'; |
904 $class[] = $current_order; |
1053 $class[] = $current_order; |
905 } else { |
1054 } else { |
906 $order = $desc_first ? 'desc' : 'asc'; |
1055 $order = $desc_first ? 'desc' : 'asc'; |
907 $class[] = 'sortable'; |
1056 $class[] = 'sortable'; |
909 } |
1058 } |
910 |
1059 |
911 $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>'; |
1060 $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>'; |
912 } |
1061 } |
913 |
1062 |
|
1063 $tag = ( 'cb' === $column_key ) ? 'td' : 'th'; |
|
1064 $scope = ( 'th' === $tag ) ? 'scope="col"' : ''; |
914 $id = $with_id ? "id='$column_key'" : ''; |
1065 $id = $with_id ? "id='$column_key'" : ''; |
915 |
1066 |
916 if ( !empty( $class ) ) |
1067 if ( !empty( $class ) ) |
917 $class = "class='" . join( ' ', $class ) . "'"; |
1068 $class = "class='" . join( ' ', $class ) . "'"; |
918 |
1069 |
919 echo "<th scope='col' $id $class $style>$column_display_name</th>"; |
1070 echo "<$tag $scope $id $class>$column_display_name</$tag>"; |
920 } |
1071 } |
921 } |
1072 } |
922 |
1073 |
923 /** |
1074 /** |
924 * Display the table |
1075 * Display the table |
925 * |
1076 * |
926 * @since 3.1.0 |
1077 * @since 3.1.0 |
927 * @access public |
|
928 */ |
1078 */ |
929 public function display() { |
1079 public function display() { |
930 $singular = $this->_args['singular']; |
1080 $singular = $this->_args['singular']; |
931 |
1081 |
932 $this->display_tablenav( 'top' ); |
1082 $this->display_tablenav( 'top' ); |
933 |
1083 |
|
1084 $this->screen->render_screen_reader_content( 'heading_list' ); |
934 ?> |
1085 ?> |
935 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>"> |
1086 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>"> |
936 <thead> |
1087 <thead> |
937 <tr> |
1088 <tr> |
938 <?php $this->print_column_headers(); ?> |
1089 <?php $this->print_column_headers(); ?> |
1023 |
1172 |
1024 /** |
1173 /** |
1025 * Generate the table rows |
1174 * Generate the table rows |
1026 * |
1175 * |
1027 * @since 3.1.0 |
1176 * @since 3.1.0 |
1028 * @access public |
|
1029 */ |
1177 */ |
1030 public function display_rows() { |
1178 public function display_rows() { |
1031 foreach ( $this->items as $item ) |
1179 foreach ( $this->items as $item ) |
1032 $this->single_row( $item ); |
1180 $this->single_row( $item ); |
1033 } |
1181 } |
1034 |
1182 |
1035 /** |
1183 /** |
1036 * Generates content for a single row of the table |
1184 * Generates content for a single row of the table |
1037 * |
1185 * |
1038 * @since 3.1.0 |
1186 * @since 3.1.0 |
1039 * @access public |
|
1040 * |
1187 * |
1041 * @param object $item The current item |
1188 * @param object $item The current item |
1042 */ |
1189 */ |
1043 public function single_row( $item ) { |
1190 public function single_row( $item ) { |
1044 echo '<tr>'; |
1191 echo '<tr>'; |
1045 $this->single_row_columns( $item ); |
1192 $this->single_row_columns( $item ); |
1046 echo '</tr>'; |
1193 echo '</tr>'; |
1047 } |
1194 } |
1048 |
1195 |
|
1196 /** |
|
1197 * |
|
1198 * @param object $item |
|
1199 * @param string $column_name |
|
1200 */ |
1049 protected function column_default( $item, $column_name ) {} |
1201 protected function column_default( $item, $column_name ) {} |
1050 |
1202 |
|
1203 /** |
|
1204 * |
|
1205 * @param object $item |
|
1206 */ |
1051 protected function column_cb( $item ) {} |
1207 protected function column_cb( $item ) {} |
1052 |
1208 |
1053 /** |
1209 /** |
1054 * Generates the columns for a single row of the table |
1210 * Generates the columns for a single row of the table |
1055 * |
1211 * |
1056 * @since 3.1.0 |
1212 * @since 3.1.0 |
1057 * @access protected |
|
1058 * |
1213 * |
1059 * @param object $item The current item |
1214 * @param object $item The current item |
1060 */ |
1215 */ |
1061 protected function single_row_columns( $item ) { |
1216 protected function single_row_columns( $item ) { |
1062 list( $columns, $hidden ) = $this->get_column_info(); |
1217 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); |
1063 |
1218 |
1064 foreach ( $columns as $column_name => $column_display_name ) { |
1219 foreach ( $columns as $column_name => $column_display_name ) { |
1065 $class = "class='$column_name column-$column_name'"; |
1220 $classes = "$column_name column-$column_name"; |
1066 |
1221 if ( $primary === $column_name ) { |
1067 $style = ''; |
1222 $classes .= ' has-row-actions column-primary'; |
1068 if ( in_array( $column_name, $hidden ) ) |
1223 } |
1069 $style = ' style="display:none;"'; |
1224 |
1070 |
1225 if ( in_array( $column_name, $hidden ) ) { |
1071 $attributes = "$class$style"; |
1226 $classes .= ' hidden'; |
1072 |
1227 } |
1073 if ( 'cb' == $column_name ) { |
1228 |
|
1229 // Comments column uses HTML in the display name with screen reader text. |
|
1230 // Instead of using esc_attr(), we strip tags to get closer to a user-friendly string. |
|
1231 $data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"'; |
|
1232 |
|
1233 $attributes = "class='$classes' $data"; |
|
1234 |
|
1235 if ( 'cb' === $column_name ) { |
1074 echo '<th scope="row" class="check-column">'; |
1236 echo '<th scope="row" class="check-column">'; |
1075 echo $this->column_cb( $item ); |
1237 echo $this->column_cb( $item ); |
1076 echo '</th>'; |
1238 echo '</th>'; |
1077 } |
1239 } elseif ( method_exists( $this, '_column_' . $column_name ) ) { |
1078 elseif ( method_exists( $this, 'column_' . $column_name ) ) { |
1240 echo call_user_func( |
|
1241 array( $this, '_column_' . $column_name ), |
|
1242 $item, |
|
1243 $classes, |
|
1244 $data, |
|
1245 $primary |
|
1246 ); |
|
1247 } elseif ( method_exists( $this, 'column_' . $column_name ) ) { |
1079 echo "<td $attributes>"; |
1248 echo "<td $attributes>"; |
1080 echo call_user_func( array( $this, 'column_' . $column_name ), $item ); |
1249 echo call_user_func( array( $this, 'column_' . $column_name ), $item ); |
|
1250 echo $this->handle_row_actions( $item, $column_name, $primary ); |
|
1251 echo "</td>"; |
|
1252 } else { |
|
1253 echo "<td $attributes>"; |
|
1254 echo $this->column_default( $item, $column_name ); |
|
1255 echo $this->handle_row_actions( $item, $column_name, $primary ); |
1081 echo "</td>"; |
1256 echo "</td>"; |
1082 } |
1257 } |
1083 else { |
1258 } |
1084 echo "<td $attributes>"; |
1259 } |
1085 echo $this->column_default( $item, $column_name ); |
1260 |
1086 echo "</td>"; |
1261 /** |
1087 } |
1262 * Generates and display row actions links for the list table. |
1088 } |
1263 * |
1089 } |
1264 * @since 4.3.0 |
|
1265 * |
|
1266 * @param object $item The item being acted upon. |
|
1267 * @param string $column_name Current column name. |
|
1268 * @param string $primary Primary column name. |
|
1269 * @return string The row actions HTML, or an empty string if the current column is the primary column. |
|
1270 */ |
|
1271 protected function handle_row_actions( $item, $column_name, $primary ) { |
|
1272 return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : ''; |
|
1273 } |
1090 |
1274 |
1091 /** |
1275 /** |
1092 * Handle an incoming ajax request (called from admin-ajax.php) |
1276 * Handle an incoming ajax request (called from admin-ajax.php) |
1093 * |
1277 * |
1094 * @since 3.1.0 |
1278 * @since 3.1.0 |
1095 * @access public |
|
1096 */ |
1279 */ |
1097 public function ajax_response() { |
1280 public function ajax_response() { |
1098 $this->prepare_items(); |
1281 $this->prepare_items(); |
1099 |
1282 |
1100 ob_start(); |
1283 ob_start(); |