Template-based pagination

Discussion corner for Developers of Serendipity.
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Template-based pagination

Post by yellowled »

Hi guys,

I've been working on code for a "real" pagination, i.e. not only links to the next and previous pages, but to the next and previous three pages of a blog. Other objectives I gave myself: I wanted to do this using only Smarty (which according to http://board.s9y.org/viewtopic.php?t=1794 is possible - and indeed it is), plus I also wanted to display seven links even if there are no three previous or next pages (i.e. show pages 1-7 if the current page is between 1 and 7 etc.).

So this is the code I came up with:

Code: Select all

<div class="pagelinks">
    {eval var=$footer_currentPage-3 assign="paginationStartPage"}
    {eval var=$footer_currentPage+3 assign="paginationEndPage"}

    {if $paginationStartPage <= 0}{assign var="paginationStartPage" value="1"}{/if}
    {if $paginationEndPage <= 7}{assign var="paginationEndPage" value="7"}{/if}

    {if $paginationEndPage >= $footer_totalPages+1}
        {eval var=$footer_totalPages-6 assign="paginationStartPage"}
        {eval var=$footer_totalPages assign="paginationEndPage"}
    {/if}

    {section name=i loop=$footer_totalPages+1}
        {if $smarty.section.i.index >= $paginationStartPage and $smarty.section.i.index <= $paginationEndPage}
            {if $smarty.section.i.index != $footer_currentPage}<a href="{$footer_pageLink|@sprintf:$smarty.section.i.index}">{/if}{$smarty.section.i.index}{if $smarty.section.i.index != $footer_currentPage}</a>{/if}
        {/if}
    {/section} 
</div>
Although this works the way it is supposed to, it also looks kind of clumsy, and I'm not sure it's the best solution. It doesn't seem to have any issues, but it comes from a mind not used to "real" programming, so someone please have a look at it and tell me whether it's total BS or actually usable :lol:

YL
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

That's not bad at all.

The only problem I see is when you've got less than 7 total pages. For instance, if your blog is only 3 pages long, your $paginationStartPage will be -3. It'll still work, since that's only used for comparison. There's also a bit of inefficiency in the {section}, since it could loop through a bunch of pages where nothing happens.

However, we might be able to improve things by using a different algorithm. {section} allows you to skip some initial entries using the "start" attribute, and we can constrain it to 7 pages using the "max" attribute. I think we can use that to ensure that we always start and end on a valid page number, eliminating the big {if}, and thereby making a few variables unnecessary.

We can figure out where to start by guessing "3 pages ago"; if that won't let us display 7 pages, we re-guess "7 from the end"; if that's negative, we guess page 1. Then we print at most 7 pages starting from the guessed page, going to the number of pages total.

I assume footer_totalPages indicates the total number of pages, not the index of the last page. (If I'm wrong, add one to it.)

Here's the programmer's version:

Code: Select all

<div class="pagelinks">
    {eval var=$footer_currentPage-3 assign="paginationStartPage"}
    {if $footer_currentPage+3 > $footer_totalPages}
        {eval var=$footer_totalPages-6 assign="paginationStartPage"}
    {/if}
    {if $paginationStartPage <= 0}{assign var="paginationStartPage" value="1"}{/if}

    {section name=i start=$paginationStartPage loop=$footer_totalPages+1 max=7}
        {if $smarty.section.i.index != $footer_currentPage}
            <a href="{$smarty.section.i.index|string_format:$footer_pageLink}">{$smarty.section.i.index}</a>
        {else}
            {$smarty.section.i.index}
        {/if} 
    {/section}
</div>
I gave this code a trial on my sandbox, which unfortunately only has one page. There were no syntax errors. I forced it to output the link, just to verify the string_format was working; it does. I'm confident it'll work on blogs with any number of pages, though.

Finally, this sets us up for a neato: if there are more pages than we can display, add a link to the last one.

Code: Select all

    {if $smarty.section.i.loop < $footer_totalPages}
        ...<a href="{$footer_pageLink}">{$footer_totalPages}</a>
    {/if}
Judebert
---
Website | Wishlist | PayPal
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

judebert wrote:I gave this code a trial on my sandbox, which unfortunately only has one page. There were no syntax errors. I forced it to output the link, just to verify the string_format was working; it does. I'm confident it'll work on blogs with any number of pages, though.
So am I - I just gave it a test run in my devblog (which has more than one page :)), it worked flawlessly. Thanks, Jude :)
judebert wrote:Finally, this sets us up for a neato: if there are more pages than we can display, add a link to the last one.

