PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 2.05.09
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v2.05.09
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmListHelper.php

FrmListHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 2.05.09, at classes/helpers/FrmListHelper.php

1,103 lines 28.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined('ABSPATH') ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmListHelper {
7 /**
8 * The current list of items
9 *
10 * @since 2.0.18
11 * @var array
12 * @access public
13 */
14 public $items;
15
16 /**
17 * Various information about the current table
18 *
19 * @since 2.0.18
20 * @var array
21 * @access protected
22 */
23 protected $_args;
24
25 /**
26 * Various information needed for displaying the pagination
27 *
28 * @since 2.0.18
29 * @var array
30 */
31 protected $_pagination_args = array();
32
33 /**
34 * The current screen
35 *
36 * @since 2.0.18
37 * @var object
38 * @access protected
39 */
40 protected $screen;
41
42 /**
43 * Cached bulk actions
44 *
45 * @since 2.0.18
46 * @var array
47 * @access private
48 */
49 private $_actions;
50
51 /**
52 * Cached pagination output
53 *
54 * @since 2.0.18
55 * @var string
56 * @access private
57 */
58 private $_pagination;
59
60 /**
61 * The view switcher modes.
62 *
63 * @since 2.0.18
64 * @var array
65 * @access protected
66 */
67 protected $modes = array();
68
69 /**
70 *
71 * @var array
72 */
73 protected $params;
74
75 /**
76 * Stores the value returned by ->get_column_info()
77 *
78 * @var array
79 */
80 protected $_column_headers;
81
82 protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
83
84 protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions', 'row_actions', 'view_switcher', 'get_items_per_page', 'pagination', 'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav', 'single_row_columns' );
85
86 /**
87 * Construct the table object
88 */
89 public function __construct( $args ) {
90 $args = wp_parse_args( $args, array(
91 'params' => array(),
92 'plural' => '',
93 'singular' => '',
94 'ajax' => false,
95 'screen' => null,
96 ) );
97
98 $this->params = $args['params'];
99
100 $this->screen = convert_to_screen( $args['screen'] );
101
102 add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
103
104 if ( ! $args['plural'] ) {
105 $args['plural'] = $this->screen->base;
106 }
107
108 $args['plural'] = sanitize_key( $args['plural'] );
109 $args['singular'] = sanitize_key( $args['singular'] );
110
111 $this->_args = $args;
112
113 if ( $args['ajax'] ) {
114 // wp_enqueue_script( 'list-table' );
115 add_action( 'admin_footer', array( $this, '_js_vars' ) );
116 }
117
118 if ( empty( $this->modes ) ) {
119 $this->modes = array(
120 'list' => __( 'List View' ),
121 'excerpt' => __( 'Excerpt View' ),
122 );
123 }
124 }
125
126 public function ajax_user_can() {
127 return current_user_can( 'administrator' );
128 }
129
130 public function get_columns() {
131 return array();
132 }
133
134 public function display_rows() {
135 foreach ( $this->items as $item ) {
136 echo "\n\t", $this->single_row( $item );
137 }
138 }
139
140 /**
141 * Prepares the list of items for displaying.
142 * @uses FrmListHelper::set_pagination_args()
143 *
144 * @since 2.0.18
145 * @access public
146 * @abstract
147 */
148 public function prepare_items() {
149 die( 'function FrmListHelper::prepare_items() must be over-ridden in a sub-class.' );
150 }
151
152 /**
153 * @since 3.0
154 */
155 protected function get_param( $args ) {
156 return FrmAppHelper::get_simple_request( array(
157 'param' => $args['param'],
158 'default' => isset( $args['default'] ) ? $args['default'] : '',
159 'sanitize' => isset( $args['sanitize'] ) ? $args['sanitize'] : 'sanitize_title',
160 'type' => 'request',
161 ) );
162 }
163
164 /**
165 * An internal method that sets all the necessary pagination arguments
166 *
167 * @param array $args An associative array with information about the pagination
168 * @access protected
169 *
170 * @param array|string $args
171 */
172 protected function set_pagination_args( $args ) {
173 $args = wp_parse_args( $args, array(
174 'total_items' => 0,
175 'total_pages' => 0,
176 'per_page' => 0,
177 ) );
178
179 if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
180 $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
181 }
182
183 // Redirect if page number is invalid and headers are not already sent.
184 if ( ! headers_sent() && ! FrmAppHelper::wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
185 wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
186 exit;
187 }
188
189 $this->_pagination_args = $args;
190 }
191
192 /**
193 * Access the pagination args.
194 *
195 * @since 2.0.18
196 * @access public
197 *
198 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
199 * 'total_pages', 'per_page', or 'infinite_scroll'.
200 * @return int Number of items that correspond to the given pagination argument.
201 */
202 public function get_pagination_arg( $key ) {
203 if ( 'page' == $key ) {
204 return $this->get_pagenum();
205 }
206
207 if ( isset( $this->_pagination_args[ $key ] ) ) {
208 return $this->_pagination_args[ $key ];
209 }
210 }
211
212 /**
213 * Whether the table has items to display or not
214 *
215 * @since 2.0.18
216 * @access public
217 *
218 * @return bool
219 */
220 public function has_items() {
221 return ! empty( $this->items );
222 }
223
224 /**
225 * Message to be displayed when there are no items
226 *
227 * @since 2.0.18
228 * @access public
229 */
230 public function no_items() {
231 _e( 'No items found.' );
232 }
233
234 /**
235 * Display the search box.
236 *
237 * @since 2.0.18
238 * @access public
239 *
240 * @param string $text The search button text
241 * @param string $input_id The search input id
242 */
243 public function search_box( $text, $input_id ) {
244 if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
245 return;
246 }
247
248 $input_id = $input_id . '-search-input';
249
250 foreach ( array( 'orderby', 'order' ) as $search_params ) {
251 $this->hidden_search_inputs( $search_params );
252 }
253 ?>
254 <p class="search-box">
255 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ) ?>"><?php echo wp_kses( $text, array() ); ?>:</label>
256 <input type="search" id="<?php echo esc_attr( $input_id ) ?>" name="s" value="<?php _admin_search_query(); ?>" />
257 <?php submit_button( $text, 'button', '', false, array( 'id' => 'search-submit' ) ); ?>
258 </p>
259 <?php
260 }
261
262 private function hidden_search_inputs( $param_name ) {
263 if ( ! empty( $_REQUEST[ $param_name ] ) ) {
264 echo '<input type="hidden" name="' . esc_attr( $param_name ) . '" value="' . esc_attr( $_REQUEST[ $param_name ] ) . '" />';
265 }
266 }
267
268 /**
269 * Get an associative array ( id => link ) with the list
270 * of views available on this table.
271 *
272 * @since 2.0.18
273 * @access protected
274 *
275 * @return array
276 */
277 protected function get_views() {
278 return array();
279 }
280
281 /**
282 * Display the list of views available on this table.
283 *
284 * @since 2.0.18
285 * @access public
286 */
287 public function views() {
288 $views = $this->get_views();
289 /**
290 * Filter the list of available list table views.
291 *
292 * The dynamic portion of the hook name, `$this->screen->id`, refers
293 * to the ID of the current screen, usually a string.
294 *
295 * @since 3.5.0
296 *
297 * @param array $views An array of available list table views.
298 */
299 $views = apply_filters( 'views_' . $this->screen->id, $views );
300
301 if ( empty( $views ) ) {
302 return;
303 }
304
305 echo "<ul class='subsubsub'>\n";
306 foreach ( $views as $class => $view ) {
307 $views[ $class ] = "\t<li class='$class'>$view";
308 }
309 echo implode( " |</li>\n", $views ) . "</li>\n";
310 echo '</ul>';
311 }
312
313 /**
314 * Get an associative array ( option_name => option_title ) with the list
315 * of bulk actions available on this table.
316 *
317 * @since 2.0.18
318 * @access protected
319 *
320 * @return array
321 */
322 protected function get_bulk_actions() {
323 return array();
324 }
325
326 /**
327 * Display the bulk actions dropdown.
328 *
329 * @since 2.0.18
330 * @access protected
331 *
332 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
333 * This is designated as optional for backwards-compatibility.
334 */
335 protected function bulk_actions( $which = '' ) {
336 if ( is_null( $this->_actions ) ) {
337 $no_new_actions = $this->get_bulk_actions();
338 $this->_actions = $no_new_actions;
339
340 /**
341 * Filter the list table Bulk Actions drop-down.
342 *
343 * The dynamic portion of the hook name, `$this->screen->id`, refers
344 * to the ID of the current screen, usually a string.
345 *
346 * This filter can currently only be used to remove bulk actions.
347 *
348 * @since 3.5.0
349 *
350 * @param array $actions An array of the available bulk actions.
351 */
352 $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
353 $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
354 $two = '';
355 } else {
356 $two = '2';
357 }
358
359 if ( empty( $this->_actions ) ) {
360 return;
361 }
362
363 echo "<label for='bulk-action-selector-" . esc_attr( $which ) . "' class='screen-reader-text'>" . esc_attr__( 'Select bulk action' ) . '</label>';
364 echo "<select name='action" . esc_attr( $two ) . "' id='bulk-action-selector-" . esc_attr( $which ) . "'>\n";
365 echo "<option value='-1' selected='selected'>" . esc_attr__( 'Bulk Actions' ) . "</option>\n";
366
367 foreach ( $this->_actions as $name => $title ) {
368 $class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
369
370 echo "\t<option value='" . esc_attr( $name ) . "'$class>$title</option>\n";
371 }
372
373 echo "</select>\n";
374
375 submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
376 echo "\n";
377 }
378
379 /**
380 * Get the current action selected from the bulk actions dropdown.
381 *
382 * @since 2.0.18
383 * @access public
384 *
385 * @return string|false The action name or False if no action was selected
386 */
387 public function current_action() {
388 if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) {
389 return false;
390 }
391
392 $action = $this->get_bulk_action( 'action' );
393 if ( $action === false ) {
394 $action = $this->get_bulk_action( 'action2' );
395 }
396
397 return $action;
398 }
399
400 private static function get_bulk_action( $action_name ) {
401 $action = false;
402 $action_param = self::get_param( array(
403 'param' => $action_name,
404 'sanitize' => 'sanitize_text_field',
405 ) );
406 if ( $action_param && -1 != $action_param ) {
407 $action = $action_param;
408 }
409 return $action;
410 }
411
412 /**
413 * Generate row actions div
414 *
415 * @since 2.0.18
416 * @access protected
417 *
418 * @param array $actions The list of actions
419 * @param bool $always_visible Whether the actions should be always visible
420 * @return string
421 */
422 protected function row_actions( $actions, $always_visible = false ) {
423 $action_count = count( $actions );
424 $i = 0;
425
426 if ( ! $action_count ) {
427 return '';
428 }
429
430 $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
431 foreach ( $actions as $action => $link ) {
432 ++$i;
433 ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
434 $out .= "<span class='$action'>$link$sep</span>";
435 }
436 $out .= '</div>';
437
438 $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
439
440 return $out;
441 }
442
443 /**
444 * Display a view switcher
445 *
446 * @since 2.0.18
447 * @access protected
448 *
449 * @param string $current_mode
450 */
451 protected function view_switcher( $current_mode ) {
452 ?>
453 <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
454 <div class="view-switch">
455 <?php
456 foreach ( $this->modes as $mode => $title ) {
457 $classes = array( 'view-' . $mode );
458 if ( $current_mode == $mode ) {
459 $classes[] = 'current';
460 }
461
462 printf(
463 "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
464 esc_url( add_query_arg( 'mode', $mode ) ),
465 implode( ' ', $classes ),
466 $title
467 );
468 }
469 ?>
470 </div>
471 <?php
472 }
473
474 /**
475 * Get the current page number
476 *
477 * @since 2.0.18
478 * @access public
479 *
480 * @return int
481 */
482 public function get_pagenum() {
483 $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
484
485 if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
486 $pagenum = $this->_pagination_args['total_pages'];
487 }
488
489 return max( 1, $pagenum );
490 }
491
492 /**
493 * Get number of items to display on a single page
494 *
495 * @since 2.0.18
496 * @access protected
497 *
498 * @param string $option
499 * @param int $default
500 * @return int
501 */
502 protected function get_items_per_page( $option, $default = 20 ) {
503 $per_page = (int) get_user_option( $option );
504 if ( empty( $per_page ) || $per_page < 1 ) {
505 $per_page = $default;
506 }
507
508 /**
509 * Filter the number of items to be displayed on each page of the list table.
510 *
511 * The dynamic hook name, $option, refers to the `per_page` option depending
512 * on the type of list table in use. Possible values include: 'edit_comments_per_page',
513 * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
514 * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
515 * 'edit_{$post_type}_per_page', etc.
516 *
517 * @since 2.9.0
518 *
519 * @param int $per_page Number of items to be displayed. Default 20.
520 */
521 return (int) apply_filters( $option, $per_page );
522 }
523
524 /**
525 * Display the pagination.
526 *
527 * @since 2.0.18
528 * @access protected
529 *
530 * @param string $which
531 */
532 protected function pagination( $which ) {
533 if ( empty( $this->_pagination_args ) ) {
534 return;
535 }
536
537 $total_items = $this->_pagination_args['total_items'];
538 $total_pages = $this->_pagination_args['total_pages'];
539 $infinite_scroll = false;
540 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
541 $infinite_scroll = $this->_pagination_args['infinite_scroll'];
542 }
543
544 $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
545
546 $current = $this->get_pagenum();
547
548 $current_url = set_url_scheme( 'http://' . FrmAppHelper::get_server_value( 'HTTP_HOST' ) . FrmAppHelper::get_server_value( 'REQUEST_URI' ) );
549
550 $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
551
552 $page_links = array();
553
554 $total_pages_before = '<span class="paging-input">';
555 $total_pages_after = '</span>';
556
557 $disable_first = false;
558 $disable_last = false;
559 $disable_prev = false;
560 $disable_next = false;
561
562 if ( $current == 1 ) {
563 $disable_first = true;
564 $disable_prev = true;
565 }
566 if ( $current == 2 ) {
567 $disable_first = true;
568 }
569 if ( $current == $total_pages ) {
570 $disable_last = true;
571 $disable_next = true;
572 }
573 if ( $current == $total_pages - 1 ) {
574 $disable_last = true;
575 }
576
577 if ( $disable_first ) {
578 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
579 } else {
580 $page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
581 esc_url( remove_query_arg( 'paged', $current_url ) ),
582 __( 'First page' ),
583 '&laquo;'
584 );
585 }
586
587 if ( $disable_prev ) {
588 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
589 } else {
590 $page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
591 esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
592 __( 'Previous page' ),
593 '&lsaquo;'
594 );
595 }
596
597 if ( 'bottom' == $which ) {
598 $html_current_page = $current;
599 $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input">';
600 } else {
601 $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' />",
602 '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
603 $current,
604 strlen( $total_pages )
605 );
606 }
607 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
608 $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
609
610 if ( $disable_next ) {
611 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
612 } else {
613 $page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
614 esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
615 __( 'Next page' ),
616 '&rsaquo;'
617 );
618 }
619
620 if ( $disable_last ) {
621 $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
622 } else {
623 $page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
624 esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
625 __( 'Last page' ),
626 '&raquo;'
627 );
628 }
629
630 $pagination_links_class = 'pagination-links';
631 if ( ! empty( $infinite_scroll ) ) {
632 $pagination_links_class = ' hide-if-js';
633 }
634 $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
635
636 if ( $total_pages ) {
637 $page_class = $total_pages < 2 ? ' one-page' : '';
638 } else {
639 $page_class = ' no-pages';
640 }
641 $this->_pagination = "<div class='tablenav-pages" . esc_attr( $page_class ) . "'>$output</div>";
642
643 echo $this->_pagination;
644 }
645
646 /**
647 * Get a list of sortable columns. The format is:
648 * 'internal-name' => 'orderby'
649 * or
650 * 'internal-name' => array( 'orderby', true )
651 *
652 * The second format will make the initial sorting order be descending
653 *
654 * @since 2.0.18
655 * @access protected
656 *
657 * @return array
658 */
659 protected function get_sortable_columns() {
660 return array();
661 }
662
663 /**
664 * Gets the name of the default primary column.
665 *
666 * @since 4.3.0
667 * @access protected
668 *
669 * @return string Name of the default primary column, in this case, an empty string.
670 */
671 protected function get_default_primary_column_name() {
672 $columns = $this->get_columns();
673 $column = '';
674
675 // We need a primary defined so responsive views show something,
676 // so let's fall back to the first non-checkbox column.
677 foreach ( $columns as $col => $column_name ) {
678 if ( 'cb' === $col ) {
679 continue;
680 }
681
682 $column = $col;
683 break;
684 }
685
686 return $column;
687 }
688
689 /**
690 * Gets the name of the primary column.
691 *
692 * @since 4.3.0
693 * @access protected
694 *
695 * @return string The name of the primary column.
696 */
697 protected function get_primary_column_name() {
698 $columns = $this->get_columns();
699 $default = $this->get_default_primary_column_name();
700
701 // If the primary column doesn't exist fall back to the
702 // first non-checkbox column.
703 if ( ! isset( $columns[ $default ] ) ) {
704 $default = FrmListHelper::get_default_primary_column_name();
705 }
706
707 /**
708 * Filter the name of the primary column for the current list table.
709 *
710 * @since 4.3.0
711 *
712 * @param string $default Column name default for the specific list table, e.g. 'name'.
713 * @param string $context Screen ID for specific list table, e.g. 'plugins'.
714 */
715 $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
716
717 if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
718 $column = $default;
719 }
720
721 return $column;
722 }
723
724 /**
725 * Get a list of all, hidden and sortable columns, with filter applied
726 *
727 * @since 2.0.18
728 * @access protected
729 *
730 * @return array
731 */
732 protected function get_column_info() {
733 // $_column_headers is already set / cached
734 if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
735 // Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
736 // In 4.3, we added a fourth argument for primary column.
737 $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
738 foreach ( $this->_column_headers as $key => $value ) {
739 $column_headers[ $key ] = $value;
740 }
741
742 return $column_headers;
743 }
744
745 $columns = get_column_headers( $this->screen );
746 $hidden = get_hidden_columns( $this->screen );
747
748 $sortable_columns = $this->get_sortable_columns();
749 /**
750 * Filter the list table sortable columns for a specific screen.
751 *
752 * The dynamic portion of the hook name, `$this->screen->id`, refers
753 * to the ID of the current screen, usually a string.
754 *
755 * @since 3.5.0
756 *
757 * @param array $sortable_columns An array of sortable columns.
758 */
759 $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
760
761 $sortable = array();
762 foreach ( $_sortable as $id => $data ) {
763 if ( empty( $data ) ) {
764 continue;
765 }
766
767 $data = (array) $data;
768 if ( ! isset( $data[1] ) ) {
769 $data[1] = false;
770 }
771
772 $sortable[ $id ] = $data;
773 }
774
775 $primary = $this->get_primary_column_name();
776 $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
777
778 return $this->_column_headers;
779 }
780
781 /**
782 * Return number of visible columns
783 *
784 * @since 2.0.18
785 * @access public
786 *
787 * @return int
788 */
789 public function get_column_count() {
790 list ( $columns, $hidden ) = $this->get_column_info();
791 $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
792 return count( $columns ) - count( $hidden );
793 }
794
795 /**
796 * Print column headers, accounting for hidden and sortable columns.
797 *
798 * @since 2.0.18
799 * @access public
800 *
801 * @staticvar int $cb_counter
802 *
803 * @param bool $with_id Whether to set the id attribute or not
804 */
805 public function print_column_headers( $with_id = true ) {
806 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
807
808 $current_url = set_url_scheme( 'http://' . FrmAppHelper::get_server_value( 'HTTP_HOST' ) . FrmAppHelper::get_server_value( 'REQUEST_URI' ) );
809 $current_url = remove_query_arg( 'paged', $current_url );
810
811 if ( isset( $_GET['orderby'] ) ) {
812 $current_orderby = sanitize_text_field( $_GET['orderby'] );
813 } else {
814 $current_orderby = '';
815 }
816
817 if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) {
818 $current_order = 'desc';
819 } else {
820 $current_order = 'asc';
821 }
822
823 if ( ! empty( $columns['cb'] ) ) {
824 static $cb_counter = 1;
825 $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
826 . '<input id="cb-select-all-' . esc_attr( $cb_counter ) . '" type="checkbox" />';
827 $cb_counter++;
828 }
829
830 foreach ( $columns as $column_key => $column_display_name ) {
831 $class = array( 'manage-column', "column-$column_key" );
832
833 if ( in_array( $column_key, $hidden ) ) {
834 $class[] = 'hidden';
835 }
836
837 if ( 'cb' == $column_key ) {
838 $class[] = 'check-column';
839 } else if ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) {
840 $class[] = 'num';
841 }
842
843 if ( $column_key === $primary ) {
844 $class[] = 'column-primary';
845 }
846
847 if ( isset( $sortable[ $column_key ] ) ) {
848 list( $orderby, $desc_first ) = $sortable[ $column_key ];
849
850 if ( $current_orderby == $orderby ) {
851 $order = 'asc' == $current_order ? 'desc' : 'asc';
852 $class[] = 'sorted';
853 $class[] = $current_order;
854 } else {
855 $order = $desc_first ? 'desc' : 'asc';
856 $class[] = 'sortable';
857 $class[] = $desc_first ? 'asc' : 'desc';
858 }
859
860 $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
861 }
862
863 $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
864 $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
865 $id = $with_id ? "id='" . esc_attr( $column_key ) . "'" : '';
866
867 if ( ! empty( $class ) ) {
868 $class = "class='" . join( ' ', $class ) . "'";
869 }
870
871 echo "<$tag $scope $id $class>$column_display_name</$tag>";
872 }
873 }
874
875 /**
876 * Display the table
877 *
878 * @since 2.0.18
879 * @access public
880 */
881 public function display() {
882 $singular = $this->_args['singular'];
883
884 $this->display_tablenav( 'top' );
885 ?>
886 <table class="wp-list-table <?php echo esc_attr( implode( ' ', $this->get_table_classes() ) ); ?>">
887 <thead>
888 <tr>
889 <?php $this->print_column_headers(); ?>
890 </tr>
891 </thead>
892
893 <tbody id="the-list"<?php echo ( $singular ? " data-wp-lists='list:" . esc_attr( $singular ) . "'" : '' ); ?>>
894 <?php $this->display_rows_or_placeholder(); ?>
895 </tbody>
896
897 <tfoot>
898 <tr>
899 <?php $this->print_column_headers( false ); ?>
900 </tr>
901 </tfoot>
902
903 </table>
904 <?php
905 $this->display_tablenav( 'bottom' );
906 }
907
908 /**
909 * Get a list of CSS classes for the list table table tag.
910 *
911 * @since 2.0.18
912 * @access protected
913 *
914 * @return array List of CSS classes for the table tag.
915 */
916 protected function get_table_classes() {
917 return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
918 }
919
920 /**
921 * Generate the table navigation above or below the table
922 *
923 * @since 2.0.18
924 * @access protected
925 * @param string $which
926 */
927 protected function display_tablenav( $which ) {
928 if ( 'top' == $which ) {
929 wp_nonce_field( 'bulk-' . $this->_args['plural'] );
930 }
931 ?>
932 <div class="tablenav <?php echo esc_attr( $which ); ?>">
933
934 <div class="alignleft actions bulkactions">
935 <?php $this->bulk_actions( $which ); ?>
936 </div>
937 <?php
938 $this->extra_tablenav( $which );
939 $this->pagination( $which );
940 ?>
941
942 <br class="clear" />
943 </div>
944 <?php
945 }
946
947 /**
948 * Extra controls to be displayed between bulk actions and pagination
949 *
950 * @since 2.0.18
951 * @access protected
952 *
953 * @param string $which
954 */
955 protected function extra_tablenav( $which ) {}
956
957 /**
958 * Generate the tbody element for the list table.
959 *
960 * @since 2.0.18
961 * @access public
962 */
963 public function display_rows_or_placeholder() {
964 if ( $this->has_items() ) {
965 $this->display_rows();
966 } else {
967 echo '<tr class="no-items"><td class="colspanchange" colspan="' . esc_attr( $this->get_column_count() ) . '">';
968 $this->no_items();
969 echo '</td></tr>';
970 }
971 }
972
973 /**
974 * Generates content for a single row of the table
975 *
976 * @since 2.0.18
977 * @access public
978 *
979 * @param object $item The current item
980 */
981 public function single_row( $item ) {
982 echo '<tr>';
983 $this->single_row_columns( $item );
984 echo '</tr>';
985 }
986
987 /**
988 * Generates the columns for a single row of the table
989 *
990 * @since 2.0.18
991 * @access protected
992 *
993 * @param object $item The current item
994 */
995 protected function single_row_columns( $item ) {
996 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
997
998 foreach ( $columns as $column_name => $column_display_name ) {
999 $classes = "$column_name column-$column_name";
1000 if ( $primary === $column_name ) {
1001 $classes .= ' has-row-actions column-primary';
1002 }
1003
1004 if ( in_array( $column_name, $hidden ) ) {
1005 $classes .= ' hidden';
1006 }
1007
1008 // Comments column uses HTML in the display name with screen reader text.
1009 // Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
1010 $data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
1011
1012 $attributes = "class='$classes' $data";
1013
1014 if ( 'cb' == $column_name ) {
1015 echo '<th scope="row" class="check-column"></th>';
1016 } elseif ( method_exists( $this, '_column_' . $column_name ) ) {
1017 echo call_user_func(
1018 array( $this, '_column_' . $column_name ),
1019 $item,
1020 $classes,
1021 $data,
1022 $primary
1023 );
1024 } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
1025 echo "<td $attributes>";
1026 echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1027 echo $this->handle_row_actions( $item, $column_name, $primary );
1028 echo '</td>';
1029 } else {
1030 echo "<td $attributes>";
1031 echo $this->handle_row_actions( $item, $column_name, $primary );
1032 echo '</td>';
1033 }
1034 }
1035 }
1036
1037 /**
1038 * Generates and display row actions links for the list table.
1039 *
1040 * @since 4.3.0
1041 * @access protected
1042 *
1043 * @param object $item The item being acted upon.
1044 * @param string $column_name Current column name.
1045 * @param string $primary Primary column name.
1046 * @return string The row actions output. In this case, an empty string.
1047 */
1048 protected function handle_row_actions( $item, $column_name, $primary ) {
1049 return $column_name == $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1050 }
1051
1052 /**
1053 * Handle an incoming ajax request (called from admin-ajax.php)
1054 *
1055 * @since 2.0.18
1056 * @access public
1057 */
1058 public function ajax_response() {
1059 $this->prepare_items();
1060
1061 ob_start();
1062 if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
1063 $this->display_rows();
1064 } else {
1065 $this->display_rows_or_placeholder();
1066 }
1067
1068 $rows = ob_get_clean();
1069
1070 $response = array( 'rows' => $rows );
1071
1072 if ( isset( $this->_pagination_args['total_items'] ) ) {
1073 $response['total_items_i18n'] = sprintf(
1074 _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1075 number_format_i18n( $this->_pagination_args['total_items'] )
1076 );
1077 }
1078 if ( isset( $this->_pagination_args['total_pages'] ) ) {
1079 $response['total_pages'] = $this->_pagination_args['total_pages'];
1080 $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1081 }
1082
1083 die( wp_json_encode( $response ) );
1084 }
1085
1086 /**
1087 * Send required variables to JavaScript land
1088 *
1089 * @access public
1090 */
1091 public function _js_vars() {
1092 $args = array(
1093 'class' => get_class( $this ),
1094 'screen' => array(
1095 'id' => $this->screen->id,
1096 'base' => $this->screen->base,
1097 ),
1098 );
1099
1100 printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
1101 }
1102 }
1103