Monday, October 19, 2020

SXA tokens in a Sitecore Treelist datasource

Treelist datasource

A Treelist is a field type in Sitecore that is commonly used to select one or more items from a tree view. It is easy to use for editors and has a rather special way to set the root (datasource) for the tree that is available to those editors. 

A few related field types are available ootb (e.g. TreelistEx) and the Sitecore community has provided rather a few blogpost already on how to customize the Treelist field - and especially the way we set the datasource. 

The basics of how to set a Treelist datasource using the "datasource=" parameters syntax are described very well in this post from Mark Ursino. Or in the official Sitecore archives where setting items available for selecting within the TreeList field was posted already in 2009! Out of the box you are able to select a parent path and determine which templates can be shown and/or selected.

As I mentioned Sitecore community people have been posting blogs on how to extend the datasource based on several different requirements. 

SXA tokens

Sitecore eXperience Accelerator has a pipeline called resolveTokens. This pipeline is used to resolve tokens that can be used in queries and is (amongst other) useful to make templates multisite-friendly. 

The fields on your templates should have sources when applicable - i.e. when another item is selected. This applies to link type fields, list type fields, the image field...  Setting those sources is easy in a single site environment, but when you want to use those templates in a multisite setup you usually want the source to be relative to the site, the site's data folder... 

This is possible with the SXA tokens. You can set sources like "query:$site/..." and those will be resolved relatively within your sites. In a previous post I already showed how to extend this by creating a token for the real media root folder (to be able to add additional -lower- folders after the token).

This is a really nice way of working, but... it only applies to sources that start with "query:".

SXA tokens in a Treelist datasource

I do have a multisite setup. With SXA. And some of my templates do have Treelist fields. You can use the query notation in the datasource and use tokens that way, but then you loose the flexibility of the other parameters - so I was looking for a solution to have both. I want parameters to define the possible templates and I also want to use tokens to define the parent. 

My research led me to two blogposts that inspired me. Diego Moretto wrote about dynamic datasources in Treelist fields but his solution was for non-SXA and with the SXA tokens we do already have so much flexibility. He based his solution on a post from Kamruz Jaman that explains a solution on how to embed queries within the datasource syntax.

While reading that (really good) post I thought I had my solution. By implementing this you actually do have a source that (can) start with "query:" so the resolveTokens pipeline will run and everything will work. I had a tweet ready to thank Kamruz, but then I stumbled upon a small issue: 

SXA cannot resolve the tokens outside an SXA site. This is very logical, but as this is the case in your standard values that resulted in errors when I used this in a feature template with standard values. I don't want errors and the solution was simple - just return a standard path instead of null when the result of the query is null.

As a recap, I'll add my version of the code here (but still - credits to Kamruz for the original):
public class TokenedTreelist : Sitecore.Shell.Applications.ContentEditor.TreeList
{
  public override string DataSource
  {
    get
    {
      var data = base.DataSource;
      if (!base.DataSource.StartsWith("query:", StringComparison.OrdinalIgnoreCase))
      {
        return data;
      }

      if (Sitecore.Context.ContentDatabase == null || ItemID == null)
      {
        return null;
      }

      var currentItem = Sitecore.Context.ContentDatabase.GetItem(ItemID);
      Item source = null;
      try
      {
        source = LookupSources.GetItems(currentItem, data).FirstOrDefault();
      }
      catch (Exception ex)
      {
        Log.Error("Treelist field failed to execute query.", ex, this);
      }

      return source == null ? "/sitecore/Content" : source.Paths.FullPath;
    }

    set => base.DataSource = value;
  }
}  
We are actually creating a new field here - fully using the TreeList as a base and overriding the Datasource property. A few minor changes were made to the code from Kamruz - but the most important one is the last line of the getter where we return "/sitecore/content" as default instead of null. 

Don't forget to register this field in the core database as this is a new field. Add an item at /sitecore/system/Field types/List Types and fill your assembly and class information.


Also add it to the field types list in the Sitecore config:
<sitecore>
  <fieldTypes>
    <fieldType patch:after="*[@name='Treelist']" name="Tokened Treelist" type="Sitecore.Data.Fields.MultilistField, Sitecore.Kernel" />
  </fieldTypes>
</sitecore>
You might want to add it to the index configuration as well - in our case that was not needed.

If we select "Tokened Treelist" as field type, we can now use all SXA tokens within our datasource. Such a datasource could look like:
datasource=query:$site/Data/Documents&allowmultipleselection=yes&includetemplatesfordisplay=Document,Document folder&includetemplatesforselection=Document

Conclusion

I don't think Kamruz realised in 2016 when he wrote that post that this would now enable me to use SXA tokens in a Treelist datasource. Sitecore community has proven this strength once more - so I had to share my findings as well to inform you about the extra options this can give us: multisite Treelists with SXA tokens! 

No comments:

Post a Comment