One of the biggest complaints of anyone using XSLT in .Net is that we are stuck on XSLT 1.0. While 1.0 provides a lot of capability it is easy to run headlong into the shortcomings of the language. For a number of reasons which I’ll never understand, Microsoft has not chosen to support XSLT 2.0 or XPATH 2.0 in .Net which forces developers to either live with the limitations of 1.0 or use a 3rd party XSLT engine. Neither of those options is really great.
There are a number of modules in DotNetNuke which rely heavily on XSLT for advanced formatting: Reports, Form & List and XML module are three that come immediately to mind. It would be great if we could break out of the limitations imposed by the reliance on XSLT 1.0.
Well, in fact you can. .Net has supported the concept of XSLT Extension Objects for quite a long time. Essentially, extension objects are .Net code that you can call from within your XSLT. With this capability you can easily code whatever functionality is missing from XSLT 1.0. This is just what a group of Microsoft MVPs did with the EXSLT.NET module which is a .NET implementation of EXSLT. Much of the work done in EXSLT was subsequently incorporated in XSLT 2.0.
In order to use the EXSLT.Net module, your application needs to tell the .Net XslCompiledTransform class where the assembly is that contains your custom extensions. This is done using the XsltArgumentList. Implementing this code is rather trivial:
// Create XslCompiledTransform and load the stylesheet.
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("blogaboutme.xsl");
// Create an XsltArgumentList.
XsltArgumentList xslArg = new XsltArgumentList();
// Add an object to calculate the new book price.
Extension obj = new DotNetNuke.Demo.Xslt.Extension();
xslArg.AddExtensionObject("DemoXslt", obj);
using (XmlWriter w = XmlWriter.Create("output.xml"))
{
// Transform the file.
xslt.Transform("blog.xml", xslArg, w);
}
Of course, unless you plan to write your own module to handle XSLT transformations this isn’t really all that useful. The useful part is being able to apply this knowledge to an existing module that supports XSLT extensions. The Reports module is the only module currently distributed with DotNetNuke which supports extension objects. The 6.0 version of the XML module will also support extension objects and I am hoping that Form & List will also support this feature in its upcoming release.
Lets look at an example of using extension objects with the reports module. In my example I am not going to focus on the XML data side of the equation. I have installed the latest version of the reports module and added it to my page. While logged in as a SuperUser, I created a new dummy query Select Test=’’ just so it would generate a little bit of xml.
<DocumentElement>
<QueryResults>
<Test />
</QueryResults>
</DocumentElement>
I want to make sure that I am using the XSL Transformation Visualizer:
![Visualizer Visualizer](/Portals/0/Blog/Files/3/3044/Windows-Live-Writer-XSLT-Extension-Objects_B4CC-Visualizer_thumb.png)
For my particular case I am going to pull some data from a user’s profile and then output this information in my XSLT. I’m also going to generate a URL for displaying the Gravatar associated with a particular email address. The first step is that I’ll need to define a class in .net that includes a method to get a users biography and a method to get the Gravatar URL.
Imports DotNetNuke.Entities.Users
Imports System.Text
Imports System.Security.Cryptography
Namespace DotNetNuke.Demo.Xslt
Public Class Extension
Public Function GetUserBio(
ByVal portalid As Integer,
ByVal UserId As Integer) As String
Dim blogger As UserInfo =
UserController.GetUserById(portalid, UserId)
Dim bio As String =
blogger.Profile.GetProperty("Biography").PropertyValue
Return System.Web.HttpUtility.HtmlDecode(bio)
End Function
Public Function GetGravatarImage(
ByVal email As String,
ByVal size As Integer) As String
Return String.Format(
"http://www.gravatar.com/avatar/{0}?s={1}",
GetMd5Hash(email.ToLower), size)
End Function
Private Function GetMd5Hash(ByVal input As String) As String
Dim md5 As MD5CryptoServiceProvider =
New MD5CryptoServiceProvider()
Dim bs As Byte() = Encoding.UTF8.GetBytes(input)
bs = md5.ComputeHash(bs)
Dim s As StringBuilder = New StringBuilder()
For Each b As Byte In bs
s.Append(b.ToString("x2").ToLower())
Next
Return s.ToString
End Function
End Class
End Namespace
In this example I am returning simple strings. If I wanted to return a node set I would return an XPathNodeIterator which is returned from an XPathNavigator. For my project, I’ve compiled this class in a DemoXSLT assembly and added this assembly to my /bin folder in my DotNetNuke website. At this point I can add this new extension object to my report definition:
![Extension Extension](/Portals/0/Blog/Files/3/3044/Windows-Live-Writer-XSLT-Extension-Objects_B4CC-Extension_thumb.png)
It is important to remember what namespace you assigned to the .Net type so that you can call it from your XSLT. We now have all of the pieces in place and just need to create our XSLT.
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:demo="DemoXslt"
exclude-result-prefixes ="demo">
<xsl:output method="html"
version="1.0"
indent="yes"
omit-xml-declaration="yes"/>
<xsl:template match="/">
<div class="section" id="about-me">
<h4>
About Me
</h4>
<div>
<xsl:value-of select="demo:GetUserBio(0,2)"
disable-output-escaping="yes"/>
<div>
<img alt="Blogger Gravatar"
src="{demo:GetGravatarImage('joe.brinkman@dotnetnuke.com', 150)}">
</img>
</div>
</div>
</div>
</xsl:template>
</xsl:stylesheet>
To use the extension object, I’ve defined a custom namespace which aliases the URN that I defined in the reports module. Now if I want to call a method in my extension object I just prefix the method name with the “demo:” namespace. Now if I add a few CSS classes to my portals.css I can generate a nice looking author block for my blog using the biography from my user profile and my Gravatar image:
![image image](/Portals/0/Blog/Files/3/3044/Windows-Live-Writer-XSLT-Extension-Objects_B4CC-image_thumb.png)
This is just one of the many techniques that I’ll be showing in my session “A Closer Look at DotNetNuke Core Modules” at CMS Expo this coming week.