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$tag>";
+ 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 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;