Code: Select all

    {if $smarty.section.i.loop < $footer_totalPages}
        ...<a href="{$footer_pageLink}">{$footer_totalPages}</a>
    {/if}
I like the idea, but I'm wondering where to put this. Within the {section} or after it?

YL
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

That would go after the {section}. You don't want it to print after every page number, after all.

Glad to hear it's working!
Judebert
---
Website | Wishlist | PayPal
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

judebert wrote:Finally, this sets us up for a neato: if there are more pages than we can display, add a link to the last one.

Code: Select all

    {if $smarty.section.i.loop < $footer_totalPages}
        ...<a href="{$footer_pageLink}">{$footer_totalPages}</a>
    {/if}
Doesn't work, which seems logical to me: In the definition statement for section i, we have loop=$footer_totalPages+1 which for obvious reasons is never < footer_totalPages.

We need some way to assign the number of the last page linked to to a variable and compare that variable to $footer_totalPages, I guess. I'm at it, but I haven't figured it out yet.

YL
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

Perhaps I'm just using the documentation wrong. The .loop attribute is supposed to contain the last index looped in the {section}; with our max=7, that could be less than $footer_totalPages, couldn't it? If you had 10 pages, and you started at 1, you'd loop from 1 to 7. $smarty.section.i.loop should then be 8, which is less than 10, and would trigger the {if}.

Of course, an ounce of empirical data is worth a thousand pounds of speculation. If it's not working, then either the documentation is wrong, or I'm reading it wrong.

Perhaps the .last and .iteration properties work as expected? In that case, we could move the endcap inside the {section}, using an {if} like this one:

Code: Select all

{section}
    {* earlier section stuff *}
    {if $smarty.section.i.last}
        {if $smarty.section.i.iteration < $footer_totalPages}
            ...<a href="{$footer_pageLink}">{$footer_totalPages}</a>
        {/if}
    {/if}
{/section}
Judebert
---
Website | Wishlist | PayPal
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

I'm just guessing here, too, since I am at my girlfriend's place, which means I don't have my machine at home which runs my devblog and holds my code, so I can't do any testing right now.
judebert wrote:The .loop attribute is supposed to contain the last index looped in the {section}; with our max=7, that could be less than $footer_totalPages, couldn't it?
Well, that really depends - it's possible that all indexes in loop are "visited", but the code in the section is only executed for max of them ..?

I'll do some more testing tomorrow and report back here.

YL
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

Just tested it on my sandbox; the docs are way off. They don't mention that you can use .index outside the {section}, for instance. Here's what you want (tested to reduce your frustration):

Code: Select all

<div class="pagelinks">
    {eval var=$footer_currentPage-3 assign="paginationStartPage"}
    {if $footer_currentPage+3 > $footer_totalPages}
        {eval var=$footer_totalPages-6 assign="paginationStartPage"}
    {/if}
    {if $paginationStartPage <= 0}{assign var="paginationStartPage" value="1"}{/if}

    {section name=i start=$paginationStartPage loop=$footer_totalPages+1 max=7}
        {if $smarty.section.i.index != $footer_currentPage}
            <a href="{$smarty.section.i.index|string_format:$footer_pageLink}">{$smarty.section.i.index}</a>
        {else}
            {$smarty.section.i.index}
        {/if}
    {/section}
    {if $smarty.section.i.index < $footer_totalPages}
        ... <a href="{$footer_totalPages|string_format:$footer_pageLink}">{$footer_totalPages}</a>
    {/if}
