Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/wp-admin/includes/class-wp-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ protected function pagination( $which ) {
$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 = $this->get_current_url();

$current_url = remove_query_arg( $removable_query_args, $current_url );

Expand Down Expand Up @@ -1184,6 +1184,36 @@ protected function pagination( $which ) {
echo $this->_pagination;
}

/**
* Builds the current request URL for list table links.
*
* When `HTTP_HOST` omits the port, non-default ports from `SERVER_PORT`
* are preserved so search, sorting, and pagination keep working.
*
* @since 7.2.0
*
* @return string Current URL including scheme, host, port, and request URI.
*/
protected function get_current_url() {
$host = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '';
$uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';

if ( isset( $_SERVER['SERVER_PORT'] ) ) {
$port = (int) $_SERVER['SERVER_PORT'];
$parsed = wp_parse_url( 'http://' . $host );

if ( $port && empty( $parsed['port'] ) ) {
$default_port = is_ssl() ? 443 : 80;

if ( $default_port !== $port ) {
$host .= ':' . $port;
}
}
}

return set_url_scheme( 'http://' . $host . $uri );
}

/**
* Gets a list of columns.
*
Expand Down Expand Up @@ -1408,7 +1438,7 @@ public function get_column_count() {
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 = $this->get_current_url();
$current_url = remove_query_arg( 'paged', $current_url );

// When users click on a column header to sort by other columns.
Expand Down
99 changes: 99 additions & 0 deletions tests/phpunit/tests/admin/wpListTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -592,4 +592,103 @@ public function test_search_box_works_with_orderby_string() {

$this->assertStringContainsString( $expected_html, $actual );
}

/**
* Tests that list table pagination links preserve a non-default port
* when HTTP_HOST does not include it.
*
* @ticket 59272
*
* @covers WP_List_Table::get_current_url
* @covers WP_List_Table::pagination
*/
public function test_pagination_links_preserve_non_default_port() {
$list_table = new WP_List_Table_Current_Url_Test_Double();

$_SERVER['HTTP_HOST'] = 'localhost';
$_SERVER['REQUEST_URI'] = '/wp-admin/edit.php';
$_SERVER['SERVER_PORT'] = '8182';
unset( $_SERVER['HTTPS'] );

$output = $list_table->get_pagination_html();

$this->assertStringContainsString( 'localhost:8182/wp-admin/edit.php', $output );
$this->assertStringNotContainsString( 'http://localhost/wp-admin/edit.php', $output );
}

/**
* Tests that list table pagination links do not duplicate a port already
* present in HTTP_HOST.
*
* @ticket 59272
*
* @covers WP_List_Table::get_current_url
* @covers WP_List_Table::pagination
*/
public function test_pagination_links_do_not_duplicate_port_in_host() {
$list_table = new WP_List_Table_Current_Url_Test_Double();

$_SERVER['HTTP_HOST'] = 'localhost:8182';
$_SERVER['REQUEST_URI'] = '/wp-admin/edit.php';
$_SERVER['SERVER_PORT'] = '8182';
unset( $_SERVER['HTTPS'] );

$output = $list_table->get_pagination_html();

$this->assertStringContainsString( 'localhost:8182/wp-admin/edit.php', $output );
$this->assertStringNotContainsString( 'localhost:8182:8182', $output );
}

/**
* Tests that default ports are not appended when HTTP_HOST has no port.
*
* @ticket 59272
*
* @covers WP_List_Table::get_current_url
* @covers WP_List_Table::pagination
*/
public function test_pagination_links_do_not_append_default_http_port() {
$list_table = new WP_List_Table_Current_Url_Test_Double();

$_SERVER['HTTP_HOST'] = 'example.org';
$_SERVER['REQUEST_URI'] = '/wp-admin/edit.php';
$_SERVER['SERVER_PORT'] = '80';
unset( $_SERVER['HTTPS'] );

$output = $list_table->get_pagination_html();

$this->assertStringContainsString( 'example.org/wp-admin/edit.php', $output );
$this->assertStringNotContainsString( 'example.org:80/', $output );
}
}

/**
* Test double that exposes pagination HTML for URL assertions.
*/
class WP_List_Table_Current_Url_Test_Double extends WP_List_Table {
public function get_columns() {
return array(
'title' => 'Title',
);
}

/**
* Returns pagination markup for the current request URL.
*
* @return string Pagination HTML.
*/
public function get_pagination_html() {
$this->_pagination = null;
$this->set_pagination_args(
array(
'total_items' => 40,
'total_pages' => 4,
'per_page' => 10,
)
);

ob_start();
$this->pagination( 'bottom' );
return ob_get_clean();
}
}