Completed
Push — master ( 3f715e...b50b70 )
by Stephanie
06:18 queued 02:33
created

FrmListHelper::get_views()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
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(
85
		'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
86
		'row_actions', 'view_switcher', 'get_items_per_page', 'pagination',
87
		'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
88
		'single_row_columns',
89
	);
90
91
	/**
92
	* Construct the table object
93
	*/
94
	public function __construct( $args ) {
95
	    $args = wp_parse_args( $args, array(
96
			'params' => array(),
97
			'plural' => '',
98
			'singular' => '',
99
			'ajax' => false,
100
			'screen' => null,
101
		) );
102
103
		$this->params = $args['params'];
104
105
		$this->screen = convert_to_screen( $args['screen'] );
106
107
		add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
108
109
		if ( ! $args['plural'] ) {
110
			$args['plural'] = $this->screen->base;
111
		}
112
113
		$args['plural'] = sanitize_key( $args['plural'] );
114
		$args['singular'] = sanitize_key( $args['singular'] );
115
116
		$this->_args = $args;
117
118
		if ( $args['ajax'] ) {
119
			// wp_enqueue_script( 'list-table' );
120
			add_action( 'admin_footer', array( $this, '_js_vars' ) );
121
		}
122
123
		if ( empty( $this->modes ) ) {
124
			$this->modes = array(
125
				'list'    => __( 'List View' ),
126
				'excerpt' => __( 'Excerpt View' )
127
			);
128
		}
129
	}
130
131
	public function ajax_user_can() {
132
		return current_user_can( 'administrator' );
133
	}
134
135
	public function get_columns() {
136
		return array();
137
	}
138
139
	public function display_rows() {
140
		$style = '';
0 ignored issues
show
Unused Code introduced by
$style is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
141
		foreach ( $this->items as $item ) {
142
			echo "\n\t", $this->single_row( $item );
143
		}
144
	}
145
146
	/**
147
	 * Make private properties readable for backwards compatibility.
148
	 *
149
	 * @since 4.0.0
150
	 * @access public
151
	 *
152
	 * @param string $name Property to get.
153
	 * @return mixed Property.
154
	 */
155
	public function __get( $name ) {
156
		if ( in_array( $name, $this->compat_fields ) ) {
157
			return $this->$name;
158
		}
159
	}
160
161
	/**
162
	 * Make private properties settable for backwards compatibility.
163
	 *
164
	 * @since 4.0.0
165
	 * @access public
166
	 *
167
	 * @param string $name  Property to check if set.
168
	 * @param mixed  $value Property value.
169
	 * @return mixed Newly-set property.
170
	 */
171
	public function __set( $name, $value ) {
172
		if ( in_array( $name, $this->compat_fields ) ) {
173
			return $this->$name = $value;
174
		}
175
	}
176
177
	/**
178
	 * Make private properties checkable for backwards compatibility.
179
	 *
180
	 * @since 4.0.0
181
	 * @access public
182
	 *
183
	 * @param string $name Property to check if set.
184
	 * @return bool Whether the property is set.
185
	 */
186
	public function __isset( $name ) {
187
		if ( in_array( $name, $this->compat_fields ) ) {
188
			return isset( $this->$name );
189
		}
190
	}
191
192
	/**
193
	 * Make private properties un-settable for backwards compatibility.
194
	 *
195
	 * @since 4.0.0
196
	 * @access public
197
	 *
198
	 * @param string $name Property to unset.
199
	 */
200
	public function __unset( $name ) {
201
		if ( in_array( $name, $this->compat_fields ) ) {
202
			unset( $this->$name );
203
		}
204
	}
205
206
	/**
207
	 * Make private/protected methods readable for backwards compatibility.
208
	 *
209
	 * @since 4.0.0
210
	 * @access public
211
	 *
212
	 * @param callable $name      Method to call.
213
	 * @param array    $arguments Arguments to pass when calling.
214
	 * @return mixed|bool Return value of the callback, false otherwise.
215
	 */
216
	public function __call( $name, $arguments ) {
217
		if ( in_array( $name, $this->compat_methods ) ) {
218
			return call_user_func_array( array( $this, $name ), $arguments );
219
		}
220
		return false;
221
	}
