Completed
Push — master ( b50b70...923c4d )
by Stephanie
04:24 queued 51s
created

FrmListHelper::bulk_actions()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 41
Code Lines 19

Duplication

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