</div> 
Note that, once the loop is done, $smarty.section.i.index is the last printed value + 1. Personally, I like this; it means I never see "7 8 9 ... 10". (In that case, it wouldn't have triggered the {if}, since the 10 < 10 is false.) If you want that, change the < to <= and everything will work as you desire.
Judebert
---
Website | Wishlist | PayPal
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

judebert wrote:Here's what you want (tested to reduce your frustration):
Awesome. Code works perfectly. Thanks, Jude! :)
judebert wrote:Personally, I like this; it means I never see "7 8 9 ... 10". (In that case, it wouldn't have triggered the {if}, since the 10 < 10 is false.) If you want that, change the < to <= and everything will work as you desire.
I did indeed change that; I think it's a usability thing. In that scenario, people would probably wonder why the last page is gone all of a sudden.

YL
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

yellowled wrote:Code works perfectly.
Now also live in my blog, which is a real test for the code with its 214 pages :wink:

I added one more little thing:

Code: Select all

{else}
    <span id="thispage">{$smarty.section.i.index}</span>
{/if}
That way, you can apply formatting to the unlinked page number.

YL
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

Great idea! I think displaying these as little colored boxes would be cool.
Judebert
---
Website | Wishlist | PayPal
yellowled
Regular
Posts: 7111
Joined: Fri Jan 13, 2006 11:46 am
Location: Eutin, Germany
Contact:

Post by yellowled »

judebert wrote:Great idea! I think displaying these as little colored boxes would be cool.
http://www.smashingmagazine.com/2007/11 ... practices/

Knock yourself out :wink:

YL
Don Chambers
Regular
Posts: 3652
Joined: Mon Feb 13, 2006 2:40 am
Location: Chicago, IL, USA
Contact:

Post by Don Chambers »

This functionality was recently added to bulletproof in svn, but never styled. As I began to add styling to it in bulletproof, I decided to add 2 additional things: 1) the same kind of link to the first page that is provided for the last page, 2) not showing the [...] if the first or last page is the next consecutive page in the list, and 3) links to previous page/next page.

I have not finished styling the other colorsets in bp yet, but will do so later today. My code revision - which I intend to commit to bp unless there any further suggestions or objections - is as follows:

Code: Select all

    {if $template_option.show_pagination == 'true'}
        <div class="pagination">
            {eval var=$footer_currentPage-3 assign="paginationStartPage"}
            {if $footer_currentPage+3 > $footer_totalPages}
                {eval var=$footer_totalPages-6 assign="paginationStartPage"}
            {/if}
            {if $paginationStartPage <= 0}
                {assign var="paginationStartPage" value="1"}
            {/if}
            {if $footer_prev_page}
                <a title="{$CONST.PREVIOUS_PAGE}" href="{$footer_prev_page}">◄</a>
            {/if}
            {if $paginationStartPage > 1}
                <a href="{'1'|string_format:$footer_pageLink}">1</a>
            {/if}
            {if $paginationStartPage > 2}
                …
            {/if}
            {section name=i start=$paginationStartPage loop=$footer_totalPages+1 max=7}
                {if $smarty.section.i.index != $footer_currentPage}
                    <a href="{$smarty.section.i.index|string_format:$footer_pageLink}">{$smarty.section.i.index}</a>
                {else}
                    <span id="thispage">{$smarty.section.i.index}</span>
                {/if}
            {/section}
            {if $smarty.section.i.index < $footer_totalPages}
                …
            {/if}
            {if $smarty.section.i.index <= $footer_totalPages}
                <a href="{$footer_totalPages|string_format:$footer_pageLink}">{$footer_totalPages}</a>
            {/if}
            {if $footer_next_page}
                <a title="{$CONST.NEXT_PAGE}" href="{$footer_next_page}">►</a>
            {/if}
        </div>
    {/if}
Oddly enough, the code piece above renders the html code for the left and right arrows graphically, not as code. Left /previous is #9668; and right/next is #9658; both preceeded by ampersands.

A screenshot of how this looks in the bp purple colorset (not yet comitted) is:
Image

The number 6 is the current page, and the number 4 is the hover color.
=Don=
Don Chambers
Regular
Posts: 3652
Joined: Mon Feb 13, 2006 2:40 am
Location: Chicago, IL, USA
Contact:

Post by Don Chambers »

Committed changes to bulletproof in svn. These are how the styles ended up for each colorset:

Image
=Don=
judebert
Regular
Posts: 2478
Joined: Sat Oct 15, 2005 6:57 am
Location: Orlando, FL
Contact:

Post by judebert »

Looking sharp!
Judebert
---
Website | Wishlist | PayPal
Post Reply