WordPress.org

Make WordPress Core

Opened 15 months ago

Last modified 4 months ago

#40108 new enhancement

Pagination Enhancement wp_link_pages()

Reported by: mshumacher Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Themes Keywords: has-screenshots needs-refresh
Focuses: template Cc:

Description

Current implementation of the function is not well suited for posts containing 10+ pages, this creates usability issues for desktop and especially mobile users. Most popular themes which rely on this built-in function are affected. Popular hosting providers such as Wordpress.com do not allow 3rd party plugins or any enhancements to work around this issue.

Example: http://i.imgur.com/yqLOfD0.jpg Goal: Provide theme creators posibility to reduce the number of visible page links and enhance CSS theming by wrapping the current page in CSS selectable tag, allowing improved usability on mobile devices and reducing the reliance of wider community on 3rd party plugins or custom code.

Solution: Accept additional 'mixed' value for the [next_or_number] parameter and create a helper parameter to control the max number of outputted page links. Wrap the current page in a <span> to allow CSS theming.

The looks/functionality as per the screenshot above can be achieved with only ~20 additional lines of code (below) It accounts for any edge cases and ensures full backwards compatibility, however, additional testing is needed.

Enhanced wp_link_pages Function:

<?php
function wp_link_pages( $args = '' ) {
        global $page, $numpages, $multipage, $more;

        $defaults = array(
                                /**
                                 *Start of New code for making pagination into CSS friendly
                                 */
                'before'           => '<p>' . __( 'Pages:' ),
                'after'            => '</p>',
                'link_before'      => '',
                'link_after'       => '',
                'next_or_number'   => 'number',
                // new param for controling max link number in 'mixed' mode
                'navwidth'         => '3', //number of links displayed before and after current page
                'separator'        => ' ',
                'separator'        => ' ',
                'nextpagelink'     => __( 'Next page' ),
                'previouspagelink' => __( 'Previous page' ),
                'pagelink'         => '%',
                'echo'             => 1
        );

        $params = wp_parse_args( $args, $defaults );

        /**
         * Filters the arguments used in retrieving page links for paginated posts.
         *
         * @since 3.0.0
         *
         * @param array $params An array of arguments for page links for paginated posts.
         */
        $r = apply_filters( 'wp_link_pages_args', $params );

        $output = '';
        if ( $multipage ) {
                if ( 'number' == $r['next_or_number'] ) {
                        $output .= $r['before'];
                        for ( $i = 1; $i <= $numpages; $i++ ) {
                                $link = $r['link_before'] . str_replace( '%', $i, $r['pagelink'] ) . $r['link_after'];
                                if ( $i != $page || ! $more && 1 == $page ) {
                                        $link = _wp_link_page( $i ) . $link . '</a>';
                                }
                                /**
                                 * Filters the HTML output of individual page number links.
                                 *
                                 * @since 3.6.0
                                 *
                                 * @param string $link The page number HTML output.
                                 * @param int    $i    Page number for paginated posts' page links.
                                 */
                                $link = apply_filters( 'wp_link_pages_link', $link, $i );

                                // Use the custom links separator beginning with the second link.
                                $output .= ( 1 === $i ) ? ' ' : $r['separator'];
                                $output .= $link;
                        }
                        $output .= $r['after'];
                } elseif ( $more ) {
                        $output .= $r['before'];
                        $prev = $page - 1;
                        if ( $prev > 0 ) {
                                $link = _wp_link_page( $prev ) . $r['link_before'] . $r['previouspagelink'] . $r['link_after'] . '</a>';

                                /** This filter is documented in wp-includes/post-template.php */
                                $output .= apply_filters( 'wp_link_pages_link', $link, $prev );
                        }
                                                /** 
                                                 *Start of New Code for 'mixed' navigation mode
                                                 */
                                                 
                                                // Output number of links equal to $navwidth before current page
                                                if ( 'mixed' == $r['next_or_number'] ) {
                                                                For ( $i = $page - $navwidth; $i < $page; $i++) {
                                                                                if ( $i > 0) {
                                                                                $link = _wp_link_page( $i ) . $r['link_before'] . str_replace( '%', $i, $r['pagelink'] ) . $r['link_after'] . '</a>';
                                                                                $link = apply_filters( 'wp_link_pages_link', $link, $i );
                                                                                
                                                                                // Use the custom links separator beginning with the second link.
                                                                                $output .= ( 1 === $i ) ? ' ' : $r['separator'];
                                                                                $output .= $link
                                                                                }
                                                                }
                                                                // Output current page within <span> tags for enhanced styling capability
                                                                if ( $prev ) {
                                                                                $output .= $r['separator'];
                                                                }
                                                                $output .= $r['link_before'] . ('<span>') . str_replace( '%', $i, $r['pagelink'] ) . ('</span>') . $r['link_after'];
                                                                
                                                                // Output number of links equal to $navwidth after current page
                                                                For ($i = $page + 1; $i <= $numpages; $i++) {
                                                                                $link = _wp_link_page( $i ) . $r['link_before'] . str_replace( '%', $i, $r['pagelink'] ) . $r['link_after'] . '</a>';
                                                                                $link = apply_filters( 'wp_link_pages_link', $link, $i );
                                                                                $output .= $r['separator'] . $link;
                                                                }                       
                                                }
                                                
                                                /** 
                                                 *End of New Code for 'mixed' navigation mode
                                                 */
                                                 
                        $next = $page + 1;
                        
                                                if ( $next <= $numpages ) {
                                if ( $prev ) {
                                        $output .= $r['separator'];
                                }
                                $link = _wp_link_page( $next ) . $r['link_before'] . $r['nextpagelink'] . $r['link_after'] . '</a>';

                                /** This filter is documented in wp-includes/post-template.php */
                                $output .= apply_filters( 'wp_link_pages_link', $link, $next );
                        }
                        $output .= $r['after'];
                }
        }

        /**
         * Filters the HTML output of page links for paginated posts.
         *
         * @since 3.6.0
         *
         * @param string $output HTML output of paginated posts' page links.
         * @param array  $args   An array of arguments.
         */
        $html = apply_filters( 'wp_link_pages', $output, $args );

        if ( $r['echo'] ) {
                echo $html;
        }
        return $html;
} 

