Convert Data Source Paths to IDs Using the Sitecore ASP.NET CMS

This blog post explains how you can configure Sitecore to store IDs of presentation component data sources in layout details rather than storing their paths as the Sitecore ASP.NET web Content Management System (CMS) does by default. With this solution, you can move and rename data source items without the need to publish items containing layout details that refer to those items. Comments on the blog post Add Presentation Component Data Sources to the Links Database in the Sitecore ASP.NET CMS and the Data Source as GUID vs Path thread on the Sitecore Developer Network (SDN) forums prompted me to write this blog post. For more information about presentation components and data sources, see The Sitecore Presentation Component Reference and the blog post How to Apply Data Sources to Sitecore ASP.NET Presentation Components.

This solution uses a rules engine action so you can use the infrastructure described in the blog post Apply Rules to All Items in the Sitecore ASP.NET CMS to update current layout details for all existing items and with an Item Saved rule to update layout details as they change in the future. I highly recommend that you use this solution in conjunction with that described in the blog post Add Presentation Component Data Sources to the Links Database in the Sitecore ASP.NET CMS to configure Sitecore to track presentation component data sources in the links database, but beware that I have not extensively tested either solution.

To support layout deltas, always use the Sitecore.Data.Fields.LayoutField class to manipulate layout details as described in the blog post Programmatically Update Layout Details with the Sitecore ASP.NET CMS. For more information about layout deltas, see The Sitecore Presentation Component Reference.

I am really happy with the rules engine for at least the following reasons:

  • I don't have to create a Web.confg include file as I would have to for an event handler or pipeline processor.
  • I can invoke logic in multiple contexts (such as applying it to every existing item or to each item after a save event) without duplicating code or factoring it out to a helper class.
  • Conditions and actions are reusable and help with separation of concerns.
  • I can reconfigure logic dynamically though a browser-based user interface.
  • I prefer named and explicitly typed parameters to generic positional parameters such as those used provided to event handlers.

In combination with the scheduled agent that applies rules against all items described in the blog post Apply Rules to All Items in the Sitecore ASP.NET CMS and the UI customization that allows me to run agents interactively described in the blog post Run Scheduled Agents Interactively in the Sitecore ASP.NET CMS, the rules engine appears to provide a capable, flexible, configurable, usable, powerful solution for defining administrative tasks. I realize this could result in some performance drawbacks, such as the logic on each item save, but this should be minimal, and cannot be much worse than standard item event handlers. For more information about the rules engine, see The Sitecore Rules Engine Cookbook. Whether you choose to use the rules engine or other features to implement this type of logic may depend on your familiarity with the rules engine, the PowerShell Console Sitecore Shared Source project, and the relevant events and pipelines, as well as whether you prefer to maintain logic in Sitecore items or configuration files. In the SDN forum thread Data Source as GUID vs Path@techphoria414 provides sample code that uses an event handler instead of the rules engine, and explains why he prefers events to the rules engine.

As is often the case with my rules engine actions, I put some logic in the action that could be part of the condition because I cannot count on the user to always select the right condition. Specifically, the action does not update fields that contain their standard value. In that case, the action could update the standard values item itself, but that didn’t seem straightforward, and the blog post Apply Rules to All Items in the Sitecore ASP.NET CMS provides a technique to apply the action to all standard values items, and the Item Saved rule should change paths to IDs in standard values items when you save those items. In fact, the condition that causes Sitecore to apply the action only to those items that contain layout details may be less efficient than using the “when true (actions always execute)”, because the action only processes items that contain layout details. Note also that because all versions in all languages share layout details, it would be unnecessary to run this action against all existing versions or languages of an item; to update existing layout details, you only need to apply this action to a single version of each item in each language.

The archive provided with this blog post actually contains two solutions: one to update paths to data sources to IDs when users save items, and another to manage the links database for those data source items. The following brief overview of the links database may provide some relevant context. The links database is actually a table that Sitecore uses to record the relations between items, specifically which items contain references to other items. Certain operations in Sitecore, such as saving an item or using the Database Control panel to rebuild the links database, cause Sitecore to parse items and update records in this table. The various data template field types store their values in different formats, so Sitecore uses different classes to parse different types of fields. The /App_Config/FieldTypes.config file maps the field type identifiers to the classes that process the links database for those types of fields. These classes contain three key methods:

  • Relink(): Updates references to an item to specify an alternate item
  • RemoveLink(): Removes links to an item from a field value
  • ValidateLinks(): Updates the links database to record the valid and invalid links in the field value

To update the links database, Sitecore invokes the ValidateLinks() method for the relevant fields. When you delete an item, Sitecore shows a user interface that allows you to select whether to delete links to that item, which results in Sitecore calling the RemoveLink() method for the fields that contain references to that item, or update those links to reference an alternate item, which results in Sitecore calling the Relink() method for those fields.

