Gallery Transformer Example

Consider the following sitemap definition to implement a gallery:

<map:pipelines> <map:pipeline> <map:match pattern="*.html"> <map:aggregate element="root" label="aggr-content"> <map:part src="cocoon:/menus.xml" element="menus" strip-root="true"/> <map:part src="cocoon:/{1}.xml" element="content" strip-root="true"/> </map:aggregate> <map:transform src="context://resources/transforms/page2xhtml.xsl" label="page-transform"> <map:parameter name="page" value="{1}"/> </map:transform> <map:serialize type="xhtml"/> </map:match> </map:pipeline> <map:pipeline internal-only="true"> <map:match pattern="menus.xml"> <map:generate src="context://content/menus.xml" label="menus-content"/> <map:serialize/> </map:match> <map:match pattern="gallery.xml"> <map:generate src="context://content/gallery.xml" label="xml-content"/> <map:transform type="gallery" label="gallery-transform"> <map:parameter name="root" value="context://gallery/gallery-1"/> </map:transform> <map:transform src="context://resources/transforms/gallery2html.xsl" label="page-transform"/> <map:serialize/> </map:match> <map:match pattern="*.xml"> <map:generate src="context://content/{1}.xml" label="xml-content"/> <map:serialize/> </map:match> </map:pipeline> </map:pipelines>

Defining the Gallery Index File

Within each folder of your gallery you must place a file that describes that level of the gallery. The default file name for this is gallery.xml. If you wish to change this (globally for all gallery folders) then change the entry in the paloose.php that you have in the root of your web site. There is a constant GALLERY_INDEX defined here in the line

define( 'GALLERY_INDEX', 'gallery.xml' );

Change this line to suit your site.

The contents of this file will be melded into your XML file as it is processed in the pipeline. The format of this file is relatively straight forward. For example (from the Guinness Park Farm site)

