XSLT FAQ

How can I inherit meta data from parent pages?

I have an optional page metadata field – when rendering a page I would like to get the value of this field, and if it’s not specified, then the value from the nearest parent having a specified value.

This would be great for features like controlling what is shown in information columns etc. on top level pages, while still having the ability to ’overload’ the selections further down the site tree.

Answer:

First off, you need to create your metadata type in a way where users can easily select if they want to specify a value for the page they are editing, or if they want to ’inherit’ the value from a parent page.

Make your fields optional and interpret a ’no value’ as a ’inherit value from parent’ selection. This will work for all field types, except boolean fields (there is no ’optional boolean’ field type in C1).

Once you have the field attached to a branch of pages and some useable test data in place, you can create an XSLT that extract meta data from ’the current page and ancestor pages’ which is half of the way to the goal. To create this XSLT execute the steps below. In this example our page metatype is named ”OfficeJungle.PageData.Section”:

  1. On the Functions perspective, add a new XSLT Function.
  2. On the Function Calls tab, add a call to ”OfficeJungle.PageData.Section.GetSectionXml” (our sample page metatype – use your own).
  3. In the field selection list, select the fields you need and also the field PageId.
  4. Click the function node of GetSectionXml (the lightning icon) and set the local name to ”Inherited data”.
  5. Click ”Filter”, Value type = ”Execute a function call”, and launch the function designer.
  6. Select the function ”OfficeJungle.PageData.Section.ActivePageReferenceFilter”.
  7. Click ”Page scope”, Value type = ”Constant”.
  8. Select "Ancestors and current (breadcrumbs)” as the page scope.
  9. Click OK.
  10. Preview the result.
  11. If you don’t get any data, or you would like other test data, go to the Settings tab and change the Page selection in the Debug field group and preview again.

As you can see from the preview, we are filtering the meta data in a way where we get only data from pages between the current page and the homepage (both inclusive). The only problem is that you cannot determine where in the page structure the data belongs – you cannot trust the sort order to follow the sitemap document order – which makes it hard to ’inherit from the nearest parent’.

In order to fix this you need the sitemap so we can 'join' with it – execute the following steps:

  1. On the Function Calls tab, add a call to ”Composite.Pages.SitemapXml
  2. Preview the result.

Now you have the page structure and you have the required meta data. Armed with this XML you can write your XSLT. In the sample XSLT below, we check if the field ’Title’ on outr meta data has a value, and if not, we search the parent and so on, all the way to the homepage. When we find a value we simply print it and stop searching upwards.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:in="http://www.composite.net/ns/transformation/input/1.0"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:sitemap="http://www.composite.net/ns/data/sitemap/1.0"
  exclude-result-prefixes="xsl in sitemap">

  <xsl:variable name="currentpage" select="/in:inputs/in:result[@name='SitemapXml']//Page[@iscurrent='true']" />

  <xsl:template match="/">
    <html>
      <head />
      <body>
        <!-- we start by looking at the current page -->
        <xsl:apply-templates select="$currentpage" />
      </body>
    </html>
  </xsl:template>
  
  <xsl:template match="Page">
    <!-- here we 'join' the page with it's meta data -->
    <xsl:variable name="metadata" select="/in:inputs/in:result[@name='Inherited data']/*[@PageId=current()/@Id]"  />
    
    <!-- if we have a value, we print it. Otherwise we visit the parent page -->
    <xsl:choose>
      <xsl:when test="string-length($metadata/@Title) &gt; 0">
        <xsl:value-of select="$metadata/@Title" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="parent::node()[@isopen='true']" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Download the source

You probably want to do something fancier than printing a title string, but the XSLT above should show how to get ahold of the correct meta data

In this sample we stopped searching upwards at the ’nearest’ data we found. You can fairly easily change the XSLT flow so all parent pages are visited, if this is a desired feature.

Note that this also works on page data folders – inheritance is not limited to meta data.