Blog

Context Based Datasource

February 23, 2015 | Charlie Turano

The Problem

When building a Sitecore solution, the taxonomy of the solution plays a large role in how difficult or easy the content editor’s job is. Maintaining a poorly organized content tree is far more difficult than a well-organized one. Cluttered and disorganized content trees rarely, if ever, get better as time goes on, making the organization problem worse.

Pages in a modern Sitecore site are built using a large number of Sitecore items. The page under the Home item is the starting point for building a page, but it is rarely the end. Components on the page often use Datasources to refer to additional page content. This article is about organizing additional page content.

Keeping associated page data well organized ensures content editors can quickly create, find and update content as needed. Bringing new content editors onto a project is much easier if the page content follows well defined rules. Defining and enforcing these rules can be a challenge to even the best teams.

Organizing Datasource Content

Page data items can be kept close to the page by creating a data structure directly under the page item. While appealing from a data organization point of view, this doesn’t seem like a good practice because all items under the Home item have a browsable Url. This also adds to “clutter” in the content tree, making it hard to differentiate pages from data. There are many ways of working around this, but it has a bit of an unclean smell to it.

Here at Hedgehog, we like to create a “Data” folder as a sibling of the Home item for page data. This keeps items close, but doesn’t clutter the browsable content tree. The issue we run into with this arrangement is keeping the data folder structure clean.

Ideally, there is a location for page specific data and another for global data. The global data and the associated UI components are used on many pages. In the case where the data for components is only associated with a single page, it would be useful if the content editors created a structure under the Data folder that matched the structure of the items under Home. This would make finding the data items associated with a page very easy. Maintaining this sort of structure requires discipline on the content editor’s part.

Screenshot of a global datasource item in the Sitecore Content tree

This is a very simple example of the folder structure described above.

In most recent versions of Sitecore, the Datasource Location field in the sublayout can be set to multiple locations. The user is presented with the ability to browse to the location for the data item from multiple roots. One of the locations should be the “global” location. Another is the tree for page specific items. The problem content editors face with the page specific locations is the content editor is forced to browse to the correct location every time they need to update the Datasource. It would be very helpful if the content editor was presented with the correct folder whenever they browse for the content associated with the page component.

Extending Sitecore

Since Sitecore is an incredibly robust and extensible platform, there must be an easy way to add our desired functionality. Fortunately, the getRenderingDatasource pipeline provides the perfect place to help the content editor locate the correct place to find or create content. By default, Sitecore uses the pipeline step Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceLocation to parse the value in the Datasource Location field specified in the sublayout definition and populate the collection GetRenderingDatasourceArgs.DatasourceRoots. By replacing this pipeline step with more robust functionality, we can easily improve the content editor experience.

New Pipeline Step

The implementation of the new pipeline step is relatively simple. The Datasource Location field is parsed and if it contains a special macro token, it is replaced with the path from the root of the current site.

Everything in this post was implemented with Sitecore 7.2 (rev. 140526), but it should work well for other versions of Sitecore.

First, we need to create a class and setup a couple of useful items:

public class ContextBasedDatasourceFolder
{
    const string MACRO_NAME = "{$HomePath}";

    static char[] BAR_ARRAY = new char[] { '|' };
    static char[] SLASH_ARRAY = new char[] { '/' };
    static Guid FOLDER_ID = new Guid("{A87A00B1-E6DB-45AB-8B54-636FEC3B5523}");
}
  These will help us in a bit to parse the Datasource Location and create our folders. The Process method is pretty simple:
public void Process(GetRenderingDatasourceArgs args)
{
    string dataSourceFolderExpressions = args.RenderingItem["Datasource Location"];

    ProcessDataSources(args, dataSourceFolderExpressions);
}

 

The Datasource Locations are processed by looking for the macro token in each pipe (‘|’) delimited path. If the macro token is found, the proper path is calculated. If no token is found, the path is processed much like it was in the Sitecore pipeline step. The pipeline step will not only calculate the correct path, it will automatically create the folders if needed.

private void ProcessDataSources(GetRenderingDatasourceArgs args, string dataSourceFolderExpressions)
{
    string homePath = FindSiteHome(args.ContextItemPath);

    string[] siteFolders = null;

    if (!string.IsNullOrEmpty(homePath))
    {
        siteFolders = GetFoldersFromHome(homePath, args.ContextItemPath);
    }

    //Loop through the folder definitions
    foreach (string dataSourceFolderExpression in new ListString(dataSourceFolderExpressions))
    {
        //See if there is a replacement macro in the name
        if (siteFolders != null && dataSourceFolderExpression.Contains(MACRO_NAME))
        {
            //Break up the folder name
            int macroNamePos = dataSourceFolderExpression.IndexOf(MACRO_NAME);
            string rootDataFolderPath = dataSourceFolderExpressions.Substring(0, macroNamePos - 1);
            string dataFolderSuffix = "";

            if (macroNamePos + MACRO_NAME.Length + 1 < dataSourceFolderExpression.Length)
            {
                dataFolderSuffix = dataSourceFolderExpression.Substring(macroNamePos + MACRO_NAME.Length + 1);
            }

            //Create the full list of folders
            List<string> allSubFolders = new List<string>(siteFolders);
            allSubFolders.AddRange(dataFolderSuffix.Split(SLASH_ARRAY, StringSplitOptions.RemoveEmptyEntries));

            Item rootDataFolder = args.ContentDatabase.GetItem(rootDataFolderPath);
            if (rootDataFolder != null)
            {
                Item dataSourceFolder = CreateSubFoldersIfNeeded(rootDataFolder, allSubFolders);

                args.DatasourceRoots.Add(dataSourceFolder);
            }
        }
        else
        {
            //Get the current path
            string path = dataSourceFolderExpression;

            //See if it is a relative path (yuck)
            if (path.StartsWith("./", System.StringComparison.InvariantCulture) && !string.IsNullOrEmpty(args.ContextItemPath))
            {
                path = args.ContextItemPath + path.Remove(0, 1);
            }

            //Find the item
            Item item = args.ContentDatabase.GetItem(path);
            if (item != null)
            {
                args.DatasourceRoots.Add(item);
            }
        }
    }
}

 

The support methods are pretty straight forward. The most interesting one is FindSiteHome. This method loops through the defined sites and tries to determine if the current item is under one of the Home items for the site.

/// <summary>
/// Takes a root folder and a list of sub folders and returns the final sub folder. If the folders do not exist, they are created.
/// </summary>
/// <param name="rootDataFolder"></param>
/// <param name="allSubFolders"></param>
/// <returns></returns>
private Item CreateSubFoldersIfNeeded(Item rootDataFolder, List<string> allSubFolders)
{
    Item currentFolder = rootDataFolder;

    foreach (string subFolder in allSubFolders)
    {
        Item childItem = currentFolder.Axes.GetChild(subFolder);

        if (childItem == null)
        {
            childItem = ItemManager.CreateItem(subFolder, currentFolder, new ID(FOLDER_ID));
            childItem.Versions.AddVersion();
        }

        currentFolder = childItem;
    }

    return currentFolder;
}

/// <summary>
/// Retuns an array of strings that represents the names of folders from the home node
/// </summary>
/// <param name="homePath"></param>
/// <param name="currentPath"></param>
/// <returns></returns>
private string[] GetFoldersFromHome(string homePath, string currentPath)
{
    string folders = currentPath.Substring(homePath.Length);

    return folders.Split(SLASH_ARRAY, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
/// Returns the path to the site home node
/// </summary>
/// <param name="currentPath"></param>
/// <returns></returns>
private string FindSiteHome(string currentPath)
{
    currentPath = currentPath.ToLower();

    //Loop through the sites looking for one that the current path starts with
    foreach (Site site in SiteManager.GetSites())
    {
        if (site.Properties["database"] != "core")
        {
            string siteHome = site.Properties["rootPath"] + site.Properties["startItem"];

            if (!string.IsNullOrEmpty(siteHome))
            {
                if (currentPath.StartsWith(siteHome.ToLower()))
                {
                    return siteHome;
                }
            }
        }
    }

    return null;
}

 

 

Patch File

To enable the new functionality for datasources, we need to patch the Sitecore configuration and change the getRenderingDatasource pipeline to use our new step:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getRenderingDatasource>
        <!-- Replace the existing processor for getting the data source location-->
        <processor type="Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceLocation, Sitecore.Kernel">
          <patch:attribute name="type">Hedgehog.Pipelines.ContextBasedDatasourceFolder, Hedgehog</patch:attribute>
        </processor>
      </getRenderingDatasource>
    </pipelines>
  </sitecore>
</configuration>

 

Using the dynamic Datasource

The dynamic Datasource location works much like the default functionality. The main difference is there is the new macro token called {$HomePath} that gets replaced with the current path to the item from the sites' home folder. The following are example folder paths that will work with this component:
  • /sitecore/content/Data/PageDataSources/{$HomePath}
  • /sitecore/content/Data/PageDataSources/{$HomePath}/Modules
  • /sitecore/content/Data/PageDataSources/{$HomePath}|/sitecore/content/Data/Global

Conclusion

Being able to neatly organize the data used by your datasources makes the site much more supportable. Having Sitecore automatically create and/or choose a location that aids this process makes the content editors job much easier. Using this makes the lives of the content editors we support so much easier. Leave a comment letting me know what you think!

Sitecore Development Datasources sitecore development

Related Blog Posts

Sitecore to acquire Hedgehog
Hedgehog is thrilled to announce we have entered into a definitive agreement to be acquired by Sitecore.
SUGCON Europe 2019: A Hedgehog Live Chat
Hedgehog is headed for London! Our SUGCON Europe team talks sessions, SUGCON memories and shares tips for getting the most out of the conference.&nbsp;<span style="white-space: pre;"> </span>
Hedgehog Announces the Release of TDS Classic 5.8
Hedgehog announced the release of TDS Classic 5.8. The newest update now supports Azure and Visual Studio 2019.
Sitecore, Docker, SUGCON - Oh My!
Sitecore MVP Sean Holmesby took the time to talk with us about his presentation and SUGCON.
Enhanced Content Editing for Sitecore: An Avtor Live Chat
In our first product-centered live chat, the team covers Avtor, the essential Sitecore content editing tool.
Hedgehog Announces Release of Avtor
Hedgehog, a digital consultancy engineering high - performance, multi-channel solutions, today announced the release of a brand-new product called Avtor, a part of the Team Development for Sitecore Essential Collection.
How Feydra Improves Front-End Development for Sitecore
Feydra offers a more efficient modern approach to front-end development workflow for Sitecore.
Guide to Sitecore Packaging
Development of features and components requires a set of Sitecore items to be packaged - things like renderings and templates. With TDS Classic, Sitecore developers can automate the packaging process.&nbsp;
TDS Classic: Content File Sync
Content File Sync is a fantastic time saver for any Sitecore developer.
TDS Classic How-To: Perform Unattended Installation of TDS Classic for Visual Studio 2017
The TDS Classic version for Visual Studio 2017 is installed a bit differently than previous versions; the process is separated in two parts and performed by two different installers.
Sitecore Security and Strengthening Your Sitecore Platform
Improve the security of your Sitecore platform with these tips
TDS Classic 5.7 - Lightning Deploy
Lightning Deploy Mode&nbsp;can be used to enable Lightning Mode for all deployments that utilize the TDS Sitecore Connector in their configuration, improving their speed and efficiency.
TDS Classic 5.7 - Lightning Sync
Lightning Sync allows both sync and quick push operations to use TDS Classic 5.7's new Lightning Mode feature
TDS Classic 5.7 - Solution Wide Sync
A simple new feature in TDS Classic 5.7, Solution Wide Sync makes a big difference when working with many TDS Classic projects in a solution.
TDS Classic 5.7 - Lightning Mode
Lightning Mode helps to improve the speed and efficiency of both deploy and sync operations.&nbsp;This enhancement is achieved by modifying how item comparisons are performed.
Sitecore Experience Accelerator: Missing Video Description
<p>A fix for the missing Video Description field in SXA 1.4</p>
RAZL Best Practices: Lightning Mode and Deep Compare
From scheduling Razl scripts to sync changes between Production and QA environments to keeping logs from scheduled Razl scripts, our team has a few tips and tricks to make the Razl experience even better.
Azure Sitecore Deployment: Deploying to a Slot
Setting up Azure staging slots, so the next release of our project can be placed there, allows us to deploy the new code to a private website (the slot), and test it before pushing it live. We are going to script this process to make it easier for the devops team to automate.
TDS Classic Best Practices: NuGet Build Components and TDS Classic .user configs
There are certain systems and processes that you can put in place to make a TDS Classic project run more smoothly. We're highlighting the best practices that our team recommends for getting the most out of TDS Classic.
Azure Sitecore Deployment: Adding Project's Code and Items to the Azure Deployment
Modify the scripts so that the compiled LaunchSitecore site is also provisioned into the new XP environment.
TDS Classic Best Practices: Bundle Packages, Delta Builds and Delta Packages
Following TDS Classic best practices, like using Delta Builds and Delta Packages, can make the entire development experience run much more smoothly.
Azure Sitecore Deployment: Adding Custom Modules
Modify the previous install so that the initial install contains the Sitecore Package Deployer module. It is an excellent way to enable continuous integration to the website.
TDS Classic Best Practices: Validators and the Sitecore Package Deployer
TDS Classic can be used in many ways, but the goal is always the same: make development (and developers lives) easier. Whether it's using the Sitecore Package Deployer or using validators, following best practices can make your entire experience run much more smoothly.
Azure Sitecore Deployment: Preparing the Default Scripts and Packages
Preparing the default packages for a Sitecore Azure deployment and extending to add a custom module to the install.<br> <br> <br> <br> <br>
Azure Sitecore Deployment: Setting Up the Solution and VSO Build
<p>The first in our series on setting up a Sitecore instance on Azure, with an initial deployment that includes custom built modules as add-ons to the setup.</p>
Troubleshoot and Prevent Failed TDS Classic Project Builds
When building an .update package with TDS Classic, the build might fail with no additional information. From increasing log verbosity to using validators, there are ways to minimize or prevent this type of error.
TDS Classic How-To: Disable Automatic Code Generation
Code Generation is automatically triggered after every change in the TDS Project tree. If a project contains many items, users can disable this feature for their convenience.
TDS Classic Sitecore Deploy Folder
Sitecore Deploy Folder is a setting, located in the build tab of the TDS Classic Project's Properties page, and used to tell TDS Classic where the webroot is located.<br>
TDS Classic Builds on Jenkins Build Server with NuGet Packages
Our simple scenario includes 2 developers using TDS Classic and checking-in changes to source control. The Jenkins build server takes the changes and performs the build, and then deploys the created package to two Sitecore environments.
Features to Improve Sitecore Development: TDS Classic Strikes Back
Each and every feature in TDS Classic is aimed at helping developers. Whether the feature is out front or running quietly in the background the goal is always the same: make the development experience better. &nbsp;&nbsp;
Feydra and the Virtual Sandbox
Feydra virtualizes all front end assets (css, js &amp; cshtml) of a Sitecore instance. With Feydra, front-end developers can commit their changes to Source Control without requiring the intervention of a back-end developer. We call it a virtual sandbox.&nbsp;
Feydra Frequently Asked Questions
Answering a number of excellent questions we've gotten from the community regarding Feydra, including how long it takes to set up a Feydra environment and how to install the product.&nbsp;
TDS Classic Features to Improve Sitecore Development
Each version of TDS Classic comes with the same goal: to make Sitecore development and, by extension, developers, lives easier. Every feature in our products is aimed at making the process better - some of these features aren't quite as well-known as others, but they all help smooth and improve the development experience.
Deployment Properties and the Deployment Property Manager
When working with TDS Classic, you will eventually need to deploy your items to a Sitecore instance and you might not want the default behavior of every item in your TDS project deploying every time. This is where the TDS Sitecore Deployment Property Manager comes in!
Feydra: A Front-End Assessment
Feydra allowed me to start building the front-end in a very short time with no Sitecore experience, and it let me use tools that I was comfortable and familiar with.
Feydra: A Quick Start Guide
A step-by-step guide for installing, configuring and, most importantly, using Feydra from the front-end.
TDS Classic 5.6 Feature Spotlight - Prevent Deployment of Incorrect Assemblies
This feature, new to TDS Classic 5.6, will prevent a solution from deploying unless all assemblies (except the excluded assemblies we allow you to specify) match what exists in your webroot.&nbsp;
Feydra from the Front-End
Feydra eliminates common roadblocks for designers and front-end developers working on Sitecore projects by getting them up and running more quickly and allowing them to use the development environment and workflow tools they prefer.&nbsp;
FxCop Rules for Sitecore Helix
TDS How To: Install the TDS Connector for Rocks 2.0
Manually install the TDS connector for Sitecore Rocks 2.0
Create TDS Classic Custom Post Deploy Step
Team Development for Sitecore Classic version 5.5 allows developers to add post deployment steps to to their deployments and update packages. TDS Classic has used post deployment steps internally to perform a number of useful functions. Many of the developers using TDS Classic have requested the ability to add their own post deploy functionality. With the release of TDS Classic 5.5 in early 2016, this functionality is now available.
Using NuGet Packages in TDS
New to TDS 5.5 is the ability to create and consume NuGet packages in your TDS project, allowing developers to capture their Sitecore items and easily distribute them across multiple teams.
Occasional Issue Seen with TDS Installer
When upgrading to TDS 5.5.0.x from an older version and trying to load a solution with a TDS project inside, the following error might occur:
TDS: The Evolution of Auto-Sync
Auto-Sync has been described as a new feature, but in reality has existed in TDS since 2010 and has taken a new form in TDS 5.5, due to be released March 22, 2016
TDS for SC Hackathon 2016
For all those participating in the Sitecore Hackathon, check out our Habitat for TDS and some other cool surprises.
3 Critical (and Common) Sitecore Wellness Issues
There are a lot of factors that can adversely affect Sitecore and its performance. There a few common errors that we almost always uncover when running a wellness evaluation.
Config Transforms for Config Files
If you don't want to use a third party development tool for your config transforms, I have good news. Config transforms are supported natively within TDS!
Package Installer from the Command Line
The TDS package installer allows you to install packages through the command line. Learn how...<br>
Sitecore Placeholder Fallback
When implementing Sitecore websites, we sometimes run into a situation where the Content Editor wants to personalize or A/B test components that are common to multiple pages in the site. A good example of this is the header and/or navigation components. The problem we run into is that the components need to behave the same on all pages and it would be very difficult, if not impossible for content editors to maintain these components on all pages on the site. I have seen a few ways of solving this problem, but most solutions had some drawbacks that limited the capabilities of the content editor.
Customizing WFFM in Sitecore 8
WFFM is a great tool for allowing content editors to build custom forms. The main issue with WFFM is that it doesn't always generate the HTML we need for the site. This blog post shows how to customize the HTML in different ways for each sub-site.
Robust MVC Rendering Exception Handler
While working on a recent Sitecore MVC implementation, I started to think about how Sitecore handles errors in the MVC components on a page. In past implementations, I had added a processor to the mvc.exception pipeline to route the user to the error page for the current site. This works reasonably well, but I began to notice a few drawbacks.
Predefined Roles and Creating Your Own
What are the out of the box Sitecore roles, when should you make your own, and how do you do it?
New Build for TDS 5.0!
A couple bugs were reported over the last few months. We've been able to fix the bugs and now have a stable CTP version for use.
It's finally here!
Team Development for Sitecore has finally been released go download it now!
Team Development for Sitecore Webinar
Our Sitecore MVPs Charlie and Sean recently did a demo of TDS to all Sitecore partners. We recorded the demo to share with the world.