Search

@vladG, nope definately <xsl:import>ing.

@bauhouse - your comment in this [thread][1] about how you use your master template is really intriguing.

<xsl:template match="/">
    <html lang="en">
        <head>
            <xsl:apply-templates mode="page-title" />
            <meta name="description" content="" />
            <meta name="author" content="" />
            <xsl:apply-templates mode="css" />
        </head>
        <body id="{$current-page}-page">
            <xsl:apply-templates mode="header" />
            <xsl:apply-templates mode="nav" />
            <xsl:apply-templates mode="aside" />
            <xsl:apply-templates />
            <xsl:apply-templates mode="footer" />
            <xsl:apply-templates mode="js" />
        </body>
    </html>
</xsl:template>

I am able to override any of these apply-templates instructions in each page template, sometimes depending on whether a JavaScript, CSS, or XML file has been uploaded with the entry.

Each entry can potentially have a completely different layout.

I don't know if I am remembering correctly, but I believe you or someone wrote a blog post or an article discussing how to use your master, and how you override the template.

I know Allen did and article about overriding templates. But, I was wondering if you had an article discussing how you used your setup. Could you point me in the right direction? Thanks for your time.

[1]: http://getsymphony.com/discuss/thread/56620/1/#position-7completely different layout.

@bzerangue, sorry for the delay in responding. I still don't have a full explanation ready yet. But I can point to one of my gists as an example of what I had in mind. I wanted to create a simple ensemble that demonstrated how I use it.

Basically, this project.xsl file would be imported as a master template, and I could override any part of the markup in the page template.

So, I could do something as simple as this, where I use the existing templates:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:import href="../utilities/project.xsl" />

<xsl:template match="data">
    <xsl:apply-templates select="resource/entry" />
</xsl:template>

</xsl:stylesheet>

Or, I can do something a little more complex, where I am testing for the configuration settings of the entries, which can be checkbox fields, and serve custom JavaScript which has been added to an upload field for an entry:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:import href="../utilities/project.xsl" />

<xsl:template match="data" mode="css">
    <link rel="stylesheet" href="{$workspace}/assets/intranet/css/main.css?v1.0" />
    <xsl:apply-templates select="resource" mode="css" />
    <xsl:apply-templates select="resource-by-id" mode="css" />
    <xsl:apply-templates select="resource-latest" mode="css" />
</xsl:template>

<xsl:template match="data" mode="js">
    &lt;script type="text/javascript" src="{$workspace}/assets/intranet/js/lib/jquery-1.4.4.min.js">&lt;/script>
    &lt;script type="text/javascript" src="{$workspace}/assets/intranet/js/system.js">&lt;/script>
    &lt;script type="text/javascript" src="{$workspace}/assets/intranet/js/resources.js">&lt;/script>
    <xsl:apply-templates select="resource" mode="js" />
    <xsl:apply-templates select="resource-by-id" mode="js" />
    <xsl:apply-templates select="resource-latest" mode="js" />
</xsl:template>

<xsl:template match="resource | resource-by-id | resource-latest" mode="js">
    <xsl:apply-templates select="entry/javascript" mode="js" />
    <xsl:apply-templates select="entry[table-of-contents = 'Yes']" mode="contents-js" />
    <xsl:apply-templates select="entry[syntax-highlighting = 'Yes']" mode="syntax-js" />
</xsl:template>

<xsl:template match="entry/javascript" mode="js">
    &lt;script type="text/javascript" src="{$workspace}{@path}/{filename}">&lt;/script>
</xsl:template>

<xsl:template match="entry" mode="contents-js">
    &lt;script src="{$workspace}/assets/intranet/js/contents.js" />
    &lt;script type="text/javascript">init_contents();&lt;/script>
</xsl:template>

<xsl:template match="entry" mode="syntax-js">
    &lt;script type="text/javascript" src="{$workspace}/assets/intranet/js/syntax.js">&lt;/script>
    &lt;script type="text/javascript">
        <![CDATA[
        SyntaxHighlighter.config.tagName = 'textarea';
        SyntaxHighlighter.defaults['wrap-lines'] = false;
        SyntaxHighlighter.defaults['auto-links'] = false;
        SyntaxHighlighter.defaults['toolbar'] = false;
        SyntaxHighlighter.defaults['tab-size'] = 4;
        SyntaxHighlighter.all();
        ]]>
    &lt;/script>
