: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @staticvar int $cb_counter
* @param bool $with_id Whether to set the id attribute or not
public function print_column_headers( $with_id = true ) {
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$sbpb_server_host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field( $_SERVER['HTTP_HOST'] ) : '';
$sbpb_server_requesturi = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field( $_SERVER['REQUEST_URI'] ) : '';
$current_url = set_url_scheme( 'http://' . $sbpb_server_host . $sbpb_server_requesturi );
$current_url = remove_query_arg( 'paged', $current_url );
if ( isset( $_GET['orderby'] ) )
$current_orderby = sanitize_text_field($_GET['orderby']);
if ( isset( $_GET['order'] ) && 'desc' == sanitize_text_field($_GET['order']) )
if ( ! empty( $columns['cb'] ) ) {
$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
foreach ( $columns as $column_key => $column_display_name ) {
$class = array( 'manage-column', "column-$column_key", 'table__head' );
if ( in_array( $column_key, $hidden ) ) {
if ( 'cb' == $column_key )
$class[] = 'check-column';
elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
if ( $column_key === $primary ) {
$class[] = 'column-primary';
if ( isset( $sortable[$column_key] ) ) {
list( $orderby, $desc_first ) = $sortable[$column_key];
if ( $current_orderby == $orderby ) {
$order = 'asc' == $current_order ? 'desc' : 'asc';
$class[] = $current_order;
$order = $desc_first ? 'desc' : 'asc';
$class[] = $desc_first ? 'asc' : 'desc';
$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . esc_html($column_display_name) . '</span><span class="sorting-indicator"></span></a>';
$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
$id = $with_id ? "id='$column_key'" : '';
$class = "class='" . join( ' ', $class ) . "'";
echo wp_kses("<$tag $scope $id $class>$column_display_name</$tag>", AdminHelper::allowed_html_tags());
public function display() {
$singular = $this->_args['singular'];
$this->display_tablenav( 'top' );
<table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>">
<?php $this->print_column_headers(); ?>
<tbody id="the-list"<?php
echo esc_attr(" data-wp-lists='list:$singular'");
<?php $this->display_rows_or_placeholder(); ?>
<?php $this->print_column_headers( false ); ?>
$this->display_tablenav( 'bottom' );
* Get a list of CSS classes for the list table table tag.
* @return array List of CSS classes for the table tag.
protected function get_table_classes() {
return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
* Generate the table navigation above or below the table
protected function display_tablenav( $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
$this->extra_tablenav( $which );
$this->pagination( $which );
* Extra controls to be displayed between bulk actions and pagination
protected function extra_tablenav( $which ) {}
* Generate the tbody element for the list table.
public function display_rows_or_placeholder() {
if ( $this->has_items() ) {
echo '<tr class="no-items"><td class="colspanchange" colspan="' . esc_attr($this->get_column_count()) . '">';
* Generate the table rows
public function display_rows() {
foreach ( $this->items as $item )
$this->single_row( $item );
* Generates content for a single row of the table
* @param object $item The current item
public function single_row( $item ) {
$this->single_row_columns( $item );
* @param string $column_name
protected function column_default( $item, $column_name ) {}
protected function column_cb( $item ) {}
* Generates the columns for a single row of the table
* @param object $item The current item
protected function single_row_columns( $item ) {
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
foreach ( $columns as $column_name => $column_display_name ) {
$classes = "$column_name column-$column_name";
if ( $primary === $column_name ) {
$classes .= ' has-row-actions column-primary';
if ( in_array( $column_name, $hidden ) ) {
// Comments column uses HTML in the display name with screen reader text.
// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
$data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
$attributes = "class='$classes' $data";
if ( 'cb' == $column_name ) {
echo '<th scope="row" class="check-column">';
echo wp_kses($this->column_cb( $item ), AdminHelper::allowed_html_tags());
} elseif ( method_exists( $this, '_column_' . $column_name ) ) {
echo wp_kses(call_user_func(
array( $this, '_column_' . $column_name ),
), AdminHelper::allowed_html_tags());
} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
//<div class="table__data">01</div>
echo "<td ".esc_attr($attributes).">";
echo wp_kses(call_user_func( array( $this, 'column_' . $column_name ), $item ), AdminHelper::allowed_html_tags());
echo wp_kses($this->handle_row_actions( $item, $column_name, $primary ), AdminHelper::allowed_html_tags());
echo "<td ".esc_attr($attributes).">";
echo wp_kses($this->column_default( $item, $column_name ), AdminHelper::allowed_html_tags());
echo wp_kses($this->handle_row_actions( $item, $column_name, $primary ), AdminHelper::allowed_html_tags());
* Generates and display row actions links for the list table.
* @param object $item The item being acted upon.
* @param string $column_name Current column name.
* @param string $primary Primary column name.
* @return string The row actions output. In this case, an empty string.
protected function handle_row_actions( $item, $column_name, $primary ) {
return $column_name == $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
* Handle an incoming ajax request (called from admin-ajax.php)
public function ajax_response() {
if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
$this->display_rows_or_placeholder();
$response = array( 'rows' => $rows );
if ( isset( $this->_pagination_args['total_items'] ) ) {
$response['total_items_i18n'] = sprintf(
/* translators: total item, total items */
_n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
number_format_i18n( $this->_pagination_args['total_items'] )
if ( isset( $this->_pagination_args['total_pages'] ) ) {
$response['total_pages'] = $this->_pagination_args['total_pages'];
$response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
die( wp_json_encode( $response ) );
* Send required variables to JavaScript land
public function _js_vars() {
'class' => get_class( $this ),
'id' => $this->screen->id,
'base' => $this->screen->base,
wp_register_script( 'sgpb-listtable-js-footer', '', array("jquery"), SGPB_POPUP_VERSION, true );
wp_enqueue_script( 'sgpb-listtable-js-footer' );
wp_add_inline_script( 'sgpb-listtable-js-footer', printf( "list_args = %s;", wp_json_encode( $args ) ));