<paloose:gallery xmlns:paloose="" xmlns:t="" xmlns:link=""> <paloose:name>Photos</paloose:name> <paloose:dir></paloose:dir> <paloose:breadcrumb> <paloose:name src="">GPF</paloose:name> </paloose:breadcrumb> <paloose:description> <t:p> We will add galleries as we get them. If you have any to put up please email them to <link:link type="email" ref=""/>. Any format is acceptable, but please do not process them before you send them. If they are too many or too big then let us have them on CDROM.</t:p> </paloose:description> <paloose:folders> <paloose:folder src="general">General Views around the farm (lessons etc)</paloose:folder> <paloose:folder src="RDA">Some of our RDA activities</paloose:folder> <paloose:folder src="racing">Our racing successes!</paloose:folder> <paloose:folder src="horses">Some of our horses</paloose:folder> </paloose:folders> <paloose:images type="multi"/> </paloose:gallery>
It is important that the Paloose namespace ( is used for correct running of the gallery transformer (since version 1.1.1b1).

There will eventually be a schema defining this to make it easier to understand the above and to assist in creating them. However a quick look will show that it is relatively simple in format. The description information can be any element form, dependent on your following gallery2html transformer. Image information is displayed in a similar fashion to the above as:

<paloose:gallery> <paloose:name>Farm Views</paloose:name> <paloose:dir>general/farm</paloose:dir> <paloose:breadcrumb> <paloose:name src="">GPF</paloose:name> <paloose:name src="general">General</paloose:name> <paloose:name src="general/farm">The Farm</paloose:name> </paloose:breadcrumb> <paloose:description> <t:p>Some selected views around the farm. As you can see the countryside around here is beautiful with even a hack for an hour staying close to the farm or its immediate surrounds.</t:p> </paloose:description> <paloose:folders/> <paloose:images type="multi"> <paloose:image name="gpf-1.jpg"></paloose:image> <paloose:image name="gpf-2.jpg"></paloose:image> <paloose:image name="gpf-3.jpg"></paloose:image> <paloose:image name="gpf-4.jpg"></paloose:image> </paloose:images> </paloose:gallery>

Note that the folder in this case is null. It is perfectly possible to mix folders and images.

Adding the Gallery to your XML Content

In order to use the transformer a single element is placed in your content file that you wish to place the gallery. For example:

<page:content xmlns:page=""> <paloose:gallery xmlns:paloose=""/> </page:content>

Note that the <page:content> is not obligatory on your site. You could embed the gallery line within your own XML. That is just how it is used on one of my existing sites.. We can use the attribute root to override the parameter in the component declaration above.

Processing the Transformer Output

After running through the transformer this element is replaced by the contents of the gallery.xml file. How this information is processed is really up to the user, but the example of the Guinness Park Farm site would be instructive. We need two files: a transformer and a style file. The transformer takes the above XML and turns it into HTML suitable for display using the style file. First the declarations (mainly namespaces, which are used in the GPF site. Another site could use something else for the content structure. The only proviso is that they have to be the same, and that there should be a namespace declaration for Paloose.

<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="" xmlns:graphic="" xmlns:list="" xmlns:link="" xmlns:text="" xmlns:news="" xmlns:paloose="" xmlns:page="" version="1.0">

The following is an included XSL file processing the text (all coming from the namespaces above. Other sites would change this if required.

<xsl:include href="text2xhtml.xsl"/>

I enclose the whole gallery in a simple panel:

<xsl:template match="//paloose:gallery"> <xsl:element name="div"> <xsl:attribute name="class">galleryPanel</xsl:attribute> <xsl:apply-templates/> </xsl:element> </xsl:template>

The name of the gallery sits within a simple panel:

<xsl:template match="paloose:name"> <xsl:element name="div"> <xsl:attribute name="class">galleryNamePanel</xsl:attribute> <xsl:value-of select="."/> </xsl:element> </xsl:template>

We can safely eat the directory information that is the relative directory underneath the root for the displayed gallery folder.

<xsl:template match="paloose:dir"/>

The breadcrumb trail is assembled:

<xsl:template match="paloose:breadcrumb"> <xsl:element name="div"> <xsl:attribute name="class">galleryBreadcrumbPanel</xsl:attribute> <xsl:for-each select="paloose:name"> <xsl:element name="a"> <xsl:attribute name="href"> <xsl:choose> <xsl:when test="@src = ''" > <xsl:value-of select="'gallery.html'"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat( 'gallery.html?src=', @src )"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:value-of select="."/> </xsl:element> <xsl:if test="not( position() = last() )"> <xsl:text>&#160;&gt;&gt;&#160;&lt;/xsl:text> </xsl:if> </xsl:for-each> </xsl:element> </xsl:template>

The description of the gallery folder sits within a simple panel. The text is processed by the included text templates included at the beginning (change to suit site):

<xsl:template match="paloose:description"> <xsl:element name="div"> <xsl:attribute name="class">galleryDescriptionPanel</xsl:attribute> <xsl:apply-templates mode="inline-text"/> </xsl:element> </xsl:template>

Process the folders in to a simple vertical list (I use a small graphic to the left of the gallery title):

<xsl:template match="paloose:folders"> <xsl:if test="paloose:folder"> <xsl:element name="div"> <xsl:attribute name="class">galleryFoldersPanel</xsl:attribute> <xsl:for-each select="paloose:folder"> <xsl:element name="div"> <xsl:attribute name="class">galleryFolderPanel</xsl:attribute> <xsl:element name="a"> <xsl:attribute name="href"> <xsl:choose> <xsl:when test="@src = ''" > <xsl:value-of select="'gallery.html'"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat( 'gallery.html?src=', @src )"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <table> <tr> <td><xsl:element name="img"> <xsl:attribute name="src">AlbumIcon.png</xsl:attribute> </xsl:element></td> <td><xsl:value-of select="."/></td> </tr> </table> </xsl:element> </xsl:element> </xsl:for-each> </xsl:element> </xsl:if> </xsl:template>

The images are shown in a set of image tags containing the main image, the thumbnail image, the cache directory where they are stored, and the description (plain string at present).

<xsl:template match="paloose:images"> <xsl:choose> <xsl:when test="@type='multi'"> <!-- Displaying a set of thumbnails --> <xsl:if test="paloose:image"> <xsl:element name="div"> <xsl:attribute name="class">galleryImagesPanel</xsl:attribute> <xsl:for-each select="paloose:image"> <xsl:call-template name="outputImageAndLink"> <xsl:with-param name="inNode"><xsl:value-of select="."/></xsl:with-param> </xsl:call-template> </xsl:for-each> </xsl:element> </xsl:if> </xsl:when> <xsl:otherwise> <!-- Displaying single image --> <xsl:variable name="thisImageName"><xsl:value-of select="@name"/></xsl:variable> <xsl:element name="div"> <xsl:attribute name="class">galleryImagePanel</xsl:attribute> <xsl:call-template name="outputImage"> <xsl:with-param name="inImage"> <xsl:value-of select="//paloose:image[ @name = $thisImageName ]/@img"/> </xsl:with-param> <xsl:with-param name="inNextImageName"> <xsl:value-of select="//paloose:image[ @name = $thisImageName ]/following-sibling::*[1]/@name"/> </xsl:with-param> <xsl:with-param name="inPrevImageName"> <xsl:value-of select="//paloose:image[ @name = $thisImageName ]/preceding-sibling::*[1]/@name"/> </xsl:with-param> <xsl:with-param name="inImageDescription"> <xsl:value-of select="//paloose:image[ @name = $thisImageName ]"/> </xsl:with-param> <xsl:with-param name="inGalleryDir"> <xsl:value-of select="//paloose:dir"/> </xsl:with-param> </xsl:call-template> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:template>

Outputting a single image:

<xsl:template name="outputImage"> <xsl:param name="inImage"/> <xsl:param name="inNextImageName"/> <xsl:param name="inPrevImageName"/> <xsl:param name="inImageDescription"/> <xsl:param name="inGalleryDir"/> <xsl:variable name="fullImage"> <xsl:value-of select="concat( 'resources/images/cache/', $inImage )"/> </xsl:variable> <xsl:element name="div"> <xsl:attribute name="class">galleryPrevNextPanel</xsl:attribute> <xsl:element name="div"> <xsl:attribute name="class">galleryPrevButtonPanel</xsl:attribute> <xsl:choose> <xsl:when test=" $inPrevImageName != ''"> <xsl:element name="a"> <xsl:attribute name="href"> <xsl:value-of select="concat( 'gallery.html?name=', $inPrevImageName, '&src=', $inGalleryDir )"/> </xsl:attribute> <xsl:value-of select="'<<<'"/> </xsl:element> </xsl:when> <xsl:otherwise> <xsl:value-of select="' '"/> </xsl:otherwise> </xsl:choose> </xsl:element> <xsl:element name="div"> <xsl:attribute name="class">galleryNextButtonPanel</xsl:attribute> <xsl:choose> <xsl:when test=" $inNextImageName != ''"> <xsl:element name="a"> <xsl:attribute name="href"> <xsl:value-of select="concat( 'gallery.html?name=', $inNextImageName, '&src=', $inGalleryDir )"/> </xsl:attribute> <xsl:value-of select="'>>>'"/> </xsl:element> </xsl:when> <xsl:otherwise> <xsl:value-of select="' '"/> </xsl:otherwise> </xsl:choose> </xsl:element> </xsl:element> <xsl:element name="div"> <xsl:attribute name="class">galleryImageDescriptionPanel</xsl:attribute> <xsl:value-of select="$inImageDescription"/> </xsl:element> <xsl:element name="div"> <xsl:attribute name="class">galleryImagePanel</xsl:attribute> <xsl:element name="img"> <xsl:attribute name="src"><xsl:value-of select="$fullImage"/></xsl:attribute> </xsl:element> </xsl:element> </xsl:template>

Outputting a gallery of images:

<xsl:template name="outputImageAndLink"> <xsl:param name="inNode"/> <xsl:variable name="thumbnailImage"><xsl:value-of select="@thumb"/></xsl:variable> <xsl:variable name="imageName"><xsl:value-of select="@name"/></xsl:variable> <xsl:variable name="cachedImage"><xsl:value-of select="@img"/></xsl:variable> <xsl:variable name="fullThumbnailImage"> <xsl:value-of select="concat( 'resources/images/cache/', $thumbnailImage )"/> </xsl:variable> <xsl:variable name="galleryDir"> <xsl:value-of select="//paloose:dir"/> </xsl:variable> <xsl:element name="span"> <xsl:attribute name="class">galleryThumbnailImage</xsl:attribute> <xsl:element name="a"> <xsl:attribute name="href"> <xsl:value-of select="concat( 'gallery.html?name=', $imageName, '&src=', $galleryDir )"/> </xsl:attribute> <xsl:element name="img"> <xsl:attribute name="src"> <xsl:value-of select="$fullThumbnailImage"/> </xsl:attribute> </xsl:element> </xsl:element> </xsl:element> </xsl:template>

Finally pass everything not eaten above.

<xsl:template match="node()|@*" priority="-1"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet>

All the above looks a bit frightening, but it is really straight-forward. It can be changed to meet local requirements which is why it is not part of Paloose. The Paloose Gallery transformer only gets the information, it does not do anything with it for display — as should be the case with any XML/XSL engine like Cocoon or Paloose; separation of duties is the thing.

The only thing remaining is the CSS some of which is shown below (again taken from the Guinness Park Farm site. Go there to see how it all turned out :-):

.galleryPanel { margin: 0px 0px 0px 0px; font-family: Verdana, Arial, Helvetica, sans-serif; } .galleryNamePanel { margin: 15px 0px 0px 0px; font-weight: bold; font-size: 120%; } .galleryBreadcrumbPanel { margin: 10px 10px 10px 10px; padding: 3px 3px 3px 3px; border: 1px dashed #414b37; color: black; } .galleryDescriptionPanel { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 100%; margin: 0px 0px 10px 0px; } .galleryFoldersPanel { padding: 10px 0px 10px 0px; border-top: 1px solid #414b37; } .galleryFolderPanel { padding: 5px 0px 0px 0px; } .galleryImagesPanel { text-align: center; border-top: 1px solid #414b37; margin: 0px 0px 10px 0px; } .galleryThumbnailImage img { margin: 10px 10px 0px 10px; border: 2px solid white; } .galleryImagePanel img { margin: 0px 0px 10px 0px; border: 2px solid white; text-align: center; } .galleryImageDescriptionPanel { margin: 0px 0px 10px 0px; padding: 0px 0px 0px 0px; text-align: center; font-style: italic; font-size: 110%; font-family: Georgia, Times, serif; } .galleryPrevNextPanel { margin: 15px 0px 0px 0px; width: 600px; height: 20px; } .galleryPrevButtonPanel { position:relative; text-align: left; width: 100px; top: 0px; font-weight: bold; font-size: 120%; float:left; } .galleryNextButtonPanel { position:relative; width: 100px; top: 0px; text-align: right; font-weight: bold; font-size: 120%; float:right; }

OK, so I am not going to win prizes for the best way of doing things, but there we are, it works.

Copyright 2006 – 2023 Hugh Field-Richards. All Rights Reserved.