Sitecore SXA

How to migrate NVelocity Extensions to Scriban

Dominic Sinclair-Moore
Time for change.

With the latest introduction of Scriban in SXA 9.3.0 and more importantly the removal of NVelocity you will find that all of your NVelocity templates no longer work. Depending on what those NVelocity templates previously did, it should be possible to handle the majority of the logic within the Sitecore SXA Scriban extensions or methods available directly in Scriban. There is plenty of documentation on what Scriban can do. If neither of these are suitable for your needs you may want to create an item extension or create a function.

It is entirely possible that you have a number of methods you want to be available in Scriban which are all contained in one class. You can certainly go down the route of creating functions for each and every method, but if you need to make the entire class (or even certain methods available) there is a much faster way to achieve this. This post extends on the top of the creating a function post so I would recommend reading that first before continuing. We are going to start with our existing code and configuration for the LinkField function.

using Scriban.Runtime;
using Sitecore.XA.Foundation.Scriban.Pipelines.GenerateScribanContext;
using Sitecore.Data.Items;
using ItemExtensions = Sitecore.Foundation.Extensions.ItemExtensions.ItemExtensions;
using System;

namespace Sitecore.Foundation.Variants.Pipelines.GenerateScribanContext
{
    public class AddLinkFieldFunction : IGenerateScribanContextProcessor
    {
        public void Process(GenerateScribanContextPipelineArgs args)
        {
            var linkField = new LinkFieldIt(LinkField);
            args.GlobalScriptObject.Import("sc_linkField", (Delegate)linkField);
        }

        public string LinkField(Item item, string field, bool useAbsoluteUrl = false){
            return ItemExtensions.GetLinkFieldUrl(item, field, useAbsoluteUrl);
        }

        private delegate string LinkFieldIt(Item item, string field, bool useAbsoluteUrl = false);
    }
}

We are assuming we have another class which contains a number of static methods (these will most likely be your NVelocity extensions previously created). In this case, we have a LinkTool class which contains a series of static methods to return LinkField field values.

using Scriban.Runtime;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;

namespace Sitecore.Foundation.Variants.ScribanExtensions
{
    public class LinkTool
    {

        public static string GetLinkFieldText(Item item, string fieldName)
        {
            LinkField field = (LinkField)item.Fields[fieldName];
            if (field == null) return "";

            return field.Text;
        }

        public static string GetLinkFieldTitle(Item item, string fieldName)
        {
            LinkField field = (LinkField)item.Fields[fieldName];
            if (field == null) return "";

            return field.Title;
        }
    }
}

Returning back to our AddLinkFieldFunction class, we want our LinkTool available within Scriban templates. To do this we rely on Scriban itself than Sitecore. We need to create a new variable which instantiates a ScriptObject from Scriban and add our LinkTool to it. We can then Import this object to the GlobalScriptObject.

var linkToolScriptObj = new ScriptObject
{
    ["sc_linkTool"] = new LinkTool()
};
args.GlobalScriptObject.Import(linkToolScriptObj);

However, if you attempt to try and use your new "function" in a Scriban template at this point, you will get a lovely error message from Scriban. This is because we need to reference a Scriban base class in our LinkTool itself. No surprises here, the base class is also called ScriptObject from the Scriban.Runtime library. Once this base class is added to our LinkTool class, Scriban kicks into life. Although to actually use this object is slightly different from the functions we've created before.

public class LinkTool : ScriptObject

We have now enabled the GetLinkFieldText and GetLinkFieldTitle methods in Scriban through our "sc_linkTool" function member. Before we head towards Scriban templates themselves, there are a couple of interesting things to know about. We mentioned earlier that we may only want a few extensions to be available through Scriban. Luckily, this is really simple. We need to add an attribute to the methods we want Scriban to ignore.

[ScriptMemberIgnore]

With this attribute added, Scriban simply ignores that method and does not add it to our member we've defined.

Adding to this, we may also have many different tools in our project which we want to be available through Scriban. Again, this is simple to do. In this example we are adding a search tool which does... something? Anyway, we can include it our ScriptObject instantiation as we did previously defining again another member name. In this case we have also renamed our ScriptObject variable to be something a bit more relevant (and also rename the surrounding class for consistency and readability).

var toolScriptObj = new ScriptObject
{
    ["sc_linkTool"] = new LinkTool(),
    ["sc_searchTool"] = new SearchTool()
};

Now comes the Scriban differences within the templates when including a ScriptObject type. At this point, we've dealt with functions that immediately take arguments, not objects with method names. Our member name is available again on a global level so we can simply use the "sc_linkTool" syntax as normal for a function except we now need to define the method. Scriban automatically exposes our .NET object with lowercase and _ names. Simply put, our method name for GetLinkFieldText will be exposed through Scriban as get_link_field_text.

Putting this all together now means that we can access the LinkField text or title through our LinkTool. We give our arguments again with space separation and pipe to move to a new function.

<span>{{ sc_linkTool.get_link_field_title i_item "Link" }}</span>
<span>{{ sc_linkTool.get_link_field_text i_item "Link" }}</span>
Cookie Policy© 2020 — DOTNET Ltd