222
223
	/**
224
	 * Prepares the list of items for displaying.
225
	 * @uses FrmListHelper::set_pagination_args()
226
	 *
227
	 * @since 2.0.18
228
	 * @access public
229
	 * @abstract
230
	 */
231
	public function prepare_items() {
232
		die( 'function FrmListHelper::prepare_items() must be over-ridden in a sub-class.' );
233
	}
234
235
	/**
236
	 * An internal method that sets all the necessary pagination arguments
237
	 *
238
	 * @param array $args An associative array with information about the pagination
239
	 * @access protected
240
	 *
241
	 * @param array|string $args
242
	 */
243
	protected function set_pagination_args( $args ) {
244
		$args = wp_parse_args( $args, array(
245
			'total_items' => 0,
246
			'total_pages' => 0,
247
			'per_page' => 0,
248
		) );
249
250
		if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
251
			$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
252
		}
253
254
		// Redirect if page number is invalid and headers are not already sent.
255
		if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
256
			wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
257
			exit;
258
		}
259
260
		$this->_pagination_args = $args;
261
	}
262
263
	/**
264
	 * Access the pagination args.
265
	 *
266
	 * @since 2.0.18
267
	 * @access public
268
	 *
269
	 * @param string $key Pagination argument to retrieve. Common values include 'total_items',
270
	 *                    'total_pages', 'per_page', or 'infinite_scroll'.
271
	 * @return int Number of items that correspond to the given pagination argument.
272
	 */
273
	public function get_pagination_arg( $key ) {
274
		if ( 'page' == $key ) {
275
			return $this->get_pagenum();
276
		}
277
278
		if ( isset( $this->_pagination_args[ $key ] ) ) {
279
			return $this->_pagination_args[ $key ];
280
		}
281
	}
282
283
	/**
284
	 * Whether the table has items to display or not
285
	 *
286
	 * @since 2.0.18
287
	 * @access public
288
	 *
289
	 * @return bool
290
	 */
291
	public function has_items() {
292
		return ! empty( $this->items );
293
	}
294
295
	/**
296
	 * Message to be displayed when there are no items
297
	 *
298
	 * @since 2.0.18
299
	 * @access public
300
	 */
301
	public function no_items() {
302
		_e( 'No items found.' );
303
	}
304
305
	/**
306
	 * Display the search box.
307
	 *
308
	 * @since 2.0.18
309
	 * @access public
310
	 *
311
	 * @param string $text The search button text
312
	 * @param string $input_id The search input id
313
	 */
314
	public function search_box( $text, $input_id ) {
315
		if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
316
			return;
317
		}
318
319
		$input_id = $input_id . '-search-input';
320
321
		foreach ( array( 'orderby', 'order' ) as $search_params ) {
322
			$this->hidden_search_inputs( $search_params );
323
		}
324
?>
325
<p class="search-box">
326
	<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ) ?>"><?php echo wp_kses( $text, array() ); ?>:</label>
327
	<input type="search" id="<?php echo esc_attr( $input_id ) ?>" name="s" value="<?php _admin_search_query(); ?>" />
328
	<?php submit_button( $text, 'button', '', false, array( 'id' => 'search-submit' ) ); ?>
329
</p>
330
<?php
331
	}
