Reduce multisite chaos with Sitecore queries

Posted 9/13/2012 by Przemysław Taront

This post concerns Sitecore 6.6.0 (rev. 120622).

Sitecore multisite deployment

source: sitecore.net


One instance of Sitecore can host multiple sites. This is usually the best architecture if you need to host dozens of small microsites or if you have one website differing across multiple markets. In most cases, your sites share common layouts and templates and you do your best to separate the actual content.

How do you enforce this on editors? You must narrow down the set of items available for selection in datasource and list fields to simply, the scope of your current site. How do you do that? Sitecore query is your best friend here. However, this powerful tool does not always work. There are some good ideas scattered across the web but again, I couldn't find a holistic approach. In this post I'm going to fill this gap by addressing three most painful cases which currently do not benefit from query support.


Queryable Treelists

This problem has been partially solved in this article


While most of Sitecore list types, e.g. Multilist and Droplist, do support relative queries to restrict list of items available for selection, Treelist accepts absolute paths only. To overcome this you need a custom type inheriting from Treelist that will detect and resolve if the source contains a query:
public class QueryableTreeList : TreeList
{
  public new string Source
  {
    get { return base.Source; }
    set
    {
      string dataSource = StringUtil
          .ExtractParameter("DataSource", value).Trim();
      if (dataSource.StartsWith("query:"))
      {
        base.Source = value.Replace(dataSource, ResolveQuery(dataSource));
      }
      else
      {
        base.Source = value.StartsWith("query:") ? ResolveQuery(value) : value;
      }
    }
  }

  private string ResolveQuery(string query)
  {
    query = query.Substring("query:".Length);
    Item contextItem = Sitecore.Context.ContentDatabase.Items[ItemID];
    Item queryItem = contextItem.Axes.SelectSingleItem(query);
    if (queryItem != null)
    {
      return queryItem.Paths.FullPath;
    }
    return string.Empty;
  }
}
To register your new field type:
  1. switch to core database,
  2. duplicate /sitecore/system/Field types/List Types/Treelist item,
  3. rename it to Queryable Treelist,
  4. clear Control field, and
  5. fill in Assembly and Class fields.
Now you can use queries in the Source field of the customized Treelist, i.e. query:./ancestor-or-self::*[@@templatename='HomeTemplate']/Settings/SocialMedia.


Queryable Datasource Locations

This problem has been partially solved in this article


Datasource Location is a field that you must fill in to ensure smooth authoring experience while adding a new sublayout to a page. The field holds location where datasource items for your sublayouts are stored. In a multisite environment you don't want this location to be absolute, because datasources from all sites will land in one place. Once again, we need support for Sitecore queries. In this case,plug into the getRenderingDatasource pipeline:
public class GetQueryableDatasourceLocation
{
  public void Process(GetRenderingDatasourceArgs args)
  {
    foreach (var location in
        new ListString(args.RenderingItem["Datasource Location"]))
    {
      if (location.StartsWith("query:"))
      {
        Item contextItem = args.ContentDatabase.Items[args.ContextItemPath];
        if (contextItem != null)
        {
          string query = location.Substring("query:".Length);
          Item queryItem = contextItem.Axes.SelectSingleItem(query);
          if (queryItem != null)
          {
            args.DatasourceRoots.Add(queryItem);
          }
        }
      }
    }
  }
}


Queryable Datasources

Queryable Datatsource Location seemed to have solved most of my problems, until I came across another case. I want my sublayout to pull data from a location relative to my site root, but I don't want to bother the author with selecting this location manually. In this case you need to fill in the Datasource field in sublayout item, leaving the Datasource Location field empty. Thus we've got another field requiring Sitecore query support. To enable queries in the Datasource field we need to extend insertRenderings pipeline:
public class ResolveQueryableDatasources : InsertRenderingsProcessor
{
  public override void Process(InsertRenderingsArgs args)
  {
    foreach (RenderingReference reference in args.Renderings)
    {
      string dataSource = reference.Settings.DataSource;
      if (dataSource.StartsWith("query:"))
      {
        string query = dataSource.Substring("query:".Length);
        Item queryItem = args.ContextItem.Axes.SelectSingleItem(query);
        if (queryItem != null)
        {
          reference.Settings.DataSource = queryItem.Paths.FullPath;
        }
      }
    }
  }
}


How can I use it?

This time I won't provide you with a package, but just tell you how you can easily do it yourself. Simply create three classes described above and for issues #2 and #3 create a configuration file with the following content:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <insertRenderings>
        <processor patch:before="*[@type='Sitecore.Pipelines
            .InsertRenderings.Processors.EvaluateConditions, Sitecore.Kernel']"
            type="[namespace].ResolveQueryableDatasources, [assembly]"/>
      </insertRenderings>
      <getRenderingDatasource>
        <processor patch:before="*[@type='Sitecore.Pipelines
            .GetRenderingDatasource.GetDatasourceLocation, Sitecore.Kernel']"
            type="[namespace].GetQueryableDatasourceLocation, [assembly]" />
      </getRenderingDatasource>
    </pipelines>
  </sitecore>
</configuration>
Now you can use Sitecore queries to build a multisite environment: Queryable datasource location Queryable datasource
 Did you enjoy my post? If you did, please let me know by posting a comment below.
comments powered by Disqus
24

Przemysław Taront

Sitecore Certified Developer who began his career at the Microsoft HQ in Redmond and developed a strong passion for .NET technology. Now, enjoys developing tailor-made content management solutions.

Archive