HEX
Server: Apache/2
System: Linux vps32496.sdns.vn 3.10.0-1160.99.1.el7.x86_64 #1 SMP Wed Sep 13 14:19:20 UTC 2023 x86_64
User: khuondaotc (1075)
PHP: 7.4.33
Disabled: exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: /home/khuondaotc/public_html/wp-content/plugins/unbounce/UBWPListTable.php
<?php
/**
 * Base class for displaying a list of items in an ajaxified HTML table.
 *
 * @since 3.1.0
 * @access private
 *
 * @package WordPress
 * @subpackage List_Table
 */
class UBWPListTable
{

    /**
     * The current list of items
     *
     * @since 3.1.0
     * @var array
     * @access public
     */
    public $items;

    /**
     * Various information about the current table
     *
     * @since 3.1.0
     * @var array
     * @access protected
     */
    protected $_args;

    /**
     * Various information needed for displaying the pagination
     *
     * @since 3.1.0
     * @var array
     */
    protected $_pagination_args = array();

    /**
     * The current screen
     *
     * @since 3.1.0
     * @var object
     * @access protected
     */
    protected $screen;

    /**
     * Cached bulk actions
     *
     * @since 3.1.0
     * @var array
     * @access private
     */
    private $_actions;

    /**
     * Cached pagination output
     *
     * @since 3.1.0
     * @var string
     * @access private
     */
    private $_pagination;

    /**
     * The view switcher modes.
     *
     * @since 4.1.0
     * @var array
     * @access protected
     */
    protected $modes = array();

    /**
     * Stores the value returned by ->get_column_info()
     *
     * @var array
     */
    protected $_column_headers;

    protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );

    protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
        'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
        'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
        'single_row_columns' );

    /**
     * Constructor.
     *
     * The child class should call this constructor from its own constructor to override
     * the default $args.
     *
     * @since 3.1.0
     * @access public
     *
     * @param array|string $args {
     *     Array or string of arguments.
     *
     *     @type string $plural   Plural value used for labels and the objects being listed.
     *                            This affects things such as CSS class-names and nonces used
     *                            in the list table, e.g. 'posts'. Default empty.
     *     @type string $singular Singular label for an object being listed, e.g. 'post'.
     *                            Default empty
     *     @type bool   $ajax     Whether the list table supports AJAX. This includes loading
     *                            and sorting data, for example. If true, the class will call
     *                            the {@see _js_vars()} method in the footer to provide variables
     *                            to any scripts handling AJAX events. Default false.
     *     @type string $screen   String containing the hook name used to determine the current
     *                            screen. If left null, the current screen will be automatically set.
     *                            Default null.
     * }
     */
    public function __construct($args = array())
    {
        $args = wp_parse_args($args, array(
            'plural' => '',
            'singular' => '',
            'ajax' => false,
            'screen' => null,
        ));

        $this->screen = convert_to_screen($args['screen']);

        add_filter("manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0);

        if (!$args['plural']) {
            $args['plural'] = $this->screen->base;
        }

        $args['plural'] = sanitize_key($args['plural']);
        $args['singular'] = sanitize_key($args['singular']);

        $this->_args = $args;

        if ($args['ajax']) {
            // wp_enqueue_script( 'list-table' );
            add_action('admin_footer', array( $this, '_js_vars' ));
        }

        if (empty($this->modes)) {
            $this->modes = array(
                'list'    => __('List View'),
                'excerpt' => __('Excerpt View')
            );
        }
    }

    /**
     * Make private properties readable for backwards compatibility.
     *
     * @since 4.0.0
     * @access public
     *
     * @param string $name Property to get.
     * @return mixed Property.
     */
    public function __get($name)
    {
        if (in_array($name, $this->compat_fields)) {
            return $this->$name;
        }
    }

    /**
     * Make private properties settable for backwards compatibility.
     *
     * @since 4.0.0
     * @access public
     *
     * @param string $name  Property to check if set.
     * @param mixed  $value Property value.
     * @return mixed Newly-set property.
     */
    public function __set($name, $value)
    {
        if (in_array($name, $this->compat_fields)) {
            return $this->$name = $value;
        }
    }

    /**
     * Make private properties checkable for backwards compatibility.
     *
     * @since 4.0.0
     * @access public
     *
     * @param string $name Property to check if set.
     * @return bool Whether the property is set.
     */
    public function __isset($name)
    {
        if (in_array($name, $this->compat_fields)) {
            return isset($this->$name);
        }
    }

    /**
     * Make private properties un-settable for backwards compatibility.
     *
     * @since 4.0.0
     * @access public
     *
     * @param string $name Property to unset.
     */
    public function __unset($name)
    {
        if (in_array($name, $this->compat_fields)) {
            unset($this->$name);
        }
    }

    /**
     * Make private/protected methods readable for backwards compatibility.
     *
     * @since 4.0.0
     * @access public
     *
     * @param callable $name      Method to call.
     * @param array    $arguments Arguments to pass when calling.
     * @return mixed|bool Return value of the callback, false otherwise.
     */
    public function __call($name, $arguments)
    {
        if (in_array($name, $this->compat_methods)) {
            return call_user_func_array(array( $this, $name ), $arguments);
        }
        return false;
    }

    /**
     * Checks the current user's permissions
     *
     * @since 3.1.0
     * @access public
     * @abstract
     */
    public function ajax_user_can()
    {
        die('function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.');
    }

    /**
     * Prepares the list of items for displaying.
     * @uses WP_List_Table::set_pagination_args()
     *
     * @since 3.1.0
     * @access public
     * @abstract
     */
    public function prepare_items()
    {
        die('function WP_List_Table::prepare_items() must be over-ridden in a sub-class.');
    }

    /**
     * An internal method that sets all the necessary pagination arguments
     *
     * @param array $args An associative array with information about the pagination
     * @access protected
     */
    protected function set_pagination_args($args)
    {
        $args = wp_parse_args($args, array(
            'total_items' => 0,
            'total_pages' => 0,
            'per_page' => 0,
        ));

        if (!$args['total_pages'] && $args['per_page'] > 0) {
            $args['total_pages'] = ceil($args['total_items'] / $args['per_page']);
        }

        $doingAjax = defined('DOING_AJAX') && DOING_AJAX;

        // Redirect if page number is invalid and headers are not already sent.
        if (!headers_sent() && !$doingAjax && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages']) {
            wp_redirect(add_query_arg('paged', $args['total_pages']));
            exit;
        }

        $this->_pagination_args = $args;
    }

    /**
     * Access the pagination args.
     *
     * @since 3.1.0
     * @access public
     *
     * @param string $key Pagination argument to retrieve. Common values include 'total_items',
     *                    'total_pages', 'per_page', or 'infinite_scroll'.
     * @return int Number of items that correspond to the given pagination argument.
     */
    public function get_pagination_arg($key)
    {
        if ('page' == $key) {
            return $this->get_pagenum();
        }

        if (isset($this->_pagination_args[$key])) {
            return $this->_pagination_args[$key];
        }
    }

    /**
     * Whether the table has items to display or not
     *
     * @since 3.1.0
     * @access public
     *
     * @return bool
     */
    public function has_items()
    {
        return !empty($this->items);
    }

    /**
     * Message to be displayed when there are no items
     *
     * @since 3.1.0
     * @access public
     */
    public function no_items()
    {
        _e('No items found.');
    }

    /**
     * Display the search box.
     *
     * @since 3.1.0
     * @access public
     *
     * @param string $text The search button text
     * @param string $input_id The search input id
     */
    public function search_box($text, $input_id)
    {
        if (empty($_REQUEST['s']) && !$this->has_items()) {
            return;
        }

        $input_id = $input_id . '-search-input';

        if (! empty($_REQUEST['orderby'])) {
            echo '<input type="hidden" name="orderby" value="' . esc_attr($_REQUEST['orderby']) . '" />';
        }
        if (! empty($_REQUEST['order'])) {
            echo '<input type="hidden" name="order" value="' . esc_attr($_REQUEST['order']) . '" />';
        }
        if (! empty($_REQUEST['post_mime_type'])) {
            echo '<input type="hidden" name="post_mime_type" value="' . esc_attr($_REQUEST['post_mime_type']) . '" />';
        }
        if (! empty($_REQUEST['detached'])) {
            echo '<input type="hidden" name="detached" value="' . esc_attr($_REQUEST['detached']) . '" />';
        }
        ?>
<p class="search-box">
    <label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
    <input type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
        <?php submit_button($text, 'button', '', false, array('id' => 'search-submit')); ?>
</p>
        <?php
    }

    /**
     * Get an associative array ( id => link ) with the list
     * of views available on this table.
     *
     * @since 3.1.0
     * @access protected
     *
     * @return array
     */
    protected function get_views()
    {
        return array();
    }

    /**
     * Display the list of views available on this table.
     *
     * @since 3.1.0
     * @access public
     */
    public function views()
    {
        $views = $this->get_views();
        /**
         * Filter the list of available list table views.
         *
         * The dynamic portion of the hook name, `$this->screen->id`, refers
         * to the ID of the current screen, usually a string.
         *
         * @since 3.5.0
         *
         * @param array $views An array of available list table views.
         */
        $views = apply_filters("views_{$this->screen->id}", $views);

        if (empty($views)) {
            return;
        }

        echo "<ul class='subsubsub'>\n";
        foreach ($views as $class => $view) {
            $views[ $class ] = "\t<li class='$class'>$view";
        }
        echo implode(" |</li>\n", $views) . "</li>\n";
        echo "</ul>";
    }

    /**
     * Get an associative array ( option_name => option_title ) with the list
     * of bulk actions available on this table.
     *
     * @since 3.1.0
     * @access protected
     *
     * @return array
     */
    protected function get_bulk_actions()
    {
        return array();
    }

    /**
     * Display the bulk actions dropdown.
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $which The location of the bulk actions: 'top' or 'bottom'.
     *                      This is designated as optional for backwards-compatibility.
     */
    protected function bulk_actions($which = '')
    {
        if (is_null($this->_actions)) {
            $no_new_actions = $this->_actions = $this->get_bulk_actions();
            /**
             * Filter the list table Bulk Actions drop-down.
             *
             * The dynamic portion of the hook name, `$this->screen->id`, refers
             * to the ID of the current screen, usually a string.
             *
             * This filter can currently only be used to remove bulk actions.
             *
             * @since 3.5.0
             *
             * @param array $actions An array of the available bulk actions.
             */
            $this->_actions = apply_filters("bulk_actions-{$this->screen->id}", $this->_actions);
            $this->_actions = array_intersect_assoc($this->_actions, $no_new_actions);
            $two = '';
        } else {
            $two = '2';
        }

        if (empty($this->_actions)) {
            return;
        }

        echo "<label for='bulk-action-selector-" . esc_attr($which) . "' class='screen-reader-text'>" .
             __('Select bulk action') . "</label>";
        echo "<select name='action$two' id='bulk-action-selector-" . esc_attr($which) . "'>\n";
        echo "<option value='-1' selected='selected'>" . __('Bulk Actions') . "</option>\n";

        foreach ($this->_actions as $name => $title) {
            $class = 'edit' == $name ? ' class="hide-if-no-js"' : '';

            echo "\t<option value='$name'$class>$title</option>\n";
        }

        echo "</select>\n";

        submit_button(__('Apply'), 'action', '', false, array( 'id' => "doaction$two" ));
        echo "\n";
    }

    /**
     * Get the current action selected from the bulk actions dropdown.
     *
     * @since 3.1.0
     * @access public
     *
     * @return string|bool The action name or False if no action was selected
     */
    public function current_action()
    {
        if (isset($_REQUEST['filter_action']) && ! empty($_REQUEST['filter_action'])) {
            return false;
        }

        if (isset($_REQUEST['action']) && -1 != $_REQUEST['action']) {
            return $_REQUEST['action'];
        }

        if (isset($_REQUEST['action2']) && -1 != $_REQUEST['action2']) {
            return $_REQUEST['action2'];
        }

        return false;
    }

    /**
     * Generate row actions div
     *
     * @since 3.1.0
     * @access protected
     *
     * @param array $actions The list of actions
     * @param bool $always_visible Whether the actions should be always visible
     * @return string
     */
    protected function row_actions($actions, $always_visible = false)
    {
        $action_count = count($actions);
        $i = 0;

        if (!$action_count) {
            return '';
        }

        $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
        foreach ($actions as $action => $link) {
            ++$i;
            ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
            $out .= "<span class='$action'>$link$sep</span>";
        }
        $out .= '</div>';

        return $out;
    }

    /**
     * Display a monthly dropdown for filtering items
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $post_type
     */
    protected function months_dropdown($post_type)
    {
        global $wpdb, $wp_locale;

        /**
         * Filter whether to remove the 'Months' drop-down from the post list table.
         *
         * @since 4.2.0
         *
         * @param bool   $disable   Whether to disable the drop-down. Default false.
         * @param string $post_type The post type.
         */
        if (apply_filters('disable_months_dropdown', false, $post_type)) {
            return;
        }

        $months = $wpdb->get_results($wpdb->prepare("
			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
			FROM $wpdb->posts
			WHERE post_type = %s
			ORDER BY post_date DESC
		", $post_type));

        /**
         * Filter the 'Months' drop-down results.
         *
         * @since 3.7.0
         *
         * @param object $months    The months drop-down query results.
         * @param string $post_type The post type.
         */
        $months = apply_filters('months_dropdown_results', $months, $post_type);

        $month_count = count($months);

        if (!$month_count || ( 1 == $month_count && 0 == $months[0]->month )) {
            return;
        }

        $m = isset($_GET['m']) ? (int) $_GET['m'] : 0;
        ?>
        <label for="filter-by-date" class="screen-reader-text"><?php _e('Filter by date'); ?></label>
        <select name="m" id="filter-by-date">
            <option<?php selected($m, 0); ?> value="0"><?php _e('All dates'); ?></option>
        <?php
        foreach ($months as $arc_row) {
            if (0 == $arc_row->year) {
                continue;
            }

            $month = zeroise($arc_row->month, 2);
            $year = $arc_row->year;

            printf(
                "<option %s value='%s'>%s</option>\n",
                selected($m, $year . $month, false),
                esc_attr($arc_row->year . $month),
                /* translators: 1: month name, 2: 4-digit year */
                sprintf(__('%1$s %2$d'), $wp_locale->get_month($month), $year)
            );
        }
        ?>
        </select>
        <?php
    }

    /**
     * Display a view switcher
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $current_mode
     */
    protected function view_switcher($current_mode)
    {
        ?>
        <input type="hidden" name="mode" value="<?php echo esc_attr($current_mode); ?>" />
        <div class="view-switch">
        <?php
        foreach ($this->modes as $mode => $title) {
            $classes = array( 'view-' . $mode );
            if ($current_mode == $mode) {
                $classes[] = 'current';
            }
            printf(
                "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
                esc_url(add_query_arg('mode', $mode)),
                implode(' ', $classes),
                $title
            );
        }
        ?>
        </div>
        <?php
    }

    /**
     * Display a comment count bubble
     *
     * @since 3.1.0
     * @access protected
     *
     * @param int $post_id          The post ID.
     * @param int $pending_comments Number of pending comments.
     */
    protected function comments_bubble($post_id, $pending_comments)
    {
        $pending_phrase = sprintf(__('%s pending'), number_format($pending_comments));

        if ($pending_comments) {
            echo '<strong>';
        }

        printf(
            '<a href="%s" title="%s" class="post-com-count"><span class="comment-count">%s</span></a>',
            esc_url(add_query_arg('p', $post_id, admin_url('edit-comments.php'))),
            esc_attr($pending_phrase),
            number_format_i18n(get_comments_number())
        );

        if ($pending_comments) {
            echo '</strong>';
        }
    }

    /**
     * Get the current page number
     *
     * @since 3.1.0
     * @access public
     *
     * @return int
     */
    public function get_pagenum()
    {
        $pagenum = isset($_REQUEST['paged']) ? absint($_REQUEST['paged']) : 0;

        if (isset($this->_pagination_args['total_pages']) && $pagenum > $this->_pagination_args['total_pages']) {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max(1, $pagenum);
    }

    /**
     * Get number of items to display on a single page
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $option
     * @param int    $default
     * @return int
     */
    protected function get_items_per_page($option, $default = 20)
    {
        $per_page = (int) get_user_option($option);
        if (empty($per_page) || $per_page < 1) {
            $per_page = $default;
        }

        /**
         * Filter the number of items to be displayed on each page of the list table.
         *
         * The dynamic hook name, $option, refers to the `per_page` option depending
         * on the type of list table in use. Possible values include: 'edit_comments_per_page',
         * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
         * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
         * 'edit_{$post_type}_per_page', etc.
         *
         * @since 2.9.0
         *
         * @param int $per_page Number of items to be displayed. Default 20.
         */
        return (int) apply_filters($option, $per_page);
    }

    /**
     * Display the pagination.
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $which
     */
    protected function pagination($which)
    {
        if (empty($this->_pagination_args)) {
            return;
        }

        $total_items = $this->_pagination_args['total_items'];
        $total_pages = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if (isset($this->_pagination_args['infinite_scroll'])) {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        $output = '<span class="displaying-num">'
                  . sprintf(_n('1 item', '%s items', $total_items), number_format_i18n($total_items))
                  . '</span>';

        $current = $this->get_pagenum();

        $current_url = set_url_scheme('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);

        $current_url = remove_query_arg(array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url);

        $page_links = array();

        $disable_first = $disable_last = '';
        if ($current == 1) {
            $disable_first = ' disabled';
        }
        if ($current == $total_pages) {
            $disable_last = ' disabled';
        }
        $page_links[] = sprintf(
            "<a class='%s' title='%s' href='%s'>%s</a>",
            'first-page' . $disable_first,
            esc_attr__('Go to the first page'),
            esc_url(remove_query_arg('paged', $current_url)),
            '&laquo;'
        );

        $page_links[] = sprintf(
            "<a class='%s' title='%s' href='%s'>%s</a>",
            'prev-page' . $disable_first,
            esc_attr__('Go to the previous page'),
            esc_url(add_query_arg('paged', max(1, $current-1), $current_url)),
            '&lsaquo;'
        );

        if ('bottom' == $which) {
            $html_current_page = $current;
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' id='current-page-selector' title='%s'
                          type='text'name='paged' value='%s' size='%d' />",
                '<label for="current-page-selector" class="screen-reader-text">' . __('Select Page') . '</label>',
                esc_attr__('Current page'),
                $current,
                strlen($total_pages)
            );
        }
        $html_total_pages = sprintf("<span class='total-pages'>%s</span>", number_format_i18n($total_pages));
        $page_links[] = '<span class="paging-input">'
                        . sprintf(_x('%1$s of %2$s', 'paging'), $html_current_page, $html_total_pages)
                        . '</span>';

        $page_links[] = sprintf(
            "<a class='%s' title='%s' href='%s'>%s</a>",
            'next-page' . $disable_last,
            esc_attr__('Go to the next page'),
            esc_url(add_query_arg('paged', min($total_pages, $current+1), $current_url)),
            '&rsaquo;'
        );

        $page_links[] = sprintf(
            "<a class='%s' title='%s' href='%s'>%s</a>",
            'last-page' . $disable_last,
            esc_attr__('Go to the last page'),
            esc_url(add_query_arg('paged', $total_pages, $current_url)),
            '&raquo;'
        );

        $pagination_links_class = 'pagination-links';
        if (! empty($infinite_scroll)) {
            $pagination_links_class = ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join("\n", $page_links) . '</span>';

        if ($total_pages) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    /**
     * Get a list of columns. The format is:
     * 'internal-name' => 'Title'
     *
     * @since 3.1.0
     * @access public
     * @abstract
     *
     * @return array
     */
    public function get_columns()
    {
        die('function WP_List_Table::get_columns() must be over-ridden in a sub-class.');
    }

    /**
     * Get a list of sortable columns. The format is:
     * 'internal-name' => 'orderby'
     * or
     * 'internal-name' => array( 'orderby', true )
     *
     * The second format will make the initial sorting order be descending
     *
     * @since 3.1.0
     * @access protected
     *
     * @return array
     */
    protected function get_sortable_columns()
    {
        return array();
    }

    /**
     * Get a list of all, hidden and sortable columns, with filter applied
     *
     * @since 3.1.0
     * @access protected
     *
     * @return array
     */
    protected function get_column_info()
    {
        if (isset($this->_column_headers)) {
            return $this->_column_headers;
        }

        $columns = get_column_headers($this->screen);
        $hidden = get_hidden_columns($this->screen);

        $sortable_columns = $this->get_sortable_columns();
        /**
         * Filter the list table sortable columns for a specific screen.
         *
         * The dynamic portion of the hook name, `$this->screen->id`, refers
         * to the ID of the current screen, usually a string.
         *
         * @since 3.5.0
         *
         * @param array $sortable_columns An array of sortable columns.
         */
        $_sortable = apply_filters("manage_{$this->screen->id}_sortable_columns", $sortable_columns);

        $sortable = array();
        foreach ($_sortable as $id => $data) {
            if (empty($data)) {
                continue;
            }

            $data = (array) $data;
            if (!isset($data[1])) {
                $data[1] = false;
            }

            $sortable[$id] = $data;
        }

        $this->_column_headers = array( $columns, $hidden, $sortable );

        return $this->_column_headers;
    }

    /**
     * Return number of visible columns
     *
     * @since 3.1.0
     * @access public
     *
     * @return int
     */
    public function get_column_count()
    {
        list ( $columns, $hidden ) = $this->get_column_info();
        $hidden = array_intersect(array_keys($columns), array_filter($hidden));
        return count($columns) - count($hidden);
    }

    /**
     * Print column headers, accounting for hidden and sortable columns.
     *
     * @since 3.1.0
     * @access public
     *
     * @param bool $with_id Whether to set the id attribute or not
     */
    public function print_column_headers($with_id = true)
    {
        list( $columns, $hidden, $sortable ) = $this->get_column_info();

        $current_url = set_url_scheme('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        $current_url = remove_query_arg('paged', $current_url);

        if (isset($_GET['orderby'])) {
            $current_orderby = $_GET['orderby'];
        } else {
            $current_orderby = '';
        }

        if (isset($_GET['order']) && 'desc' == $_GET['order']) {
            $current_order = 'desc';
        } else {
            $current_order = 'asc';
        }

        if (! empty($columns['cb'])) {
            static $cb_counter = 1;
            $columns['cb'] = sprintf(
                '<label class="screen-reader-text" for="cb-select-all-%s">%s</label>
                   <input id="cb-select-all-%s" type="checkbox" />',
                $cb_counter,
                __('Select All'),
                $cb_counter
            );
            $cb_counter++;
        }

        foreach ($columns as $column_key => $column_display_name) {
            $class = array( 'manage-column', "column-$column_key" );

            $style = '';
            if (in_array($column_key, $hidden)) {
                $style = 'display:none;';
            }

            $style = ' style="' . $style . '"';

            if ('cb' == $column_key) {
                $class[] = 'check-column';
            } elseif (in_array($column_key, array( 'posts', 'comments', 'links' ))) {
                $class[] = 'num';
            }

            if (isset($sortable[$column_key])) {
                list( $orderby, $desc_first ) = $sortable[$column_key];

                if ($current_orderby == $orderby) {
                    $order = 'asc' == $current_order ? 'desc' : 'asc';
                    $class[] = 'sorted';
                    $class[] = $current_order;
                } else {
                    $order = $desc_first ? 'desc' : 'asc';
                    $class[] = 'sortable';
                    $class[] = $desc_first ? 'asc' : 'desc';
                }

                $column_display_name = sprintf(
                    '<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
                    esc_url(add_query_arg(compact('orderby', 'order'), $current_url)),
                    $column_display_name
                );
            }

            $id = $with_id ? "id='$column_key'" : '';

            if (!empty($class)) {
                $class = "class='" . join(' ', $class) . "'";
            }

            echo "<th scope='col' $id $class $style>$column_display_name</th>";
        }
    }

    /**
     * Display the table
     *
     * @since 3.1.0
     * @access public
     */
    public function display()
    {
        $singular = $this->_args['singular'];

        $this->display_tablenav('top');

        ?>
<table class="wp-list-table <?php echo implode(' ', $this->get_table_classes()); ?>">
    <thead>
    <tr>
        <?php $this->print_column_headers(); ?>
    </tr>
    </thead>

    <tbody id="the-list"<?php
    if ($singular) {
        echo " data-wp-lists='list:$singular'";
    } ?>>
        <?php $this->display_rows_or_placeholder(); ?>
    </tbody>

    <tfoot>
    <tr>
        <?php $this->print_column_headers(false); ?>
    </tr>
    </tfoot>

</table>
        <?php
        $this->display_tablenav('bottom');
    }

    /**
     * Get a list of CSS classes for the list table table tag.
     *
     * @since 3.1.0
     * @access protected
     *
     * @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
     *
     * @since 3.1.0
     * @access protected
     * @param string $which
     */
    protected function display_tablenav($which)
    {
        if ('top' == $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); ?>
        </div>
        <?php
        $this->extra_tablenav($which);
        $this->pagination($which);
        ?>

        <br class="clear" />
    </div>
        <?php
    }

    /**
     * Extra controls to be displayed between bulk actions and pagination
     *
     * @since 3.1.0
     * @access protected
     *
     * @param string $which
     */
    protected function extra_tablenav($which)
    {
    }

    /**
     * Generate the tbody element for the list table.
     *
     * @since 3.1.0
     * @access public
     */
    public function display_rows_or_placeholder()
    {
        if ($this->has_items()) {
            $this->display_rows();
        } else {
            echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
            $this->no_items();
            echo '</td></tr>';
        }
    }

    /**
     * Generate the table rows
     *
     * @since 3.1.0
     * @access public
     */
    public function display_rows()
    {
        foreach ($this->items as $item) {
            $this->single_row($item);
        }
    }

    /**
     * Generates content for a single row of the table
     *
     * @since 3.1.0
     * @access public
     *
     * @param object $item The current item
     */
    public function single_row($item)
    {
        echo '<tr>';
        $this->single_row_columns($item);
        echo '</tr>';
    }

    protected function column_default($item, $column_name)
    {
    }

    protected function column_cb($item)
    {
    }

    /**
     * Generates the columns for a single row of the table
     *
     * @since 3.1.0
     * @access protected
     *
     * @param object $item The current item
     */
    protected function single_row_columns($item)
    {
        list( $columns, $hidden ) = $this->get_column_info();

        foreach ($columns as $column_name => $column_display_name) {
            $class = "class='$column_name column-$column_name'";

            $style = '';
            if (in_array($column_name, $hidden)) {
                $style = ' style="display:none;"';
            }

            $attributes = "$class$style";

            if ('cb' == $column_name) {
                echo '<th scope="row" class="check-column">';
                echo $this->column_cb($item);
                echo '</th>';
            } elseif (method_exists($this, 'column_' . $column_name)) {
                echo "<td $attributes>";
                echo call_user_func(array( $this, 'column_' . $column_name ), $item);
                echo "</td>";
            } else {
                echo "<td $attributes>";
                echo $this->column_default($item, $column_name);
                echo "</td>";
            }
        }
    }

    /**
     * Handle an incoming ajax request (called from admin-ajax.php)
     *
     * @since 3.1.0
     * @access public
     */
    public function ajax_response()
    {
        $this->prepare_items();

        ob_start();
        if (! empty($_REQUEST['no_placeholder'])) {
            $this->display_rows();
        } else {
            $this->display_rows_or_placeholder();
        }

        $rows = ob_get_clean();

        $response = array( 'rows' => $rows );

        if (isset($this->_pagination_args['total_items'])) {
            $response['total_items_i18n'] = sprintf(
                _n('1 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
     *
     * @access public
     */
    public function _js_vars()
    {
        $args = array(
            'class'  => get_class($this),
            'screen' => array(
                'id'   => $this->screen->id,
                'base' => $this->screen->base,
            )
        );

        printf("<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode($args));
    }
}