SXA: Create custom query token

Rendering Variants offer convenient way of customising how SXA components get rendered. One of the most frequently used fields for building rendering variants is Query, which allows you to utilise item(s) out of the component's scope.

What SXA offers out of the box is a decent collection of predefined, routinely used tokens which help to simplify the queries and makes their usage much more convenient. They are listed here in the SXA docs for resolveTokens pipeline:

• $compatibleThemes - path to all themes
• $theme - currently used theme
• $pageDesigns - root of page designs
• $partialDesigns - root of partial designs
• $currenttemplate - name of the current template
• $tenant - path to the current tenant
• $site - path to the current site
• $home - path to the current site start item (Home)
• $templates - path to the current site templates
• $siteMedia - path to Virtual Media folder located under site

As you can see, it contains the most handy tokens we might need for building queries, such as home item ($home) or component template ($currentTemplate). Of course every application, depending on its business specific needs, 'develops' along the time it's own crucial nodes which become frequently referenced and used in the queries. Very good example is the root item for articles, which occurs in every web site offering news to the user.

In the following use case we will create $articles token referring to articles, which will be translated by resolveTokens pipeline as a query returning all items implementing template Article under the Articles root node.

 

Technically, what we need to do is extending the resolveTokens pipeline:

<resolveTokens patch:source="Sitecore.XA.Foundation.Multisite.config">
  <processor type="Sitecore.XA.Foundation.Multisite.Pipelines.ResolveTokens.ResolveMultisiteTokens, Sitecore.XA.Foundation.Multisite" resolve="true"/>
  <processor type="Sitecore.XA.Foundation.Presentation.Pipelines.ResolveTokens.ResolvePresentationTokens, Sitecore.XA.Foundation.Presentation" resolve="true" patch:source="Sitecore.XA.Foundation.Presentation.config"/>
  <processor type="Sitecore.XA.Foundation.Theming.Pipelines.ResolveTokens.ResolveThemingTokens, Sitecore.XA.Foundation.Theming" resolve="true" patch:source="Sitecore.XA.Foundation.Theming.config"/>
  <processor type="Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens.CurrentTemplateToken, Sitecore.XA.Foundation.TokenResolution" resolve="true" patch:source="Sitecore.XA.Foundation.TokenResolution.config"/>
  <processor type="Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens.EscapeQueryTokens, Sitecore.XA.Foundation.TokenResolution" resolve="true" patch:source="Sitecore.XA.Foundation.TokenResolution.config"/>
</resolveTokens>

by adding a new processor handling the custom token.

 

To figure out what exactly has to be implemented and how are the currently existing processors handling the predefined tokens, we can use ILSpy, dotPeek or any other .NET decompiler to take a closer look at Sitecore.XA.Foundation.TokenResolution.dll. It turns out that to extend the resolveTokens pipeline to handle custom token resolution, we need to implement the Pipelines.ResolveTokens.ResolveTokensProcessor abstract class.

To fulfill the use case mentioned before, I implemented it this way:

using Sitecore.Data.Items;
using Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens;

namespace Sitecore91.Foundation.SXAExtensions.Pipelines.Tokens
{
    public class ResolveCustomTokens : ResolveTokensProcessor
    {
        public override void Process(ResolveTokensArgs args)
        {
            args.Query = ReplaceTokenWithValue(args.Query, "$articles", () => GetArticles(args.ContextItem));
        }

        private string GetArticles(Item contextItem)
        {
            var articlesRootPath = contextItem.Database.GetItem(Constants.Items.ArticlesHome).Paths.FullPath;
            var templateId = Constants.Templates.Article.ToString();

            return articlesRootPath + "//*[@@templateid='" + templateId + "']";
        }
    }
}

 

Just to clarify, Constants static class used in the processor is just a standard helper containing IDs of frequently used items:

using Sitecore.Data;

namespace Sitecore91.Foundation.SXAExtensions
{
    public struct Constants
    {
        public struct Items
        {
            public static ID ArticlesHome { get { return new ID("{4109F746-326F-4EF9-9BEB-D14261E8BD83}"); } }
        }

        public struct Templates
        {
            public static ID Article { get { return new ID("{3A98D12C-1911-49A6-8F8E-AAEDBCC9C3FD}"); } }
        }
    }
}

 

As the implementation code is in place we're almost there, what’s left is just wiring it up in the pipeline. We locate the processor right before the last one in the resolveTokens pipeline:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <resolveTokens>
        <processor type="Sitecore91.Foundation.SXAExtensions.Pipelines.Tokens.ResolveCustomTokens, Sitecore91.Foundation.SXAExtensions" 
                   patch:before="processor[@type='Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens.EscapeQueryTokens, Sitecore.XA.Foundation.TokenResolution']" />
      </resolveTokens>
    </pipelines>
  </sitecore>
</configuration>

 

To double check the result, pipeline after the amendments should look like this:

<resolveTokens patch:source="Sitecore.XA.Foundation.Multisite.config">
  <processor type="Sitecore.XA.Foundation.Multisite.Pipelines.ResolveTokens.ResolveMultisiteTokens, Sitecore.XA.Foundation.Multisite" resolve="true"/>
  <processor type="Sitecore.XA.Foundation.Presentation.Pipelines.ResolveTokens.ResolvePresentationTokens, Sitecore.XA.Foundation.Presentation" resolve="true" patch:source="Sitecore.XA.Foundation.Presentation.config"/>
  <processor type="Sitecore.XA.Foundation.Theming.Pipelines.ResolveTokens.ResolveThemingTokens, Sitecore.XA.Foundation.Theming" resolve="true" patch:source="Sitecore.XA.Foundation.Theming.config"/>
  <processor type="Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens.CurrentTemplateToken, Sitecore.XA.Foundation.TokenResolution" resolve="true" patch:source="Sitecore.XA.Foundation.TokenResolution.config"/>
  <processor type="Sitecore91.Foundation.SXAExtensions.Pipelines.Tokens.ResolveCustomTokens, Sitecore91.Foundation.SXAExtensions" patch:source="ResolveCustomTokens.config"/>
  <processor type="Sitecore.XA.Foundation.TokenResolution.Pipelines.ResolveTokens.EscapeQueryTokens, Sitecore.XA.Foundation.TokenResolution" resolve="true" patch:source="Sitecore.XA.Foundation.TokenResolution.config"/>
</resolveTokens>

 

OK, backend to support the $articles token is done, so let's start making use of it.

In Content Editor I created the Landing Page rendering variant to display few page values: Title, Published date and a Query referring to the collection of articles:

 

Of course, as articles are usually quite complex items, let's tailor their display to absolute minimum showing only their names for this example purpose. To achieve that, I added a child field of type Template to the Articles query and called it Name.

Template field thanks to the support of NVelocity templates refers to its context (article) item by usage of $item token. This means that for each returned article we select and display only it's name:

That's it.

 

Now, after making sure I applied the Landing Page rendering variant to the desired component, I am presented with the fields corresponding to the content of the item: Title, Published date, together with all (4, it's a test) of the articles names:

Hope this helps.