Transforming Data to HTML


QBO leverages extensible stylesheet language transforms (XSLT) to convert data into HTML, rendering a UI. You can specify an XSLT to use when rendering data via a request parameter, or via configuration.

By default, XSLT documents are stored on a QBO 3 installation in a Templates folder, with appropriate child folders:
  • Templates
    • Attachment: attachment module related XSLTs
    • Decision: workflow module related XSLTs
      • Email: overrides for Decision XSLTs that target an email user agent
      • Mobile: overrides for Decision XSLTs that target mobile phones
    • Message: message module related XSLTs
      • Email: overrides for Decision XSLTs that target an email user agent
      • Mobile: overrides for Decision XSLTs that target mobile phones
    • etc.

Transform Request Parameter

Developers may specify a transform on the query string (or via a HTTP POST) like this:


Note in the first example, only an XSLT file name was specified; the relative path to the XSLT file was omitted.  Each class configuration file (e.g. Contact.config) defines a BaseTransform attribute that all handler (e.g. Contact.ashx) use to determine the location of XSLTs.  In the case of Contact.config, we have:

<Contact Table="Contact" Type="qbo.Contact.ContactObject, qbo.Contact" BaseTransform="Templates/Contact" ...>

If no relative path is specified with a Transform parameter, the handler will assume that the transform exists in the folder defined by BaseTransform, and will be loaded accordingly. If you specify a relative path, the BaseTransform will be ignored.


It would be tedious to specify a commonly used XSLT with a commonly used statement for every call. For example, the Search statement for all classes would typically be paired with a search XSLT, such as:


For this reason, QBO 3 methods and statements allow a developer to define a default transform with any statement. For example, the Contact.config's Search statement would be:

<Statement Name="Search" Transform="Contact.Search.xslt" Query=".../>

Note that many statements are generic (defined in a base class file such as Abstract.config instead of Contact.config). These statements may still effectively specify a default parameter as follows:

<Statement Name="Search" Transform="{Table}.Search.xslt" Query=".../>

At load time, the configuration handle will replace {Table} with the name of the table being configured, so Contact.ashx/Search binds to Contact.Search.xslt, and Attachment.ashx/Search binds to Attachment.Search.xslt.

User Agent Overrides

XSLT normally produces HTML targeting a web browser, and may include CSS, navigation, and AJAX controls to provide for a rich web experience. In some cases, such richness can be a burden, especially for mobile phone and email clients.

When checking for such XSLTs, if one matching the user agent is found, it is used, otherwise the default is used.

The qbo.Application application setting 'UserAgentMap' will allow you to route requests based on a user agent to alternate XSLTs. For example, you may have:
  • Contact.Search.xslt: targets a desktop browser, renders 8 columns with fancy navigation
  • SmartPhone/Contact.Search.xslt: targets a smart phone (iOS or Android), renders 4 columns with alternate navigation
  • Mobile/Contact.Search.xslt: targets other mobile phones, renders 3 columns without navigation
  • Email/Contact.Search.xslt: targets an email client, renders 8 columns without any navigation, using inline CSS
The UserAgentMap for such a scenario might look like:

Email, iPhone>SmartPhone, Android>SmartPhone, Mobile>Mobile

This setting is parsed, splitting on commas, and if a '>' exists, the value is split as follows:

User Agent StringFolder 
iPhone SmartPhone 

Mapping to alternate XSLT folders works like this:
  • if the user agent contains the string 'Email', check Templates/Contact/Email/Contact.Search.xslt, 
  • else if the user agent contains the string 'iPhone', check Templates/Contact/SmartPhone/Contact.Search.xslt
  • else if the user agent contains the string 'Android', check Templates/Contact/SmartPhone/Contact.Search.xslt, 
  • else if the user agent contains the string 'Mobile', check Templates/Contact/Mobile/Contact.Search.xslt,
  • otherwise, use Templates/Contact/Contact.Search.xslt

Under the Hood

The code that handles this is embedded in the qbo.Application.Utilities.XslTransform class, used as follows:

XslTransform.Create(string relativePath, string userAgent)

This is called from qbo.Application.HttpHandler as follows:

/// <summary>
/// Creates an XslCompiledTransform object based on TransformUrl.
/// </summary>
public XslCompiledTransform Transform
        if (_Transform == null)
_Transform = Utilities.XslTransform.Create(TransformUrl, Context.Request.UserAgent);
        return _Transform;
        _Transform = value;

If you are instantiating XslCompiledTransforms directly from your code, use this static method to leverage this functionality.