When publishing items in Sitecore, the publishItem pipeline is called for each item that must be published, or unpublished. Except for children of items that have been deleted. Let’s take this example:
If I publish the top item, the publishItem pipeline will be called for every child with the PublishAction.PublishVersion property in the context.
If I then delete “Page1” and republish the top item, the publishItem pipeline is run again. This time the “Page1” will be called with PublishAction.DeleteTargetItem property in the context. But there is no publishItem being called for each of the children of “Page1“.
This is because Sitecore assumes that when a parent is being unpublished, it will automatically clean up any child items, because the relationship between parent and child have been broken.
SO THAT IS THE CHALLENGE THEN?
In my solution, we synchronize certain Sitecore items with an external database. This database have no parent-child relation. so when the “Page1” is deleted, the children below is not deleted from the external database, because no one tells the database to do so.
HOW CAN THIS BE FIXED:
My colleague Odin Jespersen helped me fix this issue. He created a new pipeline step for the publishItem pipeline that compares the web database with the master database to find items that have been deleted from the master database but not in the web database. These items are deemed deleteable and we then delete the items from the external database.
ENOUGH TALK, SHOW ME THE CODE:
This is how it looks like:
STEP 1: ADD THE STEP TO THE PUBLISHITEM PIPELINE:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:env="http://www.sitecore.net/xmlconfig/env/">
<sitecore>
<pipelines>
<publishItem>
<processor patch:before="*[@type='Sitecore.Publishing.Pipelines.PublishItem.DetermineAction, Sitecore.Kernel']" type="MyCode.DeleteFromExternalDatabase, MyDll" />
</publishItem>
</pipelines>
</sitecore>
</configuration>
STEP 2: THE PROCESSOR:
using System.Linq;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Globalization;
using Sitecore.Publishing;
using Sitecore.Publishing.Pipelines.PublishItem;
namespace MyCode
{
public class DeleteFromExternalDatabase : PublishItemProcessor
{
private readonly MasterDatabaseFactory _sourceDatabaseFactory = Sitecore.Data.Database.GetDatabase("master");
private readonly WebDatabaseFactory _targetDatabaseFactory = Sitecore.Data.Database.GetDatabase("web");
public override void Process([CanBeNull] PublishItemContext context)
{
if (context == null)
return;
if (context.Aborted)
return;
if (context?.PublishContext == null)
return;
Item sourceItem = context.PublishHelper.GetSourceItem(context.ItemId);
if (sourceItem == null)
return;
if (!sourceItem.Paths.IsContentItem)
return;
// Only do this trick for the items that are being synchronized with the
// external database
if (sourceItem.TemplateID != MyTemplate)
return;
Item targetFolder = _targetDatabaseFactory.Get().GetItem(sourceItem.ID);
if (targetFolder == null)
return;
var targetItems = targetFolder.GetChildrenDerivedFrom(MyTemplate);
if (targetItems != null)
{
foreach (Item targetItem in targetItems)
{
if (_sourceDatabaseFactory.Get().GetItem(targetItem.ID) != null)
{
continue;
}
var items = targetItem.GetChildrenDerivedFrom(MyTemplate);
if (items == null)
continue;
foreach (Item item in items)
{
if (_sourceDatabaseFactory.Get().GetItem(item.ID) == null)
{
// Do the delete from the external database
}
}
}
}
}
}
}
MORE TO READ:
- Sitecore Publish Items using the PublishManager from briancaos
- Sitecore Publishing – Programmatically determine if item should be published by briancaos
- Sync Sitecore content to external database by briancaos
- Data Integration using the Sitecore Publish Pipeline by Mike Skutta