332
333
	private function hidden_search_inputs( $param_name ) {
334
		if ( ! empty( $_REQUEST[ $param_name ] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
335
			echo '<input type="hidden" name="' . esc_attr( $param_name ) . '" value="' . esc_attr( $_REQUEST[ $param_name ] ) . '" />';
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
336
		}
337
	}
338
339
	/**
340
	 * Get an associative array ( id => link ) with the list
341
	 * of views available on this table.
342
	 *
343
	 * @since 2.0.18
344
	 * @access protected
345
	 *
346
	 * @return array
347
	 */
348
	protected function get_views() {
349
		return array();
350
	}
351
352
	/**
353
	 * Display the list of views available on this table.
354
	 *
355
	 * @since 2.0.18
356
	 * @access public
357
	 */
358
	public function views() {
359
		$views = $this->get_views();
360
		/**
361
		 * Filter the list of available list table views.
362
		 *
363
		 * The dynamic portion of the hook name, `$this->screen->id`, refers
364
		 * to the ID of the current screen, usually a string.
365
		 *
366
		 * @since 3.5.0
367
		 *
368
		 * @param array $views An array of available list table views.
369
		 */
370
		$views = apply_filters( 'views_' . $this->screen->id, $views );
371
372
		if ( empty( $views ) ) {
373
			return;
374
		}
375
376
		echo "<ul class='subsubsub'>\n";
377
		foreach ( $views as $class => $view ) {
378
			$views[ $class ] = "\t<li class='$class'>$view";
379
		}
380
		echo implode( " |</li>\n", $views ) . "</li>\n";
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'implode'
Loading history...
381
		echo '</ul>';
382
	}
383
384
	/**
385
	 * Get an associative array ( option_name => option_title ) with the list
386
	 * of bulk actions available on this table.
387
	 *
388
	 * @since 2.0.18
389
	 * @access protected
390
	 *
391
	 * @return array
392
	 */
393
	protected function get_bulk_actions() {
394
		return array();
395
	}
396
397
	/**
398
	 * Display the bulk actions dropdown.
399
	 *
400
	 * @since 2.0.18
401
	 * @access protected
402
	 *
403
	 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
404
	 *                      This is designated as optional for backwards-compatibility.
405
	 */
406
	protected function bulk_actions( $which = '' ) {
407
		if ( is_null( $this->_actions ) ) {
408
			$no_new_actions = $this->_actions = $this->get_bulk_actions();
409
			/**
410
			 * Filter the list table Bulk Actions drop-down.
411
			 *
412
			 * The dynamic portion of the hook name, `$this->screen->id`, refers
413
			 * to the ID of the current screen, usually a string.
414
			 *
415
			 * This filter can currently only be used to remove bulk actions.
416
			 *
417
			 * @since 3.5.0
418
			 *
419
			 * @param array $actions An array of the available bulk actions.
420
			 */
421
			$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
422
			$this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
423
			$two = '';
424
		} else {
425
			$two = '2';
426
		}
427
428
		if ( empty( $this->_actions ) ) {
429
			return;
430
		}
431
432
		echo "<label for='bulk-action-selector-" . esc_attr( $which ) . "' class='screen-reader-text'>" . esc_attr__( 'Select bulk action' ) . "</label>";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal </label> does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
433
		echo "<select name='action" . esc_attr( $two ) . "' id='bulk-action-selector-" . esc_attr( $which ) . "'>\n";
434
		echo "<option value='-1' selected='selected'>" . esc_attr__( 'Bulk Actions' ) . "</option>\n";
435
436
		foreach ( $this->_actions as $name => $title ) {
437
			$class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
438
439
			echo "\t<option value='". esc_attr( $name ) ."'$class>$title</option>\n";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"'$class>$title</option>\n"'
Loading history...
440
		}
441
442
		echo "</select>\n";
443
444
		submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
445
		echo "\n";
446
	}
447
448
	/**
449
	 * Get the current action selected from the bulk actions dropdown.
450
	 *
451
	 * @since 2.0.18
452
	 * @access public
453
	 *
454
	 * @return string|false The action name or False if no action was selected
455
	 */
456
	public function current_action() {
457
		if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
458
			return false;
459
		}
460
461
		$action = $this->get_bulk_action( 'action' );
462
		if ( $action === false ) {
463
			$action = $this->get_bulk_action( 'action2' );
464
		}
465
466
		return $action;
467
	}
468
469
	private static function get_bulk_action( $action_name ) {
470
		$action = false;
471
		if ( isset( $_REQUEST[ $action_name ] ) && -1 != sanitize_text_field( $_REQUEST[ $action_name ] ) ) {
472
			$action = sanitize_text_field( $_REQUEST[ $action_name ] );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
473
		}
474
		return $action;
475
	}
476
477
	/**
478
	 * Generate row actions div
479
	 *
480
	 * @since 2.0.18
481
	 * @access protected
482
	 *
483
	 * @param array $actions The list of actions
484
	 * @param bool $always_visible Whether the actions should be always visible
485
	 * @return string
486
	 */
