Download wp-admin/includes/class-wp-list-table.php from AZILS/Word: direct link, hf CLI and curl.
- Browser
- Download file 52.8 kB
-
https://huggingface.co/spaces/AZILS/Word/resolve/main/wp-admin/includes/class-wp-list-table.php
- Command line
-
hf download hf://spaces/AZILS/Word/wp-admin/includes/class-wp-list-table.php
-
curl -L -o class-wp-list-table.php https://huggingface.co/spaces/AZILS/Word/resolve/main/wp-admin/includes/class-wp-list-table.php
52.8 kB
| /** | |
| * Administration API: WP_List_Table class | |
| * | |
| * @package WordPress | |
| * @subpackage List_Table | |
| * @since 3.1.0 | |
| */ | |
| /** | |
| * Base class for displaying a list of items in an ajaxified HTML table. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| class WP_List_Table { | |
| /** | |
| * The current list of items. | |
| * | |
| * @since 3.1.0 | |
| * @var array | |
| */ | |
| public $items; | |
| /** | |
| * Various information about the current table. | |
| * | |
| * @since 3.1.0 | |
| * @var array | |
| */ | |
| 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 WP_Screen | |
| */ | |
| protected $screen; | |
| /** | |
| * Cached bulk actions. | |
| * | |
| * @since 3.1.0 | |
| * @var array | |
| */ | |
| private $_actions; | |
| /** | |
| * Cached pagination output. | |
| * | |
| * @since 3.1.0 | |
| * @var string | |
| */ | |
| private $_pagination; | |
| /** | |
| * The view switcher modes. | |
| * | |
| * @since 4.1.0 | |
| * @var array | |
| */ | |
| protected $modes = array(); | |
| /** | |
| * Stores the value returned by ->get_column_info(). | |
| * | |
| * @since 4.1.0 | |
| * @var array | |
| */ | |
| protected $_column_headers; | |
| /** | |
| * {@internal Missing Summary} | |
| * | |
| * @var array | |
| */ | |
| protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' ); | |
| /** | |
| * {@internal Missing Summary} | |
| * | |
| * @var array | |
| */ | |
| 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 | |
| * | |
| * @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 _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' => __( 'Compact view' ), | |
| 'excerpt' => __( 'Extended view' ), | |
| ); | |
| } | |
| } | |
| /** | |
| * Makes private properties readable for backward compatibility. | |
| * | |
| * @since 4.0.0 | |
| * @since 6.4.0 Getting a dynamic property is deprecated. | |
| * | |
| * @param string $name Property to get. | |
| * @return mixed Property. | |
| */ | |
| public function __get( $name ) { | |
| if ( in_array( $name, $this->compat_fields, true ) ) { | |
| return $this->$name; | |
| } | |
| wp_trigger_error( | |
| __METHOD__, | |
| "The property `{$name}` is not declared. Getting a dynamic property is " . | |
| 'deprecated since version 6.4.0! Instead, declare the property on the class.', | |
| E_USER_DEPRECATED | |
| ); | |
| return null; | |
| } | |
| /** | |
| * Makes private properties settable for backward compatibility. | |
| * | |
| * @since 4.0.0 | |
| * @since 6.4.0 Setting a dynamic property is deprecated. | |
| * | |
| * @param string $name Property to check if set. | |
| * @param mixed $value Property value. | |
| */ | |
| public function __set( $name, $value ) { | |
| if ( in_array( $name, $this->compat_fields, true ) ) { | |
| $this->$name = $value; | |
| return; | |
| } | |
| wp_trigger_error( | |
| __METHOD__, | |
| "The property `{$name}` is not declared. Setting a dynamic property is " . | |
| 'deprecated since version 6.4.0! Instead, declare the property on the class.', | |
| E_USER_DEPRECATED | |
| ); | |
| } | |
| /** | |
| * Makes private properties checkable for backward compatibility. | |
| * | |
| * @since 4.0.0 | |
| * @since 6.4.0 Checking a dynamic property is deprecated. | |
| * | |
| * @param string $name Property to check if set. | |
| * @return bool Whether the property is a back-compat property and it is set. | |
| */ | |
| public function __isset( $name ) { | |
| if ( in_array( $name, $this->compat_fields, true ) ) { | |
| return isset( $this->$name ); | |
| } | |
| wp_trigger_error( | |
| __METHOD__, | |
| "The property `{$name}` is not declared. Checking `isset()` on a dynamic property " . | |
| 'is deprecated since version 6.4.0! Instead, declare the property on the class.', | |
| E_USER_DEPRECATED | |
| ); | |
| return false; | |
| } | |
| /** | |
| * Makes private properties un-settable for backward compatibility. | |
| * | |
| * @since 4.0.0 | |
| * @since 6.4.0 Unsetting a dynamic property is deprecated. | |
| * | |
| * @param string $name Property to unset. | |
| */ | |
| public function __unset( $name ) { | |
| if ( in_array( $name, $this->compat_fields, true ) ) { | |
| unset( $this->$name ); | |
| return; | |
| } | |
| wp_trigger_error( | |
| __METHOD__, | |
| "A property `{$name}` is not declared. Unsetting a dynamic property is " . | |
| 'deprecated since version 6.4.0! Instead, declare the property on the class.', | |
| E_USER_DEPRECATED | |
| ); | |
| } | |
| /** | |
| * Makes private/protected methods readable for backward compatibility. | |
| * | |
| * @since 4.0.0 | |
| * | |
| * @param string $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, true ) ) { | |
| return $this->$name( ...$arguments ); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Checks the current user's permissions | |
| * | |
| * @since 3.1.0 | |
| * @abstract | |
| */ | |
| public function ajax_user_can() { | |
| die( 'function WP_List_Table::ajax_user_can() must be overridden in a subclass.' ); | |
| } | |
| /** | |
| * Prepares the list of items for displaying. | |
| * | |
| * @uses WP_List_Table::set_pagination_args() | |
| * | |
| * @since 3.1.0 | |
| * @abstract | |
| */ | |
| public function prepare_items() { | |
| die( 'function WP_List_Table::prepare_items() must be overridden in a subclass.' ); | |
| } | |
| /** | |
| * Sets all the necessary pagination arguments. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param array|string $args Array or string of arguments with information about the pagination. | |
| */ | |
| 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'] = (int) ceil( $args['total_items'] / $args['per_page'] ); | |
| } | |
| // Redirect if page number is invalid and headers are not already sent. | |
| if ( ! headers_sent() && ! wp_doing_ajax() && $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 | |
| * | |
| * @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 ]; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Determines whether the table has items to display or not | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @return bool | |
| */ | |
| public function has_items() { | |
| return ! empty( $this->items ); | |
| } | |
| /** | |
| * Message to be displayed when there are no items | |
| * | |
| * @since 3.1.0 | |
| */ | |
| public function no_items() { | |
| _e( 'No items found.' ); | |
| } | |
| /** | |
| * Displays the search box. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string $text The 'submit' button label. | |
| * @param string $input_id ID attribute value for the search input field. | |
| */ | |
| 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'] ) ) { | |
| if ( is_array( $_REQUEST['orderby'] ) ) { | |
| foreach ( $_REQUEST['orderby'] as $key => $value ) { | |
| echo '<input type="hidden" name="orderby[' . esc_attr( $key ) . ']" value="' . esc_attr( $value ) . '" />'; | |
| } | |
| } else { | |
| 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 esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label> | |
| <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" /> | |
| <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?> | |
| </p> | |
| <?php | |
| } | |
| /** | |
| * Generates views links. | |
| * | |
| * @since 6.1.0 | |
| * | |
| * @param array $link_data { | |
| * An array of link data. | |
| * | |
| * @type string $url The link URL. | |
| * @type string $label The link label. | |
| * @type bool $current Optional. Whether this is the currently selected view. | |
| * } | |
| * @return string[] An array of link markup. Keys match the `$link_data` input array. | |
| */ | |
| protected function get_views_links( $link_data = array() ) { | |
| if ( ! is_array( $link_data ) ) { | |
| _doing_it_wrong( | |
| __METHOD__, | |
| sprintf( | |
| /* translators: %s: The $link_data argument. */ | |
| __( 'The %s argument must be an array.' ), | |
| '<code>$link_data</code>' | |
| ), | |
| '6.1.0' | |
| ); | |
| return array( '' ); | |
| } | |
| $views_links = array(); | |
| foreach ( $link_data as $view => $link ) { | |
| if ( empty( $link['url'] ) || ! is_string( $link['url'] ) || '' === trim( $link['url'] ) ) { | |
| _doing_it_wrong( | |
| __METHOD__, | |
| sprintf( | |
| /* translators: %1$s: The argument name. %2$s: The view name. */ | |
| __( 'The %1$s argument must be a non-empty string for %2$s.' ), | |
| '<code>url</code>', | |
| '<code>' . esc_html( $view ) . '</code>' | |
| ), | |
| '6.1.0' | |
| ); | |
| continue; | |
| } | |
| if ( empty( $link['label'] ) || ! is_string( $link['label'] ) || '' === trim( $link['label'] ) ) { | |
| _doing_it_wrong( | |
| __METHOD__, | |
| sprintf( | |
| /* translators: %1$s: The argument name. %2$s: The view name. */ | |
| __( 'The %1$s argument must be a non-empty string for %2$s.' ), | |
| '<code>label</code>', | |
| '<code>' . esc_html( $view ) . '</code>' | |
| ), | |
| '6.1.0' | |
| ); | |
| continue; | |
| } | |
| $views_links[ $view ] = sprintf( | |
| '<a href="%s"%s>%s</a>', | |
| esc_url( $link['url'] ), | |
| isset( $link['current'] ) && true === $link['current'] ? ' class="current" aria-current="page"' : '', | |
| $link['label'] | |
| ); | |
| } | |
| return $views_links; | |
| } | |
| /** | |
| * Gets the list of views available on this table. | |
| * | |
| * The format is an associative array: | |
| * - `'id' => 'link'` | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @return array | |
| */ | |
| protected function get_views() { | |
| return array(); | |
| } | |
| /** | |
| * Displays the list of views available on this table. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| public function views() { | |
| $views = $this->get_views(); | |
| /** | |
| * Filters 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. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string[] $views An array of available list table views. | |
| */ | |
| $views = apply_filters( "views_{$this->screen->id}", $views ); | |
| if ( empty( $views ) ) { | |
| return; | |
| } | |
| $this->screen->render_screen_reader_content( 'heading_views' ); | |
| 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>'; | |
| } | |
| /** | |
| * Retrieves the list of bulk actions available for this table. | |
| * | |
| * The format is an associative array where each element represents either a top level option value and label, or | |
| * an array representing an optgroup and its options. | |
| * | |
| * For a standard option, the array element key is the field value and the array element value is the field label. | |
| * | |
| * For an optgroup, the array element key is the label and the array element value is an associative array of | |
| * options as above. | |
| * | |
| * Example: | |
| * | |
| * [ | |
| * 'edit' => 'Edit', | |
| * 'delete' => 'Delete', | |
| * 'Change State' => [ | |
| * 'feature' => 'Featured', | |
| * 'sale' => 'On Sale', | |
| * ] | |
| * ] | |
| * | |
| * @since 3.1.0 | |
| * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup. | |
| * | |
| * @return array | |
| */ | |
| protected function get_bulk_actions() { | |
| return array(); | |
| } | |
| /** | |
| * Displays the bulk actions dropdown. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string $which The location of the bulk actions: Either 'top' or 'bottom'. | |
| * This is designated as optional for backward compatibility. | |
| */ | |
| protected function bulk_actions( $which = '' ) { | |
| if ( is_null( $this->_actions ) ) { | |
| $this->_actions = $this->get_bulk_actions(); | |
| /** | |
| * Filters the items in the bulk actions menu of the list table. | |
| * | |
| * The dynamic portion of the hook name, `$this->screen->id`, refers | |
| * to the ID of the current screen. | |
| * | |
| * @since 3.1.0 | |
| * @since 5.6.0 A bulk action can now contain an array of options in order to create an optgroup. | |
| * | |
| * @param array $actions An array of the available bulk actions. | |
| */ | |
| $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores | |
| $two = ''; | |
| } else { | |
| $two = '2'; | |
| } | |
| if ( empty( $this->_actions ) ) { | |
| return; | |
| } | |
| echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Select bulk action' ) . | |
| '</label>'; | |
| echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n"; | |
| echo '<option value="-1">' . __( 'Bulk actions' ) . "</option>\n"; | |
| foreach ( $this->_actions as $key => $value ) { | |
| if ( is_array( $value ) ) { | |
| echo "\t" . '<optgroup label="' . esc_attr( $key ) . '">' . "\n"; | |
| foreach ( $value as $name => $title ) { | |
| $class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : ''; | |
| echo "\t\t" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . $title . "</option>\n"; | |
| } | |
| echo "\t" . "</optgroup>\n"; | |
| } else { | |
| $class = ( 'edit' === $key ) ? ' class="hide-if-no-js"' : ''; | |
| echo "\t" . '<option value="' . esc_attr( $key ) . '"' . $class . '>' . $value . "</option>\n"; | |
| } | |
| } | |
| echo "</select>\n"; | |
| submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) ); | |
| echo "\n"; | |
| } | |
| /** | |
| * Gets the current action selected from the bulk actions dropdown. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @return string|false The action name. 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']; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Generates the required HTML for a list of row action links. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string[] $actions An array of action links. | |
| * @param bool $always_visible Whether the actions should be always visible. | |
| * @return string The HTML for the row actions. | |
| */ | |
| protected function row_actions( $actions, $always_visible = false ) { | |
| $action_count = count( $actions ); | |
| if ( ! $action_count ) { | |
| return ''; | |
| } | |
| $mode = get_user_setting( 'posts_list_mode', 'list' ); | |
| if ( 'excerpt' === $mode ) { | |
| $always_visible = true; | |
| } | |
| $output = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">'; | |
| $i = 0; | |
| foreach ( $actions as $action => $link ) { | |
| ++$i; | |
| $separator = ( $i < $action_count ) ? ' | ' : ''; | |
| $output .= "<span class='$action'>{$link}{$separator}</span>"; | |
| } | |
| $output .= '</div>'; | |
| $output .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Show more details' ) . | |
| '</span></button>'; | |
| return $output; | |
| } | |
| /** | |
| * Displays a dropdown for filtering items in the list table by month. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @global wpdb $wpdb WordPress database abstraction object. | |
| * @global WP_Locale $wp_locale WordPress date and time locale object. | |
| * | |
| * @param string $post_type The post type. | |
| */ | |
| protected function months_dropdown( $post_type ) { | |
| global $wpdb, $wp_locale; | |
| /** | |
| * Filters 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; | |
| } | |
| /** | |
| * Filters whether to short-circuit performing the months dropdown query. | |
| * | |
| * @since 5.7.0 | |
| * | |
| * @param object[]|false $months 'Months' drop-down results. Default false. | |
| * @param string $post_type The post type. | |
| */ | |
| $months = apply_filters( 'pre_months_dropdown_query', false, $post_type ); | |
| if ( ! is_array( $months ) ) { | |
| $extra_checks = "AND post_status != 'auto-draft'"; | |
| if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) { | |
| $extra_checks .= " AND post_status != 'trash'"; | |
| } elseif ( isset( $_GET['post_status'] ) ) { | |
| $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] ); | |
| } | |
| $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 | |
| $extra_checks | |
| ORDER BY post_date DESC", | |
| $post_type | |
| ) | |
| ); | |
| } | |
| /** | |
| * Filters the 'Months' drop-down results. | |
| * | |
| * @since 3.7.0 | |
| * | |
| * @param object[] $months Array of 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 === (int) $months[0]->month ) ) { | |
| return; | |
| } | |
| $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0; | |
| <label for="filter-by-date" class="screen-reader-text"><?php echo get_post_type_object( $post_type )->labels->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 === (int) $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> | |
| } | |
| /** | |
| * Displays a view switcher. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @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 ); | |
| $aria_current = ''; | |
| if ( $current_mode === $mode ) { | |
| $classes[] = 'current'; | |
| $aria_current = ' aria-current="page"'; | |
| } | |
| printf( | |
| "<a href='%s' class='%s' id='view-switch-$mode'$aria_current>" . | |
| "<span class='screen-reader-text'>%s</span>" . | |
| "</a>\n", | |
| esc_url( remove_query_arg( 'attachment-filter', add_query_arg( 'mode', $mode ) ) ), | |
| implode( ' ', $classes ), | |
| $title | |
| ); | |
| } | |
| </div> | |
| } | |
| /** | |
| * Displays a comment count bubble. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param int $post_id The post ID. | |
| * @param int $pending_comments Number of pending comments. | |
| */ | |
| protected function comments_bubble( $post_id, $pending_comments ) { | |
| $post_object = get_post( $post_id ); | |
| $edit_post_cap = $post_object ? 'edit_post' : 'edit_posts'; | |
| if ( ! current_user_can( $edit_post_cap, $post_id ) | |
| && ( post_password_required( $post_id ) | |
| || ! current_user_can( 'read_post', $post_id ) ) | |
| ) { | |
| // The user has no access to the post and thus cannot see the comments. | |
| return false; | |
| } | |
| $approved_comments = get_comments_number(); | |
| $approved_comments_number = number_format_i18n( $approved_comments ); | |
| $pending_comments_number = number_format_i18n( $pending_comments ); | |
| $approved_only_phrase = sprintf( | |
| /* translators: %s: Number of comments. */ | |
| _n( '%s comment', '%s comments', $approved_comments ), | |
| $approved_comments_number | |
| ); | |
| $approved_phrase = sprintf( | |
| /* translators: %s: Number of comments. */ | |
| _n( '%s approved comment', '%s approved comments', $approved_comments ), | |
| $approved_comments_number | |
| ); | |
| $pending_phrase = sprintf( | |
| /* translators: %s: Number of comments. */ | |
| _n( '%s pending comment', '%s pending comments', $pending_comments ), | |
| $pending_comments_number | |
| ); | |
| if ( ! $approved_comments && ! $pending_comments ) { | |
| // No comments at all. | |
| printf( | |
| '<span aria-hidden="true">—</span>' . | |
| '<span class="screen-reader-text">%s</span>', | |
| __( 'No comments' ) | |
| ); | |
| } elseif ( $approved_comments && 'trash' === get_post_status( $post_id ) ) { | |
| // Don't link the comment bubble for a trashed post. | |
| printf( | |
| '<span class="post-com-count post-com-count-approved">' . | |
| '<span class="comment-count-approved" aria-hidden="true">%s</span>' . | |
| '<span class="screen-reader-text">%s</span>' . | |
| '</span>', | |
| $approved_comments_number, | |
| $pending_comments ? $approved_phrase : $approved_only_phrase | |
| ); | |
| } elseif ( $approved_comments ) { | |
| // Link the comment bubble to approved comments. | |
| printf( | |
| '<a href="%s" class="post-com-count post-com-count-approved">' . | |
| '<span class="comment-count-approved" aria-hidden="true">%s</span>' . | |
| '<span class="screen-reader-text">%s</span>' . | |
| '</a>', | |
| esc_url( | |
| add_query_arg( | |
| array( | |
| 'p' => $post_id, | |
| 'comment_status' => 'approved', | |
| ), | |
| admin_url( 'edit-comments.php' ) | |
| ) | |
| ), | |
| $approved_comments_number, | |
| $pending_comments ? $approved_phrase : $approved_only_phrase | |
| ); | |
| } else { | |
| // Don't link the comment bubble when there are no approved comments. | |
| printf( | |
| '<span class="post-com-count post-com-count-no-comments">' . | |
| '<span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span>' . | |
| '<span class="screen-reader-text">%s</span>' . | |
| '</span>', | |
| $approved_comments_number, | |
| $pending_comments ? | |
| /* translators: Hidden accessibility text. */ | |
| __( 'No approved comments' ) : | |
| /* translators: Hidden accessibility text. */ | |
| __( 'No comments' ) | |
| ); | |
| } | |
| if ( $pending_comments ) { | |
| printf( | |
| '<a href="%s" class="post-com-count post-com-count-pending">' . | |
| '<span class="comment-count-pending" aria-hidden="true">%s</span>' . | |
| '<span class="screen-reader-text">%s</span>' . | |
| '</a>', | |
| esc_url( | |
| add_query_arg( | |
| array( | |
| 'p' => $post_id, | |
| 'comment_status' => 'moderated', | |
| ), | |
| admin_url( 'edit-comments.php' ) | |
| ) | |
| ), | |
| $pending_comments_number, | |
| $pending_phrase | |
| ); | |
| } else { | |
| printf( | |
| '<span class="post-com-count post-com-count-pending post-com-count-no-pending">' . | |
| '<span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span>' . | |
| '<span class="screen-reader-text">%s</span>' . | |
| '</span>', | |
| $pending_comments_number, | |
| $approved_comments ? | |
| /* translators: Hidden accessibility text. */ | |
| __( 'No pending comments' ) : | |
| /* translators: Hidden accessibility text. */ | |
| __( 'No comments' ) | |
| ); | |
| } | |
| } | |
| /** | |
| * Gets the current page number. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @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 ); | |
| } | |
| /** | |
| * Gets the number of items to display on a single page. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string $option User option name. | |
| * @param int $default_value Optional. The number of items to display. Default 20. | |
| * @return int | |
| */ | |
| protected function get_items_per_page( $option, $default_value = 20 ) { | |
| $per_page = (int) get_user_option( $option ); | |
| if ( empty( $per_page ) || $per_page < 1 ) { | |
| $per_page = $default_value; | |
| } | |
| /** | |
| * Filters 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 filter names 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` | |
| * - `edit_post_tag_per_page` | |
| * - `edit_category_per_page` | |
| * - `edit_{$taxonomy}_per_page` | |
| * - `site_users_network_per_page` | |
| * - `users_per_page` | |
| * | |
| * @since 2.9.0 | |
| * | |
| * @param int $per_page Number of items to be displayed. Default 20. | |
| */ | |
| return (int) apply_filters( "{$option}", $per_page ); | |
| } | |
| /** | |
| * Displays the pagination. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string $which The location of the pagination: Either 'top' or 'bottom'. | |
| */ | |
| 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']; | |
| } | |
| if ( 'top' === $which && $total_pages > 1 ) { | |
| $this->screen->render_screen_reader_content( 'heading_pagination' ); | |
| } | |
| $output = '<span class="displaying-num">' . sprintf( | |
| /* translators: %s: Number of items. */ | |
| _n( '%s item', '%s items', $total_items ), | |
| number_format_i18n( $total_items ) | |
| ) . '</span>'; | |
| $current = $this->get_pagenum(); | |
| $removable_query_args = wp_removable_query_args(); | |
| $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); | |
| $current_url = remove_query_arg( $removable_query_args, $current_url ); | |
| $page_links = array(); | |
| $total_pages_before = '<span class="paging-input">'; | |
| $total_pages_after = '</span></span>'; | |
| $disable_first = false; | |
| $disable_last = false; | |
| $disable_prev = false; | |
| $disable_next = false; | |
| if ( 1 === $current ) { | |
| $disable_first = true; | |
| $disable_prev = true; | |
| } | |
| if ( $total_pages === $current ) { | |
| $disable_last = true; | |
| $disable_next = true; | |
| } | |
| if ( $disable_first ) { | |
| $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">«</span>'; | |
| } else { | |
| $page_links[] = sprintf( | |
| "<a class='first-page button' href='%s'>" . | |
| "<span class='screen-reader-text'>%s</span>" . | |
| "<span aria-hidden='true'>%s</span>" . | |
| '</a>', | |
| esc_url( remove_query_arg( 'paged', $current_url ) ), | |
| /* translators: Hidden accessibility text. */ | |
| __( 'First page' ), | |
| '«' | |
| ); | |
| } | |
| if ( $disable_prev ) { | |
| $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">‹</span>'; | |
| } else { | |
| $page_links[] = sprintf( | |
| "<a class='prev-page button' href='%s'>" . | |
| "<span class='screen-reader-text'>%s</span>" . | |
| "<span aria-hidden='true'>%s</span>" . | |
| '</a>', | |
| esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ), | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Previous page' ), | |
| '‹' | |
| ); | |
| } | |
| if ( 'bottom' === $which ) { | |
| $html_current_page = $current; | |
| $total_pages_before = sprintf( | |
| '<span class="screen-reader-text">%s</span>' . | |
| '<span id="table-paging" class="paging-input">' . | |
| '<span class="tablenav-paging-text">', | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Current Page' ) | |
| ); | |
| } else { | |
| $html_current_page = sprintf( | |
| '<label for="current-page-selector" class="screen-reader-text">%s</label>' . | |
| "<input class='current-page' id='current-page-selector' type='text' | |
| name='paged' value='%s' size='%d' aria-describedby='table-paging' />" . | |
| "<span class='tablenav-paging-text'>", | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Current Page' ), | |
| $current, | |
| strlen( $total_pages ) | |
| ); | |
| } | |
| $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) ); | |
| $page_links[] = $total_pages_before . sprintf( | |
| /* translators: 1: Current page, 2: Total pages. */ | |
| _x( '%1$s of %2$s', 'paging' ), | |
| $html_current_page, | |
| $html_total_pages | |
| ) . $total_pages_after; | |
| if ( $disable_next ) { | |
| $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">›</span>'; | |
| } else { | |
| $page_links[] = sprintf( | |
| "<a class='next-page button' href='%s'>" . | |
| "<span class='screen-reader-text'>%s</span>" . | |
| "<span aria-hidden='true'>%s</span>" . | |
| '</a>', | |
| esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ), | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Next page' ), | |
| '›' | |
| ); | |
| } | |
| if ( $disable_last ) { | |
| $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</span>'; | |
| } else { | |
| $page_links[] = sprintf( | |
| "<a class='last-page button' href='%s'>" . | |
| "<span class='screen-reader-text'>%s</span>" . | |
| "<span aria-hidden='true'>%s</span>" . | |
| '</a>', | |
| esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Last page' ), | |
| '»' | |
| ); | |
| } | |
| $pagination_links_class = 'pagination-links'; | |
| if ( ! empty( $infinite_scroll ) ) { | |
| $pagination_links_class .= ' hide-if-js'; | |
| } | |
| $output .= "\n<span class='$pagination_links_class'>" . implode( "\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; | |
| } | |
| /** | |
| * Gets a list of columns. | |
| * | |
| * The format is: | |
| * - `'internal-name' => 'Title'` | |
| * | |
| * @since 3.1.0 | |
| * @abstract | |
| * | |
| * @return array | |
| */ | |
| public function get_columns() { | |
| die( 'function WP_List_Table::get_columns() must be overridden in a subclass.' ); | |
| } | |
| /** | |
| * Gets a list of sortable columns. | |
| * | |
| * The format is: | |
| * - `'internal-name' => 'orderby'` | |
| * - `'internal-name' => array( 'orderby', bool, 'abbr', 'orderby-text', 'initially-sorted-column-order' )` - | |
| * - `'internal-name' => array( 'orderby', 'asc' )` - The second element sets the initial sorting order. | |
| * - `'internal-name' => array( 'orderby', true )` - The second element makes the initial order descending. | |
| * | |
| * In the second format, passing true as second parameter will make the initial | |
| * sorting order be descending. Following parameters add a short column name to | |
| * be used as 'abbr' attribute, a translatable string for the current sorting, | |
| * and the initial order for the initial sorted column, 'asc' or 'desc' (default: false). | |
| * | |
| * @since 3.1.0 | |
| * @since 6.3.0 Added 'abbr', 'orderby-text' and 'initially-sorted-column-order'. | |
| * | |
| * @return array | |
| */ | |
| protected function get_sortable_columns() { | |
| return array(); | |
| } | |
| /** | |
| * Gets the name of the default primary column. | |
| * | |
| * @since 4.3.0 | |
| * | |
| * @return string Name of the default primary column, in this case, an empty string. | |
| */ | |
| protected function get_default_primary_column_name() { | |
| $columns = $this->get_columns(); | |
| $column = ''; | |
| if ( empty( $columns ) ) { | |
| return $column; | |
| } | |
| /* | |
| * We need a primary defined so responsive views show something, | |
| * so let's fall back to the first non-checkbox column. | |
| */ | |
| foreach ( $columns as $col => $column_name ) { | |
| if ( 'cb' === $col ) { | |
| continue; | |
| } | |
| $column = $col; | |
| break; | |
| } | |
| return $column; | |
| } | |
| /** | |
| * Gets the name of the primary column. | |
| * | |
| * Public wrapper for WP_List_Table::get_default_primary_column_name(). | |
| * | |
| * @since 4.4.0 | |
| * | |
| * @return string Name of the default primary column. | |
| */ | |
| public function get_primary_column() { | |
| return $this->get_primary_column_name(); | |
| } | |
| /** | |
| * Gets the name of the primary column. | |
| * | |
| * @since 4.3.0 | |
| * | |
| * @return string The name of the primary column. | |
| */ | |
| protected function get_primary_column_name() { | |
| $columns = get_column_headers( $this->screen ); | |
| $default = $this->get_default_primary_column_name(); | |
| /* | |
| * If the primary column doesn't exist, | |
| * fall back to the first non-checkbox column. | |
| */ | |
| if ( ! isset( $columns[ $default ] ) ) { | |
| $default = self::get_default_primary_column_name(); | |
| } | |
| /** | |
| * Filters the name of the primary column for the current list table. | |
| * | |
| * @since 4.3.0 | |
| * | |
| * @param string $default Column name default for the specific list table, e.g. 'name'. | |
| * @param string $context Screen ID for specific list table, e.g. 'plugins'. | |
| */ | |
| $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id ); | |
| if ( empty( $column ) || ! isset( $columns[ $column ] ) ) { | |
| $column = $default; | |
| } | |
| return $column; | |
| } | |
| /** | |
| * Gets a list of all, hidden, and sortable columns, with filter applied. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @return array | |
| */ | |
| protected function get_column_info() { | |
| // $_column_headers is already set / cached. | |
| if ( | |
| isset( $this->_column_headers ) && | |
| is_array( $this->_column_headers ) | |
| ) { | |
| /* | |
| * Backward compatibility for `$_column_headers` format prior to WordPress 4.3. | |
| * | |
| * In WordPress 4.3 the primary column name was added as a fourth item in the | |
| * column headers property. This ensures the primary column name is included | |
| * in plugins setting the property directly in the three item format. | |
| */ | |
| if ( 4 === count( $this->_column_headers ) ) { | |
| return $this->_column_headers; | |
| } | |
| $column_headers = array( array(), array(), array(), $this->get_primary_column_name() ); | |
| foreach ( $this->_column_headers as $key => $value ) { | |
| $column_headers[ $key ] = $value; | |
| } | |
| $this->_column_headers = $column_headers; | |
| return $this->_column_headers; | |
| } | |
| $columns = get_column_headers( $this->screen ); | |
| $hidden = get_hidden_columns( $this->screen ); | |
| $sortable_columns = $this->get_sortable_columns(); | |
| /** | |
| * Filters 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. | |
| * | |
| * @since 3.1.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; | |
| // Descending initial sorting. | |
| if ( ! isset( $data[1] ) ) { | |
| $data[1] = false; | |
| } | |
| // Current sorting translatable string. | |
| if ( ! isset( $data[2] ) ) { | |
| $data[2] = ''; | |
| } | |
| // Initial view sorted column and asc/desc order, default: false. | |
| if ( ! isset( $data[3] ) ) { | |
| $data[3] = false; | |
| } | |
| // Initial order for the initial sorted column, default: false. | |
| if ( ! isset( $data[4] ) ) { | |
| $data[4] = false; | |
| } | |
| $sortable[ $id ] = $data; | |
| } | |
| $primary = $this->get_primary_column_name(); | |
| $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); | |
| return $this->_column_headers; | |
| } | |
| /** | |
| * Returns the number of visible columns. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @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 ); | |
| } | |
| /** | |
| * Prints column headers, accounting for hidden and sortable columns. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @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(); | |
| $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); | |
| $current_url = remove_query_arg( 'paged', $current_url ); | |
| // When users click on a column header to sort by other columns. | |
| if ( isset( $_GET['orderby'] ) ) { | |
| $current_orderby = $_GET['orderby']; | |
| // In the initial view there's no orderby parameter. | |
| } else { | |
| $current_orderby = ''; | |
| } | |
| // Not in the initial view and descending order. | |
| if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) { | |
| $current_order = 'desc'; | |
| } else { | |
| // The initial view is not always 'asc', we'll take care of this below. | |
| $current_order = 'asc'; | |
| } | |
| if ( ! empty( $columns['cb'] ) ) { | |
| static $cb_counter = 1; | |
| $columns['cb'] = '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" /> | |
| <label for="cb-select-all-' . $cb_counter . '">' . | |
| '<span class="screen-reader-text">' . | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Select All' ) . | |
| '</span>' . | |
| '</label>'; | |
| ++$cb_counter; | |
| } | |
| foreach ( $columns as $column_key => $column_display_name ) { | |
| $class = array( 'manage-column', "column-$column_key" ); | |
| $aria_sort_attr = ''; | |
| $abbr_attr = ''; | |
| $order_text = ''; | |
| if ( in_array( $column_key, $hidden, true ) ) { | |
| $class[] = 'hidden'; | |
| } | |
| if ( 'cb' === $column_key ) { | |
| $class[] = 'check-column'; | |
| } elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ), true ) ) { | |
| $class[] = 'num'; | |
| } | |
| if ( $column_key === $primary ) { | |
| $class[] = 'column-primary'; | |
| } | |
| if ( isset( $sortable[ $column_key ] ) ) { | |
| $orderby = isset( $sortable[ $column_key ][0] ) ? $sortable[ $column_key ][0] : ''; | |
| $desc_first = isset( $sortable[ $column_key ][1] ) ? $sortable[ $column_key ][1] : false; | |
| $abbr = isset( $sortable[ $column_key ][2] ) ? $sortable[ $column_key ][2] : ''; | |
| $orderby_text = isset( $sortable[ $column_key ][3] ) ? $sortable[ $column_key ][3] : ''; | |
| $initial_order = isset( $sortable[ $column_key ][4] ) ? $sortable[ $column_key ][4] : ''; | |
| /* | |
| * We're in the initial view and there's no $_GET['orderby'] then check if the | |
| * initial sorting information is set in the sortable columns and use that. | |
| */ | |
| if ( '' === $current_orderby && $initial_order ) { | |
| // Use the initially sorted column $orderby as current orderby. | |
| $current_orderby = $orderby; | |
| // Use the initially sorted column asc/desc order as initial order. | |
| $current_order = $initial_order; | |
| } | |
| /* | |
| * True in the initial view when an initial orderby is set via get_sortable_columns() | |
| * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby. | |
| */ | |
| if ( $current_orderby === $orderby ) { | |
| // The sorted column. The `aria-sort` attribute must be set only on the sorted column. | |
| if ( 'asc' === $current_order ) { | |
| $order = 'desc'; | |
| $aria_sort_attr = ' aria-sort="ascending"'; | |
| } else { | |
| $order = 'asc'; | |
| $aria_sort_attr = ' aria-sort="descending"'; | |
| } | |
| $class[] = 'sorted'; | |
| $class[] = $current_order; | |
| } else { | |
| // The other sortable columns. | |
| $order = strtolower( $desc_first ); | |
| if ( ! in_array( $order, array( 'desc', 'asc' ), true ) ) { | |
| $order = $desc_first ? 'desc' : 'asc'; | |
| } | |
| $class[] = 'sortable'; | |
| $class[] = 'desc' === $order ? 'asc' : 'desc'; | |
| /* translators: Hidden accessibility text. */ | |
| $asc_text = __( 'Sort ascending.' ); | |
| /* translators: Hidden accessibility text. */ | |
| $desc_text = __( 'Sort descending.' ); | |
| $order_text = 'asc' === $order ? $asc_text : $desc_text; | |
| } | |
| if ( '' !== $order_text ) { | |
| $order_text = ' <span class="screen-reader-text">' . $order_text . '</span>'; | |
| } | |
| // Print an 'abbr' attribute if a value is provided via get_sortable_columns(). | |
| $abbr_attr = $abbr ? ' abbr="' . esc_attr( $abbr ) . '"' : ''; | |
| $column_display_name = sprintf( | |
| '<a href="%1$s">' . | |
| '<span>%2$s</span>' . | |
| '<span class="sorting-indicators">' . | |
| '<span class="sorting-indicator asc" aria-hidden="true"></span>' . | |
| '<span class="sorting-indicator desc" aria-hidden="true"></span>' . | |
| '</span>' . | |
| '%3$s' . | |
| '</a>', | |
| esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ), | |
| $column_display_name, | |
| $order_text | |
| ); | |
| } | |
| $tag = ( 'cb' === $column_key ) ? 'td' : 'th'; | |
| $scope = ( 'th' === $tag ) ? 'scope="col"' : ''; | |
| $id = $with_id ? "id='$column_key'" : ''; | |
| if ( ! empty( $class ) ) { | |
| $class = "class='" . implode( ' ', $class ) . "'"; | |
| } | |
| echo "<$tag $scope $id $class $aria_sort_attr $abbr_attr>$column_display_name</$tag>"; | |
| } | |
| } | |
| /** | |
| * Print a table description with information about current sorting and order. | |
| * | |
| * For the table initial view, information about initial orderby and order | |
| * should be provided via get_sortable_columns(). | |
| * | |
| * @since 6.3.0 | |
| * @access public | |
| */ | |
| public function print_table_description() { | |
| list( $columns, $hidden, $sortable ) = $this->get_column_info(); | |
| if ( empty( $sortable ) ) { | |
| return; | |
| } | |
| // When users click on a column header to sort by other columns. | |
| if ( isset( $_GET['orderby'] ) ) { | |
| $current_orderby = $_GET['orderby']; | |
| // In the initial view there's no orderby parameter. | |
| } else { | |
| $current_orderby = ''; | |
| } | |
| // Not in the initial view and descending order. | |
| if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) { | |
| $current_order = 'desc'; | |
| } else { | |
| // The initial view is not always 'asc', we'll take care of this below. | |
| $current_order = 'asc'; | |
| } | |
| foreach ( array_keys( $columns ) as $column_key ) { | |
| if ( isset( $sortable[ $column_key ] ) ) { | |
| $orderby = isset( $sortable[ $column_key ][0] ) ? $sortable[ $column_key ][0] : ''; | |
| $desc_first = isset( $sortable[ $column_key ][1] ) ? $sortable[ $column_key ][1] : false; | |
| $abbr = isset( $sortable[ $column_key ][2] ) ? $sortable[ $column_key ][2] : ''; | |
| $orderby_text = isset( $sortable[ $column_key ][3] ) ? $sortable[ $column_key ][3] : ''; | |
| $initial_order = isset( $sortable[ $column_key ][4] ) ? $sortable[ $column_key ][4] : ''; | |
| if ( ! is_string( $orderby_text ) || '' === $orderby_text ) { | |
| return; | |
| } | |
| /* | |
| * We're in the initial view and there's no $_GET['orderby'] then check if the | |
| * initial sorting information is set in the sortable columns and use that. | |
| */ | |
| if ( '' === $current_orderby && $initial_order ) { | |
| // Use the initially sorted column $orderby as current orderby. | |
| $current_orderby = $orderby; | |
| // Use the initially sorted column asc/desc order as initial order. | |
| $current_order = $initial_order; | |
| } | |
| /* | |
| * True in the initial view when an initial orderby is set via get_sortable_columns() | |
| * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby. | |
| */ | |
| if ( $current_orderby === $orderby ) { | |
| /* translators: Hidden accessibility text. */ | |
| $asc_text = __( 'Ascending.' ); | |
| /* translators: Hidden accessibility text. */ | |
| $desc_text = __( 'Descending.' ); | |
| $order_text = 'asc' === $current_order ? $asc_text : $desc_text; | |
| echo '<caption class="screen-reader-text">' . $orderby_text . ' ' . $order_text . '</caption>'; | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Displays the table. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| public function display() { | |
| $singular = $this->_args['singular']; | |
| $this->display_tablenav( 'top' ); | |
| $this->screen->render_screen_reader_content( 'heading_list' ); | |
| <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>"> | |
| <?php $this->print_table_description(); ?> | |
| <thead> | |
| <tr> | |
| <?php $this->print_column_headers(); ?> | |
| </tr> | |
| </thead> | |
| <tbody id="the-list" | |
| <?php | |
| if ( $singular ) { | |
| echo " data-wp-lists='list:$singular'"; | |
| } | |
| > | |
| $this->display_rows_or_placeholder(); | |
| </tbody> | |
| <tfoot> | |
| <tr> | |
| $this->print_column_headers( false ); | |
| </tr> | |
| </tfoot> | |
| </table> | |
| $this->display_tablenav( 'bottom' ); | |
| } | |
| /** | |
| * Gets a list of CSS classes for the WP_List_Table table tag. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @return string[] Array of CSS classes for the table tag. | |
| */ | |
| protected function get_table_classes() { | |
| $mode = get_user_setting( 'posts_list_mode', 'list' ); | |
| $mode_class = esc_attr( 'table-view-' . $mode ); | |
| return array( 'widefat', 'fixed', 'striped', $mode_class, $this->_args['plural'] ); | |
| } | |
| /** | |
| * Generates the table navigation above or below the table | |
| * | |
| * @since 3.1.0 | |
| * @param string $which The location of the navigation: Either 'top' or 'bottom'. | |
| */ | |
| protected function display_tablenav( $which ) { | |
| if ( 'top' === $which ) { | |
| wp_nonce_field( 'bulk-' . $this->_args['plural'] ); | |
| } | |
| <div class="tablenav <?php echo esc_attr( $which ); ?>"> | |
| <?php if ( $this->has_items() ) : ?> | |
| <div class="alignleft actions bulkactions"> | |
| <?php $this->bulk_actions( $which ); ?> | |
| </div> | |
| <?php | |
| endif; | |
| $this->extra_tablenav( $which ); | |
| $this->pagination( $which ); | |
| ?> | |
| <br class="clear" /> | |
| </div> | |
| <?php | |
| } | |
| /** | |
| * Displays extra controls between bulk actions and pagination. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param string $which | |
| */ | |
| protected function extra_tablenav( $which ) {} | |
| /** | |
| * Generates the tbody element for the list table. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| 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>'; | |
| } | |
| } | |
| /** | |
| * Generates the table rows. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| 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 | |
| * | |
| * @param object|array $item The current item | |
| */ | |
| public function single_row( $item ) { | |
| echo '<tr>'; | |
| $this->single_row_columns( $item ); | |
| echo '</tr>'; | |
| } | |
| /** | |
| * @param object|array $item | |
| * @param string $column_name | |
| */ | |
| protected function column_default( $item, $column_name ) {} | |
| /** | |
| * @param object|array $item | |
| */ | |
| protected function column_cb( $item ) {} | |
| /** | |
| * Generates the columns for a single row of the table. | |
| * | |
| * @since 3.1.0 | |
| * | |
| * @param object|array $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, true ) ) { | |
| $classes .= ' hidden'; | |
| } | |
| /* | |
| * Comments column uses HTML in the display name with screen reader text. | |
| * Strip tags to get closer to a user-friendly string. | |
| */ | |
| $data = 'data-colname="' . esc_attr( wp_strip_all_tags( $column_display_name ) ) . '"'; | |
| $attributes = "class='$classes' $data"; | |
| 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 call_user_func( | |
| array( $this, '_column_' . $column_name ), | |
| $item, | |
| $classes, | |
| $data, | |
| $primary | |
| ); | |
| } elseif ( method_exists( $this, 'column_' . $column_name ) ) { | |
| echo "<td $attributes>"; | |
| echo call_user_func( array( $this, 'column_' . $column_name ), $item ); | |
| echo $this->handle_row_actions( $item, $column_name, $primary ); | |
| echo '</td>'; | |
| } else { | |
| echo "<td $attributes>"; | |
| echo $this->column_default( $item, $column_name ); | |
| echo $this->handle_row_actions( $item, $column_name, $primary ); | |
| echo '</td>'; | |
| } | |
| } | |
| } | |
| /** | |
| * Generates and display row actions links for the list table. | |
| * | |
| * @since 4.3.0 | |
| * | |
| * @param object|array $item The item being acted upon. | |
| * @param string $column_name Current column name. | |
| * @param string $primary Primary column name. | |
| * @return string The row actions HTML, or an empty string | |
| * if the current column is not the primary column. | |
| */ | |
| protected function handle_row_actions( $item, $column_name, $primary ) { | |
| return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . | |
| /* translators: Hidden accessibility text. */ | |
| __( 'Show more details' ) . | |
| '</span></button>' : ''; | |
| } | |
| /** | |
| * Handles an incoming ajax request (called from admin-ajax.php) | |
| * | |
| * @since 3.1.0 | |
| */ | |
| 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( | |
| /* translators: Number of 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 ) ); | |
| } | |
| /** | |
| * Sends required variables to JavaScript land. | |
| * | |
| * @since 3.1.0 | |
| */ | |
| 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 ) ); | |
| } | |
| } | |