Clones are Sitecore items that have a reference to another item that serves as the “source” for any fields for which the cloned item doesn’t have a value. You can make a clone out of just about any item with the system, allowing you to quickly duplicate and maintain several pieces of content through a single source item. This is a very powerful and often underutilized concept in Sitecore.
Recently we upgraded one our clients from Sitecore 6.6 to Sitecore 8.1 update 2. Everything was going fine until they had to publish some of the cloned items within the upgraded system. Every cloned item that was published would result in a 404 when viewed from the publishing target, after a bit of digging we found the root cause.
This issue only occurred on items that didn’t have an explicit value for the final renderings field. Items that don’t have this value set normally use the value located in the renderings field to get their presentation details. However, in this case, although after publishing the item the final renderings value was still empty it was no longer null. This caused Sitecore to behave as though no presentation details were not set for the item, thus resulting in a 404.
We were able to confirm this by viewing the item in the DB Browser.
Although the final renderings field appears empty, it is not null as denoted by the button with the “X” to the right of the field. To fix this we decided to create a publish item pipeline processor:
using Sitecore.Publishing.Pipelines.PublishItem;
namespace Sitecore.iMedia
{
public class AfterPublish : PublishItemProcessor
{
public override void Process(PublishItemContext context)
{
//Only if we are publishing and only if the publish has already happened
if (context.Action != Sitecore.Publishing.PublishAction.PublishVersion &&
context.Action != Sitecore.Publishing.PublishAction.PublishSharedFields &&
context.Result.Operation != Sitecore.Publishing.PublishOperation.Updated)
return;
//get the source and target versions of the item
Sitecore.Data.Items.Item target = context.PublishOptions.TargetDatabase.GetItem(
context.ItemId,
context.PublishOptions.Language);
Sitecore.Data.Items.Item source = context.PublishOptions.SourceDatabase.GetItem(
context.ItemId,
context.PublishOptions.Language);
//only continue if we are able to get both versions
if (target == null || source == null)
return;
//if the source doesn't have a value but the target does, reset the value
if (source.Fields["__Final Renderings"] != null && !source.Fields["__Final Renderings"].HasValue &&
target.Fields["__Final Renderings"] != null && target.Fields["__Final Renderings"].HasValue)
{
using (new Sitecore.Data.Items.EditContext(target, false, false))
target.Fields["__Final Renderings"].Reset();
}
}
}
}
That processor was placed after the perform action processor:
<?xml version="1.0"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <publishItem> <processor patch:after="*[@type='Sitecore.Publishing.Pipelines.PublishItem.PerformAction, Sitecore.Kernel']" type="Sitecore.iMedia.SyncFinalRenderings, Sitecore.iMedia"/> </publishItem> </pipelines> </sitecore> </configuration>
This pipeline processor will first check so see if the desired action is being taken, in this case we only want: publish version or publish shared fields; and that the operation is set to: updated. Next it tries to retrieve the item from the source and target databases and checks to see if both items are not null. Lastly, if the source item doesn’t have a value for the final renderings field but the target item does, reset the field on the target item.
Hopefully this helps, happy coding!