487
	protected function row_actions( $actions, $always_visible = false ) {
488
		$action_count = count( $actions );
489
		$i = 0;
490
491
		if ( ! $action_count ) {
492
			return '';
493
		}
494
495
		$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
496
		foreach ( $actions as $action => $link ) {
497
			++$i;
498
			( $i == $action_count ) ? $sep = '' : $sep = ' | ';
499
			$out .= "<span class='$action'>$link$sep</span>";
500
		}
501
		$out .= '</div>';
502
503
		$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
504
505
		return $out;
506
	}
507
508
	/**
509
	 * Display a view switcher
510
	 *
511
	 * @since 2.0.18
512
	 * @access protected
513
	 *
514
	 * @param string $current_mode
515
	 */
516
	protected function view_switcher( $current_mode ) {
517
?>
518
		<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
519
		<div class="view-switch">
520
<?php
521
			foreach ( $this->modes as $mode => $title ) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 0 tabs, found 3
Loading history...
522
				$classes = array( 'view-' . $mode );
523
				if ( $current_mode == $mode )
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
524
					$classes[] = 'current';
525
				printf(
526
					"<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
527
					esc_url( add_query_arg( 'mode', $mode ) ),
528
					implode( ' ', $classes ),
529
					$title
530
				);
531
			}
532
		?>
533
		</div>
534
<?php
535
	}
536
537
	/**
538
	 * Get the current page number
539
	 *
540
	 * @since 2.0.18
541
	 * @access public
542
	 *
543
	 * @return int
544
	 */
545
	public function get_pagenum() {
546
		$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
547
548
		if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
549
			$pagenum = $this->_pagination_args['total_pages'];
550
		}
551
552
		return max( 1, $pagenum );
553
	}
554
555
	/**
556
	 * Get number of items to display on a single page
557
	 *
558
	 * @since 2.0.18
559
	 * @access protected
560
	 *
561
	 * @param string $option
562
	 * @param int    $default
563
	 * @return int
564
	 */
565
	protected function get_items_per_page( $option, $default = 20 ) {
566
		$per_page = (int) get_user_option( $option );
567
		if ( empty( $per_page ) || $per_page < 1 ) {
568
			$per_page = $default;
569
		}
570
571
		/**
572
		 * Filter the number of items to be displayed on each page of the list table.
573
		 *
574
		 * The dynamic hook name, $option, refers to the `per_page` option depending
575
		 * on the type of list table in use. Possible values include: 'edit_comments_per_page',
576
		 * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
577
		 * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
578
		 * 'edit_{$post_type}_per_page', etc.
579
		 *
580
		 * @since 2.9.0
581
		 *
582
		 * @param int $per_page Number of items to be displayed. Default 20.
583
		 */
584
		return (int) apply_filters( $option, $per_page );
585
	}
586
587
	/**
588
	 * Display the pagination.
589
	 *
590
	 * @since 2.0.18
591
	 * @access protected
592
	 *
593
	 * @param string $which
594
	 */
595
	protected function pagination( $which ) {
596
		if ( empty( $this->_pagination_args ) ) {
597
			return;
598
		}
599
600
		$total_items = $this->_pagination_args['total_items'];
601
		$total_pages = $this->_pagination_args['total_pages'];
602
		$infinite_scroll = false;
603
		if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
604
			$infinite_scroll = $this->_pagination_args['infinite_scroll'];
605
		}
606
607
		$output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
608
609
		$current = $this->get_pagenum();
610
611
		$current_url = set_url_scheme( 'http://' . FrmAppHelper::get_server_value( 'HTTP_HOST' ) . FrmAppHelper::get_server_value( 'REQUEST_URI' ) );
612
613
		$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
614
615
		$page_links = array();
616
617
		$total_pages_before = '<span class="paging-input">';
618
		$total_pages_after  = '</span>';
619
620
		$disable_first = $disable_last = $disable_prev = $disable_next = false;
621
622
 		if ( $current == 1 ) {
623
			$disable_first = true;
624
			$disable_prev = true;
625
 		}
626
		if ( $current == 2 ) {
627
			$disable_first = true;
628
		}
