XSLT Macros

One simple but powerful feature that can help you in a CMS are macros: functions with parameters that get expanded into HTML markup. They not only make it possible to have markup which is less verbose, but also add consistency and flexibility. For instance, in my photo website, StoppingDown.net, I embedded photos in each blog post. When I started I just used inlined images; in time I changed my mind about their size; then I integrated LightBox for popping out larger versions. Every time I changed style, I didn't find the time to update all the older posts. At a certain point, I decided to change the way photos pop up, but enough was enough and I didn't want to add more inconsistency to my web site. So I introduced XsltFilterMacro to NorthernWind 1.0-ALPHA-15 and reprocessed all the existing blog entries to use a macro for inlining photos. 

A fundamental choice when implementing a macro facility is the syntax. A very common one is a functional-like approach with a specific character prefix, such as $macro(param)$, or @macro(param). The problem is that it cannot be interpreted by a plain HTML editor, which will typically also mangle some characters such as $ - and one of NorthernWind principles is simplicity, so you must be able to edit stuff with a simple tool. Applying an XSLT transformation to (X)HTML makes it possible to write macros in (X)HTML itself. Here it is how my macro for inlining photos looks like:

<div class="nwXsltMacro_Photo">
    <p class="nwXsltMacro_Photo_photoId">20050817-0092</p>
    <p class="nwXsltMacro_Photo_caption">The lower Albegna valley and Isola del Giglio on the horizon.</p>
</div>

Since this is valid HTML, it won't be changed by any decent editor. The trick lies in picking a proper name scheme for the class attribute, that isn't intended to match a CSS style, rather an XSLT template:

<xsl:template match="div[@class='nwXsltMacro_Photo']">
    <xsl:element name="div">
        <xsl:attribute name="align">center</xsl:attribute>
        <xsl:element name="img">
            <xsl:attribute name="src">$mediaLink(relativePath='/stillimages/800/<xsl:value-of select="p[@class='nwXsltMacro_Photo_photoId']"/>.jpg')$</xsl:attribute>
            <xsl:attribute name="class">framedPhoto</xsl:attribute>
        </xsl:element>
        <xsl:element name="p">
            <xsl:attribute name="class">caption</xsl:attribute>
            <xsl:value-of select="p[@class='nwXsltMacro_Photo_caption']"/>
        </xsl:element>
    </xsl:element>
</xsl:template>

When applied, the XSTL template will replace the macro with the following HTML, that can be directly interpreted by NorthernWind:

<div align="center">
    <img src="$mediaLink (relativePath='/stillimages/800/20050817-0092.jpg')$" class="framedPhoto"/>
    <p class="caption">The lower Albegna valley and Isola del Giglio on the horizon.</p>
</div>

A different version of the macro can generate the proper code for LightBox or other similar facilities.

To enable XSLT macros you just need to put in the general configuration:

nw.beans: ..., XsltMacroFilter

Their XSLT templates must be written in files under the folder content/library/XsltTemplates or a subfolder of it. They will be automatically discovered by NorthernWind at startup.

As a bonus, if you use an HTML editor which supports “live” CSS, such as Bluegriffon, you can put XSLT macros in evidence during editing by embedding some CSS code in the edited file (since NorthernWind strips away everything outside the <body> element, the extra CSS won't cause harm in production). For instance, the following CSS:

p.nwXsltMacro_Photo_photoId:before
  {
    content:          "Photo id: ";
    font-family:      sans-serif;
    font-weight:      bold;
  }

p.nwXsltMacro_Photo_caption:before
  {
    content:          "Caption: ";
    font-family:      sans-serif;
    font-weight:      bold;
  }

p.nwXsltMacro_Photo_photoId,
p.nwXsltMacro_Photo_caption
  {
    font-family:      sans-serif;
  }

div.nwXsltMacro_Photo
  {
    border:           1px;
    background-color: #e0e0e0;
    padding:          10px;
  }

will make your macro look in the editor like this: