Experience editor. Add all editable items on page to related items publishing

Recently I faced one challenge. A Sitecore page has a number of rendered fields and all of them can be marked as “editable” in Sitecore out of the box.
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.

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 so 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…

Advertisements

One thought on “Experience editor. Add all editable items on page to related items publishing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s