629
 		if ( $current == $total_pages ) {
630
			$disable_last = true;
631
			$disable_next = true;
632
 		}
633
		if ( $current == $total_pages - 1 ) {
634
			$disable_last = true;
635
		}
636
637 View Code Duplication
		if ( $disable_first ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
638
			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
639
		} else {
640
			$page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
641
				esc_url( remove_query_arg( 'paged', $current_url ) ),
642
				__( 'First page' ),
643
				'&laquo;'
644
			);
645
		}
646
647
		if ( $disable_prev ) {
648
			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
649 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
650
			$page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
651
				esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
652
				__( 'Previous page' ),
653
				'&lsaquo;'
654
			);
655
		}
656
657
		if ( 'bottom' == $which ) {
658
			$html_current_page  = $current;
659
			$total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input">';
660
		} else {
661
			$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' />",
662
				'<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
663
				$current,
664
				strlen( $total_pages )
665
			);
666
		}
667
		$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
668
		$page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$total_pages_after'
Loading history...
669
670 View Code Duplication
		if ( $disable_next ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
671
			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
672
		} else {
673
			$page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
674
				esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
675
				__( 'Next page' ),
676
				'&rsaquo;'
677
			);
678
		}
679
680 View Code Duplication
		if ( $disable_last ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
681
			$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
682
		} else {
683
			$page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
684
				esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
685
				__( 'Last page' ),
686
				'&raquo;'
687
			);
688
		}
689
690
		$pagination_links_class = 'pagination-links';
691
		if ( ! empty( $infinite_scroll ) ) {
692
			$pagination_links_class = ' hide-if-js';
693
		}
694
		$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
695
696
		if ( $total_pages ) {
697
			$page_class = $total_pages < 2 ? ' one-page' : '';
698
		} else {
699
			$page_class = ' no-pages';
700
		}
701
		$this->_pagination = "<div class='tablenav-pages" . esc_attr( $page_class ) . "'>$output</div>";
702
703
		echo $this->_pagination;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
704
	}
705
706
	/**
707
	 * Get a list of sortable columns. The format is:
708
	 * 'internal-name' => 'orderby'
709
	 * or
710
	 * 'internal-name' => array( 'orderby', true )
711
	 *
712
	 * The second format will make the initial sorting order be descending
713
	 *
714
	 * @since 2.0.18
715
	 * @access protected
716
	 *
717
	 * @return array
718
	 */
719
	protected function get_sortable_columns() {
720
		return array();
721
	}
722
723
	/**
724
	 * Gets the name of the default primary column.
725
	 *
726
	 * @since 4.3.0
727
	 * @access protected
728
	 *
729
	 * @return string Name of the default primary column, in this case, an empty string.
730
	 */
731
	protected function get_default_primary_column_name() {
732
		$columns = $this->get_columns();
733
		$column = '';
734
735
		// We need a primary defined so responsive views show something,
736
		// so let's fall back to the first non-checkbox column.
737
		foreach ( $columns as $col => $column_name ) {
738
			if ( 'cb' === $col ) {
739
				continue;
740
			}
741
742
			$column = $col;
743
			break;
744
		}
745
746
		return $column;
747
	}
748
749
	/**
750
	 * Gets the name of the primary column.
751
	 *
752
	 * @since 4.3.0
753
	 * @access protected
754
	 *
755
	 * @return string The name of the primary column.
756
	 */
757
	protected function get_primary_column_name() {
758
		$columns = $this->get_columns();
759
		$default = $this->get_default_primary_column_name();
760
761
		// If the primary column doesn't exist fall back to the
762
		// first non-checkbox column.
763
		if ( ! isset( $columns[ $default ] ) ) {
764
			$default = FrmListHelper::get_default_primary_column_name();
765
		}
766
767
		/**
768
		 * Filter the name of the primary column for the current list table.
769
		 *
770
		 * @since 4.3.0
771
		 *
772
		 * @param string $default Column name default for the specific list table, e.g. 'name'.
773
		 * @param string $context Screen ID for specific list table, e.g. 'plugins'.
774
		 */
775
		$column  = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
776
777
		if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
778
			$column = $default;
779
		}