</xsl:template>

<xsl:template match="data">
    <xsl:apply-templates select="resource/entry" />
</xsl:template>

</xsl:stylesheet>

It's pretty abstract unless you have a working example, or I spend a little more time explaining how this works. But it's a lot of fun to see how far you can take this.

By the way, for those who are looking for Allen's original explanation of Overriding Templates, it's a great resource, which sparked my experiments.

I created another gist with a more basic example. This gist includes the master.xsl file, imported by the home.xsl file to transform the default XML data when you first create a Symphony page.

The output would look like the index.html file.

How to override templates

Say you wanted all your pages to have the same CSS. You could modify the master.xsl template to include the styles to be applied to all pages.

So, instead of the empty template:

<xsl:template match="data" mode="css" />

You would add the link element for the CSS:

<xsl:template match="data" mode="css">
    <link rel="stylesheet" href="{$workspace}/assets/css/main.css" />
</xsl:template>

But, if you wanted to override the styles for the home page, you could modify the home.xsl file to serve styles that are specific to the home page, put the <h1> element in the mode="header" template and pull articles data into the default match="data" template:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:import href="../utilities/master.xsl" />

<xsl:template match="data" mode="header">
    <h1><xsl:value-of select="$page-title"/></h1>
</xsl:template>

<xsl:template match="data">
    <xsl:copy-of select="articles/entry/body" />
</xsl:template>

<xsl:template match="data" mode="css">
    <link rel="stylesheet" href="{$workspace}/assets/css/home.css" />
</xsl:template>

</xsl:stylesheet>

I hope that provides a better example.

This is very smart, the only thing I miss is calling the overridden template. I better explained the problem elsewhere, maybe someone has a nice idea to share :)

EDIT I found something very very interesting: apply-imports.

@alpacaaa, that is very interesting. I didn't know about the apply-imports instruction. Thanks for the links.

Is it something that would work in xpathr? I was wondering if there was support for importing templates, but it seems the processor expects everything in the master.xsl file.

Hi bauhouse thanks, your method of applying mode templates is very interesting because it gives a nice control on scope and seems very flexible. in my setup I usually use named templates but I think your system allows more complex stuff:

this is my usual setup:

in master.xsl,

<head>
    …
    <xsl:call-template name="javascript"/>
    ...
</head>

in the master I import a library.xsl file where I store commonly used templates:

<xsl:template name="javascript">
    &lt;script src="//ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js">&lt;/script>
    ...
    <xsl:call-template name="javascript-load"/>
    &lt;script type="text/javascript">
        $(document).ready(function() { 
            initPage() //or whatever js stuff needed in all pages
            <xsl:call-template name="javascript-exec"/>  
             });
    &lt;/script>
</xsl:template>

<xsl:template name="javascript-load"></xsl:template>

<xsl:template name="javascript-exec"></xsl:template>

this way at page level I can simply override the default empty templates and put something like this to add some plugin or external js:

<xsl:template name="javascript-load">
    &lt;script src="{$workspace}/script/jquery.pluginname.min.js" type="text/javascript">&lt;/script>
</xsl:template>

and/or add some raw javascript to be execute at page load:

<xsl:template name="javascript-exec">   
    $('.someclass').someplugin({someproperty:val,someotherproperty:<xsl:value-of select="count(//data/ds-example/entry)/>"}).whatever();
</xsl:template>

I use a similar approach with css files (with a css-load and page-styles templates).

hope it makes sense

I'm starting a large project and wanted something scalable, so I took a different approach:

base.xsl

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

<xsl:import href="functions/main.xsl"/> 
<xsl:import href="blocks/page.xsl"/>    

<xsl:output method="xml"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
    omit-xml-declaration="yes"
    encoding="UTF-8"
    indent="yes"/>

<xsl:key name="getBlockByName" match="block" use="@name"/>