Let me know if I can further help to get this be pushed into the next release.

Martin https://martinshreder.com

Attachments (2)

40108.patch (2.6 KB) - added by dingo_bastard 15 months ago.
40108.2.patch (4.2 KB) - added by mshumacher 4 months ago.

Download all attachments as: .zip

Change History (12)

#1 @dingo_bastard
15 months ago

Hi @mshumacher

You can always submit a patch, and see if it'll pass.

I have one ready with the changes you've made, if you're not familiar with how patches work, I can upload it here.

#2 @mshumacher
14 months ago

  • Keywords needs-testing added

@dingo_bastard thanks a lot for the patch!

I would appreciate if anyone can do a basic test and see if the code works as expected.

Basic test scenario:

  1. Create a 10+ page post using either the <!--nextpage--> tag or Alt+Shift+P keyboard shortcut
  2. Open the post and inspect the resulting pagination HTML (copy it somewhere)
  3. Apply the patch;
  4. Reload the page ignoring cache Control+Shift+R
  5. Compare the resulting pagination HTML

(the only difference between the HTML output should be the current page which will become wrapped in a <span> tag)

Advanced test scenario:

  1. Modify the wp_link_pages() function call inside the theme loop (usually called from content.php in the theme components folder) with the following code:
    <?php
    wp_link_pages( array(
                                            'before'           => '<div class="pagination"> <p>' . __( 'Page ' ) . $page . __( ' of  ' ) . $numpages . __( ' pages' ) . '</p> <ul>',
                                            'after'            => '</ul> </div>',
                                            'link_before'      => '<li>',
                                            'link_after'       => '</li>',
                                            'navwidth'         => 3,
                                            'next_or_number'   => 'mixed',
                                            'separator'        => ' ',
                                            'nextpagelink'     => __( 'Next' ) . ' >',
                                            'previouspagelink' => '< ' . __( 'Previous' ),
                                    ) );
    
  2. Navigate to a multi page page post
  3. Check the pagination html section for errors

(output should be similar to the example provided in my original post)

Themes that call wp_link_pages() function:

  • IXION
  • Twenty Sixteen
  • Twenty Fifteen
  • Others1

[1]: Any theme is suitable for testing as long as it makes a call to the wp_link_pages function Martin https://martinshreder.com

This ticket was mentioned in Slack in #core by martinshreder. View the logs.


14 months ago

#4 @mshumacher
10 months ago

Would appreciate if there is a volunteer to help testing this patch.

#5 @mshumacher
4 months ago

  • Keywords has-patch has-screenshots added
  • Version 4.8 deleted

This ticket was mentioned in Slack in #core by martinshreder. View the logs.


4 months ago

#7 follow-up: @afercia
4 months ago

Re: the CSS part, worth noting [42440] added a couple new CSS classes: post-nav-links and post-page-numbers, together with an aria-current attribute for the current page.

#8 in reply to: ↑ 7 @mshumacher
4 months ago

Replying to afercia:

Re: the CSS part, worth noting [42440] added a couple new CSS classes: post-nav-links and post-page-numbers, together with an aria-current attribute for the current page.

Thanks, I've reviewed [42440] and updated the patch to include both the aria-current and my next_or_number enhancements.

@afercia would you be able to review and move this forward? I am happy to help with updating documentation and such

@mshumacher
4 months ago

This ticket was mentioned in Slack in #core by martinshreder. View the logs.


4 months ago

#10 @kraftbj
4 months ago

  • Keywords needs-refresh added; needs-testing has-patch removed

Can you double check 40108.2.patch?

I applied it and it has a Parse error: syntax error, unexpected '}' in /srv/www/wordpress-develop/public_html/src/wp-includes/post-template.php on line 969 error from the preceding line lacking a ;. Fixing that yielded an unexpected end of file error, so something is lacking a brace somewhere. I didn't keep digging though since I presume you have working code on your box and it was an issue in the patching.

Note: See TracTickets for help on using tickets.