NVelocity: Sitecore’s original template engine

I sometimes wonder how many people look at the various dependencies that come with Sitecore.  One has to be slightly careful around how these are licensed, especially with Telerik, but I think that there’s one which deserves a mention: NVelocity.  NVelocity was originally a .Net port of the Java-based Velocity templating engine.  Sadly the project itself is more-or-less dead as far as I can tell; having been overtaken by more modern engines like Razor, but I still use it now and then. I thought that I’d explain the basics for people who hadn’t heard of it until now.

That said, I still use NVelocity every now and then.  I find it quite useful for setting up simple to moderately complicated token replacement within heavily content-managed text.  Obviously, you could do similar stuff with the Razor view engine; and if you want to do something really complex then perhaps you ought to use that; however, based on the various blog posts that I’ve seen on the topic, you’ll end up writing much more plumbing to do so than with NVelocity.

The Velocity Template Language (VTL) is pretty comprehensive, but I’m really just going to cover the basics here.  I’d suggest looking at the VTL reference if you want to dive into more complex template structures.  The templates are pretty concise and don’t require any special declarations for simple stuff: you typically prefix any token with a dollar sign, although other options are available, and directives are usually surrounded with hash (“#”) signs.  One cool feature is that you can access methods and properties on your tokens, like with Razor. That is, if you provide a Sitecore item as a token then you could write something like ‘$item.Paths.FullPath‘ in your template.  Below is a fairly simple template that just outputs a couple of things, which I’ll be using for the sample code later.

<p>Hi, $name.<br />
Your account balance was $balance.ToString("C2") as of $date.ToShortDateString().</p>

In order to start off with NVelocity, you need to initialise it and then create a context. You add your tokens to this context and then feed it the template text. Out comes the resulting text.

I’ve put together the following sample method, which does all that:

public class TemplatingHelper
{
    public TemplatingHelper()
    {
        Velocity.Init();
    }

    /// <summary>
    /// Executes a given template
    /// </summary>
    /// <param name="templateName">Name of the template being processed (mainly used for logging)</param>
    /// <param name="templateText">Body of the template</param>
    /// <param name="arguments">Dictionary of tokens to include in template</param>
    /// <returns>The evaluated template</returns>
    public string ExecuteTemplate(string templateName, string templateText, Dictionary<string, object> arguments)
    {
        using (new Sitecore.Diagnostics.LongRunningOperationWatcher(250, "Executing template {0}", templateName))
        {
            var context = new VelocityContext();
            arguments.ForEach(arg => context.Put(arg.Key, arg.Value));

            var output = new StringWriter();
            Velocity.Evaluate(context, output, templateName, templateText);
            return output.GetStringBuilder().ToString();
        }
    }
}

The above code is illustrative, in a production scenario you may want to spend additional time with error handling and ensuring that you don’t initialise NVelocity multiple times.  You can also pass a Stream to Velocity.Evaluate if you wish.  One thing that I would also suggest is that, whenever you’re dealing with NVelocity template text, check whether you’re in Page Editing mode (and if you are, just return the template text itself).  So, with the above class, if we wanted to execute the sample template (which we’ll assume is held in a field on the context item), we can write something like this:

public string ExecuteSampleTemplate()
{
    if (Sitecore.Context.Item == null)
    {
        return null;
    }

    var templateText = Sitecore.Context.Item.RenderField("SampleTemplateText");
    if (Sitecore.Context.PageMode.IsPageEditorEditing)
    {
        // If in page editing mode, then let the user see the actual template text
        return templateText;
    }

    var helper = new TemplatingHelper();
    var tokens = new Dictionary<string, object>
    {
        {"name", "Bob"},
        {"balance", RandomValues.NewMoney(0, 10000)},
        {"date", DateTime.Now}
    };

    return helper.ExecuteTemplate("Sample template", templateText, tokens);
}

Next steps… Once you’ve got that, you may be wondering how to apply it to your solution.  Fortunately, there are a few straight-forward ways that you can employ it:

  1. You can invoke NVelocity directly from your code.
    Advantage: this approach is perfectly fine when you want to directly execute a template in a specific location.
    Disadvantage: you have to invoke NVelocity every time you want to use it.
  2. You can add a processor that invokes NVelocity to the renderField pipeline.
    Advantage: this approach is useful if you want to perform field-level templating on a fairly wide basis (e.g. throughout the whole solution, specific fields on all pages, or specific types of fields).
    Disadvantage: you should take real care with this as you need to consider the overhead you’ll be adding to every page execution, where the renderField pipeline could be invoked hundreds of times per request.
  3. You can also add the logic to the Render event of a sublayout.
    Advantage: this approach is useful if you want to send a large chunk of content through NVelocity in one go.  In particular, you could apply this to a sublayout that contains a placeholder, thereby allowing token replacement in any renderings placed inside it.  This is quite possibly my favourite way to apply NVelocity.
    Disadvantage: you should take care around caching any such sublayouts; if you do mark it as cacheable then you should ideally link the cache key back to the value of the tokens you’re using.

Hopefully some people will find this useful, I’ve certainly found it to be a handy tool.