4

How can I get a query-based treelist to return a list of items relative to the current item, that fits a template, instead of only the first result

Search structure:

-Parent Item
--Segments
---Item 1 (template = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})
---Item 2 (template = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})

For example, if I set this as the source, it correctly finds a descendant but only the first result is available for selection in the treelist, which in the case above would only return Item 1:

query:self:://*[@@templateId = '{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
6

The reason only the first result is available for selection is because the treelist query sets the root item for selection, not the available list of items.

Instead you should use enhanced syntax using Parameterized datasource, which allows you to add some additional restrictions as well as setting the root node.

datasource=/sitecore/content/location/to/root-item&IncludeTemplatesForDisplay=TemplateName1,TemplateName2

Set the datasource and a list of list of template names (no id's) that should be displayed.

You can read more about the syntax and additional options in this blog post.

Note that you cannot specify a query: and use extended parameters for Treelist field since the field does not support it. You can however extend the behaviour to support this, you can find details in this blog post which I wrote.


Create a new class which extends the default Treelist implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Web.UI.HtmlControls.Data;

namespace MyProject.CMS.Custom.Controls
{
    public class Treelist : Sitecore.Shell.Applications.ContentEditor.TreeList 
    {
        private string _dataSource = string.Empty;
        public override string DataSource
        {
            get
            {
                if (_dataSource.StartsWith("query:"))
                {
                    if (Sitecore.Context.ContentDatabase == null || base.ItemID == null)
                        return null;
                    Item current = Sitecore.Context.ContentDatabase.GetItem(base.ItemID);

                    Item obj = null;
                    try
                    {
                        obj = Enumerable.FirstOrDefault<Item>((IEnumerable<Item>)LookupSources.GetItems(current, _dataSource));
                    }
                    catch (Exception ex)
                    {
                        Log.Error("Treelist field failed to execute query.", ex, (object)this);
                    }
                    if (obj == null)
                        return null;
                    return obj.Paths.FullPath;
                }
                return _dataSource;
            }
            set { _dataSource = value; }
        }
    }
}

Then switch on over to the core database, make a copy of /sitecore/system/Field types/List Types/Treelist, delete the existing Control field text and instead set the ASSEMBLY and CLASS fields to point to your implementation.

Now you can specify the source as the following in your custom field type:

datasource=query:./&IncludeTemplatesForDisplay=TemplateName1,TemplateName2

And don't forget to patch the config so the new field type is mapped correctly:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>

    <fieldTypes>
      <fieldType name="CustomTreelist" type="Sitecore.Data.Fields.MultilistField,Sitecore.Kernel"/>
    </fieldTypes>

    <contentSearch>      
      <indexConfigurations>
        <defaultLuceneIndexConfiguration>
          <fieldMap>
            <fieldTypes>
              <fieldType fieldTypeName="customtreelist" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
            </fieldTypes>
          </fieldMap>
          <fieldReaders>
            <mapFieldByTypeName>
              <fieldReader fieldTypeName="customtreelist" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.MultiListFieldReader, Sitecore.ContentSearch" />
            </mapFieldByTypeName>
          </fieldReaders>
        </defaultLuceneIndexConfiguration>
      </indexConfigurations>
    </contentSearch>

  </sitecore>
</configuration>

Note: The above patches for Lucene, update the config as approrpiate for the provider you are using.

  • Ah. I forgot to mention, that I need it relative to the current item. It seems that this uses an absolute path (datasource=/sitecore/content/location/to/root) Is there a relative path variable I can use for this? – Jesper Hoff Sep 25 '17 at 14:51
  • I have updated the question – Jesper Hoff Sep 25 '17 at 14:52
  • Which you cannot do using OOTB treelist, you require a custom field (or update the existing field type) to achieve. I linked to a post I wrote to achieve this. I can update the above answer to include that but please make your question clear that is what you need. – jammykam Sep 25 '17 at 14:58
  • Thanks. I was looking for an OOTB way to do it. I think I've found another way to do it in the meanwhile using a multilist, which does not require any custom code. – Jesper Hoff Sep 26 '17 at 8:14
0

I managed to get a relative query selection to work out of the box, with a multilist using a hierarchy like this:

BASE-TEMPLATE-ID item
-CHILD-CONTAINER-TEMPLATE-ID
--Child
--Child

With the template source query looking like this:

query:ancestor-or-self::*/.[@@templateid='{BASE-TEMPLATE-ID}']/*[@@templateid='{CHILD-CONTAINER-TEMPLATE-ID}']//*

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.