780
781
		return $column;
782
	}
783
784
	/**
785
	 * Get a list of all, hidden and sortable columns, with filter applied
786
	 *
787
	 * @since 2.0.18
788
	 * @access protected
789
	 *
790
	 * @return array
791
	 */
792
	protected function get_column_info() {
793
		// $_column_headers is already set / cached
794
		if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
795
			// Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
796
			// In 4.3, we added a fourth argument for primary column.
797
			$column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
798
			foreach ( $this->_column_headers as $key => $value ) {
799
				$column_headers[ $key ] = $value;
800
			}
801
802
			return $column_headers;
803
		}
804
805
		$columns = get_column_headers( $this->screen );
806
		$hidden = get_hidden_columns( $this->screen );
807
808
		$sortable_columns = $this->get_sortable_columns();
809
		/**
810
		 * Filter the list table sortable columns for a specific screen.
811
		 *
812
		 * The dynamic portion of the hook name, `$this->screen->id`, refers
813
		 * to the ID of the current screen, usually a string.
814
		 *
815
		 * @since 3.5.0
816
		 *
817
		 * @param array $sortable_columns An array of sortable columns.
818
		 */
819
		$_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
820
821
		$sortable = array();
822
		foreach ( $_sortable as $id => $data ) {
823
			if ( empty( $data ) ) {
824
				continue;
825
			}
826
827
			$data = (array) $data;
828
			if ( ! isset( $data[1] ) ) {
829
				$data[1] = false;
830
			}
831
832
			$sortable[ $id ] = $data;
833
		}
834
835
		$primary = $this->get_primary_column_name();
836
		$this->_column_headers = array( $columns, $hidden, $sortable, $primary );
837
838
		return $this->_column_headers;
839
	}
840
841
	/**
842
	 * Return number of visible columns
843
	 *
844
	 * @since 2.0.18
845
	 * @access public
846
	 *
847
	 * @return int
848
	 */
849
	public function get_column_count() {
850
		list ( $columns, $hidden ) = $this->get_column_info();
851
		$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
852
		return count( $columns ) - count( $hidden );
853
	}
854
855
	/**
856
	 * Print column headers, accounting for hidden and sortable columns.
857
	 *
858
	 * @since 2.0.18
859
	 * @access public
860
	 *
861
	 * @staticvar int $cb_counter
862
	 *
863
	 * @param bool $with_id Whether to set the id attribute or not
864
	 */
865
	public function print_column_headers( $with_id = true ) {
866
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
867
868
		$current_url = set_url_scheme( 'http://' . FrmAppHelper::get_server_value( 'HTTP_HOST' ) . FrmAppHelper::get_server_value( 'REQUEST_URI' ) );
869
		$current_url = remove_query_arg( 'paged', $current_url );
870
871
		if ( isset( $_GET['orderby'] ) ) {
872
			$current_orderby = sanitize_text_field( $_GET['orderby'] );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
873
		} else {
874
			$current_orderby = '';
875
		}
876
877
		if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) {
878
			$current_order = 'desc';
879
		} else {
880
			$current_order = 'asc';
881
		}
882
883
		if ( ! empty( $columns['cb'] ) ) {
884
			static $cb_counter = 1;
885
			$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
886
				. '<input id="cb-select-all-' . esc_attr( $cb_counter ) . '" type="checkbox" />';
887
			$cb_counter++;
888
		}
889
890
		foreach ( $columns as $column_key => $column_display_name ) {
891
			$class = array( 'manage-column', "column-$column_key" );
892
893
			if ( in_array( $column_key, $hidden ) ) {
894
				$class[] = 'hidden';
895
			}
896
897
			if ( 'cb' == $column_key ) {
898
				$class[] = 'check-column';
899
			} else if ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) {
900
				$class[] = 'num';
901
			}
902
903
			if ( $column_key === $primary ) {
904
				$class[] = 'column-primary';
905
			}