<xsl:template match="/">
    <xsl:variable name="layout">
        <xsl:apply-templates select="/" mode="layout"/>
    </xsl:variable> 

    <!-- apply layout updates -->
    <xsl:variable name="layout-updated">        
        <xsl:apply-templates select="exsl:node-set($layout)/*" mode="layout"/>
    </xsl:variable>

    <!-- convert to html -->
    <xsl:variable name="html">
        <xsl:apply-templates select="exsl:node-set($layout-updated)/*"/>
    </xsl:variable>

    <!-- render output -->  
    <xsl:apply-templates select="exsl:node-set($html)/*" mode="html"/>  
</xsl:template>

<xsl:template match="/data"/>
<xsl:template match="/data" mode="layout"/>

<xsl:template match="*" mode="layout">
    <xsl:element name="{name()}">
        <xsl:apply-templates select="* | @* | text()" mode="layout"/>
    </xsl:element>
</xsl:template>

<xsl:template match="@*" mode="layout">
    <xsl:attribute name="{name(.)}">
        <xsl:value-of select="." mode="layout"/>
    </xsl:attribute>
</xsl:template>

<xsl:template match="*" mode="html">
    <xsl:element name="{name()}">
        <xsl:apply-templates select="* | @* | text()" mode="html"/>
    </xsl:element>
</xsl:template>

<xsl:template match="@*" mode="html">
    <xsl:attribute name="{name(.)}">
        <xsl:value-of select="." mode="html"/>
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

page.xsl (not done.. but you can see where I'm going with this)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:main="http://getsymphony.com/functions">

<xsl:template match="/" mode="layout">
    <block name="page">         
        <block name="head">
            <title><xsl:value-of select="main:pagetitle()"/></title>
            &lt;scripts>
                <minify type="stylesheet" compress="0">                 
                    <stylesheet>vars.css</stylesheet>
                    <stylesheet>normalize.css</stylesheet>
                    <stylesheet>grid.css</stylesheet>
                    <stylesheet>template.css</stylesheet>
                    <stylesheet>assets.css</stylesheet>
                    <stylesheet>main.css</stylesheet>
                </minify>
                <minify type="javascript" compress="0">             
                    &lt;script>libs/modernizr.js&lt;/script>
                    &lt;script>libs/yepnope.js&lt;/script>
                    &lt;script>libs.respond.js&lt;/script>
                    &lt;script>site.js&lt;/script>
                </minify>
            &lt;/scripts>
        </block>
        <block name="container">
            <block name="top"/>
            <block name="header">
                <block name="header_menu"/>
            </block>            
            <block name="main">
                <block name="left_wrapper">
                    <block name="left"/>
                </block>
                <block name="content" />
            </block>
        </block>
    </block>
</xsl:template> 

<xsl:template match="block[@name='page']">
    <html lang="en" class="no-js">
        <xsl:apply-templates select="block[@name='head']"/>     
        <body>
            <xsl:attribute name="class">
                <xsl:value-of select="main:pageclass()"/>
            </xsl:attribute>            
        </body> 
    </html>
</xsl:template>

<xsl:template match="block[@name='head']">
    <head>
        <title><xsl:value-of select="title"/></title>

        &lt;script>var WORKSPACE = '<xsl:value-of select="$workspace"/>';&lt;/script>
        &lt;script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js">&lt;/script>    
        <xsl:text disable-output-escaping="yes">
            <![CDATA[&lt;script>window.jQuery || document.write('&lt;script src="'+WORKSPACE+'/assets/js/libs/jquery-1.6.2.min.js"></script>');&lt;/script>]]>
        </xsl:text>         
        <xsl:apply-templates select="scripts"/>
    </head>
</xsl:template>

<xsl:template match="minify[@type='stylesheet']">
    <link rel="stylesheet">
        <xsl:attribute name="href">
            <xsl:value-of select="$workspace"/>
            <xsl:text>/assets/css/SFC.css?path=assets/css&amp;files=</xsl:text>
            <xsl:for-each select="stylesheet">
                <xsl:if test="position() &gt; 1">,</xsl:if>             
                <xsl:value-of select="." />
            </xsl:for-each>
            <xsl:if test="@compress=1">&amp;compress</xsl:if>           
        </xsl:attribute>
    </link>
</xsl:template>

<xsl:template match="block[@name='top']">
    <div>top</div>
</xsl:template>

</xsl:stylesheet>

It might be a little processor heavy. But if I architect it right, I should be able to use exsl:document to handle caching if I need to. Thoughts, suggestions?

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