These methods in the Sitecore.Data.Fields.LayoutField class that Sitecore uses for the Layout field type used to store layout details handle references to device definition items, layout definition items, placeholder settings definition items, and rendering definition items, but not references to data source items for those renderings. These three methods in the override to that class provided with this blog post call the corresponding methods in the default implementation, and then apply logic to handle those data source items.

This implementation that overrides the class used to process links in layout details has a few advantages over that provided in the blog post Add Presentation Component Data Sources to the Links Database in the Sitecore ASP.NET CMS: it handles deletion of data source items by allowing the user to remove the reference or set the data source to an alternate item.

This untested prototype contains the following files:

  • /Data/serialization/master/sitecore/system/Settings/Rules/Common/Actions/Apply Data Source IDs.item: Serialization item for rules engine action definition item that converts paths to data source items in layout details to IDs (for more information about serialization with Sitecore, see The Sitecore Serialization Guide)
  • /Data/serialization/master/sitecore/system/Settings/Rules/Common/Conditions/Items/Layout Details for Any Device.item: Serialization item for rules engine condition definition item that evaluates to true if the item contains layout details for any device
  • /Data/serialization/master/sitecore/system/Settings/Rules/Item Saved/Rules/Apply Data Source IDs.item: Item Saved rules definition item that invokes the action against the item if the condition evaluates to true
  • /Website/App_Config/Include/FieldTypes.config: Web.config include file to override the class used to process links in fields of type Layout (for more information about Web.config include files, see the blog post All About web config Include Files with the Sitecore ASP.NET CMS)
  • /Website/Data/Fields/LayoutField.cs: Override class used to process fields of type Layout, specifically the field in the standard template used to store layout details (for more information about layout details, see The Sitecore Data Definition Reference)
  • /Website/Rules/Actions/ApplyDataSourceIDs.cs: Rules engine action implentation
  • /Website/Rules/Conditions/HasLayoutDetailsForAnyDevice.cs: Rules engine condition implementation

While making this change, I also recommend changing the type of the Data source field in the following templates to Droptree. This provides a nicer user interface for selecting a data source (which stores the ID of the item by default instead of its path), and causes Sitecore to use the links database to manage the value of this field in definition items for presentation components.

  • System/Layout/Renderings/Xsl Rendering
  • System/Layout/Renderings/Sublayout
  • System/Layout/Renderings/Method Rendering
  • System/Layout/Rendering Parameters/Standard Rendering Parameters

The solution described in this blog post does not work for relative data sources, such as data sources that begin with an axis (for example ../path) rather than a full path or the ID of an item.

  • This solution works great.. I only ended up developing a command so I can send a save and publish to a tree of items to update the datasource values to GUIDs.

  • @Carlos: Thanks for the feedback! Glad to hear someone uses this solution.

  • John. I know this post is almost a year old, but it I have finally found an answer I have been searching for for ages.... THANK YOU !!!. Just a pity that it is toward the end of my project, so I have a few late nights ahead to implement. However having struggled with this problem for a while, I do have a comment to make ... Since the Standard Rendering Template defines the Datasource property as just a string and since the Sitecore API supports getting an Item by  path or by GUID anyway, there should be NO reason why all the Sitecore shell interfaces, including page editor dialogues and content editor screens should NOT write the referenced items GUID to the Datasource field. Could you indicate if this has been implemented as standard functionality in Sitecore 6.6 and up, or is there some obscure reason why this approach should not be implemented as an enhancement in Sitecore.

  • @Simon: Sitecore 7 should finally address this issue - storage of data sources as IDs rather than paths and maintenance of those records in the links database.

  • Does Sitecore 7 also finally address and solve dynamic placeholders? If so, in which way?

  • @Francesco:  Nothing to my knowledge. I can't promise anything, but I suggest that you file a support case with explicit details and suggestions, then posting that case ID here and everywhere so others can file their own related cases. The more customers/partners/developers/etc. to request a feature, the more likely Sitecore is to implement that feature.

  • in v6.6 Once I changed the Datasource Field Type to DropTree it stores by GUID.  If i want to restore it back to Internal Link, all the Paths are replaced with 32-digit Guid. Have we seen any way to restore it back to Paths?

  • Not sure why you would want to restore to paths, since GUIDS are probably the best approach. If the field is INTERNAL link, and you switch to view RAW values you should see the GUID. If you switch OFF raw values the UI should interpret this to a path. Not sure if it does though.

  • @raghuveer: I have not. I would imagine that a script or possibly even Sitecore Rocks could help with this.

  • I think internal link stores some XML that includes the ID rather than just the ID, which could cause other issues. Without testing, I would avoid that.