906
907
			if ( isset( $sortable[ $column_key ] ) ) {
908
				list( $orderby, $desc_first ) = $sortable[ $column_key ];
909
910
				if ( $current_orderby == $orderby ) {
911
					$order = 'asc' == $current_order ? 'desc' : 'asc';
912
					$class[] = 'sorted';
913
					$class[] = $current_order;
914
				} else {
915
					$order = $desc_first ? 'desc' : 'asc';
916
					$class[] = 'sortable';
917
					$class[] = $desc_first ? 'asc' : 'desc';
918
				}
919
920
				$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>';
921
			}
922
923
			$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
924
			$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
925
			$id = $with_id ? "id='" . esc_attr( $column_key ) . "'" : '';
926
927
			if ( ! empty( $class ) ) {
928
				$class = "class='" . join( ' ', $class ) . "'";
929
			}
930
931
			echo "<$tag $scope $id $class>$column_display_name</$tag>";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"<$tag $scope $id $class>$column_display_name</$tag>"'
Loading history...
932
		}
933
	}
934
935
	/**
936
	 * Display the table
937
	 *
938
	 * @since 2.0.18
939
	 * @access public
940
	 */
941
	public function display() {
942
		$singular = $this->_args['singular'];
943
944
		$this->display_tablenav( 'top' );
945
?>
946
<table class="wp-list-table <?php echo esc_attr( implode( ' ', $this->get_table_classes() ) ); ?>">
947
	<thead>
948
	<tr>
949
		<?php $this->print_column_headers(); ?>
950
	</tr>
951
	</thead>
952
953
	<tbody id="the-list"<?php
954
		if ( $singular ) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 1 tabs, found 2
Loading history...
955
			echo " data-wp-lists='list:" . esc_attr( $singular ) . "'";
956
		} ?>>
957
		<?php $this->display_rows_or_placeholder(); ?>
958
	</tbody>
959
960
	<tfoot>
961
	<tr>
962
		<?php $this->print_column_headers( false ); ?>
963
	</tr>
964
	</tfoot>
965
966
</table>
967
<?php
968
		$this->display_tablenav( 'bottom' );
969
	}
970
971
	/**
972
	 * Get a list of CSS classes for the list table table tag.
973
	 *
974
	 * @since 2.0.18
975
	 * @access protected
976
	 *
977
	 * @return array List of CSS classes for the table tag.
978
	 */
979
	protected function get_table_classes() {
980
		return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
981
	}
982
983
	/**
984
	 * Generate the table navigation above or below the table
985
	 *
986
	 * @since 2.0.18
987
	 * @access protected
988
	 * @param string $which
989
	 */
990
	protected function display_tablenav( $which ) {
991
		if ( 'top' == $which ) {
992
			wp_nonce_field( 'bulk-' . $this->_args['plural'] );
993
		}
994
?>
995
	<div class="tablenav <?php echo esc_attr( $which ); ?>">
996
997
		<div class="alignleft actions bulkactions">
998
			<?php $this->bulk_actions( $which ); ?>
999
		</div>
1000
<?php
1001
		$this->extra_tablenav( $which );
1002
		$this->pagination( $which );
1003
?>
1004
1005
		<br class="clear" />
1006
	</div>
1007
<?php
1008
	}
1009
1010
	/**
1011
	 * Extra controls to be displayed between bulk actions and pagination
1012
	 *
1013
	 * @since 2.0.18
1014
	 * @access protected
1015
	 *
1016
	 * @param string $which
1017
	 */
1018
	protected function extra_tablenav( $which ) {}
1019
1020
	/**
1021
	 * Generate the tbody element for the list table.
1022
	 *
1023
	 * @since 2.0.18
1024
	 * @access public
1025
	 */
1026
	public function display_rows_or_placeholder() {
1027
		if ( $this->has_items() ) {
1028
			$this->display_rows();
1029
		} else {
1030
			echo '<tr class="no-items"><td class="colspanchange" colspan="' . esc_attr( $this->get_column_count() ) . '">';
1031
			$this->no_items();
1032
			echo '</td></tr>';
1033
		}
1034
	}
1035
1036
	/**
1037
	 * Generates content for a single row of the table
1038
	 *
1039
	 * @since 2.0.18
1040
	 * @access public
1041
	 *
1042
	 * @param object $item The current item
1043
	 */
1044
	public function single_row( $item ) {
1045
		echo '<tr>';
1046
		$this->single_row_columns( $item );
1047
		echo '</tr>';
1048
	}
