[su_note note_color=”#fafafa”]I wasn’t going to write a part two when I published my original article on versioned layouts. Having worked with Sitecore 8 some more I now have more thoughts and notes to share.
UPDATE: There’s now also part 3. You will not want to miss it![/su_note]
Layout Delta
We’ve had layout deltas since 6.4. A layout delta looks like this:
<r xmlns:p="p" xmlns:s="s" p:p="1"> ... </r>Delta is calculated. You don’t have to do anything special – Sitecore does everything for you on save. Prior to Sitecore 8, modifying the item’s presentation details resulted in a delta against standard values.
Shared and Final
Versioned Layouts feature introduced new layers of layout deltas. Sitecore 8 items have two layout fields – Shared and Final. The Shared portion is the good old
__Renderings
field. The Final portion (__Final Renderings
) is new to Sitecore 8 and it’s aVersioned
field (unlike__Renderings
that isShared
) hence the name of the feature – Versioned Layouts.Merge Process
The merge of all the pieces of the layout details is nicely documented on this diagram. I would also recommend looking at the
LayoutField.GetFieldValue()
method inSitecore.Kernel
. Here it is simplified (and I will explain it):var source = (IEnumerable<Lazy<string>>) new[] { new Lazy<string>(() => fields[FieldIDs.FinalLayoutField].GetValue(false, false)), new Lazy<string>(() => fields[FieldIDs.LayoutField].GetValue(false, false)), new Lazy<string>(() => fields[FieldIDs.FinalLayoutField].GetStandardValue()), new Lazy<string>(() => fields[FieldIDs.LayoutField].GetStandardValue()) }; bool isStandardValues = obj.Name == "__Standard Values"; bool isSharedLayout = field.ID == FieldIDs.LayoutField; if (isStandardValues && isSharedLayout) { source = source.Skip(3); } else if (isStandardValues) { source = source.Skip(2); } else if (isSharedLayout) { source = source.Skip(1); } // ... foreach (var lazy in source) { if (!string.IsNullOrWhiteSpace(lazy.Value)) { if (XmlPatchUtils.IsXmlPatch(lazy.Value)) { stack.Push(lazy.Value); } else { seed = lazy.Value; break; } } }The logic is actually very straightforward:
- First, we put all parts of the layout details in order – Standard Values Shared, Standard Values Final, Item Shared, Item Final.
- Next, you remove variables that you don’t need to consider based on what you’re rendering
- Next, you find the starting point that is not a patch (
seed
) and apply all patches (stack
) in order.
Do you see the issue?
Puzzle #1
While it’s very clear and straightforward, something doesn’t make sense to me. Let’s see. Shared portion of the layout is shared across versions and languages. Final is language specific and can also vary from version to version. Look at this equation one more time (I will use STD acronym not to repeat Standard Values all over the place):
STD Shared -> STD Final -> Item Shared -> Item FinalDo you see the issue now? Does it make sense to you to have
Item Shared
applied overSTD Final
? SDT Final may be different in every language version (that’s what being a versioned field means). Item Shared, however, is not version specific. I am struggling to understand how it all will work in scenarios when I do have variations in my STD Final per language. For all I know I can even have different layout documents used on my Standard Values in different languages. Maybe I am missing something obvious but I don’t see how Item Shared can handle that nicely.[su_note note_color=”#fafafa”]It almost feels like Standard Values should not have the Final portion of the layout and should only have Shared to allow language variations on the pages without conflicts and weird merge scenarios. Or maybe the pages should only have the Final part and not have the Shared part.[/su_note]
Puzzle #2
I don’t believe new delta calculation logic works as expected. So far I talked about how it should work. Both documentation and the code that I showed you suggest that STD Final can be a delta against SDT Shared. This isn’t always the case. Try playing with presentation details on your __Standard Values via Device Editor (Presentation -> Details in Content Editor) and Experience Editor and observe what happens with the layout fields by looking at the raw values. You will see that Standard Values Final is either not recorded as delta or is not the delta you expect.
Experience Editor
On save from Experience Editor, for example, this code in
Sitecore.ExperienceEditor.Utils.WebUtility
will skip over delta calculation for__Standard Values
:public static void AddLayoutField(string layout, Packet packet, Item item) { // ... if (item.Name != "__Standard Values") { layout = XmlDeltas.GetDelta(layout, new LayoutField(item.Fields[FieldIDs.LayoutField]).Value); } // ... packet.SetAttribute("fieldid", FieldIDs.FinalLayoutField.ToString()); packet.AddElement("value", layout, new string[0]); // ... }Working with the layout on
__Standard Values
will just save the resulting presentation details into the Final field without calculating a delta against Shared.Device Editor
The Device Editor works via the
item:setlayoutdetails
command and does things a little different but still not how I would expect it. Here’s an example scenario. I start with a__Standard Values
that has empty presentation details. Nothing in Shared. Nothing in Final.Step 1. Add a component to Shared
In device editor I add a Sample component into a “Main” placeholder:
I didn’t even go to the FINAL LAYOUT tab. Upon saving, however, here’s what I have in my layouts:
<r xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}"> <r id="{493B3A83-0FA7-4484-8FC9-4680991CF743}" ph="Main" uid="{112304F5-C9FF-4751-A043-A9A66C8E84BD}"/> </d> </r>It’s the same in both Shared and Final and Final is NOT a delta.
Step 2. Clean Final
I switch to raw values and erase the value in the Final layout altogether. Or you can use the “reset” dialog.
Step 3. Delete component
Now I open the Device Editor again and delete the component that I inserted on Step 1. Let’s see.
In Shared:
<r xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}" /> </r>In Final:
<r xmlns:p="p" xmlns:s="s" p:p="1"> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"> <r uid="{112304F5-C9FF-4751-A043-A9A66C8E84BD}"> <p:d /> </r> </d> </r>My STD Shared is like I expect it to be. It’s clean and has no components. My STD Final, however, is weird: it’s a delta against the previous state of the STD Shared.
I haven’t looked too deep into it but I believe the problem is in how the delta is calculated upon save. Device Editor XML Dialog sends both values down. The code in the command first saves the Shared and then saves the Final as a delta against Shared. The question is – is it using the new Shared to calculate the delta against or the value that’s on the item? It feels like it’s using the value from the item. That’s why I would get a full layout on Step 1 – shared was empty in the item at the item, and that’s how I would get the
patch:delete
on Step 3 – shared had the component on the item at the time of the editing. But again, I haven’t tried to track it down.[su_divider][/su_divider]
I have posted #434718 to the support team but I don’t expect a quick and simple answer. Maybe I am missing something, who knows. Let me know what you think and please share your experience in commends. I will sure keep digging and will keep you posted.
[su_note note_color=”#fafafa”]UPDATE: There’s
Sitecore.Support.435423.435426
that should address puzzle #2. As to puzzle #1, my thoughts around the merge process andShared + Shared + Final
orShared + Final + Final
but notShared + Final + Shared + Final
were registered as an information request for the product team to consider. It appears to be “by design” but I believe all 4 pieces don’t make sense together. Something tells me there will be a part 3 to this blog series. Stay tuned![/su_note]
I have observed these same issues while working on our first Sitecore 8 implementation. I thought I messed up with our custom standard values provider but it appears I don’t need to start looking. Thanks for the headsup!
Regarding puzzle #1 – If I understand correctly – If I setup standard values for each language on my template, it becomes STD Final, right? If that is the case, when I edit that page’s renderings in page editor, am I not editing the Item Final? Does STD shared and Item Shared even come into the picture? What would be saved in those fields?
Can you verify the version of Sitecore 8.0 you found this? We are using Sitecore 8.0 update 3 (150427), and it doesn’t seem to be occurring. Hopefully that means Sitecore found and fixed this fairly quickly. Thanks!