Sitecore, Uncategorized, XSL

XslExtensions be gone – part 2


As described in part one of my post, we at Pentia have for some time had a bad feeling about the XSLT extensions concept introduced by Microsoft in the XsltTransform class and implemented further in Sitecore. We feel that XSLT’s belong in the UI tier (in a traditional n-tier architecture) and XSLT extensions has a tendency to introduce a mix of UI and business layer functionality and limited by the type restrictions in XSLT.

All developers (in their right minds) knows that asp.net codebehind files should contain UI layer functionality, not business logic, and that one c# codebehind file is bound to one asp.net file. What we want to achieve, is to have the same link between codebehind files in ASP.NET and XSLTs, therefore in our internal projects at Pentia, we have introduced the concept of XSLT codehind files.

So how does that work, you might ask?

Basically, we’ve developed a module which is included in all our projects (through our build environment), which automatically links XSLT extension classes to XSLT files through a common namespace. The module consists of an overridden Sitecore.Web.UI.WebControls.XslFile class, which overrides the AddExtensionObjects function:

  public class XslCodebehindFile : Sitecore.Web.UI.WebControls.XslFile
  {
    protected override void AddExtensionObjects(XsltArgumentList list, Item item)
    {
      //...
    }
  }

The AddExtensionObjects function dynamically determines which .NET class is the codebehind file for the current XSLT and adds it to the XsltArgumentList with a specific namespace (in our case http://www.pentia.net/codebehind). The class to add is determined via a custom .NET Attribute class which decorates the codebehind class (see the example below).

Furthermore the module implements an overridden Sitecore.Web.UI.XslControlRenderingType class:

  public class XslCodebehindControlRenderingType : XslControlRenderingType
  {
    public override Control GetControl(NameValueCollection parameters, bool assert)
    {
      // Returns the XslCodebehindFile class instead of the XslFile class
      // ...
    }
  }

This class is used by the Sitecore renderings engine to return the .NET class which implements a Sitecore rendering type. The class is hooked into Sitecore through the web.config (done automatically though our build environment):

<configuration>
  <!-- ... -->
  <sitecore>
    <!-- ... -->
    <renderingControls>
      <!-- ... -->
      <control template="xsl rendering"
        type="PT.XslCodebehind.XslCodebehindControlRenderingType, PT.XslCodebehind"
        propertyMap="Path=path" />
        <!-- ... -->

So how does the developer actually use it?

The developer starts by writing his XSLT file, e.g. /xsl/newslist.xslt. Most often it turns out that XSLT files does not even require codebehind or that the required functionality is implemented in the Sitecore XSLT extensions, but in the rare cases where advanced functionality is required (e.g. paging in the list of news) the developer creates a c# class file named newslist.xslt.cs (to keep the naming conventions defined by Microsoft). The class in the file is decorated with the XslCodebehind attribute which points to the fully qualified XSLT file:

  [XslCodebehind("/xsl/NewsArchive.xslt")]
  public class NewsArchive
  {
    //All functions in this class can be called in NewsArchive.xslt
    public Int32 GetCurrentPageIndex(XPathNodeIterator archive)
    {
      //...
    }
    public Int32 GetPreviousPageIndex(XPathNodeIterator archive)
    {
      //...
    }
    public Int32 GetNextPageIndex(XPathNodeIterator archive)
    {
      //...
    }
    }

This class is then automatically linked into the XSLT file at runtime and can be called though the http://www.pentia.net/codebehind namespace:

  <?xml version="1.0" encoding="UTF-8"?>
  <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:sc="http://www.sitecore.net/sc"
    xmlns:codebehind="http://www.pentia.net/codebehind"
    exclude-result-prefixes="sc codebehind">
    <!-- ... -->
    <xsl:template match="*" mode="main">
      <!-- ... -->
      <a href="?p={codebehind:GetNextPageIndex(.)}">
        <!-- ... -->
      </a>
      <!-- ... -->
    </xsl:template>
  </xsl:stylesheet>

Easy-peasy – no meddling about in web.config and a much tighter coupling between XSLT files and their .NET functions.

The XSLT codebehind functionality took me about half a day to code and include into all our projects. It has made it practically easier for developers to link functionality to XSLT files and conceptually easier to understand the placement of the XSLT functionality in the UI layer.

Standard

8 thoughts on “XslExtensions be gone – part 2

  1. Eldblom says:

    Sorry about the long time it took to post part 2. Just came back from 2 weeks of vacation.

    My plan is to contact Sitecore and add the module to their shared source library. More on that later.

  2. alex shyba says:

    this is just super cool! Now the developers can add more logic into their xslts without loosing performance, cmplicating the code and it will be more structured too!

  3. Jannik says:

    Excellent way to implement extensions, but how would you go about using helper classes that you would use on multiple xslt’s? Just write the same code over and over?

  4. Eldblom says:

    I choose to reply with a question: How do go about using helper classes that you would use on multiple ASP.NET pages? Do you duplicate the code in all codebehind files? 🙂

    Seriously, though: XslCodebehind does not rule out using XslExtensions classes – although my blog posts denounce this functionality, I (although secretly 🙂 still consider it useful for some things (like the sc:fld/sc:field functions). What you should never do though, is make XslExtension classes which holds specific functionality for one XSLT, or one XslExtension class which holds conceptually different functionality (like the Sitecore XslHelper class).

    If you promise not to tell, I will share a secret too: It is actually possible to use the same codebehind file with two XSLT’s by merely adding the XslCodebehind attribute on the class twice:

    [XslCodebehind(“/xsl/NewsArchive.xslt”)]
    [XslCodebehind(“/xsl/NewsList.xslt”)]
    public class NewsArchive

    BUT!!! This I would consider very bad architecture. Compare this to using the same codebehind file for two .aspx files – yuck! The better solution would be to extract the functionality into a common class and call it from two different XslCodebehind classes.

  5. Pingback: Sitecore Fetch Squad » Blog Archive » XslExtensions be gone – part 2

  6. Klaus P says:

    I very much like to architecture this one represents. Thumbs up to you guys – a very nice idea with an elegant yet simple implementation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s