Search

A new XSLT utility, “HTML Truncate Advanced” is now available for download. Comments and feedback can be left here but if you discover any issues, please post it on the issue tracker.

This utility looks great but I’m having a tough time implementing it, no fault of yours, only my limited experience.

Could anyone share an example of this being used that I could see and maybe learn from?

Thanks!

<xsl:call-template name="truncate">
    <!-- path to the element containing HTML content
    e.g. a Textarea named "Comment" with Markdown enabled -->
    <xsl:with-param name="node" select="comment"/>
    <!-- words to truncate to -->
    <xsl:with-param name="limit" select="'100'"/>
</xsl:call-template>

Ahhh… thank you Nick.

This helps me finally understand call-template as well.

Much appreciated!

Just what I was looking for a few days ago.

“Solved” the problem by fetching the content of the first paragraph with <xsl:value-of select=”content/p[1]”/> instead to create previews of longer articles ;-)

Hi guys,

I’m trying to truncate a text (big text) inside a for each (it’s a news page), formatted by the rich text extension. I’ve tested it with low limits, worked perfectly. Buyt when I set the limit to 200 ( or above ), the utilitie doesn’t work properly, shorting the limit in some.

Does anyone knows if there is a problem when you use a limit like 200 or 250?

Thanks a lot!

<xsl:call-template name="truncate">
<xsl:with-param name="node" select="conteudo"/>
<xsl:with-param name="limit" select="'200'"/>
<xsl:with-param name="suffix" select="'...'"/></xsl:call-template>

I just ran into an issue where I had an image added at the top of an entry, so I needed a way to exclude the image from the text shown in the excerpt. The image was wrapped in a <p> element, so I also needed to make sure the empty element was not included. I came up with this change on line 165:

<xsl:template match="*" mode="truncate-render">
    <xsl:if test="text() != ''">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*" mode="truncate-render" />
            <xsl:apply-templates select="*[name() != 'img'] | text()" mode="truncate-render" />
        </xsl:element>
    </xsl:if>
</xsl:template>

I just realized that the predicate that checks whether the node name() is img isn't necessary. img elements don't have text nodes, so the test for a node with no text() value already excludes img elements. So this can be simplified:

<xsl:template match="*" mode="truncate-render">
    <xsl:if test="text() != ''">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*" mode="truncate-render" />
            <xsl:apply-templates select="* | text()" mode="truncate-render" />
        </xsl:element>
    </xsl:if>
</xsl:template>

hi,

Could anyone Please tell how to get only the text without applying styles at the time of using this utility. I am using content editor for adding the content, but i need only the text without applying the styles which added using the editor.

Now i am getting the text with styles like bold, italic, horizontal line, etc.,

thanks

I'm having some troubles with this one.

Given this XML:

<p>Villa Craiasa Branului (Wizard of Bran) is 4 bedroom newly built house 3km from Bran Castle, made famous in Bram Stoker’s Dracula novel. It set atop the heights of Predelut village, overlooking Bran, Zarnesti &amp; the valley of Brasov with beautiful views of the Transylvanian Alps including Piatra Craiului, the awesome Bucegi massive &amp; a host of other treats including orchards, forests &amp; plains.</p> 
<p>If you are looking for something special, why not come somewhere to get away from it all, to place equipped with everything you could need.  From a wood burner to a play station, from bicycles to Sun loungers, right through to feather-down bedding, pillows &amp; pure cotton sheets &amp; all the comforts you might wish in your own home have been accommodated.</p> 
<p><a>Bran</a> itself is a hugely underrated jewel in Romania’s crown. Without doubt, some of Europe’s finest scenery &amp; unchanged traditions lie here.  The most common response from travellers from all over the world is one of total unexpected surprise. Actual Transylvanian Romania &amp; its perception bear no comparison. Many outsiders have only seen the ruins of a former communist regime that devastated former historic cities. Not so in Bran. Much of it remains unchanged for many centuries.</p>

on calling the template:

<xsl:call-template name="truncate">
    <xsl:with-param name="node" select="path/to/content" />
    <xsl:with-param name="limit" select="900" />
</xsl:call-template>

I'm getting this out:

<p> 
    <p>Bran itself is a hugely underrated jewel in Romania’s crown. Without doubt, some of Europe’s finest scenery & unchanged traditions lie here.  The most…</p> 
</p>

Notice nested <p>. Any idea how to fix the utility?

I got the same issue, the truncated text start with the first nested tag and ends as it should, but the begining is not displayed. For me it s not a < a > but a < strong >.

I don't have this bug locally or on my demo server, but i have it on the final, so i imagine it s due to the server.

Any idea to solve this?