1049
1050
	/**
1051
	 * Generates the columns for a single row of the table
1052
	 *
1053
	 * @since 2.0.18
1054
	 * @access protected
1055
	 *
1056
	 * @param object $item The current item
1057
	 */
1058
	protected function single_row_columns( $item ) {
1059
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
0 ignored issues
show
Unused Code introduced by
The assignment to $sortable is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1060
1061
		foreach ( $columns as $column_name => $column_display_name ) {
1062
			$classes = "$column_name column-$column_name";
1063
			if ( $primary === $column_name ) {
1064
				$classes .= ' has-row-actions column-primary';
1065
			}
1066
1067
			if ( in_array( $column_name, $hidden ) ) {
1068
				$classes .= ' hidden';
1069
			}
1070
1071
			// Comments column uses HTML in the display name with screen reader text.
1072
			// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
1073
			$data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
1074
1075
			$attributes = "class='$classes' $data";
1076
1077
			if ( 'cb' == $column_name ) {
1078
				echo '<th scope="row" class="check-column"></th>';
1079
			} elseif ( method_exists( $this, '_column_' . $column_name ) ) {
1080
				echo call_user_func(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'call_user_func'
Loading history...
1081
					array( $this, '_column_' . $column_name ),
1082
					$item,
1083
					$classes,
1084
					$data,
1085
					$primary
1086
				);
1087
			} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
1088
				echo "<td $attributes>";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"<td $attributes>"'
Loading history...
1089
				echo call_user_func( array( $this, 'column_' . $column_name ), $item );
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'call_user_func'
Loading history...
1090
				echo $this->handle_row_actions( $item, $column_name, $primary );
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
1091
				echo '</td>';
1092
			} else {
1093
				echo "<td $attributes>";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"<td $attributes>"'
Loading history...
1094
				echo $this->handle_row_actions( $item, $column_name, $primary );
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
1095
				echo '</td>';
1096
			}
1097
		}
1098
	}
1099
1100
	/**
1101
	 * Generates and display row actions links for the list table.
1102
	 *
1103
	 * @since 4.3.0
1104
	 * @access protected
1105
	 *
1106
	 * @param object $item        The item being acted upon.
1107
	 * @param string $column_name Current column name.
1108
	 * @param string $primary     Primary column name.
1109
	 * @return string The row actions output. In this case, an empty string.
1110
	 */
1111
	protected function handle_row_actions( $item, $column_name, $primary ) {
0 ignored issues
show
Unused Code introduced by
The parameter $item is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1112
		return $column_name == $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1113
 	}
1114
1115
	/**
1116
	 * Handle an incoming ajax request (called from admin-ajax.php)
1117
	 *
1118
	 * @since 2.0.18
1119
	 * @access public
1120
	 */
1121
	public function ajax_response() {
1122
		$this->prepare_items();
1123
1124
		ob_start();
1125
		if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
1126
			$this->display_rows();
1127
		} else {
1128
			$this->display_rows_or_placeholder();
1129
		}
1130
1131
		$rows = ob_get_clean();
1132
1133
		$response = array( 'rows' => $rows );
1134
1135
		if ( isset( $this->_pagination_args['total_items'] ) ) {
1136
			$response['total_items_i18n'] = sprintf(
1137
				_n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1138
				number_format_i18n( $this->_pagination_args['total_items'] )
1139
			);
1140
		}
1141
		if ( isset( $this->_pagination_args['total_pages'] ) ) {
1142
			$response['total_pages'] = $this->_pagination_args['total_pages'];
1143
			$response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1144
		}
1145
1146
		die( wp_json_encode( $response ) );
1147
	}
1148
1149
	/**
1150
	 * Send required variables to JavaScript land
1151
	 *
1152
	 * @access public
1153
	 */
1154
	public function _js_vars() {
1155
		$args = array(
1156
			'class'  => get_class( $this ),
1157
			'screen' => array(
1158
				'id'   => $this->screen->id,
1159
				'base' => $this->screen->base,
1160
			)
1161
		);
1162
1163
		printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
1164
	}
1165
}
1166