Circumventing the macro placeholder

27. maj 2009 by Kasper B

The topic of the previous post was inline xsl in umbraco master templates.

Another way to circumvent the macro placeholder for those transformations that are only used in templates (main navigations and stuff) - is to create a component that allows you the input the path of a xsl file - load it up and do the transformation.

Candidates for this is xsl files wrapped in macros that do not have a tick in the "Use in editor" field - the gain should be a "simpler" umbraco installation - and a more transparent development process - and no more need to look in the macro section with the sole purpose to find out which xsl file it has a reference to :).

Prototype code that does the job is done in 67 lines - including debug code, import and namespace declarations.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Xsl;
using System.IO;


namespace InlineXsl
{
[ToolboxData("<{0}:XslFile runat=server></{0}:XslFile>")]
public class XslFile : Literal
{
private string _xslpath = "";
public string XslPath {
get { return _xslpath; }
set { _xslpath = value; }
}
private bool _debugmode = false;
public bool DebugMode
{
get { return _debugmode; }
set { _debugmode = true; }
}
protected override void Render(HtmlTextWriter writer)
{
try
{
XmlDocument xd = new XmlDocument();
xd.Load(HttpContext.Current.Server.MapPath(XslPath));
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xd);
XsltArgumentList xslArg = umbraco.macro.AddMacroXsltExtensions();
xslArg.AddExtensionObject("urn:umbraco.library", new umbraco.library());
xslArg.AddParam("currentPage", "", umbraco.library.GetXmlNodeCurrent().Current);
xslt.Transform(umbraco.library.GetXmlAll().Current, xslArg, writer);
}
catch (Exception ex)
{
if (DebugMode)
{
writer.Write("<div style='color:red;border:1px solid red;padding:10px;'>Error in xsl: <br /><pre>" + ex.ToString() + "</pre></div>");
}
else
{
try
{
HttpContext.Current.Trace.Warn("Error in inline xsl", ex.ToString());
}
catch { }
}
}
}
}
}

When using it in templates it looks like this:

  <Inline:XslFile XslPath="/xslt/test.xslt" runat="server" />

Is it something worth adding to the Umbraco project?

Xsl transformations in templates for umbraco v4

10. marts 2009 by Kasper B

This weekend I finally got around to play with umbraco v4, and its a joy!

I really like the new .net masterpage templates, and will try to share how I experienced some of the nice new features that they provide.

First of, its now possible to use .net code in the templates, which certainly will raise the numbers of wtf's per minute - a quick example:

You can now use something like this directly in your template

 <asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
<% if (Request.QueryString["test"] != null) { %>
Now theres a test in the querystring
<% } else { %>
Add a <a href="?test=haha">test in the querystring</a>
<% }%>
</asp:Content>


Aswell as inserting standard .net controls and binding them to datasources

<script language="c#" runat="server">
protected void Page_Load() {
System.Collections.Generic.List<string> list = new System.Collections.Generic.List<string>();
list.Add("Boom");
rpt.DataSource = list;
rpt.DataBind();
}
</script>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
<asp:Repeater id="rpt" runat="server">
<ItemTemplate>
<%# Container.DataItem %>
</ItemTemplate>
</asp:repeater>
</asp:Content>

Now that this is a fact, why not try to do something useful

How to use inline xsl in umbraco templates:

1: Create a custom control that takes the literal contents (which is the text/code in between the start and end element of the custom control) assumes that its xsl and then perform the transformation in the overridden render method.

2: In order to make it work as the xsl based macros - add in the umbraco xml data, the currentPage param and the xsl extension objects.

I have done so and the result is baffling - it works!

 Its now possible to do something like:

<%@ Master Language="C#" MasterPageFile="/umbraco/masterpages/default.master" AutoEventWireup="true" %>
<%@ Register assembly="InlineXsl" namespace="InlineXsl" tagprefix="Inline" %> <asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
<Inline:XslTransformer ID="mytransform" runat="server">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="msxml umbraco.library">
<xsl:output method="html" omit-xml-declaration="yes"/> <xsl:param name="currentPage"/>
<xsl:template match="/">
You are on the page <xsl:value-of select="$currentPage/@nodeName" />
</xsl:template>
</xsl:stylesheet> </Inline:XslTransformer>
</asp:Content>

Which is pure inline xsl in your templates :)

To make it a bit easier to start up - I added an option to omit the doctype and stylesheet declarations - which makes it alot shorter and readable - you just need to add the following attribute to the custom control:  OmitStyleSheetAndDoctypeDeclaration="True"

Same example:

<%@ Master Language="C#" MasterPageFile="/umbraco/masterpages/default.master" AutoEventWireup="true" %>
<%@ Register assembly="InlineXsl" namespace="InlineXsl" tagprefix="Inline" %> <asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
<Inline:XslTransformer ID="mytransform" runat="server" OmitStyleSheetAndDoctypeDeclaration="True">
<xsl:param name="currentPage"/>
<xsl:template match="/">
You are on the page <xsl:value-of select="$currentPage/@nodeName" />
</xsl:template>
</Inline:XslTransformer>
</asp:Content>

 

It really is that easy - try it out for yourself: download a .zip file with the assembly containing the custom control, extract - drop it in your umbraco v4 installations bin folder and copy paste the code from this page into a master template :)

If it fails you can add the attribute DebugMode="True" aswell - it will output a nice error message, if you dont - its in the trace.

Taking it for a testrun

10. marts 2009 by Kasper B

Ya it seems that Umbraco v4 blog package works!