The first question you’re going to ask is.. why the heck would you do that?
- Part of me is a little bit of a perfectionist. We’re not quite launched yet, and I want the presentation details as clean as possible before we start. A good foundation never hurt.
- Part of me is still a little queasy when it comes to the four-tier set of presentation details on items. I’m not alone.
- This is likely to be more performant. Even just a hair when you take into account some higher-traffic sites. I realize it’s cached, but it has to get processed a first time.
With my nice list of three out of the way, let’s get to it.
Consider this Shared Rendering:
<r xmlns:p="p" xmlns:s="s" p:p="1"> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"> <r uid="{A7B9D573-8ED5-4F71-B5EF-31EE6B92CE32}" s:ds="{3DB66721-C5B5-4006-95E6-533C6E5864EC}" s:par="" /> <r uid="{7237D059-717C-4042-9C54-24B8705C119F}" s:ds="{D0B682BD-09FF-4E7A-9666-6924BD215AA9}" s:par="CSS Class=%7B3C13A088-C7D9-4B39-992C-EDFCEC87E0E3%7D" /> <r uid="{2E0239CB-49CA-42D9-8DD0-3D4E18121FA5}" s:ds="{A337274E-ED74-4ADA-BC67-83310F37C870}" s:par="CSS Class" /> <r uid="{37289AF1-24D7-48A6-932E-5156D8F0B143}" s:ds="{B1AA856A-3DC4-4683-8D77-EE819EE9CB57}" s:par="CSS Class" /> <r uid="{2D9811DA-33C5-4EF3-BBD6-F21AFBB090D5}" s:ds="{E97D3F93-E74F-40C2-A38E-024C3704E0A7}" s:par="" /> <r uid="{CFD7251D-830B-40D2-AF4B-CD6282D0223A}" p:after="r[@uid='{2D9811DA-33C5-4EF3-BBD6-F21AFBB090D5}']" s:ds="{17E49769-25AF-467A-92F2-5EBFA562C1D2}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> <r uid="{CD7B5DFE-FFFD-4204-90A3-BE9EE06F7703}" p:after="r[@uid='{CFD7251D-830B-40D2-AF4B-CD6282D0223A}']" s:ds="{AB85D1DE-801D-414B-BFB1-E0C16F433C8D}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> <r uid="{A9169EEF-BB52-4B76-9AF8-A7EAEE3D0DA9}"> <p:d /> </r> <r uid="{1E223314-45A7-4FC2-8F38-B379348DE433}" p:after="r[@uid='{BBA08A67-552A-49F1-A73E-E7CD663E5430}']" s:ds="{EB73D71C-E0B9-4CA3-AFD2-050D765DA5AB}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> </d> </r>
That’s a lot of XML, so apologies for that.
Now take in this Final Rendering on the same item:
<r xmlns:p="p" xmlns:s="s" p:p="1"> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"> <r uid="{CD7B5DFE-FFFD-4204-90A3-BE9EE06F7703}" s:ds="{D6B73A39-EE5D-4239-BF2E-91A3A66F9F31}" /> </d> </r>
All that is doing is swapping out the DataSource. That means that in Final, someone changed the Datasource and that’s it.
How do we fix this, you’re wondering. I came up with a small chunk of code here, which worked much better than I had predicted. I think I actually said “Whoa, that’s it??” when it worked.
private void MergeLayouts(Item item) { //Grab the field that contains the layout var layoutField = new LayoutField(item.Fields[Sitecore.FieldIDs.LayoutField]); //Grab the field that contains the final layout var finalLayoutField = new LayoutField(item.Fields[Sitecore.FieldIDs.FinalLayoutField]); if (layoutField == null) throw new Exception("Couldn't find layout on: {0}".FormatWith(item.Name)); if (finalLayoutField == null) throw new Exception("Couldn't find final layout on: {0}".FormatWith(item.Name)); //If we don't have a final layout delta, we're good! if (string.IsNullOrWhiteSpace(finalLayoutField.Value)) { return; } var finalLayoutDefinition = LayoutDefinition.Parse(finalLayoutField.Value); using (new EditContext(item)) { layoutField.Value = finalLayoutDefinition.ToXml(); item.Fields["__Final Renderings"].Reset(); item.Editing.AcceptChanges(); } }
Yeah, that’s it. The beauty lies in the “Parse” method. It actually composites all the deltas and gives you a true “final” value that can be set back on the Shared Renderings.
Here’s the final result:
<r xmlns:p="p" xmlns:s="s" p:p="1"> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"> <r uid="{A7B9D573-8ED5-4F71-B5EF-31EE6B92CE32}" s:ds="{3DB66721-C5B5-4006-95E6-533C6E5864EC}" s:par="" /> <r uid="{7237D059-717C-4042-9C54-24B8705C119F}" s:ds="{D0B682BD-09FF-4E7A-9666-6924BD215AA9}" s:par="CSS Class=%7B3C13A088-C7D9-4B39-992C-EDFCEC87E0E3%7D" /> <r uid="{2E0239CB-49CA-42D9-8DD0-3D4E18121FA5}" s:ds="{A337274E-ED74-4ADA-BC67-83310F37C870}" s:par="CSS Class" /> <r uid="{37289AF1-24D7-48A6-932E-5156D8F0B143}" s:ds="{B1AA856A-3DC4-4683-8D77-EE819EE9CB57}" s:par="CSS Class" /> <r uid="{2D9811DA-33C5-4EF3-BBD6-F21AFBB090D5}" s:ds="{E97D3F93-E74F-40C2-A38E-024C3704E0A7}" s:par="" /> <r uid="{CFD7251D-830B-40D2-AF4B-CD6282D0223A}" p:after="r[@uid='{2D9811DA-33C5-4EF3-BBD6-F21AFBB090D5}']" s:ds="{17E49769-25AF-467A-92F2-5EBFA562C1D2}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> <r uid="{CD7B5DFE-FFFD-4204-90A3-BE9EE06F7703}" p:after="r[@uid='{CFD7251D-830B-40D2-AF4B-CD6282D0223A}']" s:ds="{D6B73A39-EE5D-4239-BF2E-91A3A66F9F31}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> <r uid="{A9169EEF-BB52-4B76-9AF8-A7EAEE3D0DA9}"> <p:d /> </r> <r uid="{1E223314-45A7-4FC2-8F38-B379348DE433}" p:after="r[@uid='{BBA08A67-552A-49F1-A73E-E7CD663E5430}']" s:ds="{EB73D71C-E0B9-4CA3-AFD2-050D765DA5AB}" s:id="{DC1A6EA7-986A-4CC5-ABAF-4DE85F92D031}" s:par="" s:ph="main" /> </d> </r>
Note that the only line changed is the highlighted one. This really is a surgeon’s approach to merging things back down a level, and I hope this helps someone else out!
This is pretty slick. It would be nice to see this as a PowerShell script that you can kick off from within Sitecore at anytime.
How often are you running this? Renderings added through the Experience Editor are always added to Final Renderings instead of Shared Renderings so it seems like you’d have to run this on every save or every publish.
Thanks! Right now, this is being run just before we launch. We’ve had a *massive* content mgiration and we wanted to get the site a clean start. When we turn on workflows and start versioning items, we won’t want to do this.