For example, I have a list of items which are being outputted to the page from a rendering which requests Content Search API and gets list of items from a bucket. In my rendering I can easily make those fields editable as long as Sitecore supports this.

As a content editor I can change those field values and save them from the Sitecore Experience Editor just fine. But if I click publish, those items from a bucket won’t be added into the publishing queue even if I select publish related items. This happens because my page item is not connected or referencing in any way those bucket items and that’s good actually.

Pipeline

From this article I figured out that there is a pipeline which defines which items are going to be added into publishing queue as related items and out of the box there are 4 processors added into it.

<getItemReferences>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemCloneReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddFileDropAreaMediaReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemLinkReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemAliasReferences, Sitecore.Kernel"/>
</getItemReferences>

So there is a way to extend this.
But before adding required items as related items in the publishing process we need to somehow collect them beforehand.
And what I decided is to implement extra pipeline processors for renderField and getChromeData pipelines that would collect all items which fields on the page are being rendered as “editable” including Sitecore edit frames.
Those processors will add required items into the collection and then I can get all of them in the extra getItemReferences pipeline processor.
I have packed this solution into nuget package and you can install it using

Install-Package Helpfulcore.WebEdit

And the source code is available on github here https://github.com/vhil/helpfulcore-webedit
So first, I implement a collection object where I store collected items.

ItemReferenceCollection

Then I add a new processor to the renderField pipeline with next code:

public class CollectItemReferences
{
	public void Process(RenderFieldArgs args)
	{
		try
		{
			if (this.CanCollectReferences(args))
			{
				ItemReferenceCollection.FromConfiguration().AddItemReference(args.Item.ID.Guid);
			}
		}
		catch(Exception ex)
		{
			Log.Error("Error in CollectItemReferences pipeline processor.", ex, this);
		}
	}
	protected virtual bool CanCollectReferences(RenderFieldArgs args)
	{
		return args != null && args.Item != null && !args.Aborted
			&& !args.DisableWebEdit && !args.DisableWebEditContentEditing
			&& (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing);
	}
}

In order to be able to collect items which are present on the page as within Edit Frames I need to inject into getChromeData pipeline and add next processor:

public class EditFrameCollectItemReferences : GetChromeDataProcessor
{
	public override void Process(GetChromeDataArgs args)
	{
		try
		{
			if (this.CanCollectReferences(args))
			{
				ItemReferenceCollection.FromConfiguration().AddItemReference(args.Item.ID.Guid);
			}
		}
		catch (Exception ex)
		{
			Log.Error("Error in EditFrameCollectItemReferences pipeline processor.", ex, this);
		}
	}
	protected virtual bool CanCollectReferences(GetChromeDataArgs args)
	{
		return args != null && args.Item != null && !args.Aborted
			&& args.ChromeType != null && args.ChromeData != null
			&& "editFrame".Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase)
			&& (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing);
	}
}

And finally I inject into the getItemReferences pipeline and adding new processor which will add all previously collected items for the specific page in the Experience Editor

public class FromEditableFields : GetItemReferencesProcessor
{
	protected override List<Item> GetItemReferences(PublishItemContext context)
	{
		try
		{
			if (context != null && context.VersionToPublish != null)
			{
				var references = ItemReferenceCollection.FromConfiguration().GetItemReferences(context.VersionToPublish.ID.Guid);
				return references.Select(x => context.VersionToPublish.Database.GetItem(new ID(x))).ToList();
			}
		}
		catch (Exception ex)
		{
			Log.Error("Error in FromEditableFields pipeline processor.", ex, this);
		}
		return new List<Item>();
	}
}

When the item has been published, the collection is being cleared for this item. The Sitecore page remains to be a container for any amount of components on it and removing or adding new renderings will be reflected on related items collection process.

Items are being added uniquely by their IDs to the ItemReferenceCollection, so no items are going to be published more than once.

This package has been tested on Sitecore 8.1 update 2 but is also supposed to work on Sitecore 7+ as well.
I will extend this package with more cool features later so to be continued…

Share article
See also

Using the service bus to transfer messages between instance roles

Read more Volodymyr Hil 10.06.2018

Using Swagger in Sitecore solution as a helix feature

Read more Volodymyr Hil 19.02.2018

Rebuild Sitecore Analytics Index without re-building reporting database

Read more Volodymyr Hil 16.05.2017

SOLR field definitions for Sitecore analytics index

Read more Volodymyr Hil 07.05.2017

Helpfulcore.GeoIp – Custom Geo IP location services for Sitecore

Read more Volodymyr Hil 27.03.2017