I've fixed this utility. It had issues with rendering nodes. As a side effect, it was simplified a bit. Here it is:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:str="http://exslt.org/strings"
        xmlns:exsl="http://exslt.org/common"
        extension-element-prefixes="str exsl">




    <!-- A tolerant truncation utility -->
    <xsl:template name="truncate">
        <xsl:param name="node"/>
        <xsl:param name="limit"/>

        <!-- Append this to the end of the value, if we have truncated it. -->
        <xsl:param name="suffix" select="'&#x2026;'"/>

        <!-- Force the suffix. Set to 'yes' to always show it. -->
        <xsl:param name="force_suffix" select="'no'"/>

        <!-- Tolerance allows words that fall over the required length be appended,
            as long as they don't go over $length + $tolerance. -->
        <xsl:param name="tolerance" select="3"/>

        <!-- Treat any of these characters as the end of a word. -->
        <xsl:param name="delimiters" select="str:tokenize(' -!?:)(;,.…&#x2013;&#x2014;&#160;', '')"/>

        <xsl:choose>
            <xsl:when test="$limit &lt; string-length($node) or $force_suffix = 'yes'">
                <!-- Find actual limit -->
                <xsl:variable name="real-limit">
                    <xsl:choose>
                        <xsl:when test="string-length($node) &lt;= $limit">
                            <xsl:value-of select="string-length($node)"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:variable name="preceding-delimiter">
                                <xsl:call-template name="truncate-find-delimiter">
                                    <xsl:with-param name="value">
                                        <xsl:for-each select="str:tokenize(substring($node, 1, $limit + 1), '')">
                                            <xsl:sort select="position()" data-type="number" order="descending"/>

                                            <xsl:value-of select="."/>
                                        </xsl:for-each>
                                    </xsl:with-param>
                                    <xsl:with-param name="delimiters" select="$delimiters"/>
                                </xsl:call-template>
                            </xsl:variable>

                            <xsl:variable name="following-delimiter">
                                <xsl:call-template name="truncate-find-delimiter">
                                    <xsl:with-param name="value" select="substring($node, $limit + 1)"/>
                                    <xsl:with-param name="delimiters" select="$delimiters"/>
                                </xsl:call-template>
                            </xsl:variable>

                            <xsl:choose>
                                <xsl:when test="$preceding-delimiter = 0 and $following-delimiter = 0">
                                    <xsl:value-of select="$limit"/>
                                </xsl:when>
                                <xsl:when test="$following-delimiter &lt;= $tolerance">
                                    <xsl:value-of select="$limit + $following-delimiter"/>
                                </xsl:when>
                                <xsl:when test="$preceding-delimiter &lt;= $tolerance">
                                    <xsl:value-of select="$limit - $preceding-delimiter"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="$limit"/>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:variable>

                <xsl:apply-templates select="($node/* | $node/text())[1]" mode="truncate-search">
                    <xsl:with-param name="limit" select="$real-limit"/>
                    <xsl:with-param name="suffix" select="exsl:node-set($suffix)"/>
                </xsl:apply-templates>
            </xsl:when>

            <xsl:otherwise>
                <xsl:copy-of select="exsl:node-set($node)/* | exsl:node-set($node)/text()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>





    <!-- Search for the node that runs over the limit. -->
    <xsl:template match="* | text()" mode="truncate-search">
        <xsl:param name="length" select="0"/>
        <xsl:param name="limit" select="0"/>
        <xsl:param name="position" select="1"/>
        <xsl:param name="suffix"/>

        <xsl:variable name="next" select="(../* | ../text())[$position + 1]"/>

        <xsl:choose>
            <!-- Limit found -->
            <xsl:when test="$length + string-length(.) >= $limit">
                <xsl:choose>
                    <!-- Keep searching children -->
                    <xsl:when test="*">
                        <xsl:copy>
                            <xsl:for-each select="@*">
                                <xsl:copy/>
                            </xsl:for-each>

                            <xsl:apply-templates select="(* | text())[1]" mode="truncate-search">
                                <xsl:with-param name="length" select="$length"/>
                                <xsl:with-param name="limit" select="$limit"/>
                                <xsl:with-param name="suffix" select="$suffix"/>
                            </xsl:apply-templates>
                        </xsl:copy>
                    </xsl:when>

                    <!-- The node to truncate -->
                    <xsl:otherwise>
                        <xsl:apply-templates select="." mode="truncate-apply">
                            <xsl:with-param name="limit" select="$limit - $length"/>
                            <xsl:with-param name="suffix" select="$suffix"/>
                        </xsl:apply-templates>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>

            <!-- Keep searching at this level -->
            <xsl:otherwise>
                <xsl:copy-of select="."/>

                <xsl:if test="$next">
                    <xsl:apply-templates select="$next" mode="truncate-search">
                        <xsl:with-param name="length" select="$length + string-length(.)"/>
                        <xsl:with-param name="limit" select="$limit"/>
                        <xsl:with-param name="position" select="$position + 1"/>
                        <xsl:with-param name="suffix" select="$suffix"/>
                    </xsl:apply-templates>
                </xsl:if>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>




    <!-- Apply truncation to node -->
    <xsl:template match="*" mode="truncate-apply">
        <xsl:param name="limit"/>
        <xsl:param name="suffix"/>

        <xsl:copy>
            <xsl:for-each select="@*">
                <xsl:copy/>
            </xsl:for-each>

            <xsl:apply-templates select="text()" mode="truncate-apply">
                <xsl:with-param name="limit" select="$limit"/>
                <xsl:with-param name="suffix" select="$suffix"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>


    <xsl:template match="text()" mode="truncate-apply">
        <xsl:param name="limit"/>
        <xsl:param name="suffix"/>

        <xsl:value-of select="substring(., 1, $limit)"/>

        <xsl:copy-of select="$suffix"/>
    </xsl:template>




    <!-- Search for a delimiter -->
    <xsl:template name="truncate-find-delimiter">
        <xsl:param name="value"/>
        <xsl:param name="delimiters"/>
        <xsl:param name="length" select="0"/>

        <xsl:choose>
            <xsl:when test="$value != ''">
                <xsl:choose>
                    <xsl:when test="$delimiters[starts-with(substring($value, 1, 1), .)]">
                        <xsl:value-of select="$length"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="truncate-find-delimiter">
                            <xsl:with-param name="value" select="substring($value, 2)"/>
                            <xsl:with-param name="delimiters" select="$delimiters"/>
                            <xsl:with-param name="length" select="$length + 1"/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$length"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>




</xsl:stylesheet>

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