PluginProbe ʕ •ᴥ•ʔ
Code Manager / 1.0.14
Code Manager v1.0.14
1.0.47 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.3 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 1.0.39 1.0.4 1.0.40 1.0.41 1.0.42 1.0.43 1.0.44 1.0.45 1.0.46 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9
code-manager / Code_Manager / WP_List_Table.php
code-manager / Code_Manager Last commit date
Code_Manager.php 4 years ago Code_Manager_Dashboard.php 4 years ago Code_Manager_Export.php 4 years ago Code_Manager_Form.php 4 years ago Code_Manager_Import.php 4 years ago Code_Manager_Import_File.php 4 years ago Code_Manager_List.php 4 years ago Code_Manager_List_View.php 4 years ago Code_Manager_Model.php 4 years ago Code_Manager_Preview.php 4 years ago Code_Manager_Settings.php 4 years ago Code_Manager_Tabs.php 4 years ago Message_Box.php 4 years ago WP_List_Table.php 4 years ago
WP_List_Table.php
1525 lines
1 <?php
2 // As adviced by WordPress we'll use our own copy of WP_List_Table in its own namespace.
3 namespace Code_Manager;
4
5 /**
6 * Administration API: WP_List_Table class
7 *
8 * @package WordPress
9 * @subpackage List_Table
10 * @since 3.1.0
11 */
12
13 /**
14 * Base class for displaying a list of items in an ajaxified HTML table.
15 *
16 * @since 3.1.0
17 * @access private
18 */
19 class WP_List_Table {
20
21 /**
22 * The current list of items.
23 *
24 * @since 3.1.0
25 * @var array
26 */
27 public $items;
28
29 /**
30 * Various information about the current table.
31 *
32 * @since 3.1.0
33 * @var array
34 */
35 protected $_args;
36
37 /**
38 * Various information needed for displaying the pagination.
39 *
40 * @since 3.1.0
41 * @var array
42 */
43 protected $_pagination_args = array();
44
45 /**
46 * The current screen.
47 *
48 * @since 3.1.0
49 * @var WP_Screen
50 */
51 protected $screen;
52
53 /**
54 * Cached bulk actions.
55 *
56 * @since 3.1.0
57 * @var array
58 */
59 private $_actions;
60
61 /**
62 * Cached pagination output.
63 *
64 * @since 3.1.0
65 * @var string
66 */
67 private $_pagination;
68
69 /**
70 * The view switcher modes.
71 *
72 * @since 4.1.0
73 * @var array
74 */
75 protected $modes = array();
76
77 /**
78 * Stores the value returned by ->get_column_info().
79 *
80 * @since 4.1.0
81 * @var array
82 */
83 protected $_column_headers;
84
85 /**
86 * {@internal Missing Summary}
87 *
88 * @var array
89 */
90 protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
91
92 /**
93 * {@internal Missing Summary}
94 *
95 * @var array
96 */
97 protected $compat_methods = array(
98 'set_pagination_args',
99 'get_views',
100 'get_bulk_actions',
101 'bulk_actions',
102 'row_actions',
103 'months_dropdown',
104 'view_switcher',
105 'comments_bubble',
106 'get_items_per_page',
107 'pagination',
108 'get_sortable_columns',
109 'get_column_info',
110 'get_table_classes',
111 'display_tablenav',
112 'extra_tablenav',
113 'single_row_columns',
114 );
115
116 /**
117 * Constructor.
118 *
119 * The child class should call this constructor from its own constructor to override
120 * the default $args.
121 *
122 * @since 3.1.0
123 *
124 * @param array|string $args {
125 * Array or string of arguments.
126 *
127 * @type string $plural Plural value used for labels and the objects being listed.
128 * This affects things such as CSS class-names and nonces used
129 * in the list table, e.g. 'posts'. Default empty.
130 * @type string $singular Singular label for an object being listed, e.g. 'post'.
131 * Default empty
132 * @type bool $ajax Whether the list table supports Ajax. This includes loading
133 * and sorting data, for example. If true, the class will call
134 * the _js_vars() method in the footer to provide variables
135 * to any scripts handling Ajax events. Default false.
136 * @type string $screen String containing the hook name used to determine the current
137 * screen. If left null, the current screen will be automatically set.
138 * Default null.
139 * }
140 */
141 public function __construct( $args = array() ) {
142 $args = wp_parse_args(
143 $args,
144 array(
145 'plural' => '',
146 'singular' => '',
147 'ajax' => false,
148 'screen' => null,
149 )
150 );
151
152 $this->screen = convert_to_screen( $args['screen'] );
153
154 add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
155
156 if ( ! $args['plural'] ) {
157 $args['plural'] = $this->screen->base;
158 }
159
160 $args['plural'] = sanitize_key( $args['plural'] );
161 $args['singular'] = sanitize_key( $args['singular'] );
162
163 $this->_args = $args;
164
165 if ( $args['ajax'] ) {
166 // wp_enqueue_script( 'list-table' );
167 add_action( 'admin_footer', array( $this, '_js_vars' ) );
168 }
169
170 if ( empty( $this->modes ) ) {
171 $this->modes = array(
172 'list' => __( 'Compact view' ),
173 'excerpt' => __( 'Extended view' ),
174 );
175 }
176 }
177
178 /**
179 * Make private properties readable for backward compatibility.
180 *
181 * @since 4.0.0
182 *
183 * @param string $name Property to get.
184 * @return mixed Property.
185 */
186 public function __get( $name ) {
187 if ( in_array( $name, $this->compat_fields, true ) ) {
188 return $this->$name;
189 }
190 }
191
192 /**
193 * Make private properties settable for backward compatibility.
194 *
195 * @since 4.0.0
196 *
197 * @param string $name Property to check if set.
198 * @param mixed $value Property value.
199 * @return mixed Newly-set property.
200 */
201 public function __set( $name, $value ) {
202 if ( in_array( $name, $this->compat_fields, true ) ) {
203 return $this->$name = $value;
204 }
205 }
206
207 /**
208 * Make private properties checkable for backward compatibility.
209 *
210 * @since 4.0.0
211 *
212 * @param string $name Property to check if set.
213 * @return bool Whether the property is a back-compat property and it is set.
214 */
215 public function __isset( $name ) {
216 if ( in_array( $name, $this->compat_fields, true ) ) {
217 return isset( $this->$name );
218 }
219
220 return false;
221 }
222
223 /**
224 * Make private properties un-settable for backward compatibility.
225 *
226 * @since 4.0.0
227 *
228 * @param string $name Property to unset.
229 */
230 public function __unset( $name ) {
231 if ( in_array( $name, $this->compat_fields, true ) ) {
232 unset( $this->$name );
233 }
234 }
235
236 /**
237 * Make private/protected methods readable for backward compatibility.
238 *
239 * @since 4.0.0
240 *
241 * @param string $name Method to call.
242 * @param array $arguments Arguments to pass when calling.
243 * @return mixed|bool Return value of the callback, false otherwise.
244 */
245 public function __call( $name, $arguments ) {
246 if ( in_array( $name, $this->compat_methods, true ) ) {
247 return $this->$name( ...$arguments );
248 }
249 return false;
250 }
251
252 /**
253 * Checks the current user's permissions
254 *
255 * @since 3.1.0
256 * @abstract
257 */
258 public function ajax_user_can() {
259 die( 'function WP_List_Table::ajax_user_can() must be overridden in a subclass.' );
260 }
261
262 /**
263 * Prepares the list of items for displaying.
264 *
265 * @uses WP_List_Table::set_pagination_args()
266 *
267 * @since 3.1.0
268 * @abstract
269 */
270 public function prepare_items() {
271 die( 'function WP_List_Table::prepare_items() must be overridden in a subclass.' );
272 }
273
274 /**
275 * An internal method that sets all the necessary pagination arguments
276 *
277 * @since 3.1.0
278 *
279 * @param array|string $args Array or string of arguments with information about the pagination.
280 */
281 protected function set_pagination_args( $args ) {
282 $args = wp_parse_args(
283 $args,
284 array(
285 'total_items' => 0,
286 'total_pages' => 0,
287 'per_page' => 0,
288 )
289 );
290
291 if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
292 $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
293 }
294
295 // Redirect if page number is invalid and headers are not already sent.
296 if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
297 wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
298 exit;
299 }
300
301 $this->_pagination_args = $args;
302 }
303
304 /**
305 * Access the pagination args.
306 *
307 * @since 3.1.0
308 *
309 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
310 * 'total_pages', 'per_page', or 'infinite_scroll'.
311 * @return int Number of items that correspond to the given pagination argument.
312 */
313 public function get_pagination_arg( $key ) {
314 if ( 'page' === $key ) {
315 return $this->get_pagenum();
316 }
317
318 if ( isset( $this->_pagination_args[ $key ] ) ) {
319 return $this->_pagination_args[ $key ];
320 }
321
322 return 0;
323 }
324
325 /**
326 * Whether the table has items to display or not
327 *
328 * @since 3.1.0
329 *
330 * @return bool
331 */
332 public function has_items() {
333 return ! empty( $this->items );
334 }
335
336 /**
337 * Message to be displayed when there are no items
338 *
339 * @since 3.1.0
340 */
341 public function no_items() {
342 _e( 'No items found.' );
343 }
344
345 /**
346 * Displays the search box.
347 *
348 * @since 3.1.0
349 *
350 * @param string $text The 'submit' button label.
351 * @param string $input_id ID attribute value for the search input field.
352 */
353 public function search_box( $text, $input_id ) {
354 if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
355 return;
356 }
357
358 $input_id = $input_id . '-search-input';
359
360 if ( ! empty( $_REQUEST['orderby'] ) ) {
361 echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
362 }
363 if ( ! empty( $_REQUEST['order'] ) ) {
364 echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
365 }
366 if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
367 echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
368 }
369 if ( ! empty( $_REQUEST['detached'] ) ) {
370 echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
371 }
372 ?>
373 <p class="search-box">
374 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
375 <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
376 <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
377 </p>
378 <?php
379 }
380
381 /**
382 * Gets the list of views available on this table.
383 *
384 * The format is an associative array:
385 * - `'id' => 'link'`
386 *
387 * @since 3.1.0
388 *
389 * @return array
390 */
391 protected function get_views() {
392 return array();
393 }
394
395 /**
396 * Displays the list of views available on this table.
397 *
398 * @since 3.1.0
399 */
400 public function views() {
401 $views = $this->get_views();
402 /**
403 * Filters the list of available list table views.
404 *
405 * The dynamic portion of the hook name, `$this->screen->id`, refers
406 * to the ID of the current screen.
407 *
408 * @since 3.1.0
409 *
410 * @param string[] $views An array of available list table views.
411 */
412 $views = apply_filters( "views_{$this->screen->id}", $views );
413
414 if ( empty( $views ) ) {
415 return;
416 }
417
418 $this->screen->render_screen_reader_content( 'heading_views' );
419
420 echo "<ul class='subsubsub'>\n";
421 foreach ( $views as $class => $view ) {
422 $views[ $class ] = "\t<li class='$class'>$view";
423 }
424 echo implode( " |</li>\n", $views ) . "</li>\n";
425 echo '</ul>';
426 }
427
428 /**
429 * Retrieves the list of bulk actions available for this table.
430 *
431 * The format is an associative array where each element represents either a top level option value and label, or
432 * an array representing an optgroup and its options.
433 *
434 * For a standard option, the array element key is the field value and the array element value is the field label.
435 *
436 * For an optgroup, the array element key is the label and the array element value is an associative array of
437 * options as above.
438 *
439 * Example:
440 *
441 * [
442 * 'edit' => 'Edit',
443 * 'delete' => 'Delete',
444 * 'Change State' => [
445 * 'feature' => 'Featured',
446 * 'sale' => 'On Sale',
447 * ]
448 * ]
449 *
450 * @since 3.1.0
451 * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
452 *
453 * @return array
454 */
455 protected function get_bulk_actions() {
456 return array();
457 }
458
459 /**
460 * Displays the bulk actions dropdown.
461 *
462 * @since 3.1.0
463 *
464 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
465 * This is designated as optional for backward compatibility.
466 */
467 protected function bulk_actions( $which = '' ) {
468 if ( is_null( $this->_actions ) ) {
469 $this->_actions = $this->get_bulk_actions();
470
471 /**
472 * Filters the items in the bulk actions menu of the list table.
473 *
474 * The dynamic portion of the hook name, `$this->screen->id`, refers
475 * to the ID of the current screen.
476 *
477 * @since 3.1.0
478 * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
479 *
480 * @param array $actions An array of the available bulk actions.
481 */
482 $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
483
484 $two = '';
485 } else {
486 $two = '2';
487 }
488
489 if ( empty( $this->_actions ) ) {
490 return;
491 }
492
493 echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
494 echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
495 echo '<option value="-1">' . __( 'Bulk actions' ) . "</option>\n";
496
497 foreach ( $this->_actions as $key => $value ) {
498 if ( is_array( $value ) ) {
499 echo "\t" . '<optgroup label="' . esc_attr( $key ) . '">' . "\n";
500
501 foreach ( $value as $name => $title ) {
502 $class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : '';
503
504 echo "\t\t" . '<option value="' . esc_attr( $name ) . '"' . esc_attr( $class ) . '>' . esc_attr( $title ) . "</option>\n";
505 }
506 echo "\t" . "</optgroup>\n";
507 } else {
508 $class = ( 'edit' === $key ) ? ' class="hide-if-no-js"' : '';
509
510 echo "\t" . '<option value="' . esc_attr( $key ) . '"' . esc_attr( $class ) . '>' . esc_attr( $value ) . "</option>\n";
511 }
512 }
513
514 echo "</select>\n";
515
516 submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
517 echo "\n";
518 }
519
520 /**
521 * Gets the current action selected from the bulk actions dropdown.
522 *
523 * @since 3.1.0
524 *
525 * @return string|false The action name. False if no action was selected.
526 */
527 public function current_action() {
528 if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) {
529 return false;
530 }
531
532 if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ) {
533 return $_REQUEST['action'];
534 }
535
536 return false;
537 }
538
539 /**
540 * Generates the required HTML for a list of row action links.
541 *
542 * @since 3.1.0
543 *
544 * @param string[] $actions An array of action links.
545 * @param bool $always_visible Whether the actions should be always visible.
546 * @return string The HTML for the row actions.
547 */
548 protected function row_actions( $actions, $always_visible = false ) {
549 $action_count = count( $actions );
550
551 if ( ! $action_count ) {
552 return '';
553 }
554
555 $mode = get_user_setting( 'posts_list_mode', 'list' );
556
557 if ( 'excerpt' === $mode ) {
558 $always_visible = true;
559 }
560
561 $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
562
563 $i = 0;
564
565 foreach ( $actions as $action => $link ) {
566 ++$i;
567
568 $sep = ( $i < $action_count ) ? ' | ' : '';
569
570 $out .= "<span class='$action'>$link$sep</span>";
571 }
572
573 $out .= '</div>';
574
575 $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
576
577 return $out;
578 }
579
580 /**
581 * Displays a dropdown for filtering items in the list table by month.
582 *
583 * @since 3.1.0
584 *
585 * @global wpdb $wpdb WordPress database abstraction object.
586 * @global WP_Locale $wp_locale WordPress date and time locale object.
587 *
588 * @param string $post_type The post type.
589 */
590 protected function months_dropdown( $post_type ) {
591 global $wpdb, $wp_locale;
592
593 /**
594 * Filters whether to remove the 'Months' drop-down from the post list table.
595 *
596 * @since 4.2.0
597 *
598 * @param bool $disable Whether to disable the drop-down. Default false.
599 * @param string $post_type The post type.
600 */
601 if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
602 return;
603 }
604
605 /**
606 * Filters to short-circuit performing the months dropdown query.
607 *
608 * @since 5.7.0
609 *
610 * @param object[]|false $months 'Months' drop-down results. Default false.
611 * @param string $post_type The post type.
612 */
613 $months = apply_filters( 'pre_months_dropdown_query', false, $post_type );
614
615 if ( ! is_array( $months ) ) {
616 $extra_checks = "AND post_status != 'auto-draft'";
617 if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
618 $extra_checks .= " AND post_status != 'trash'";
619 } elseif ( isset( $_GET['post_status'] ) ) {
620 $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
621 }
622
623 $months = $wpdb->get_results(
624 $wpdb->prepare(
625 "
626 SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
627 FROM $wpdb->posts
628 WHERE post_type = %s
629 $extra_checks
630 ORDER BY post_date DESC
631 ",
632 $post_type
633 )
634 );
635 }
636
637 /**
638 * Filters the 'Months' drop-down results.
639 *
640 * @since 3.7.0
641 *
642 * @param object[] $months Array of the months drop-down query results.
643 * @param string $post_type The post type.
644 */
645 $months = apply_filters( 'months_dropdown_results', $months, $post_type );
646
647 $month_count = count( $months );
648
649 if ( ! $month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) {
650 return;
651 }
652
653 $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
654 ?>
655 <label for="filter-by-date" class="screen-reader-text"><?php echo get_post_type_object( $post_type )->labels->filter_by_date; ?></label>
656 <select name="m" id="filter-by-date">
657 <option<?php selected( $m, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
658 <?php
659 foreach ( $months as $arc_row ) {
660 if ( 0 == $arc_row->year ) {
661 continue;
662 }
663
664 $month = zeroise( $arc_row->month, 2 );
665 $year = $arc_row->year;
666
667 printf(
668 "<option %s value='%s'>%s</option>\n",
669 selected( $m, $year . $month, false ),
670 esc_attr( $arc_row->year . $month ),
671 /* translators: 1: Month name, 2: 4-digit year. */
672 sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
673 );
674 }
675 ?>
676 </select>
677 <?php
678 }
679
680 /**
681 * Displays a view switcher.
682 *
683 * @since 3.1.0
684 *
685 * @param string $current_mode
686 */
687 protected function view_switcher( $current_mode ) {
688 ?>
689 <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
690 <div class="view-switch">
691 <?php
692 foreach ( $this->modes as $mode => $title ) {
693 $classes = array( 'view-' . $mode );
694 $aria_current = '';
695
696 if ( $current_mode === $mode ) {
697 $classes[] = 'current';
698 $aria_current = ' aria-current="page"';
699 }
700
701 printf(
702 "<a href='%s' class='%s' id='view-switch-$mode'$aria_current><span class='screen-reader-text'>%s</span></a>\n",
703 esc_url( remove_query_arg( 'attachment-filter', add_query_arg( 'mode', $mode ) ) ),
704 implode( ' ', $classes ),
705 $title
706 );
707 }
708 ?>
709 </div>
710 <?php
711 }
712
713 /**
714 * Displays a comment count bubble.
715 *
716 * @since 3.1.0
717 *
718 * @param int $post_id The post ID.
719 * @param int $pending_comments Number of pending comments.
720 */
721 protected function comments_bubble( $post_id, $pending_comments ) {
722 $approved_comments = get_comments_number();
723
724 $approved_comments_number = number_format_i18n( $approved_comments );
725 $pending_comments_number = number_format_i18n( $pending_comments );
726
727 $approved_only_phrase = sprintf(
728 /* translators: %s: Number of comments. */
729 _n( '%s comment', '%s comments', $approved_comments ),
730 $approved_comments_number
731 );
732
733 $approved_phrase = sprintf(
734 /* translators: %s: Number of comments. */
735 _n( '%s approved comment', '%s approved comments', $approved_comments ),
736 $approved_comments_number
737 );
738
739 $pending_phrase = sprintf(
740 /* translators: %s: Number of comments. */
741 _n( '%s pending comment', '%s pending comments', $pending_comments ),
742 $pending_comments_number
743 );
744
745 if ( ! $approved_comments && ! $pending_comments ) {
746 // No comments at all.
747 printf(
748 '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">%s</span>',
749 __( 'No comments' )
750 );
751 } elseif ( $approved_comments && 'trash' === get_post_status( $post_id ) ) {
752 // Don't link the comment bubble for a trashed post.
753 printf(
754 '<span 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></span>',
755 $approved_comments_number,
756 $pending_comments ? $approved_phrase : $approved_only_phrase
757 );
758 } elseif ( $approved_comments ) {
759 // Link the comment bubble to approved comments.
760 printf(
761 '<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>',
762 esc_url(
763 add_query_arg(
764 array(
765 'p' => $post_id,
766 'comment_status' => 'approved',
767 ),
768 admin_url( 'edit-comments.php' )
769 )
770 ),
771 $approved_comments_number,
772 $pending_comments ? $approved_phrase : $approved_only_phrase
773 );
774 } else {
775 // Don't link the comment bubble when there are no approved comments.
776 printf(
777 '<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>',
778 $approved_comments_number,
779 $pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
780 );
781 }
782
783 if ( $pending_comments ) {
784 printf(
785 '<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>',
786 esc_url(
787 add_query_arg(
788 array(
789 'p' => $post_id,
790 'comment_status' => 'moderated',
791 ),
792 admin_url( 'edit-comments.php' )
793 )
794 ),
795 $pending_comments_number,
796 $pending_phrase
797 );
798 } else {
799 printf(
800 '<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>',
801 $pending_comments_number,
802 $approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
803 );
804 }
805 }
806
807 /**
808 * Gets the current page number.
809 *
810 * @since 3.1.0
811 *
812 * @return int
813 */
814 public function get_pagenum() {
815 $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
816
817 if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
818 $pagenum = $this->_pagination_args['total_pages'];
819 }
820
821 return max( 1, $pagenum );
822 }
823
824 /**
825 * Gets the number of items to display on a single page.
826 *
827 * @since 3.1.0
828 *
829 * @param string $option
830 * @param int $default
831 * @return int
832 */
833 protected function get_items_per_page( $option, $default = 20 ) {
834 $per_page = (int) get_user_option( $option );
835 if ( empty( $per_page ) || $per_page < 1 ) {
836 $per_page = $default;
837 }
838
839 /**
840 * Filters the number of items to be displayed on each page of the list table.
841 *
842 * The dynamic hook name, `$option`, refers to the `per_page` option depending
843 * on the type of list table in use. Possible filter names include:
844 *
845 * - `edit_comments_per_page`
846 * - `sites_network_per_page`
847 * - `site_themes_network_per_page`
848 * - `themes_network_per_page'`
849 * - `users_network_per_page`
850 * - `edit_post_per_page`
851 * - `edit_page_per_page'`
852 * - `edit_{$post_type}_per_page`
853 * - `edit_post_tag_per_page`
854 * - `edit_category_per_page`
855 * - `edit_{$taxonomy}_per_page`
856 * - `site_users_network_per_page`
857 * - `users_per_page`
858 *
859 * @since 2.9.0
860 *
861 * @param int $per_page Number of items to be displayed. Default 20.
862 */
863 return (int) apply_filters( "{$option}", $per_page );
864 }
865
866 /**
867 * Displays the pagination.
868 *
869 * @since 3.1.0
870 *
871 * @param string $which
872 */
873 protected function pagination( $which ) {
874 if ( empty( $this->_pagination_args ) ) {
875 return;
876 }
877
878 $total_items = $this->_pagination_args['total_items'];
879 $total_pages = $this->_pagination_args['total_pages'];
880 $infinite_scroll = false;
881 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
882 $infinite_scroll = $this->_pagination_args['infinite_scroll'];
883 }
884
885 if ( 'top' === $which && $total_pages > 1 ) {
886 $this->screen->render_screen_reader_content( 'heading_pagination' );
887 }
888
889 $output = '<span class="displaying-num">' . sprintf(
890 /* translators: %s: Number of items. */
891 _n( '%s item', '%s items', $total_items ),
892 number_format_i18n( $total_items )
893 ) . '</span>';
894
895 $current = $this->get_pagenum();
896 $removable_query_args = wp_removable_query_args();
897
898 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
899
900 $current_url = remove_query_arg( $removable_query_args, $current_url );
901
902 $page_links = array();
903
904 $total_pages_before = '<span class="paging-input">';
905 $total_pages_after = '</span></span>';
906
907 $disable_first = false;
908 $disable_last = false;
909 $disable_prev = false;
910 $disable_next = false;
911
912 if ( 1 == $current ) {
913 $disable_first = true;
914 $disable_prev = true;
915 }
916 if ( $total_pages == $current ) {
917 $disable_last = true;
918 $disable_next = true;
919 }
920
921 if ( $disable_first ) {
922 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
923 } else {
924 $page_links[] = sprintf(
925 "<a class='first-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
926 esc_url( remove_query_arg( 'paged', $current_url ) ),
927 __( 'First page' ),
928 '&laquo;'
929 );
930 }
931
932 if ( $disable_prev ) {
933 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
934 } else {
935 $page_links[] = sprintf(
936 "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
937 esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
938 __( 'Previous page' ),
939 '&lsaquo;'
940 );
941 }
942
943 if ( 'bottom' === $which ) {
944 $html_current_page = $current;
945 $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
946 } else {
947 $html_current_page = sprintf(
948 "%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'>",
949 '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
950 $current,
951 strlen( $total_pages )
952 );
953 }
954 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
955 $page_links[] = $total_pages_before . sprintf(
956 /* translators: 1: Current page, 2: Total pages. */
957 _x( '%1$s of %2$s', 'paging' ),
958 $html_current_page,
959 $html_total_pages
960 ) . $total_pages_after;
961
962 if ( $disable_next ) {
963 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
964 } else {
965 $page_links[] = sprintf(
966 "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
967 esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
968 __( 'Next page' ),
969 '&rsaquo;'
970 );
971 }
972
973 if ( $disable_last ) {
974 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
975 } else {
976 $page_links[] = sprintf(
977 "<a class='last-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
978 esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
979 __( 'Last page' ),
980 '&raquo;'
981 );
982 }
983
984 $pagination_links_class = 'pagination-links';
985 if ( ! empty( $infinite_scroll ) ) {
986 $pagination_links_class .= ' hide-if-js';
987 }
988 $output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>';
989
990 if ( $total_pages ) {
991 $page_class = $total_pages < 2 ? ' one-page' : '';
992 } else {
993 $page_class = ' no-pages';
994 }
995 $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
996
997 echo $this->_pagination;
998 }
999
1000 /**
1001 * Gets a list of columns.
1002 *
1003 * The format is:
1004 * - `'internal-name' => 'Title'`
1005 *
1006 * @since 3.1.0
1007 * @abstract
1008 *
1009 * @return array
1010 */
1011 public function get_columns() {
1012 die( 'function WP_List_Table::get_columns() must be overridden in a subclass.' );
1013 }
1014
1015 /**
1016 * Gets a list of sortable columns.
1017 *
1018 * The format is:
1019 * - `'internal-name' => 'orderby'`
1020 * - `'internal-name' => array( 'orderby', 'asc' )` - The second element sets the initial sorting order.
1021 * - `'internal-name' => array( 'orderby', true )` - The second element makes the initial order descending.
1022 *
1023 * @since 3.1.0
1024 *
1025 * @return array
1026 */
1027 protected function get_sortable_columns() {
1028 return array();
1029 }
1030
1031 /**
1032 * Gets the name of the default primary column.
1033 *
1034 * @since 4.3.0
1035 *
1036 * @return string Name of the default primary column, in this case, an empty string.
1037 */
1038 protected function get_default_primary_column_name() {
1039 $columns = $this->get_columns();
1040 $column = '';
1041
1042 if ( empty( $columns ) ) {
1043 return $column;
1044 }
1045
1046 // We need a primary defined so responsive views show something,
1047 // so let's fall back to the first non-checkbox column.
1048 foreach ( $columns as $col => $column_name ) {
1049 if ( 'cb' === $col ) {
1050 continue;
1051 }
1052
1053 $column = $col;
1054 break;
1055 }
1056
1057 return $column;
1058 }
1059
1060 /**
1061 * Public wrapper for WP_List_Table::get_default_primary_column_name().
1062 *
1063 * @since 4.4.0
1064 *
1065 * @return string Name of the default primary column.
1066 */
1067 public function get_primary_column() {
1068 return $this->get_primary_column_name();
1069 }
1070
1071 /**
1072 * Gets the name of the primary column.
1073 *
1074 * @since 4.3.0
1075 *
1076 * @return string The name of the primary column.
1077 */
1078 protected function get_primary_column_name() {
1079 $columns = get_column_headers( $this->screen );
1080 $default = $this->get_default_primary_column_name();
1081
1082 // If the primary column doesn't exist,
1083 // fall back to the first non-checkbox column.
1084 if ( ! isset( $columns[ $default ] ) ) {
1085 $default = self::get_default_primary_column_name();
1086 }
1087
1088 /**
1089 * Filters the name of the primary column for the current list table.
1090 *
1091 * @since 4.3.0
1092 *
1093 * @param string $default Column name default for the specific list table, e.g. 'name'.
1094 * @param string $context Screen ID for specific list table, e.g. 'plugins'.
1095 */
1096 $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
1097
1098 if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
1099 $column = $default;
1100 }
1101
1102 return $column;
1103 }
1104
1105 /**
1106 * Gets a list of all, hidden, and sortable columns, with filter applied.
1107 *
1108 * @since 3.1.0
1109 *
1110 * @return array
1111 */
1112 protected function get_column_info() {
1113 // $_column_headers is already set / cached.
1114 if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
1115 /*
1116 * Backward compatibility for `$_column_headers` format prior to WordPress 4.3.
1117 *
1118 * In WordPress 4.3 the primary column name was added as a fourth item in the
1119 * column headers property. This ensures the primary column name is included
1120 * in plugins setting the property directly in the three item format.
1121 */
1122 $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
1123 foreach ( $this->_column_headers as $key => $value ) {
1124 $column_headers[ $key ] = $value;
1125 }
1126
1127 return $column_headers;
1128 }
1129
1130 $columns = get_column_headers( $this->screen );
1131 $hidden = get_hidden_columns( $this->screen );
1132
1133 $sortable_columns = $this->get_sortable_columns();
1134 /**
1135 * Filters the list table sortable columns for a specific screen.
1136 *
1137 * The dynamic portion of the hook name, `$this->screen->id`, refers
1138 * to the ID of the current screen.
1139 *
1140 * @since 3.1.0
1141 *
1142 * @param array $sortable_columns An array of sortable columns.
1143 */
1144 $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
1145
1146 $sortable = array();
1147 foreach ( $_sortable as $id => $data ) {
1148 if ( empty( $data ) ) {
1149 continue;
1150 }
1151
1152 $data = (array) $data;
1153 if ( ! isset( $data[1] ) ) {
1154 $data[1] = false;
1155 }
1156
1157 $sortable[ $id ] = $data;
1158 }
1159
1160 $primary = $this->get_primary_column_name();
1161 $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
1162
1163 return $this->_column_headers;
1164 }
1165
1166 /**
1167 * Returns the number of visible columns.
1168 *
1169 * @since 3.1.0
1170 *
1171 * @return int
1172 */
1173 public function get_column_count() {
1174 list ( $columns, $hidden ) = $this->get_column_info();
1175 $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
1176 return count( $columns ) - count( $hidden );
1177 }
1178
1179 /**
1180 * Prints column headers, accounting for hidden and sortable columns.
1181 *
1182 * @since 3.1.0
1183 *
1184 * @param bool $with_id Whether to set the ID attribute or not
1185 */
1186 public function print_column_headers( $with_id = true ) {
1187 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1188
1189 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1190 $current_url = remove_query_arg( 'paged', $current_url );
1191
1192 if ( isset( $_GET['orderby'] ) ) {
1193 $current_orderby = $_GET['orderby'];
1194 } else {
1195 $current_orderby = '';
1196 }
1197
1198 if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
1199 $current_order = 'desc';
1200 } else {
1201 $current_order = 'asc';
1202 }
1203
1204 if ( ! empty( $columns['cb'] ) ) {
1205 static $cb_counter = 1;
1206 $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
1207 . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
1208 $cb_counter++;
1209 }
1210
1211 foreach ( $columns as $column_key => $column_display_name ) {
1212 $class = array( 'manage-column', "column-$column_key" );
1213
1214 if ( in_array( $column_key, $hidden, true ) ) {
1215 $class[] = 'hidden';
1216 }
1217
1218 if ( 'cb' === $column_key ) {
1219 $class[] = 'check-column';
1220 } elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ), true ) ) {
1221 $class[] = 'num';
1222 }
1223
1224 if ( $column_key === $primary ) {
1225 $class[] = 'column-primary';
1226 }
1227
1228 if ( isset( $sortable[ $column_key ] ) ) {
1229 list( $orderby, $desc_first ) = $sortable[ $column_key ];
1230
1231 if ( $current_orderby === $orderby ) {
1232 $order = 'asc' === $current_order ? 'desc' : 'asc';
1233
1234 $class[] = 'sorted';
1235 $class[] = $current_order;
1236 } else {
1237 $order = strtolower( $desc_first );
1238
1239 if ( ! in_array( $order, array( 'desc', 'asc' ), true ) ) {
1240 $order = $desc_first ? 'desc' : 'asc';
1241 }
1242
1243 $class[] = 'sortable';
1244 $class[] = 'desc' === $order ? 'asc' : 'desc';
1245 }
1246
1247 $column_display_name = sprintf(
1248 '<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
1249 esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ),
1250 $column_display_name
1251 );
1252 }
1253
1254 $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
1255 $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
1256 $id = $with_id ? "id='$column_key'" : '';
1257
1258 if ( ! empty( $class ) ) {
1259 $class = "class='" . implode( ' ', $class ) . "'";
1260 }
1261
1262 echo "<$tag $scope $id $class>$column_display_name</$tag>";
1263 }
1264 }
1265
1266 /**
1267 * Displays the table.
1268 *
1269 * @since 3.1.0
1270 */
1271 public function display() {
1272 $singular = $this->_args['singular'];
1273
1274 $this->display_tablenav( 'top' );
1275
1276 $this->screen->render_screen_reader_content( 'heading_list' );
1277 ?>
1278 <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
1279 <thead>
1280 <tr>
1281 <?php $this->print_column_headers(); ?>
1282 </tr>
1283 </thead>
1284
1285 <tbody id="the-list"
1286 <?php
1287 if ( $singular ) {
1288 echo " data-wp-lists='list:$singular'";
1289 }
1290 ?>
1291 >
1292 <?php $this->display_rows_or_placeholder(); ?>
1293 </tbody>
1294
1295 <tfoot>
1296 <tr>
1297 <?php $this->print_column_headers( false ); ?>
1298 </tr>
1299 </tfoot>
1300
1301 </table>
1302 <?php
1303 $this->display_tablenav( 'bottom' );
1304 }
1305
1306 /**
1307 * Gets a list of CSS classes for the WP_List_Table table tag.
1308 *
1309 * @since 3.1.0
1310 *
1311 * @return string[] Array of CSS classes for the table tag.
1312 */
1313 protected function get_table_classes() {
1314 $mode = get_user_setting( 'posts_list_mode', 'list' );
1315
1316 $mode_class = esc_attr( 'table-view-' . $mode );
1317
1318 return array( 'widefat', 'fixed', 'striped', $mode_class, $this->_args['plural'] );
1319 }
1320
1321 /**
1322 * Generates the table navigation above or below the table
1323 *
1324 * @since 3.1.0
1325 * @param string $which
1326 */
1327 protected function display_tablenav( $which ) {
1328 if ( 'top' === $which ) {
1329 wp_nonce_field( 'bulk-' . $this->_args['plural'] );
1330 }
1331 ?>
1332 <div class="tablenav <?php echo esc_attr( $which ); ?>">
1333
1334 <?php if ( $this->has_items() ) : ?>
1335 <div class="alignleft actions bulkactions">
1336 <?php $this->bulk_actions( $which ); ?>
1337 </div>
1338 <?php
1339 endif;
1340 $this->extra_tablenav( $which );
1341 $this->pagination( $which );
1342 ?>
1343
1344 <br class="clear" />
1345 </div>
1346 <?php
1347 }
1348
1349 /**
1350 * Extra controls to be displayed between bulk actions and pagination.
1351 *
1352 * @since 3.1.0
1353 *
1354 * @param string $which
1355 */
1356 protected function extra_tablenav( $which ) {}
1357
1358 /**
1359 * Generates the tbody element for the list table.
1360 *
1361 * @since 3.1.0
1362 */
1363 public function display_rows_or_placeholder() {
1364 if ( $this->has_items() ) {
1365 $this->display_rows();
1366 } else {
1367 echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
1368 $this->no_items();
1369 echo '</td></tr>';
1370 }
1371 }
1372
1373 /**
1374 * Generates the table rows.
1375 *
1376 * @since 3.1.0
1377 */
1378 public function display_rows() {
1379 foreach ( $this->items as $item ) {
1380 $this->single_row( $item );
1381 }
1382 }
1383
1384 /**
1385 * Generates content for a single row of the table.
1386 *
1387 * @since 3.1.0
1388 *
1389 * @param object|array $item The current item
1390 */
1391 public function single_row( $item ) {
1392 echo '<tr>';
1393 $this->single_row_columns( $item );
1394 echo '</tr>';
1395 }
1396
1397 /**
1398 * @param object|array $item
1399 * @param string $column_name
1400 */
1401 protected function column_default( $item, $column_name ) {}
1402
1403 /**
1404 * @param object|array $item
1405 */
1406 protected function column_cb( $item ) {}
1407
1408 /**
1409 * Generates the columns for a single row of the table.
1410 *
1411 * @since 3.1.0
1412 *
1413 * @param object|array $item The current item.
1414 */
1415 protected function single_row_columns( $item ) {
1416 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1417
1418 foreach ( $columns as $column_name => $column_display_name ) {
1419 $classes = "$column_name column-$column_name";
1420 if ( $primary === $column_name ) {
1421 $classes .= ' has-row-actions column-primary';
1422 }
1423
1424 if ( in_array( $column_name, $hidden, true ) ) {
1425 $classes .= ' hidden';
1426 }
1427
1428 // Comments column uses HTML in the display name with screen reader text.
1429 // Strip tags to get closer to a user-friendly string.
1430 $data = 'data-colname="' . esc_attr( wp_strip_all_tags( $column_display_name ) ) . '"';
1431
1432 $attributes = "class='$classes' $data";
1433
1434 if ( 'cb' === $column_name ) {
1435 echo '<th scope="row" class="check-column">';
1436 echo $this->column_cb( $item );
1437 echo '</th>';
1438 } elseif ( method_exists( $this, '_column_' . $column_name ) ) {
1439 echo call_user_func(
1440 array( $this, '_column_' . $column_name ),
1441 $item,
1442 $classes,
1443 $data,
1444 $primary
1445 );
1446 } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
1447 echo "<td $attributes>";
1448 echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1449 echo $this->handle_row_actions( $item, $column_name, $primary );
1450 echo '</td>';
1451 } else {
1452 echo "<td $attributes>";
1453 echo $this->column_default( $item, $column_name );
1454 echo $this->handle_row_actions( $item, $column_name, $primary );
1455 echo '</td>';
1456 }
1457 }
1458 }
1459
1460 /**
1461 * Generates and display row actions links for the list table.
1462 *
1463 * @since 4.3.0
1464 *
1465 * @param object|array $item The item being acted upon.
1466 * @param string $column_name Current column name.
1467 * @param string $primary Primary column name.
1468 * @return string The row actions HTML, or an empty string
1469 * if the current column is not the primary column.
1470 */
1471 protected function handle_row_actions( $item, $column_name, $primary ) {
1472 return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1473 }
1474
1475 /**
1476 * Handles an incoming ajax request (called from admin-ajax.php)
1477 *
1478 * @since 3.1.0
1479 */
1480 public function ajax_response() {
1481 $this->prepare_items();
1482
1483 ob_start();
1484 if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
1485 $this->display_rows();
1486 } else {
1487 $this->display_rows_or_placeholder();
1488 }
1489
1490 $rows = ob_get_clean();
1491
1492 $response = array( 'rows' => $rows );
1493
1494 if ( isset( $this->_pagination_args['total_items'] ) ) {
1495 $response['total_items_i18n'] = sprintf(
1496 /* translators: Number of items. */
1497 _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1498 number_format_i18n( $this->_pagination_args['total_items'] )
1499 );
1500 }
1501 if ( isset( $this->_pagination_args['total_pages'] ) ) {
1502 $response['total_pages'] = $this->_pagination_args['total_pages'];
1503 $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1504 }
1505
1506 die( wp_json_encode( $response ) );
1507 }
1508
1509 /**
1510 * Sends required variables to JavaScript land.
1511 *
1512 * @since 3.1.0
1513 */
1514 public function _js_vars() {
1515 $args = array(
1516 'class' => get_class( $this ),
1517 'screen' => array(
1518 'id' => $this->screen->id,
1519 'base' => $this->screen->base,
1520 ),
1521 );
1522
1523 printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
1524 }
1525 }