run:R W Run
7.85 KB
2026-03-11 16:18:51
R W Run
3.54 KB
2026-03-11 16:18:51
R W Run
148.33 KB
2026-03-11 16:18:51
R W Run
11.45 KB
2026-03-11 16:18:51
R W Run
3.58 KB
2026-03-11 16:18:51
R W Run
2.53 KB
2026-03-11 16:18:51
R W Run
2.6 KB
2026-03-11 16:18:51
R W Run
6.59 KB
2026-03-11 16:18:51
R W Run
14.83 KB
2026-03-11 16:18:51
R W Run
21.18 KB
2026-03-11 16:18:51
R W Run
48.13 KB
2026-03-11 16:18:51
R W Run
4.07 KB
2026-03-11 16:18:51
R W Run
5.3 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
26.73 KB
2026-03-11 16:18:51
R W Run
2.8 KB
2026-03-11 16:18:51
R W Run
15.2 KB
2026-03-11 16:18:51
R W Run
192.08 KB
2026-03-11 16:18:51
R W Run
11.77 KB
2026-03-11 16:18:51
R W Run
3.2 KB
2026-03-11 16:18:51
R W Run
22.89 KB
2026-03-11 16:18:51
R W Run
12.77 KB
2026-03-11 16:18:51
R W Run
4.08 KB
2026-03-11 16:18:51
R W Run
26.27 KB
2026-03-11 16:18:51
R W Run
4.97 KB
2026-03-11 16:18:51
R W Run
5.57 KB
2026-03-11 16:18:51
R W Run
13.93 KB
2026-03-11 16:18:51
R W Run
4.09 KB
2026-03-11 16:18:51
R W Run
6.79 KB
2026-03-11 16:18:51
R W Run
60.45 KB
2026-03-11 16:18:51
R W Run
32.4 KB
2026-03-11 16:18:51
R W Run
18.24 KB
2026-03-11 16:18:51
R W Run
66.01 KB
2026-03-11 16:18:51
R W Run
23.84 KB
2026-03-11 16:18:51
R W Run
17.72 KB
2026-03-11 16:18:51
R W Run
22.71 KB
2026-03-11 16:18:51
R W Run
18.05 KB
2026-03-11 16:18:51
R W Run
22.76 KB
2026-03-11 16:18:51
R W Run
7.34 KB
2026-03-11 16:18:51
R W Run
4.51 KB
2026-03-11 16:18:51
R W Run
9.02 KB
2026-03-11 16:18:51
R W Run
1.46 KB
2026-03-11 16:18:51
R W Run
51.76 KB
2026-03-11 16:18:51
R W Run
25.29 KB
2026-03-11 16:18:51
R W Run
21.61 KB
2026-03-11 16:18:51
R W Run
27.77 KB
2026-03-11 16:18:51
R W Run
15.35 KB
2026-03-11 16:18:51
R W Run
24.54 KB
2026-03-11 16:18:51
R W Run
56.44 KB
2026-03-11 16:18:51
R W Run
1.42 KB
2026-03-11 16:18:51
R W Run
63.66 KB
2026-03-11 16:18:51
R W Run
31.9 KB
2026-03-11 16:18:51
R W Run
14.44 KB
2026-03-11 16:18:51
R W Run
36.47 KB
2026-03-11 16:18:51
R W Run
14 KB
2026-03-11 16:18:51
R W Run
121.89 KB
2026-03-11 16:18:51
R W Run
6.26 KB
2026-03-11 16:18:51
R W Run
20.73 KB
2026-03-11 16:18:51
R W Run
15.23 KB
2026-03-11 16:18:51
R W Run
10.14 KB
2026-03-11 16:18:51
R W Run
6.94 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
46.85 KB
2026-03-11 16:18:51
R W Run
18.61 KB
2026-03-11 16:18:51
R W Run
6.08 KB
2026-03-11 16:18:51
R W Run
20.06 KB
2026-03-11 16:18:51
R W Run
5.73 KB
2026-03-11 16:18:51
R W Run
68.18 KB
2026-03-11 16:18:51
R W Run
40.8 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
25.26 KB
2026-03-11 16:18:51
R W Run
95.94 KB
2026-03-11 16:18:51
R W Run
43.12 KB
2026-03-11 16:18:51
R W Run
41.73 KB
2026-03-11 16:18:51
R W Run
6.46 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
116.31 KB
2026-03-11 16:18:51
R W Run
9.39 KB
2026-03-11 16:18:51
R W Run
64.34 KB
2026-03-11 16:18:51
R W Run
44.73 KB
2026-03-11 16:18:51
R W Run
1.27 KB
2026-03-11 16:18:51
R W Run
3.68 KB
2026-03-11 16:18:51
R W Run
33.53 KB
2026-03-11 16:18:51
R W Run
48.84 KB
2026-03-11 16:18:51
R W Run
26.35 KB
2026-03-11 16:18:51
R W Run
1.12 KB
2026-03-11 16:18:51
R W Run
4.19 KB
2026-03-11 16:18:51
R W Run
38.19 KB
2026-03-11 16:18:51
R W Run
91.33 KB
2026-03-11 16:18:51
R W Run
80.39 KB
2026-03-11 16:18:51
R W Run
32.67 KB
2026-03-11 16:18:51
R W Run
16.18 KB
2026-03-11 16:18:51
R W Run
44.46 KB
2026-03-11 16:18:51
R W Run
6.23 KB
2026-03-11 16:18:51
R W Run
8.23 KB
2026-03-11 16:18:51
R W Run
96.96 KB
2026-03-11 16:18:51
R W Run
6.83 KB
2026-03-11 16:18:51
R W Run
46.62 KB
2026-03-11 16:18:51
R W Run
10.82 KB
2026-03-11 16:18:51
R W Run
68.86 KB
2026-03-11 16:18:51
R W Run
33.63 KB
2026-03-11 16:18:51
R W Run
113.3 KB
2026-03-11 16:18:51
R W Run
22.98 KB
2026-03-11 16:18:51
R W Run
10.66 KB
2026-03-11 16:18:51
R W Run
error_log
📄class-wp-list-table.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
10/**
11 * Base class for displaying a list of items in an ajaxified HTML table.
12 *
13 * @since 3.1.0
14 */
15#[AllowDynamicProperties]
16class WP_List_Table {
17
18 /**
19 * The current list of items.
20 *
21 * @since 3.1.0
22 * @var array
23 */
24 public $items;
25
26 /**
27 * Various information about the current table.
28 *
29 * @since 3.1.0
30 * @var array
31 */
32 protected $_args;
33
34 /**
35 * Various information needed for displaying the pagination.
36 *
37 * @since 3.1.0
38 * @var array
39 */
40 protected $_pagination_args = array();
41
42 /**
43 * The current screen.
44 *
45 * @since 3.1.0
46 * @var WP_Screen
47 */
48 protected $screen;
49
50 /**
51 * Cached bulk actions.
52 *
53 * @since 3.1.0
54 * @var array
55 */
56 private $_actions;
57
58 /**
59 * Cached pagination output.
60 *
61 * @since 3.1.0
62 * @var string
63 */
64 private $_pagination;
65
66 /**
67 * The view switcher modes.
68 *
69 * @since 4.1.0
70 * @var array
71 */
72 protected $modes = array();
73
74 /**
75 * Stores the value returned by ::get_column_info().
76 *
77 * @since 4.1.0
78 * @var array|null
79 */
80 protected $_column_headers;
81
82 /**
83 * {@internal Missing Summary}
84 *
85 * @var array
86 */
87 protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
88
89 /**
90 * {@internal Missing Summary}
91 *
92 * @var array
93 */
94 protected $compat_methods = array(
95 'set_pagination_args',
96 'get_views',
97 'get_bulk_actions',
98 'bulk_actions',
99 'row_actions',
100 'months_dropdown',
101 'view_switcher',
102 'comments_bubble',
103 'get_items_per_page',
104 'pagination',
105 'get_sortable_columns',
106 'get_column_info',
107 'get_table_classes',
108 'display_tablenav',
109 'extra_tablenav',
110 'single_row_columns',
111 );
112
113 /**
114 * Constructor.
115 *
116 * The child class should call this constructor from its own constructor to override
117 * the default $args.
118 *
119 * @since 3.1.0
120 *
121 * @param array|string $args {
122 * Array or string of arguments.
123 *
124 * @type string $plural Plural value used for labels and the objects being listed.
125 * This affects things such as CSS class-names and nonces used
126 * in the list table, e.g. 'posts'. Default empty.
127 * @type string $singular Singular label for an object being listed, e.g. 'post'.
128 * Default empty
129 * @type bool $ajax Whether the list table supports Ajax. This includes loading
130 * and sorting data, for example. If true, the class will call
131 * the _js_vars() method in the footer to provide variables
132 * to any scripts handling Ajax events. Default false.
133 * @type string $screen String containing the hook name used to determine the current
134 * screen. If left null, the current screen will be automatically set.
135 * Default null.
136 * }
137 */
138 public function __construct( $args = array() ) {
139 $args = wp_parse_args(
140 $args,
141 array(
142 'plural' => '',
143 'singular' => '',
144 'ajax' => false,
145 'screen' => null,
146 )
147 );
148
149 $this->screen = convert_to_screen( $args['screen'] );
150
151 add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
152
153 if ( ! $args['plural'] ) {
154 $args['plural'] = $this->screen->base;
155 }
156
157 $args['plural'] = sanitize_key( $args['plural'] );
158 $args['singular'] = sanitize_key( $args['singular'] );
159
160 $this->_args = $args;
161
162 if ( $args['ajax'] ) {
163 // wp_enqueue_script( 'list-table' );
164 add_action( 'admin_footer', array( $this, '_js_vars' ) );
165 }
166
167 if ( empty( $this->modes ) ) {
168 $this->modes = array(
169 'list' => __( 'Compact view' ),
170 'excerpt' => __( 'Extended view' ),
171 );
172 }
173 }
174
175 /**
176 * Makes private properties readable for backward compatibility.
177 *
178 * @since 4.0.0
179 * @since 6.4.0 Getting a dynamic property is deprecated.
180 *
181 * @param string $name Property to get.
182 * @return mixed Property.
183 */
184 public function __get( $name ) {
185 if ( in_array( $name, $this->compat_fields, true ) ) {
186 return $this->$name;
187 }
188
189 wp_trigger_error(
190 __METHOD__,
191 "The property `{$name}` is not declared. Getting a dynamic property is " .
192 'deprecated since version 6.4.0! Instead, declare the property on the class.',
193 E_USER_DEPRECATED
194 );
195 return null;
196 }
197
198 /**
199 * Makes private properties settable for backward compatibility.
200 *
201 * @since 4.0.0
202 * @since 6.4.0 Setting a dynamic property is deprecated.
203 *
204 * @param string $name Property to check if set.
205 * @param mixed $value Property value.
206 */
207 public function __set( $name, $value ) {
208 if ( in_array( $name, $this->compat_fields, true ) ) {
209 $this->$name = $value;
210 return;
211 }
212
213 wp_trigger_error(
214 __METHOD__,
215 "The property `{$name}` is not declared. Setting a dynamic property is " .
216 'deprecated since version 6.4.0! Instead, declare the property on the class.',
217 E_USER_DEPRECATED
218 );
219 }
220
221 /**
222 * Makes private properties checkable for backward compatibility.
223 *
224 * @since 4.0.0
225 * @since 6.4.0 Checking a dynamic property is deprecated.
226 *
227 * @param string $name Property to check if set.
228 * @return bool Whether the property is a back-compat property and it is set.
229 */
230 public function __isset( $name ) {
231 if ( in_array( $name, $this->compat_fields, true ) ) {
232 return isset( $this->$name );
233 }
234
235 wp_trigger_error(
236 __METHOD__,
237 "The property `{$name}` is not declared. Checking `isset()` on a dynamic property " .
238 'is deprecated since version 6.4.0! Instead, declare the property on the class.',
239 E_USER_DEPRECATED
240 );
241 return false;
242 }
243
244 /**
245 * Makes private properties un-settable for backward compatibility.
246 *
247 * @since 4.0.0
248 * @since 6.4.0 Unsetting a dynamic property is deprecated.
249 *
250 * @param string $name Property to unset.
251 */
252 public function __unset( $name ) {
253 if ( in_array( $name, $this->compat_fields, true ) ) {
254 unset( $this->$name );
255 return;
256 }
257
258 wp_trigger_error(
259 __METHOD__,
260 "A property `{$name}` is not declared. Unsetting a dynamic property is " .
261 'deprecated since version 6.4.0! Instead, declare the property on the class.',
262 E_USER_DEPRECATED
263 );
264 }
265
266 /**
267 * Makes private/protected methods readable for backward compatibility.
268 *
269 * @since 4.0.0
270 *
271 * @param string $name Method to call.
272 * @param array $arguments Arguments to pass when calling.
273 * @return mixed|bool Return value of the callback, false otherwise.
274 */
275 public function __call( $name, $arguments ) {
276 if ( in_array( $name, $this->compat_methods, true ) ) {
277 return $this->$name( ...$arguments );
278 }
279 return false;
280 }
281
282 /**
283 * Checks the current user's permissions
284 *
285 * @since 3.1.0
286 * @abstract
287 */
288 public function ajax_user_can() {
289 die( 'function WP_List_Table::ajax_user_can() must be overridden in a subclass.' );
290 }
291
292 /**
293 * Prepares the list of items for displaying.
294 *
295 * @uses WP_List_Table::set_pagination_args()
296 *
297 * @since 3.1.0
298 * @abstract
299 */
300 public function prepare_items() {
301 die( 'function WP_List_Table::prepare_items() must be overridden in a subclass.' );
302 }
303
304 /**
305 * Sets all the necessary pagination arguments.
306 *
307 * @since 3.1.0
308 *
309 * @param array|string $args Array or string of arguments with information about the pagination.
310 */
311 protected function set_pagination_args( $args ) {
312 $args = wp_parse_args(
313 $args,
314 array(
315 'total_items' => 0,
316 'total_pages' => 0,
317 'per_page' => 0,
318 )
319 );
320
321 if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
322 $args['total_pages'] = (int) ceil( $args['total_items'] / $args['per_page'] );
323 }
324
325 // Redirect if page number is invalid and headers are not already sent.
326 if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
327 wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
328 exit;
329 }
330
331 $this->_pagination_args = $args;
332 }
333
334 /**
335 * Access the pagination args.
336 *
337 * @since 3.1.0
338 *
339 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
340 * 'total_pages', 'per_page', or 'infinite_scroll'.
341 * @return int Number of items that correspond to the given pagination argument.
342 */
343 public function get_pagination_arg( $key ) {
344 if ( 'page' === $key ) {
345 return $this->get_pagenum();
346 }
347
348 if ( isset( $this->_pagination_args[ $key ] ) ) {
349 return $this->_pagination_args[ $key ];
350 }
351
352 return 0;
353 }
354
355 /**
356 * Determines whether the table has items to display or not
357 *
358 * @since 3.1.0
359 *
360 * @return bool
361 */
362 public function has_items() {
363 return ! empty( $this->items );
364 }
365
366 /**
367 * Message to be displayed when there are no items
368 *
369 * @since 3.1.0
370 */
371 public function no_items() {
372 _e( 'No items found.' );
373 }
374
375 /**
376 * Displays the search box.
377 *
378 * @since 3.1.0
379 *
380 * @param string $text The 'submit' button label.
381 * @param string $input_id ID attribute value for the search input field.
382 */
383 public function search_box( $text, $input_id ) {
384 if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
385 return;
386 }
387
388 $input_id = $input_id . '-search-input';
389
390 if ( ! empty( $_REQUEST['orderby'] ) ) {
391 if ( is_array( $_REQUEST['orderby'] ) ) {
392 foreach ( $_REQUEST['orderby'] as $key => $value ) {
393 echo '<input type="hidden" name="orderby[' . esc_attr( $key ) . ']" value="' . esc_attr( $value ) . '" />';
394 }
395 } else {
396 echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
397 }
398 }
399 if ( ! empty( $_REQUEST['order'] ) ) {
400 echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
401 }
402 if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
403 echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
404 }
405 if ( ! empty( $_REQUEST['detached'] ) ) {
406 echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
407 }
408 ?>
409<p class="search-box">
410 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
411 <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
412 <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
413</p>
414 <?php
415 }
416
417 /**
418 * Generates views links.
419 *
420 * @since 6.1.0
421 *
422 * @param array $link_data {
423 * An array of link data.
424 *
425 * @type string $url The link URL.
426 * @type string $label The link label.
427 * @type bool $current Optional. Whether this is the currently selected view.
428 * }
429 * @return string[] An array of link markup. Keys match the `$link_data` input array.
430 */
431 protected function get_views_links( $link_data = array() ) {
432 if ( ! is_array( $link_data ) ) {
433 _doing_it_wrong(
434 __METHOD__,
435 sprintf(
436 /* translators: %s: The $link_data argument. */
437 __( 'The %s argument must be an array.' ),
438 '<code>$link_data</code>'
439 ),
440 '6.1.0'
441 );
442
443 return array( '' );
444 }
445
446 $views_links = array();
447
448 foreach ( $link_data as $view => $link ) {
449 if ( empty( $link['url'] ) || ! is_string( $link['url'] ) || '' === trim( $link['url'] ) ) {
450 _doing_it_wrong(
451 __METHOD__,
452 sprintf(
453 /* translators: %1$s: The argument name. %2$s: The view name. */
454 __( 'The %1$s argument must be a non-empty string for %2$s.' ),
455 '<code>url</code>',
456 '<code>' . esc_html( $view ) . '</code>'
457 ),
458 '6.1.0'
459 );
460
461 continue;
462 }
463
464 if ( empty( $link['label'] ) || ! is_string( $link['label'] ) || '' === trim( $link['label'] ) ) {
465 _doing_it_wrong(
466 __METHOD__,
467 sprintf(
468 /* translators: %1$s: The argument name. %2$s: The view name. */
469 __( 'The %1$s argument must be a non-empty string for %2$s.' ),
470 '<code>label</code>',
471 '<code>' . esc_html( $view ) . '</code>'
472 ),
473 '6.1.0'
474 );
475
476 continue;
477 }
478
479 $views_links[ $view ] = sprintf(
480 '<a href="%s"%s>%s</a>',
481 esc_url( $link['url'] ),
482 isset( $link['current'] ) && true === $link['current'] ? ' class="current" aria-current="page"' : '',
483 $link['label']
484 );
485 }
486
487 return $views_links;
488 }
489
490 /**
491 * Gets the list of views available on this table.
492 *
493 * The format is an associative array:
494 * - `'id' => 'link'`
495 *
496 * @since 3.1.0
497 *
498 * @return array
499 */
500 protected function get_views() {
501 return array();
502 }
503
504 /**
505 * Displays the list of views available on this table.
506 *
507 * @since 3.1.0
508 */
509 public function views() {
510 $views = $this->get_views();
511 /**
512 * Filters the list of available list table views.
513 *
514 * The dynamic portion of the hook name, `$this->screen->id`, refers
515 * to the ID of the current screen.
516 *
517 * @since 3.1.0
518 *
519 * @param string[] $views An array of available list table views.
520 */
521 $views = apply_filters( "views_{$this->screen->id}", $views );
522
523 if ( empty( $views ) ) {
524 return;
525 }
526
527 $this->screen->render_screen_reader_content( 'heading_views' );
528
529 echo "<ul class='subsubsub'>\n";
530 foreach ( $views as $class => $view ) {
531 $views[ $class ] = "\t<li class='$class'>$view";
532 }
533 echo implode( " |</li>\n", $views ) . "</li>\n";
534 echo '</ul>';
535 }
536
537 /**
538 * Retrieves the list of bulk actions available for this table.
539 *
540 * The format is an associative array where each element represents either a top level option value and label, or
541 * an array representing an optgroup and its options.
542 *
543 * For a standard option, the array element key is the field value and the array element value is the field label.
544 *
545 * For an optgroup, the array element key is the label and the array element value is an associative array of
546 * options as above.
547 *
548 * Example:
549 *
550 * [
551 * 'edit' => 'Edit',
552 * 'delete' => 'Delete',
553 * 'Change State' => [
554 * 'feature' => 'Featured',
555 * 'sale' => 'On Sale',
556 * ]
557 * ]
558 *
559 * @since 3.1.0
560 * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
561 *
562 * @return array
563 */
564 protected function get_bulk_actions() {
565 return array();
566 }
567
568 /**
569 * Displays the bulk actions dropdown.
570 *
571 * @since 3.1.0
572 *
573 * @param string $which The location of the bulk actions: Either 'top' or 'bottom'.
574 * This is designated as optional for backward compatibility.
575 */
576 protected function bulk_actions( $which = '' ) {
577 if ( is_null( $this->_actions ) ) {
578 $this->_actions = $this->get_bulk_actions();
579
580 /**
581 * Filters the items in the bulk actions menu of the list table.
582 *
583 * The dynamic portion of the hook name, `$this->screen->id`, refers
584 * to the ID of the current screen.
585 *
586 * @since 3.1.0
587 * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup.
588 *
589 * @param array $actions An array of the available bulk actions.
590 */
591 $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
592
593 $two = '';
594 } else {
595 $two = '2';
596 }
597
598 if ( empty( $this->_actions ) ) {
599 return;
600 }
601
602 echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' .
603 /* translators: Hidden accessibility text. */
604 __( 'Select bulk action' ) .
605 '</label>';
606 echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
607 echo '<option value="-1">' . __( 'Bulk actions' ) . "</option>\n";
608
609 foreach ( $this->_actions as $key => $value ) {
610 if ( is_array( $value ) ) {
611 echo "\t" . '<optgroup label="' . esc_attr( $key ) . '">' . "\n";
612
613 foreach ( $value as $name => $title ) {
614 $class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : '';
615
616 echo "\t\t" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . $title . "</option>\n";
617 }
618 echo "\t" . "</optgroup>\n";
619 } else {
620 $class = ( 'edit' === $key ) ? ' class="hide-if-no-js"' : '';
621
622 echo "\t" . '<option value="' . esc_attr( $key ) . '"' . $class . '>' . $value . "</option>\n";
623 }
624 }
625
626 echo "</select>\n";
627
628 submit_button( __( 'Apply' ), 'action', 'bulk_action', false, array( 'id' => "doaction$two" ) );
629 echo "\n";
630 }
631
632 /**
633 * Gets the current action selected from the bulk actions dropdown.
634 *
635 * @since 3.1.0
636 *
637 * @return string|false The action name. False if no action was selected.
638 */
639 public function current_action() {
640 if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) {
641 return false;
642 }
643
644 if ( isset( $_REQUEST['action'] ) && '-1' !== $_REQUEST['action'] ) {
645 return $_REQUEST['action'];
646 }
647
648 return false;
649 }
650
651 /**
652 * Generates the required HTML for a list of row action links.
653 *
654 * @since 3.1.0
655 *
656 * @param string[] $actions An array of action links.
657 * @param bool $always_visible Whether the actions should be always visible.
658 * @return string The HTML for the row actions.
659 */
660 protected function row_actions( $actions, $always_visible = false ) {
661 $action_count = count( $actions );
662
663 if ( ! $action_count ) {
664 return '';
665 }
666
667 $mode = get_user_setting( 'posts_list_mode', 'list' );
668
669 if ( 'excerpt' === $mode ) {
670 $always_visible = true;
671 }
672
673 $output = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
674
675 $i = 0;
676
677 foreach ( $actions as $action => $link ) {
678 ++$i;
679
680 $separator = ( $i < $action_count ) ? ' | ' : '';
681
682 $output .= "<span class='$action'>{$link}{$separator}</span>";
683 }
684
685 $output .= '</div>';
686
687 $output .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' .
688 /* translators: Hidden accessibility text. */
689 __( 'Show more details' ) .
690 '</span></button>';
691
692 return $output;
693 }
694
695 /**
696 * Displays a dropdown for filtering items in the list table by month.
697 *
698 * @since 3.1.0
699 *
700 * @global wpdb $wpdb WordPress database abstraction object.
701 * @global WP_Locale $wp_locale WordPress date and time locale object.
702 *
703 * @param string $post_type The post type.
704 */
705 protected function months_dropdown( $post_type ) {
706 global $wpdb, $wp_locale;
707
708 /**
709 * Filters whether to remove the 'Months' drop-down from the post list table.
710 *
711 * @since 4.2.0
712 *
713 * @param bool $disable Whether to disable the drop-down. Default false.
714 * @param string $post_type The post type.
715 */
716 if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
717 return;
718 }
719
720 /**
721 * Filters whether to short-circuit performing the months dropdown query.
722 *
723 * @since 5.7.0
724 *
725 * @param object[]|false $months 'Months' drop-down results. Default false.
726 * @param string $post_type The post type.
727 */
728 $months = apply_filters( 'pre_months_dropdown_query', false, $post_type );
729
730 if ( ! is_array( $months ) ) {
731 $extra_checks = "AND post_status != 'auto-draft'";
732 if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
733 $extra_checks .= " AND post_status != 'trash'";
734 } elseif ( isset( $_GET['post_status'] ) ) {
735 $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
736 }
737
738 $months = $wpdb->get_results(
739 $wpdb->prepare(
740 "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
741 FROM $wpdb->posts
742 WHERE post_type = %s
743 $extra_checks
744 ORDER BY post_date DESC",
745 $post_type
746 )
747 );
748 }
749
750 /**
751 * Filters the 'Months' drop-down results.
752 *
753 * @since 3.7.0
754 *
755 * @param object[] $months Array of the months drop-down query results.
756 * @param string $post_type The post type.
757 */
758 $months = apply_filters( 'months_dropdown_results', $months, $post_type );
759
760 $month_count = count( $months );
761
762 if ( ! $month_count || ( 1 === $month_count && 0 === (int) $months[0]->month ) ) {
763 return;
764 }
765
766 $selected_month = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
767 ?>
768 <label for="filter-by-date" class="screen-reader-text"><?php echo get_post_type_object( $post_type )->labels->filter_by_date; ?></label>
769 <select name="m" id="filter-by-date">
770 <option<?php selected( $selected_month, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
771 <?php
772 foreach ( $months as $arc_row ) {
773 if ( 0 === (int) $arc_row->year ) {
774 continue;
775 }
776
777 $month = zeroise( $arc_row->month, 2 );
778 $year = $arc_row->year;
779
780 printf(
781 "<option %s value='%s'>%s</option>\n",
782 selected( $selected_month, $year . $month, false ),
783 esc_attr( $year . $month ),
784 /* translators: 1: Month name, 2: 4-digit year. */
785 esc_html( sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year ) )
786 );
787 }
788 ?>
789 </select>
790 <?php
791 }
792
793 /**
794 * Displays a view switcher.
795 *
796 * @since 3.1.0
797 *
798 * @param string $current_mode
799 */
800 protected function view_switcher( $current_mode ) {
801 ?>
802 <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
803 <div class="view-switch">
804 <?php
805 foreach ( $this->modes as $mode => $title ) {
806 $classes = array( 'view-' . $mode );
807 $aria_current = '';
808
809 if ( $current_mode === $mode ) {
810 $classes[] = 'current';
811 $aria_current = ' aria-current="page"';
812 }
813
814 printf(
815 "<a href='%s' class='%s' id='view-switch-$mode'$aria_current>" .
816 "<span class='screen-reader-text'>%s</span>" .
817 "</a>\n",
818 esc_url( remove_query_arg( 'attachment-filter', add_query_arg( 'mode', $mode ) ) ),
819 implode( ' ', $classes ),
820 $title
821 );
822 }
823 ?>
824 </div>
825 <?php
826 }
827
828 /**
829 * Displays a comment count bubble.
830 *
831 * @since 3.1.0
832 *
833 * @param int $post_id The post ID.
834 * @param int $pending_comments Number of pending comments.
835 */
836 protected function comments_bubble( $post_id, $pending_comments ) {
837 $post_object = get_post( $post_id );
838 $edit_post_cap = $post_object ? 'edit_post' : 'edit_posts';
839
840 if ( ! current_user_can( $edit_post_cap, $post_id )
841 && ( post_password_required( $post_id )
842 || ! current_user_can( 'read_post', $post_id ) )
843 ) {
844 // The user has no access to the post and thus cannot see the comments.
845 return false;
846 }
847
848 $approved_comments = get_comments_number();
849
850 $approved_comments_number = number_format_i18n( $approved_comments );
851 $pending_comments_number = number_format_i18n( $pending_comments );
852
853 $approved_only_phrase = sprintf(
854 /* translators: %s: Number of comments. */
855 _n( '%s comment', '%s comments', $approved_comments ),
856 $approved_comments_number
857 );
858
859 $approved_phrase = sprintf(
860 /* translators: %s: Number of comments. */
861 _n( '%s approved comment', '%s approved comments', $approved_comments ),
862 $approved_comments_number
863 );
864
865 $pending_phrase = sprintf(
866 /* translators: %s: Number of comments. */
867 _n( '%s pending comment', '%s pending comments', $pending_comments ),
868 $pending_comments_number
869 );
870
871 if ( ! $approved_comments && ! $pending_comments ) {
872 // No comments at all.
873 printf(
874 '<span aria-hidden="true">&#8212;</span>' .
875 '<span class="screen-reader-text">%s</span>',
876 __( 'No comments' )
877 );
878 } elseif ( $approved_comments && 'trash' === get_post_status( $post_id ) ) {
879 // Don't link the comment bubble for a trashed post.
880 printf(
881 '<span class="post-com-count post-com-count-approved">' .
882 '<span class="comment-count-approved" aria-hidden="true">%s</span>' .
883 '<span class="screen-reader-text">%s</span>' .
884 '</span>',
885 $approved_comments_number,
886 $pending_comments ? $approved_phrase : $approved_only_phrase
887 );
888 } elseif ( $approved_comments ) {
889 // Link the comment bubble to approved comments.
890 printf(
891 '<a href="%s" class="post-com-count post-com-count-approved">' .
892 '<span class="comment-count-approved" aria-hidden="true">%s</span>' .
893 '<span class="screen-reader-text">%s</span>' .
894 '</a>',
895 esc_url(
896 add_query_arg(
897 array(
898 'p' => $post_id,
899 'comment_status' => 'approved',
900 ),
901 admin_url( 'edit-comments.php' )
902 )
903 ),
904 $approved_comments_number,
905 $pending_comments ? $approved_phrase : $approved_only_phrase
906 );
907 } else {
908 // Don't link the comment bubble when there are no approved comments.
909 printf(
910 '<span class="post-com-count post-com-count-no-comments">' .
911 '<span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span>' .
912 '<span class="screen-reader-text">%s</span>' .
913 '</span>',
914 $approved_comments_number,
915 $pending_comments ?
916 /* translators: Hidden accessibility text. */
917 __( 'No approved comments' ) :
918 /* translators: Hidden accessibility text. */
919 __( 'No comments' )
920 );
921 }
922
923 if ( $pending_comments ) {
924 printf(
925 '<a href="%s" class="post-com-count post-com-count-pending">' .
926 '<span class="comment-count-pending" aria-hidden="true">%s</span>' .
927 '<span class="screen-reader-text">%s</span>' .
928 '</a>',
929 esc_url(
930 add_query_arg(
931 array(
932 'p' => $post_id,
933 'comment_status' => 'moderated',
934 ),
935 admin_url( 'edit-comments.php' )
936 )
937 ),
938 $pending_comments_number,
939 $pending_phrase
940 );
941 } else {
942 printf(
943 '<span class="post-com-count post-com-count-pending post-com-count-no-pending">' .
944 '<span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span>' .
945 '<span class="screen-reader-text">%s</span>' .
946 '</span>',
947 $pending_comments_number,
948 $approved_comments ?
949 /* translators: Hidden accessibility text. */
950 __( 'No pending comments' ) :
951 /* translators: Hidden accessibility text. */
952 __( 'No comments' )
953 );
954 }
955 }
956
957 /**
958 * Gets the current page number.
959 *
960 * @since 3.1.0
961 *
962 * @return int
963 */
964 public function get_pagenum() {
965 $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
966
967 if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
968 $pagenum = $this->_pagination_args['total_pages'];
969 }
970
971 return max( 1, $pagenum );
972 }
973
974 /**
975 * Gets the number of items to display on a single page.
976 *
977 * @since 3.1.0
978 *
979 * @param string $option User option name.
980 * @param int $default_value Optional. The number of items to display. Default 20.
981 * @return int
982 */
983 protected function get_items_per_page( $option, $default_value = 20 ) {
984 $per_page = (int) get_user_option( $option );
985 if ( empty( $per_page ) || $per_page < 1 ) {
986 $per_page = $default_value;
987 }
988
989 /**
990 * Filters the number of items to be displayed on each page of the list table.
991 *
992 * The dynamic hook name, `$option`, refers to the `per_page` option depending
993 * on the type of list table in use. Possible filter names include:
994 *
995 * - `edit_comments_per_page`
996 * - `sites_network_per_page`
997 * - `site_themes_network_per_page`
998 * - `themes_network_per_page`
999 * - `users_network_per_page`
1000 * - `edit_post_per_page`
1001 * - `edit_page_per_page`
1002 * - `edit_{$post_type}_per_page`
1003 * - `edit_post_tag_per_page`
1004 * - `edit_category_per_page`
1005 * - `edit_{$taxonomy}_per_page`
1006 * - `site_users_network_per_page`
1007 * - `users_per_page`
1008 *
1009 * @since 2.9.0
1010 *
1011 * @param int $per_page Number of items to be displayed. Default 20.
1012 */
1013 return (int) apply_filters( "{$option}", $per_page );
1014 }
1015
1016 /**
1017 * Displays the pagination.
1018 *
1019 * @since 3.1.0
1020 *
1021 * @param string $which The location of the pagination: Either 'top' or 'bottom'.
1022 */
1023 protected function pagination( $which ) {
1024 if ( empty( $this->_pagination_args['total_items'] ) ) {
1025 return;
1026 }
1027
1028 $total_items = $this->_pagination_args['total_items'];
1029 $total_pages = $this->_pagination_args['total_pages'];
1030 $infinite_scroll = false;
1031 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
1032 $infinite_scroll = $this->_pagination_args['infinite_scroll'];
1033 }
1034
1035 if ( 'top' === $which && $total_pages > 1 ) {
1036 $this->screen->render_screen_reader_content( 'heading_pagination' );
1037 }
1038
1039 $output = '<span class="displaying-num">' . sprintf(
1040 /* translators: %s: Number of items. */
1041 _n( '%s item', '%s items', $total_items ),
1042 number_format_i18n( $total_items )
1043 ) . '</span>';
1044
1045 $current = $this->get_pagenum();
1046 $removable_query_args = wp_removable_query_args();
1047
1048 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1049
1050 $current_url = remove_query_arg( $removable_query_args, $current_url );
1051
1052 $page_links = array();
1053
1054 $total_pages_before = '<span class="paging-input">';
1055 $total_pages_after = '</span></span>';
1056
1057 $disable_first = false;
1058 $disable_last = false;
1059 $disable_prev = false;
1060 $disable_next = false;
1061
1062 if ( 1 === $current ) {
1063 $disable_first = true;
1064 $disable_prev = true;
1065 }
1066 if ( $total_pages === $current ) {
1067 $disable_last = true;
1068 $disable_next = true;
1069 }
1070
1071 if ( $disable_first ) {
1072 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
1073 } else {
1074 $page_links[] = sprintf(
1075 "<a class='first-page button' href='%s'>" .
1076 "<span class='screen-reader-text'>%s</span>" .
1077 "<span aria-hidden='true'>%s</span>" .
1078 '</a>',
1079 esc_url( remove_query_arg( 'paged', $current_url ) ),
1080 /* translators: Hidden accessibility text. */
1081 __( 'First page' ),
1082 '&laquo;'
1083 );
1084 }
1085
1086 if ( $disable_prev ) {
1087 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
1088 } else {
1089 $page_links[] = sprintf(
1090 "<a class='prev-page button' href='%s'>" .
1091 "<span class='screen-reader-text'>%s</span>" .
1092 "<span aria-hidden='true'>%s</span>" .
1093 '</a>',
1094 esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
1095 /* translators: Hidden accessibility text. */
1096 __( 'Previous page' ),
1097 '&lsaquo;'
1098 );
1099 }
1100
1101 if ( 'bottom' === $which ) {
1102 $html_current_page = $current;
1103 $total_pages_before = sprintf(
1104 '<span class="screen-reader-text">%s</span>' .
1105 '<span id="table-paging" class="paging-input">' .
1106 '<span class="tablenav-paging-text">',
1107 /* translators: Hidden accessibility text. */
1108 __( 'Current Page' )
1109 );
1110 } else {
1111 $html_current_page = sprintf(
1112 '<label for="current-page-selector" class="screen-reader-text">%s</label>' .
1113 "<input class='current-page' id='current-page-selector' type='text'
1114 name='paged' value='%s' size='%d' aria-describedby='table-paging' />" .
1115 "<span class='tablenav-paging-text'>",
1116 /* translators: Hidden accessibility text. */
1117 __( 'Current Page' ),
1118 $current,
1119 strlen( $total_pages )
1120 );
1121 }
1122
1123 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
1124
1125 $page_links[] = $total_pages_before . sprintf(
1126 /* translators: 1: Current page, 2: Total pages. */
1127 _x( '%1$s of %2$s', 'paging' ),
1128 $html_current_page,
1129 $html_total_pages
1130 ) . $total_pages_after;
1131
1132 if ( $disable_next ) {
1133 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
1134 } else {
1135 $page_links[] = sprintf(
1136 "<a class='next-page button' href='%s'>" .
1137 "<span class='screen-reader-text'>%s</span>" .
1138 "<span aria-hidden='true'>%s</span>" .
1139 '</a>',
1140 esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
1141 /* translators: Hidden accessibility text. */
1142 __( 'Next page' ),
1143 '&rsaquo;'
1144 );
1145 }
1146
1147 if ( $disable_last ) {
1148 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
1149 } else {
1150 $page_links[] = sprintf(
1151 "<a class='last-page button' href='%s'>" .
1152 "<span class='screen-reader-text'>%s</span>" .
1153 "<span aria-hidden='true'>%s</span>" .
1154 '</a>',
1155 esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
1156 /* translators: Hidden accessibility text. */
1157 __( 'Last page' ),
1158 '&raquo;'
1159 );
1160 }
1161
1162 $pagination_links_class = 'pagination-links';
1163 if ( ! empty( $infinite_scroll ) ) {
1164 $pagination_links_class .= ' hide-if-js';
1165 }
1166 $output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>';
1167
1168 if ( $total_pages ) {
1169 $page_class = $total_pages < 2 ? ' one-page' : '';
1170 } else {
1171 $page_class = ' no-pages';
1172 }
1173 $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
1174
1175 echo $this->_pagination;
1176 }
1177
1178 /**
1179 * Gets a list of columns.
1180 *
1181 * The format is:
1182 * - `'internal-name' => 'Title'`
1183 *
1184 * @since 3.1.0
1185 * @abstract
1186 *
1187 * @return array
1188 */
1189 public function get_columns() {
1190 die( 'function WP_List_Table::get_columns() must be overridden in a subclass.' );
1191 }
1192
1193 /**
1194 * Gets a list of sortable columns.
1195 *
1196 * The format is:
1197 * - `'internal-name' => 'orderby'`
1198 * - `'internal-name' => array( 'orderby', bool, 'abbr', 'orderby-text', 'initially-sorted-column-order' )` -
1199 * - `'internal-name' => array( 'orderby', 'asc' )` - The second element sets the initial sorting order.
1200 * - `'internal-name' => array( 'orderby', true )` - The second element makes the initial order descending.
1201 *
1202 * In the second format, passing true as second parameter will make the initial
1203 * sorting order be descending. Following parameters add a short column name to
1204 * be used as 'abbr' attribute, a translatable string for the current sorting,
1205 * and the initial order for the initial sorted column, 'asc' or 'desc' (default: false).
1206 *
1207 * @since 3.1.0
1208 * @since 6.3.0 Added 'abbr', 'orderby-text' and 'initially-sorted-column-order'.
1209 *
1210 * @return array
1211 */
1212 protected function get_sortable_columns() {
1213 return array();
1214 }
1215
1216 /**
1217 * Gets the name of the default primary column.
1218 *
1219 * @since 4.3.0
1220 *
1221 * @return string Name of the default primary column, in this case, an empty string.
1222 */
1223 protected function get_default_primary_column_name() {
1224 $columns = $this->get_columns();
1225 $column = '';
1226
1227 if ( empty( $columns ) ) {
1228 return $column;
1229 }
1230
1231 /*
1232 * We need a primary defined so responsive views show something,
1233 * so let's fall back to the first non-checkbox column.
1234 */
1235 foreach ( $columns as $col => $column_name ) {
1236 if ( 'cb' === $col ) {
1237 continue;
1238 }
1239
1240 $column = $col;
1241 break;
1242 }
1243
1244 return $column;
1245 }
1246
1247 /**
1248 * Gets the name of the primary column.
1249 *
1250 * Public wrapper for WP_List_Table::get_default_primary_column_name().
1251 *
1252 * @since 4.4.0
1253 *
1254 * @return string Name of the default primary column.
1255 */
1256 public function get_primary_column() {
1257 return $this->get_primary_column_name();
1258 }
1259
1260 /**
1261 * Gets the name of the primary column.
1262 *
1263 * @since 4.3.0
1264 *
1265 * @return string The name of the primary column.
1266 */
1267 protected function get_primary_column_name() {
1268 $columns = get_column_headers( $this->screen );
1269 $default = $this->get_default_primary_column_name();
1270
1271 /*
1272 * If the primary column doesn't exist,
1273 * fall back to the first non-checkbox column.
1274 */
1275 if ( ! isset( $columns[ $default ] ) ) {
1276 $default = self::get_default_primary_column_name();
1277 }
1278
1279 /**
1280 * Filters the name of the primary column for the current list table.
1281 *
1282 * @since 4.3.0
1283 *
1284 * @param string $default Column name default for the specific list table, e.g. 'name'.
1285 * @param string $context Screen ID for specific list table, e.g. 'plugins'.
1286 */
1287 $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
1288
1289 if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
1290 $column = $default;
1291 }
1292
1293 return $column;
1294 }
1295
1296 /**
1297 * Gets a list of all, hidden, and sortable columns, with filter applied.
1298 *
1299 * @since 3.1.0
1300 *
1301 * @return array
1302 */
1303 protected function get_column_info() {
1304 // $_column_headers is already set / cached.
1305 if (
1306 isset( $this->_column_headers ) &&
1307 is_array( $this->_column_headers )
1308 ) {
1309 /*
1310 * Backward compatibility for `$_column_headers` format prior to WordPress 4.3.
1311 *
1312 * In WordPress 4.3 the primary column name was added as a fourth item in the
1313 * column headers property. This ensures the primary column name is included
1314 * in plugins setting the property directly in the three item format.
1315 */
1316 if ( 4 === count( $this->_column_headers ) ) {
1317 return $this->_column_headers;
1318 }
1319
1320 $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
1321 foreach ( $this->_column_headers as $key => $value ) {
1322 $column_headers[ $key ] = $value;
1323 }
1324
1325 $this->_column_headers = $column_headers;
1326
1327 return $this->_column_headers;
1328 }
1329
1330 $columns = get_column_headers( $this->screen );
1331 $hidden = get_hidden_columns( $this->screen );
1332
1333 $sortable_columns = $this->get_sortable_columns();
1334 /**
1335 * Filters the list table sortable columns for a specific screen.
1336 *
1337 * The dynamic portion of the hook name, `$this->screen->id`, refers
1338 * to the ID of the current screen.
1339 *
1340 * @since 3.1.0
1341 *
1342 * @param array $sortable_columns An array of sortable columns.
1343 */
1344 $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
1345
1346 $sortable = array();
1347 foreach ( $_sortable as $id => $data ) {
1348 if ( empty( $data ) ) {
1349 continue;
1350 }
1351
1352 $data = (array) $data;
1353 // Descending initial sorting.
1354 if ( ! isset( $data[1] ) ) {
1355 $data[1] = false;
1356 }
1357 // Current sorting translatable string.
1358 if ( ! isset( $data[2] ) ) {
1359 $data[2] = '';
1360 }
1361 // Initial view sorted column and asc/desc order, default: false.
1362 if ( ! isset( $data[3] ) ) {
1363 $data[3] = false;
1364 }
1365 // Initial order for the initial sorted column, default: false.
1366 if ( ! isset( $data[4] ) ) {
1367 $data[4] = false;
1368 }
1369
1370 $sortable[ $id ] = $data;
1371 }
1372
1373 $primary = $this->get_primary_column_name();
1374 $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
1375
1376 return $this->_column_headers;
1377 }
1378
1379 /**
1380 * Returns the number of visible columns.
1381 *
1382 * @since 3.1.0
1383 *
1384 * @return int
1385 */
1386 public function get_column_count() {
1387 list ( $columns, $hidden ) = $this->get_column_info();
1388 $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
1389 return count( $columns ) - count( $hidden );
1390 }
1391
1392 /**
1393 * Prints column headers, accounting for hidden and sortable columns.
1394 *
1395 * @since 3.1.0
1396 *
1397 * @param bool $with_id Whether to set the ID attribute or not
1398 */
1399 public function print_column_headers( $with_id = true ) {
1400 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1401
1402 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1403 $current_url = remove_query_arg( 'paged', $current_url );
1404
1405 // When users click on a column header to sort by other columns.
1406 if ( isset( $_GET['orderby'] ) ) {
1407 $current_orderby = $_GET['orderby'];
1408 // In the initial view there's no orderby parameter.
1409 } else {
1410 $current_orderby = '';
1411 }
1412
1413 // Not in the initial view and descending order.
1414 if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
1415 $current_order = 'desc';
1416 } else {
1417 // The initial view is not always 'asc', we'll take care of this below.
1418 $current_order = 'asc';
1419 }
1420
1421 if ( ! empty( $columns['cb'] ) ) {
1422 static $cb_counter = 1;
1423 $columns['cb'] = '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />
1424 <label for="cb-select-all-' . $cb_counter . '">' .
1425 '<span class="screen-reader-text">' .
1426 /* translators: Hidden accessibility text. */
1427 __( 'Select All' ) .
1428 '</span>' .
1429 '</label>';
1430 ++$cb_counter;
1431 }
1432
1433 foreach ( $columns as $column_key => $column_display_name ) {
1434 $class = array( 'manage-column', "column-$column_key" );
1435 $aria_sort_attr = '';
1436 $abbr_attr = '';
1437 $order_text = '';
1438
1439 if ( in_array( $column_key, $hidden, true ) ) {
1440 $class[] = 'hidden';
1441 }
1442
1443 if ( 'cb' === $column_key ) {
1444 $class[] = 'check-column';
1445 } elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ), true ) ) {
1446 $class[] = 'num';
1447 }
1448
1449 if ( $column_key === $primary ) {
1450 $class[] = 'column-primary';
1451 }
1452
1453 if ( isset( $sortable[ $column_key ] ) ) {
1454 $orderby = isset( $sortable[ $column_key ][0] ) ? $sortable[ $column_key ][0] : '';
1455 $desc_first = isset( $sortable[ $column_key ][1] ) ? $sortable[ $column_key ][1] : false;
1456 $abbr = isset( $sortable[ $column_key ][2] ) ? $sortable[ $column_key ][2] : '';
1457 $orderby_text = isset( $sortable[ $column_key ][3] ) ? $sortable[ $column_key ][3] : '';
1458 $initial_order = isset( $sortable[ $column_key ][4] ) ? $sortable[ $column_key ][4] : '';
1459
1460 /*
1461 * We're in the initial view and there's no $_GET['orderby'] then check if the
1462 * initial sorting information is set in the sortable columns and use that.
1463 */
1464 if ( '' === $current_orderby && $initial_order ) {
1465 // Use the initially sorted column $orderby as current orderby.
1466 $current_orderby = $orderby;
1467 // Use the initially sorted column asc/desc order as initial order.
1468 $current_order = $initial_order;
1469 }
1470
1471 /*
1472 * True in the initial view when an initial orderby is set via get_sortable_columns()
1473 * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby.
1474 */
1475 if ( $current_orderby === $orderby ) {
1476 // The sorted column. The `aria-sort` attribute must be set only on the sorted column.
1477 if ( 'asc' === $current_order ) {
1478 $order = 'desc';
1479 $aria_sort_attr = ' aria-sort="ascending"';
1480 } else {
1481 $order = 'asc';
1482 $aria_sort_attr = ' aria-sort="descending"';
1483 }
1484
1485 $class[] = 'sorted';
1486 $class[] = $current_order;
1487 } else {
1488 // The other sortable columns.
1489 $order = strtolower( $desc_first );
1490
1491 if ( ! in_array( $order, array( 'desc', 'asc' ), true ) ) {
1492 $order = $desc_first ? 'desc' : 'asc';
1493 }
1494
1495 $class[] = 'sortable';
1496 $class[] = 'desc' === $order ? 'asc' : 'desc';
1497
1498 /* translators: Hidden accessibility text. */
1499 $asc_text = __( 'Sort ascending.' );
1500 /* translators: Hidden accessibility text. */
1501 $desc_text = __( 'Sort descending.' );
1502 $order_text = 'asc' === $order ? $asc_text : $desc_text;
1503 }
1504
1505 if ( '' !== $order_text ) {
1506 $order_text = ' <span class="screen-reader-text">' . $order_text . '</span>';
1507 }
1508
1509 // Print an 'abbr' attribute if a value is provided via get_sortable_columns().
1510 $abbr_attr = $abbr ? ' abbr="' . esc_attr( $abbr ) . '"' : '';
1511
1512 $column_display_name = sprintf(
1513 '<a href="%1$s">' .
1514 '<span>%2$s</span>' .
1515 '<span class="sorting-indicators">' .
1516 '<span class="sorting-indicator asc" aria-hidden="true"></span>' .
1517 '<span class="sorting-indicator desc" aria-hidden="true"></span>' .
1518 '</span>' .
1519 '%3$s' .
1520 '</a>',
1521 esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ),
1522 $column_display_name,
1523 $order_text
1524 );
1525 }
1526
1527 $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
1528 $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
1529 $id = $with_id ? "id='$column_key'" : '';
1530 $class_attr = "class='" . implode( ' ', $class ) . "'";
1531
1532 echo "<$tag $scope $id $class_attr $aria_sort_attr $abbr_attr>$column_display_name</$tag>";
1533 }
1534 }
1535
1536 /**
1537 * Print a table description with information about current sorting and order.
1538 *
1539 * For the table initial view, information about initial orderby and order
1540 * should be provided via get_sortable_columns().
1541 *
1542 * @since 6.3.0
1543 */
1544 public function print_table_description() {
1545 list( $columns, $hidden, $sortable ) = $this->get_column_info();
1546
1547 if ( empty( $sortable ) ) {
1548 return;
1549 }
1550
1551 // When users click on a column header to sort by other columns.
1552 if ( isset( $_GET['orderby'] ) ) {
1553 $current_orderby = $_GET['orderby'];
1554 // In the initial view there's no orderby parameter.
1555 } else {
1556 $current_orderby = '';
1557 }
1558
1559 // Not in the initial view and descending order.
1560 if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
1561 $current_order = 'desc';
1562 } else {
1563 // The initial view is not always 'asc', we'll take care of this below.
1564 $current_order = 'asc';
1565 }
1566
1567 foreach ( array_keys( $columns ) as $column_key ) {
1568
1569 if ( isset( $sortable[ $column_key ] ) ) {
1570 $orderby = isset( $sortable[ $column_key ][0] ) ? $sortable[ $column_key ][0] : '';
1571 $desc_first = isset( $sortable[ $column_key ][1] ) ? $sortable[ $column_key ][1] : false;
1572 $abbr = isset( $sortable[ $column_key ][2] ) ? $sortable[ $column_key ][2] : '';
1573 $orderby_text = isset( $sortable[ $column_key ][3] ) ? $sortable[ $column_key ][3] : '';
1574 $initial_order = isset( $sortable[ $column_key ][4] ) ? $sortable[ $column_key ][4] : '';
1575
1576 if ( ! is_string( $orderby_text ) || '' === $orderby_text ) {
1577 return;
1578 }
1579 /*
1580 * We're in the initial view and there's no $_GET['orderby'] then check if the
1581 * initial sorting information is set in the sortable columns and use that.
1582 */
1583 if ( '' === $current_orderby && $initial_order ) {
1584 // Use the initially sorted column $orderby as current orderby.
1585 $current_orderby = $orderby;
1586 // Use the initially sorted column asc/desc order as initial order.
1587 $current_order = $initial_order;
1588 }
1589
1590 /*
1591 * True in the initial view when an initial orderby is set via get_sortable_columns()
1592 * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby.
1593 */
1594 if ( $current_orderby === $orderby ) {
1595 /* translators: Hidden accessibility text. */
1596 $asc_text = __( 'Ascending.' );
1597 /* translators: Hidden accessibility text. */
1598 $desc_text = __( 'Descending.' );
1599 $order_text = 'asc' === $current_order ? $asc_text : $desc_text;
1600 echo '<caption class="screen-reader-text">' . $orderby_text . ' ' . $order_text . '</caption>';
1601
1602 return;
1603 }
1604 }
1605 }
1606 }
1607
1608 /**
1609 * Displays the table.
1610 *
1611 * @since 3.1.0
1612 */
1613 public function display() {
1614 $singular = $this->_args['singular'];
1615
1616 $this->display_tablenav( 'top' );
1617
1618 $this->screen->render_screen_reader_content( 'heading_list' );
1619 ?>
1620<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
1621 <?php $this->print_table_description(); ?>
1622 <thead>
1623 <tr>
1624 <?php $this->print_column_headers(); ?>
1625 </tr>
1626 </thead>
1627
1628 <tbody id="the-list"
1629 <?php
1630 if ( $singular ) {
1631 echo " data-wp-lists='list:$singular'";
1632 }
1633 ?>
1634 >
1635 <?php $this->display_rows_or_placeholder(); ?>
1636 </tbody>
1637
1638 <tfoot>
1639 <tr>
1640 <?php $this->print_column_headers( false ); ?>
1641 </tr>
1642 </tfoot>
1643
1644</table>
1645 <?php
1646 $this->display_tablenav( 'bottom' );
1647 }
1648
1649 /**
1650 * Gets a list of CSS classes for the WP_List_Table table tag.
1651 *
1652 * @since 3.1.0
1653 *
1654 * @return string[] Array of CSS classes for the table tag.
1655 */
1656 protected function get_table_classes() {
1657 $mode = get_user_setting( 'posts_list_mode', 'list' );
1658
1659 $mode_class = esc_attr( 'table-view-' . $mode );
1660
1661 return array( 'widefat', 'fixed', 'striped', $mode_class, $this->_args['plural'] );
1662 }
1663
1664 /**
1665 * Generates the table navigation above or below the table
1666 *
1667 * @since 3.1.0
1668 * @param string $which The location of the navigation: Either 'top' or 'bottom'.
1669 */
1670 protected function display_tablenav( $which ) {
1671 if ( 'bottom' === $which && ! $this->has_items() ) {
1672 return;
1673 }
1674 if ( 'top' === $which ) {
1675 wp_nonce_field( 'bulk-' . $this->_args['plural'] );
1676 }
1677 ?>
1678 <div class="tablenav <?php echo esc_attr( $which ); ?>">
1679
1680 <?php if ( $this->has_items() ) : ?>
1681 <div class="alignleft actions bulkactions">
1682 <?php $this->bulk_actions( $which ); ?>
1683 </div>
1684 <?php
1685 endif;
1686 $this->extra_tablenav( $which );
1687 $this->pagination( $which );
1688 ?>
1689
1690 <br class="clear" />
1691 </div>
1692 <?php
1693 }
1694
1695 /**
1696 * Displays extra controls between bulk actions and pagination.
1697 *
1698 * @since 3.1.0
1699 *
1700 * @param string $which
1701 */
1702 protected function extra_tablenav( $which ) {}
1703
1704 /**
1705 * Generates the tbody element for the list table.
1706 *
1707 * @since 3.1.0
1708 */
1709 public function display_rows_or_placeholder() {
1710 if ( $this->has_items() ) {
1711 $this->display_rows();
1712 } else {
1713 echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
1714 $this->no_items();
1715 echo '</td></tr>';
1716 }
1717 }
1718
1719 /**
1720 * Generates the list table rows.
1721 *
1722 * @since 3.1.0
1723 */
1724 public function display_rows() {
1725 foreach ( $this->items as $item ) {
1726 $this->single_row( $item );
1727 }
1728 }
1729
1730 /**
1731 * Generates content for a single row of the table.
1732 *
1733 * @since 3.1.0
1734 *
1735 * @param object|array $item The current item
1736 */
1737 public function single_row( $item ) {
1738 echo '<tr>';
1739 $this->single_row_columns( $item );
1740 echo '</tr>';
1741 }
1742
1743 /**
1744 * @param object|array $item
1745 * @param string $column_name
1746 */
1747 protected function column_default( $item, $column_name ) {}
1748
1749 /**
1750 * @param object|array $item
1751 */
1752 protected function column_cb( $item ) {}
1753
1754 /**
1755 * Generates the columns for a single row of the table.
1756 *
1757 * @since 3.1.0
1758 *
1759 * @param object|array $item The current item.
1760 */
1761 protected function single_row_columns( $item ) {
1762 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1763
1764 foreach ( $columns as $column_name => $column_display_name ) {
1765 $classes = "$column_name column-$column_name";
1766 if ( $primary === $column_name ) {
1767 $classes .= ' has-row-actions column-primary';
1768 }
1769
1770 if ( in_array( $column_name, $hidden, true ) ) {
1771 $classes .= ' hidden';
1772 }
1773
1774 /*
1775 * Comments column uses HTML in the display name with screen reader text.
1776 * Strip tags to get closer to a user-friendly string.
1777 */
1778 $data = 'data-colname="' . esc_attr( wp_strip_all_tags( $column_display_name ) ) . '"';
1779
1780 $attributes = "class='$classes' $data";
1781
1782 if ( 'cb' === $column_name ) {
1783 echo '<th scope="row" class="check-column">';
1784 echo $this->column_cb( $item );
1785 echo '</th>';
1786 } elseif ( method_exists( $this, '_column_' . $column_name ) ) {
1787 echo call_user_func(
1788 array( $this, '_column_' . $column_name ),
1789 $item,
1790 $classes,
1791 $data,
1792 $primary
1793 );
1794 } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
1795 echo "<td $attributes>";
1796 echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1797 echo $this->handle_row_actions( $item, $column_name, $primary );
1798 echo '</td>';
1799 } else {
1800 echo "<td $attributes>";
1801 echo $this->column_default( $item, $column_name );
1802 echo $this->handle_row_actions( $item, $column_name, $primary );
1803 echo '</td>';
1804 }
1805 }
1806 }
1807
1808 /**
1809 * Generates and display row actions links for the list table.
1810 *
1811 * @since 4.3.0
1812 *
1813 * @param object|array $item The item being acted upon.
1814 * @param string $column_name Current column name.
1815 * @param string $primary Primary column name.
1816 * @return string The row actions HTML, or an empty string
1817 * if the current column is not the primary column.
1818 */
1819 protected function handle_row_actions( $item, $column_name, $primary ) {
1820 return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' .
1821 /* translators: Hidden accessibility text. */
1822 __( 'Show more details' ) .
1823 '</span></button>' : '';
1824 }
1825
1826 /**
1827 * Handles an incoming ajax request (called from admin-ajax.php)
1828 *
1829 * @since 3.1.0
1830 */
1831 public function ajax_response() {
1832 $this->prepare_items();
1833
1834 ob_start();
1835 if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
1836 $this->display_rows();
1837 } else {
1838 $this->display_rows_or_placeholder();
1839 }
1840
1841 $rows = ob_get_clean();
1842
1843 $response = array( 'rows' => $rows );
1844
1845 if ( isset( $this->_pagination_args['total_items'] ) ) {
1846 $response['total_items_i18n'] = sprintf(
1847 /* translators: Number of items. */
1848 _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1849 number_format_i18n( $this->_pagination_args['total_items'] )
1850 );
1851 }
1852 if ( isset( $this->_pagination_args['total_pages'] ) ) {
1853 $response['total_pages'] = $this->_pagination_args['total_pages'];
1854 $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1855 }
1856
1857 die( wp_json_encode( $response ) );
1858 }
1859
1860 /**
1861 * Sends required variables to JavaScript land.
1862 *
1863 * @since 3.1.0
1864 */
1865 public function _js_vars() {
1866 $args = array(
1867 'class' => get_class( $this ),
1868 'screen' => array(
1869 'id' => $this->screen->id,
1870 'base' => $this->screen->base,
1871 ),
1872 );
1873
1874 printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) );
1875 }
1876}
1877
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1