diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index 2976d2f982..4d0e6be813 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -461,50 +461,64 @@ table.media .column-title .filename { width: 160px; } +.sorting-indicators { + display: grid; +} + .sorting-indicator { display: block; - visibility: hidden; width: 10px; height: 4px; - margin-top: 8px; + margin-top: 4px; margin-left: 7px; } .sorting-indicator:before { - content: "\f142"; font: normal 20px/1 dashicons; speak: never; display: inline-block; padding: 0; top: -4px; left: -8px; - color: #3c434a; line-height: 0.5; position: relative; vertical-align: top; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-decoration: none !important; - color: #3c434a; + color: #a7aaad; } -.column-comments .sorting-indicator:before { - top: 0; - left: -10px; -} - -th.sorted.asc .sorting-indicator:before, -th.desc:hover span.sorting-indicator:before, -th.desc a:focus span.sorting-indicator:before { +.sorting-indicator.asc:before { content: "\f142"; } -th.sorted.desc .sorting-indicator:before, -th.asc:hover span.sorting-indicator:before, -th.asc a:focus span.sorting-indicator:before { +.sorting-indicator.desc:before { content: "\f140"; } +th.sorted.desc .sorting-indicator.desc:before { + color: #1d2327; +} + +th.sorted.asc .sorting-indicator.asc:before { + color: #1d2327; +} + +th.sorted.asc a:focus .sorting-indicator.asc:before, +th.sorted.asc:hover .sorting-indicator.asc:before, +th.sorted.desc a:focus .sorting-indicator.desc:before, +th.sorted.desc:hover .sorting-indicator.desc:before { + color: #a7aaad; +} + +th.sorted.asc a:focus .sorting-indicator.desc:before, +th.sorted.asc:hover .sorting-indicator.desc:before, +th.sorted.desc a:focus .sorting-indicator.asc:before, +th.sorted.desc:hover .sorting-indicator.asc:before { + color: #1d2327; +} + .wp-list-table .toggle-row { position: absolute; right: 8px; @@ -613,10 +627,6 @@ tr.wp-locked .row-actions .trash { display: none; } -.fixed .column-comments .sorting-indicator { - margin-top: 3px; -} - #menu-locations-wrap .widefat { width: 60%; } @@ -644,14 +654,6 @@ th.sorted a span { cursor: pointer; } -th.sorted .sorting-indicator, -th.desc:hover span.sorting-indicator, -th.desc a:focus span.sorting-indicator, -th.asc:hover span.sorting-indicator, -th.asc a:focus span.sorting-indicator { - visibility: visible; -} - .tablenav-pages .current-page { margin: 0 2px 0 0; font-size: 13px; diff --git a/src/wp-admin/includes/class-wp-comments-list-table.php b/src/wp-admin/includes/class-wp-comments-list-table.php index c7dfcc6159..49c3dc16ff 100644 --- a/src/wp-admin/includes/class-wp-comments-list-table.php +++ b/src/wp-admin/includes/class-wp-comments-list-table.php @@ -540,8 +540,8 @@ class WP_Comments_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { return array( - 'author' => 'comment_author', - 'response' => 'comment_post_ID', + 'author' => array( 'comment_author', false, __( 'Author' ), __( 'Table ordered by Comment Author.' ) ), + 'response' => array( 'comment_post_ID', false, _x( 'In Response To', 'column name' ), __( 'Table ordered by Post Replied To.' ) ), 'date' => 'comment_date', ); } @@ -580,6 +580,14 @@ class WP_Comments_List_Table extends WP_List_Table { ?> + ' . __( 'Ordered by Comment Date, descending.' ) . '

'; + } else { + $this->print_table_description(); + } + ?> print_column_headers(); ?> diff --git a/src/wp-admin/includes/class-wp-links-list-table.php b/src/wp-admin/includes/class-wp-links-list-table.php index b676629453..f29cf7b3e2 100644 --- a/src/wp-admin/includes/class-wp-links-list-table.php +++ b/src/wp-admin/includes/class-wp-links-list-table.php @@ -143,10 +143,10 @@ class WP_Links_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { return array( - 'name' => 'name', - 'url' => 'url', - 'visible' => 'visible', - 'rating' => 'rating', + 'name' => array( 'name', false, _x( 'Name', 'link name' ), __( 'Table ordered by Name.' ), 'asc' ), + 'url' => array( 'url', false, __( 'URL' ), __( 'Table ordered by URL.' ) ), + 'visible' => array( 'visible', false, __( 'Visible' ), __( 'Table ordered by Visibility.' ) ), + 'rating' => array( 'rating', false, __( 'Rating' ), __( 'Table ordered by Rating.' ) ), ); } diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php index 1e6a8ea505..4dfb825996 100644 --- a/src/wp-admin/includes/class-wp-list-table.php +++ b/src/wp-admin/includes/class-wp-list-table.php @@ -1109,10 +1109,17 @@ class WP_List_Table { * * 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 */ @@ -1253,9 +1260,22 @@ class WP_List_Table { } $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; } @@ -1292,15 +1312,19 @@ class WP_List_Table { $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 = ''; } - if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) { + // 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'; } @@ -1317,7 +1341,10 @@ class WP_List_Table { } foreach ( $columns as $column_key => $column_display_name ) { - $class = array( 'manage-column', "column-$column_key" ); + $class = array( 'manage-column', "column-$column_key" ); + $aria_sort_attr = ''; + $abbr_attr = ''; + $order_text = ''; if ( in_array( $column_key, $hidden, true ) ) { $class[] = 'hidden'; @@ -1334,29 +1361,53 @@ class WP_List_Table { } if ( isset( $sortable[ $column_key ] ) ) { - list( $orderby, $desc_first ) = $sortable[ $column_key ]; + list( $orderby, $desc_first, $abbr, $orderby_text, $initial_order ) = $sortable[ $column_key ]; + /* + * 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 ) { - $order = 'asc' === $current_order ? 'desc' : 'asc'; - + // 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'; + $class[] = 'sortable'; + $class[] = 'desc' === $order ? 'asc' : 'desc'; + $order_text = 'asc' === $order ? __( 'Sort ascending.' ) : __( 'Sort descending.' ); + } + if ( '' !== $order_text ) { + $order_text = ' ' . $order_text . ''; } - $column_display_name = sprintf( - '%s', - esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ), - $column_display_name - ); + // Print an 'abbr' attribute if a value is provided via get_sortable_columns(). + $abbr_attr = $abbr ? ' abbr="' . esc_attr( $abbr ) . '"' : ''; + $column_display_name = '' . $column_display_name . '' . $order_text . ''; } $tag = ( 'cb' === $column_key ) ? 'td' : 'th'; @@ -1367,7 +1418,73 @@ class WP_List_Table { $class = "class='" . implode( ' ', $class ) . "'"; } - echo "<$tag $scope $id $class>$column_display_name"; + echo "<$tag $scope $id $class $aria_sort_attr $abbr_attr>$column_display_name"; + } + } + + /** + * 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 4.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 ] ) ) { + + list( $orderby, $desc_first, $abbr, $orderby_text, $initial_order ) = $sortable[ $column_key ]; + + 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 ) { + $order_text = 'asc' === $current_order ? __( 'Ascending.' ) : __( 'Descending.' ); + echo '
' . $orderby_text . ' ' . $order_text . '

'; + + return; + } + } } } @@ -1384,6 +1501,7 @@ class WP_List_Table { $this->screen->render_screen_reader_content( 'heading_list' ); ?> + print_table_description(); ?> print_column_headers(); ?> diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php index cbe419d920..5ae5b25240 100644 --- a/src/wp-admin/includes/class-wp-media-list-table.php +++ b/src/wp-admin/includes/class-wp-media-list-table.php @@ -389,11 +389,11 @@ class WP_Media_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { return array( - 'title' => 'title', - 'author' => 'author', - 'parent' => 'parent', - 'comments' => 'comment_count', - 'date' => array( 'date', true ), + 'title' => array( 'title', false, _x( 'File', 'column name' ), __( 'Table ordered by File Name.' ) ), + 'author' => array( 'author', false, __( 'Author' ), __( 'Table ordered by Author.' ) ), + 'parent' => array( 'parent', false, _x( 'Uploaded to', 'column name' ), __( 'Table ordered by Uploaded To.' ) ), + 'comments' => array( 'comment_count', __( 'Comments' ), false, __( 'Table ordered by Comments.' ) ), + 'date' => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ), 'desc' ), ); } diff --git a/src/wp-admin/includes/class-wp-ms-sites-list-table.php b/src/wp-admin/includes/class-wp-ms-sites-list-table.php index 2416dd1f73..1359687235 100644 --- a/src/wp-admin/includes/class-wp-ms-sites-list-table.php +++ b/src/wp-admin/includes/class-wp-ms-sites-list-table.php @@ -389,10 +389,19 @@ class WP_MS_Sites_List_Table extends WP_List_Table { * @return array */ protected function get_sortable_columns() { + + if ( is_subdomain_install() ) { + $abbr = __( 'Domain' ); + $blogname_orderby_text = __( 'Table ordered by Site Domain Name.' ); + } else { + $abbr = __( 'Path' ); + $blogname_orderby_text = __( 'Table ordered by Site Path.' ); + } + return array( - 'blogname' => 'blogname', - 'lastupdated' => 'lastupdated', - 'registered' => 'blog_id', + 'blogname' => array( 'blogname', false, $abbr, $blogname_orderby_text ), + 'lastupdated' => array( 'lastupdated', true, __( 'Last Updated' ), __( 'Table ordered by Last Updated.' ) ), + 'registered' => array( 'blog_id', true, _x( 'Registered', 'site' ), __( 'Table ordered by Site Registered Date.' ), 'desc' ), ); } diff --git a/src/wp-admin/includes/class-wp-ms-themes-list-table.php b/src/wp-admin/includes/class-wp-ms-themes-list-table.php index d1d467f19e..6dfe3fe71d 100644 --- a/src/wp-admin/includes/class-wp-ms-themes-list-table.php +++ b/src/wp-admin/includes/class-wp-ms-themes-list-table.php @@ -343,7 +343,7 @@ class WP_MS_Themes_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { return array( - 'name' => 'name', + 'name' => array( 'name', false, __( 'Theme' ), __( 'Table ordered by Theme Name.' ), 'asc' ), ); } diff --git a/src/wp-admin/includes/class-wp-ms-users-list-table.php b/src/wp-admin/includes/class-wp-ms-users-list-table.php index e1eec05cba..2e6772f69d 100644 --- a/src/wp-admin/includes/class-wp-ms-users-list-table.php +++ b/src/wp-admin/includes/class-wp-ms-users-list-table.php @@ -212,10 +212,10 @@ class WP_MS_Users_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { return array( - 'username' => 'login', - 'name' => 'name', - 'email' => 'email', - 'registered' => 'id', + 'username' => array( 'login', false, __( 'Username' ), __( 'Table ordered by Username.' ), 'asc' ), + 'name' => array( 'name', false, __( 'Name' ), __( 'Table ordered by Name.' ) ), + 'email' => array( 'email', false, __( 'E-mail' ), __( 'Table ordered by E-mail.' ) ), + 'registered' => array( 'id', false, _x( 'Registered', 'user' ), __( 'Table ordered by User Registered Date.' ) ), ); } diff --git a/src/wp-admin/includes/class-wp-posts-list-table.php b/src/wp-admin/includes/class-wp-posts-list-table.php index 73f6194812..54bd19441d 100644 --- a/src/wp-admin/includes/class-wp-posts-list-table.php +++ b/src/wp-admin/includes/class-wp-posts-list-table.php @@ -760,12 +760,28 @@ class WP_Posts_List_Table extends WP_List_Table { * @return array */ protected function get_sortable_columns() { - return array( - 'title' => 'title', - 'parent' => 'parent', - 'comments' => 'comment_count', - 'date' => array( 'date', true ), - ); + + $post_type = $this->screen->post_type; + + if ( 'page' === $post_type ) { + $title_orderby_text = isset( $_GET['orderby'] ) ? __( 'Table ordered by Title.' ) : __( 'Table ordered by Hierarchical Menu Order and Title.' ); + $sortables = array( + 'title' => array( 'title', false, __( 'Title' ), $title_orderby_text, 'asc' ), + 'parent' => array( 'parent', false ), + 'comments' => array( 'comment_count', false, __( 'Comments' ), __( 'Table ordered by Comments.' ) ), + 'date' => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ) ), + ); + } else { + $sortables = array( + 'title' => array( 'title', false, __( 'Title' ), __( 'Table ordered by Title.' ) ), + 'parent' => array( 'parent', false ), + 'comments' => array( 'comment_count', false, __( 'Comments' ), __( 'Table ordered by Comments.' ) ), + 'date' => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ), 'desc' ), + ); + } + // Custom Post Types: there's a filter for that, see get_column_info(). + + return $sortables; } /** diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 0671f8580d..3c277d6a61 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -208,12 +208,20 @@ class WP_Terms_List_Table extends WP_List_Table { * @return array */ protected function get_sortable_columns() { + $taxonomy = $this->screen->taxonomy; + + if ( ! isset( $_GET['orderby'] ) && is_taxonomy_hierarchical( $taxonomy ) ) { + $name_orderby_text = __( 'Table ordered hierarchically.' ); + } else { + $name_orderby_text = __( 'Table ordered by Name.' ); + } + return array( - 'name' => 'name', - 'description' => 'description', - 'slug' => 'slug', - 'posts' => 'count', - 'links' => 'count', + 'name' => array( 'name', false, _x( 'Name', 'term name' ), $name_orderby_text, 'asc' ), + 'description' => array( 'description', false, __( 'Description' ), __( 'Table ordered by Description.' ) ), + 'slug' => array( 'slug', false, __( 'Slug' ), __( 'Table ordered by Slug.' ) ), + 'posts' => array( 'count', false, _x( 'Count', 'Number/count of items' ), __( 'Table ordered by Posts Count.' ) ), + 'links' => array( 'count', false, __( 'Links' ), __( 'Table ordered by Links.' ) ), ); } diff --git a/src/wp-admin/includes/class-wp-users-list-table.php b/src/wp-admin/includes/class-wp-users-list-table.php index 4c4d5a9f6d..593479abc0 100644 --- a/src/wp-admin/includes/class-wp-users-list-table.php +++ b/src/wp-admin/includes/class-wp-users-list-table.php @@ -393,8 +393,8 @@ class WP_Users_List_Table extends WP_List_Table { */ protected function get_sortable_columns() { $columns = array( - 'username' => 'login', - 'email' => 'email', + 'username' => array( 'login', false, __( 'Username' ), __( 'Table ordered by Username.' ), 'asc' ), + 'email' => array( 'email', false, __( 'E-mail' ), __( 'Table ordered by E-mail.' ) ), ); return $columns;