thanks again, works great

I've updated this template to fix the 3000 iterations bug. Originally if there were say 100 pages of results, the template would perform 100 iterations and calculate which page numbers to display. I have a section with 180,000 entries (9,000 pages of results), which causes an XSLT error since there is a limit of 3000 iterations.

The template now calculates the start/end of the page numbers to display, and iterates only between these bounds. So in the example in my original post display-number is 7, therefore there are only 7 iterations performed. Much better performance!

180,000 entries? That's impressive! It's good to hear that Symphony is robust enough to handle that volume of data.

@nickdunn: Playing around with your fascinating template I found some improvements - maybe you consider implementing them:

First: You are missing a test if it makes sense to put some dots right after the first element. This might be done like this (see the last three lines):

<xsl:template name="pagination-numbers">
    <xsl:param name="first-page" select="$first-page"/>
    <xsl:param name="last-page" select="$last-page"/>
    <xsl:param name="current-page" select="$current-page"/>
    <xsl:param name="iterations" select="$iterations"/>
    <xsl:param name="total-pages" select="$total-pages"/>
    <xsl:param name="url" select="$url"/>
    <xsl:param name="count" select="$iterations"/>

    <xsl:if test="$count &gt;= 0">

        <xsl:variable name="this-page" select="$first-page + ($iterations - $count)"/>

        <xsl:if test="$this-page = $first-page and $first-page &gt; 1">
                <a class="page">
                    <xsl:attribute name="href">
                        <xsl:call-template name="string-replace">
                            <xsl:with-param name="string" select="$url" />
                            <xsl:with-param name="search" select="'$'" />
                            <xsl:with-param name="replace" select="'1'" />
                <xsl:if test="$this-page != 2">

(I removed the class attribute, because it is unused.)

Second: The display number must not be 1 or 2. Furthermore, it must not be bigger than "number of total pages minus one". So I added this in line 8 of your utility:

<xsl:variable name="display-number">
        <xsl:when test="$display-number &lt; 3">
            <xsl:value-of select="'3'"/>
        <xsl:when test="$display-number &lt; $pagination-element/@total-pages">
            <xsl:value-of select="$display-number"/>
            <xsl:value-of select="$pagination-element/@total-pages - 1"/>

Both additions work very well in my tests. I'd be glad if you like them.

BTW: Allen's search and replace utility might easily be replaced by this one (which is dead simple):

<xsl:template name="string-replace">
    <xsl:param name="string"/>
    <xsl:param name="search"/>
    <xsl:param name="replace"/>

         <xsl:when test="contains($string, $search)">
            <xsl:variable name="before" select="substring-before($string, $search)"/>
            <xsl:variable name="after" select="substring-after($string, $search)"/>

            <xsl:value-of select="$before"/>
            <xsl:value-of select="$replace"/>

            <xsl:call-template name="string-replace">
                 <xsl:with-param name="string" select="$after"/>
                 <xsl:with-param name="search" select="$search"/>
                 <xsl:with-param name="replace" select="$replace"/>

            <xsl:value-of select="$string"/>

Need to dole out credit where credit is due. That utility came from the EXSLT website, by Jeni Tennison:

So what exactly is the big deal with Jeni's template? I'm sure there is some magic in it, but it's hard to understand.

The description page on EXSLT explains it well:

OK, I see. This one is much more powerful.

michael-e, many thanks for taking the time to update this utility. I've added both of your modifications, although I couldn't find a case when your first was required. Is it just needed when the display-number is very small?

Anyway, I've updated the Gist.

I don't remember when it changed, but this script no longer requires the additional string-replace.xsl file — it now assumes EXSLT is available and pulls the function in through the namespace rather than a named template. Keeps the script tidier and more portable.

Nick, thank you for implementing these proposals. Regarding the first: Yes, in some test situations the dots appeared between #1 and #2, which doesn't make sense to me. But to be honest, I do not remember the display-number and the number of total pages. (They were rather small in all my tests.)

No worries — I tested on several pages I had already and all seemed well.

This is a utility we end up using on almost every site we do, so it makes sense to have it bulletproof :-)

I've modified your original template to allow overriding of "Previous" and "Next" with your own custom text (ie. Older, More Recent etc).

Could be worth adding in for others too :)

I did this manually. Having parameters for this would be nice.

Nice idea. I'll add this in the next revision.

Updated to allow a user to override the next/previous labels:

<xsl:call-template name="pagination">
    <xsl:with-param name="previous" select="'Earlier posts'" />
    <xsl:with-param name="next" select="'Newer posts'" />

Nick, actually I once again had problems getting this to work on a special server. No wonder (see

To use these extensions, you need to declare this namespace as an extension namespace in your stylesheet. If your processor supports this module, then that’s all you need to do, but if it doesn’t, then you need to use a specific third-party implementation or the module stylesheet.

And on the str:replace page ( I read:

There are currently no XSLT processors that we know of that support str:replace natively.

So this means that you will definitely need another stylesheet (e.g. Jeni Tennison’s string replace template) to get the pagination utility up and running.

Wouldn’t it be better to implement a simple search/replace template like the one I posted above directly in your utility?

Here’s a small improvement for the CSS:

ul.pagination li {
    margin: 0;
    padding: 0;

By setting margin and padding to ‘0’ you won’t waste any space on the right, and so you will for example be able to make pagination float right properly.

Yes indeedy. I had removed the string-replace.xsl from the utility and added the EXSLT namespace, and all still worked on my local server so I presumed it was fine. I’ve just realised that elsewhere I have another template that is calling in the string-replace.xsl! Bah.

So you’re quite right, to ensure this script remains standalone I will integrate a simple string-replace template in with it (most likely yours, thank you ;-)).

Actually the small utility has its origin somewhere on the web – I do not remember. I simply polished it.

Thank you, Nick.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details