Just to be safe, I prefer to use root links starting with / and apply the template if the condition is met:

<xsl:template match="img/@src[starts-with(.,'/')]" mode="html">
    <xsl:attribute name="{name()}">
        <xsl:value-of select="concat($root, .)" />


This thread sure is useful.

Not directly live-changing and more symphony centric:

I needed a convenient way to handle a nested page navigation that lists entries instead of subpages. So, for each section whose entries should appear as sub navigation item, create a DS that follows a strict naming convention. Let's say you have a page called "projects" then you'd might name it TOC Projects. Choose the field that's representing your url parameter handle (e.g. a field called name) for Parameter Output and uncheck paginate results since you want all entries to appear in your ds-toc-projects parameter.

Now the xslt (apply it to your navigation DS):

<xsl:template match="page">
    <xsl:variable name="subname">
        <xsl:value-of select="concat('/data/params/ds-toc-', @handle)"/>
        <a href="{$root}/{$lang}/{@handle}/" class="xhr"><xsl:value-of select="item"/></a>
        <xsl:if test="dyn:evaluate($subname)">
            <xsl:apply-templates select="dyn:evaluate($subname)" mode="sub">
                <xsl:with-param name="parent" select="@handle"/>


    <xsl:template match="*" mode="sub">
    <xsl:param name="parent"/>
    <ul class="sub clear">
        <xsl:for-each select="item">
                <a href="{$root}/{$lang}/{$parent}/{@handle}/" class="xhr"><xsl:value-of select="."/>


Since xpath-expressions can't be created at runtime you'll need a exsl extension called dyn:evaluate. So your template header would look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl=""
    version="1.0" >

@iwyg, where does page <xsl:template match="page"> come from?

where does page <xsl:template match="page"> come from?

From a navigation datasource probably. If I'm not mistaken, it's the PLH variation :)

@vladG, yep. PLH?

Yeah,PLH? What's that then?

Page LHandles extension. You have it installed. Recognized from your code.

oh, yes. indeed.

Nice, will it be possible to post another code where Page LHandles is not installed?

sure, obmit the $lang variable and you're good. Instead of {$root}/{$lang}/{@handle} it'd be {$root}/{@handle}, though I'm not 100% sure if @handle represents the page-handle on a regular Navigation DS

[edit] To complete confusion: {$lang} is not a regular parameter. The real parameter is $url-language. In this case, I created this variable to ensure theres a default language string in case $url-language is not present. So, never use $lang without declaring it before :)

Okie, to clarify it further. The code will be insert in 'project.xsl' and datasource for 'Project' page will be 'Navigation' and 'TOC Project'?

Cool. Got it work!

I normally tend to include page-navigation stylesheets in my master stylesheet.

Let's take a node set with similar nodes and parameter $param which identifies a node (eg: $param = '4-3' must select field-4-3). How do we select the node?


Do we select it this way?

<xsl:variable name="result" select="data/*[starts-with(name(), concat('field-', $param))]"/>

Wrong! Dumping $result (<xsl:copy-of select="$result"/>) will return all field-* nodes:


The correct way to select only the desired node is by first creating an additional variable to hold the name of the node (don't ask me why, I simply noticed this):

<xsl:variable name="node-name" select="concat('field-', $param)"/>
<xsl:variable name="result" select="data/*[starts-with(name(), $node-name)]"/>

Dumping $result (<xsl:copy-of select="$result"/>) will return correct node:


Ok ... let's learn from my mistake.

I have a file called /utilities/imports.xsl in which I'm importing all the stylesheets in utilities folder:

<xsl:import href="master.xsl"/>
<xsl:import href="XXX.xsl"/>
<xsl:import href="YYY.xsl"/>
<xsl:import href="ZZZ.xsl"/>

And in every /pages/XXX.xsl or /widgets/XXX.xsl stylesheet where I need to include something, I simply import ../utilities/imports.xsl because I have auto-complete from my IDE and other sweets.

Benchmarks for page creation time as shown in Google Chrome Network tab (waiting for server to respond):

Import everywhere            Import only in master.xsl
810ms ~ 1250ms               480ms ~ 530ms


During dev it's OK to import everything everywhere (if it helps the Dev process), but before deploying to Production, make sure you're optimizing the imports.

You can escape an accolade in AVT using another similar accolade. This is useful if you're using handlebars or similar:


<div id="{{{{id}}}}">{{body}}</div>


<div id="{{id}}">{{body}}</div>

vladG is on fire :) nice performance tip there!

The performance tip is pretty neat, I didn't know about that one, will be interesting to apply to some existing projects and see the result.

Given Frontend Localisation, FLang Detection GTLDs and Page LHandles, a developer has the necessary info to build localised URLs. Here's an example on how to retrieve a link to a Symphony page by it's ID:


<!-- Store the languages in a variable -->
<xsl:variable name="langs" select="/data/fl-languages"/>

<!-- Compute current language code -->
<xsl:variable name="cur-lang">
    <xsl:value-of select="$langs/current-language/@language"/>
    <xsl:if test="$langs/current-language/@region != ''">
        <xsl:value-of select="$langs/current-language/@region"/>

<!-- This matches the parent page from Navigation DS -->
<xsl:template match="page" mode="plink">
    <xsl:param name="root" select="/data/params/root"/>
    <xsl:param name="lang" select="$cur-lang"/>

    <xsl:variable name="lang_code">

            <!-- When main language, omit it from URL -->
            <xsl:when test="$langs/supported-languages/item[ @handle = $lang ]/@main = 'yes'"/>

            <!-- Other languages must be included in URL -->
                <xsl:value-of select="concat($lang, '/')"/>


    <xsl:value-of select="concat($root, '/', $lang_code, item[ @lang = $lang ]/@handle, '/')"/>

<!-- This matches any children pages from Navigation DS -->
<xsl:template match="page/page" mode="plink">
    <xsl:param name="root" select="/data/params/root"/>
    <xsl:param name="lang" select="$cur-lang"/>

    <xsl:apply-templates select="parent::page" mode="plink">
        <xsl:with-param name="root" select="$root"/>
        <xsl:with-param name="lang" select="$lang"/>

    <xsl:value-of select="concat(item[ @lang = $lang ]/@handle, '/')"/>

Get the link to Symphony page with ID = 1.

 <xsl:apply-templates select="/data/navigation//page[ @id = 1 ]" mode="plink"/>


This is useful if you're using handlebars or similar

This is actually a great tip. Thanks. Unfortunately Handlebar has some constructs which won't compie with xml e.g. if else statements.

But nice tip :)

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