Make WordPress Core

Opened 3 years ago

Last modified 3 years ago

#54114 new defect (bug)

paginate_links() ajax

Reported by: tkama's profile Tkama Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.8.1
Component: General Keywords: has-patch
Focuses: Cc:

Description

paginate_links() may not work incorrectly for AJAX requests and other requests that pass $_GET parameters.

Let's look how paginate_links() works in AJAX request handler function. I wrote a demo plugin:

<?php
/*
Plugin Name: paginate_links() Bug
*/

add_action( 'wp_ajax_' . 'paginate_links_test', 'paginate_links_test_ajax' );

add_action(
        'admin_menu',
        function () {

                add_options_page(
                        'paginate_links() test',
                        'paginate_links() test',
                        'manage_options',
                        'paglinkstest',
                        static function() {
                                ?>
                        <script>
                                jQuery( function( $ ){

                                        $( '#test_list' ).on( 'click', 'a.page-numbers', function( event ){

                                                event.preventDefault()
                                                let url = encodeURIComponent( this.getAttribute( 'href' ) )

                                                $( '#test_list' )
                                                        .empty()
                                                        .load( `${ ajaxurl }?action=paginate_links_test&url=${ url }` )
                                        } )
                                } )
                        </script>

                        <style>
                                .pagination{ margin:3em; }
                                .pagination > *{ padding:1em 2em; font-size:150%; }
                                .pagination a:hover{ color:red; background:#fff; }
                        </style>

                        <div class="wrap">

                                <h2><?php echo get_admin_page_title(); ?></h2>

                                <div id="test_list">
                                        <?php echo paginate_links_test(); ?>
                                </div>
                        </div>
                                <?php
                        }
                );
        }
);


function paginate_links_test( $page = null ) {

        $args = array(
                'base'             => admin_url( 'options-general.php?page=paglinkstest&paged=%_%' ),
                'format'           => '%#%',
                'total'            => 20,
                'current'          => max( 1, (int) ( $_GET['paged'] ?? $page ?? 1 ) ),
                'merge_query_vars' => false,
        );

        $pagination = paginate_links( $args );

        ?>
        <pre style="white-space: pre-wrap"><?php echo htmlspecialchars( $pagination ); ?></pre>
        <div class="pagination"><?php echo $pagination; ?></div>
        <?php

}

function paginate_links_test_ajax() {

        echo '<p>$_GET[url]: ' . $_GET['url'] . '</p>';

        wp_parse_str( wp_parse_url( $_GET['url'], PHP_URL_QUERY ), $query_vars );

        paginate_links_test( $query_vars['paged'] );

        exit;
}

Let's install + activate this plugin, go to it's page (under settings menu item) and click on the pagination links:

First paginate links looks good:

<a class="page-numbers" href="https://wp.dev/wp-admin/options-general.php?page=paglinkstest&#038;paged=20">20</a>

But with each click the link to the page grows additional unnecessary parameters

Second click:

<a class="page-numbers" href="https://wp.dev/wp-admin/options-general.php?page=paglinkstest&#038;paged=20&#038;action=paginate_links_test&#038;url=https%3A%2F%2Fwp.dev%2Fwp-admin%2Foptions-general.php%3Fpage%3Dpaglinkstest%26paged%3D2">20</a>

Third:

<a class="page-numbers" href="https://wp.dev/wp-admin/options-general.php?page=paglinkstest&#038;paged=20&#038;action=paginate_links_test&#038;url=https%3A%2F%2Fwp.dev%2Fwp-admin%2Foptions-general.php%3Fpage%3Dpaglinkstest%26paged%3D3%26action%3Dpaginate_links_test%26url%3Dhttps%253A%252F%252Fwp.dev%252Fwp-admin%252Foptions-general.php%253Fpage%253Dpaglinkstest%2526paged%253D2">20</a>

This happens because paginate_links() forcibly add current request $_GET parameters to each pagination link and we have no way to disable this behavior. So we can't normally use paginate_links() in AJAX requests in some circumstance.

The best solution is to somehow cut out the merge of additional parameters, because this function is designed to build pagination links based on the passed data and this side effect only spoils everything. But I don't know what it is about backward compatibility.

So, the easiest solution is to add additional argument to paginate_links(), which allow to disable merge of additional GET parameters.

It will at least allow to use this function in AJAX requests and any other requests, where it's not necessary to add request parameters to pagination links.

Attachments (1)

paginate_links.diff (1.9 KB) - added by Tkama 3 years ago.

Download all attachments as: .zip

Change History (3)

@Tkama
3 years ago

This ticket was mentioned in Slack in #core-test by hellofromtonya. View the logs.


3 years ago

Note: See TracTickets for help on using tickets.