One challenge we faced recently was how to vary sitecore page’s layouts across multiple languages. Out the box the layout field is shared, essentially removing the ability for its content to vary by language. In single language sites this isn’t really an issue but once you introduce several languages things get a bit trickier.
In the past we experimented with simply making the field unshared – often with mixed success. All the variations of layouts language by language became rather messy rather quickly – deltas would help this but you still can end up with some languages missing out on key components. Things like language fallback can help this but the solution still didn’t feel sustainable.
With the introduction of MVC there are a heap more pipelines available, especially around the rendering of the page. The solution we arrived at was to flip the problem around a bit and selectively apply layout changes.
Let me introduce the MOLE (hmm tenuous acronym?? 🙂 – better known as ‘MVC Layout Override Extensions’.
The pipeline this taps into is:
1 2 3 |
<mvc.getXmlBasedLayoutDefinition> ... </mvc.getXmlBasedLayoutDefinition> |
Out the box there is one processor which simply reads xml from an item and returns the data parsed as an XElement.
Updating this logic slightly, we hijack which item is used as the source for the data, Have a look in reflector at ‘Sitecore.Mvc.Pipelines.Response.GetXmlBasedLayoutDefinition.GetFromLayoutField’:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Item item = PageContext.Current.Item; if (item == null) { return null; } //MOLE the item Item overrideItem = ItemQuery.GetRelatedLayoutOverrideItem(item); if (overrideItem != null) { item = overrideItem; //it can be useful to know elsewhere during the request if the current page is being mole'd - Items is an easy place to store this info. HttpContext.Current.Items[HttpContextItemsKeys.Presentation.Layout] = item.ID.Guid.ToString(); } Field field = item.Fields[FieldIDs.LayoutField]; if (field == null) { return null; } |
It’s worth noting ‘ItemQuery.GetRelatedLayoutOverrideItem(item);’ is custom code which in this implementation makes use of a new link’ed field to select when an item is being ‘Mole’d.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static Item GetRelatedLayoutOverrideItem(Item currentItem) { Field layoutOverrideField = currentItem.Fields[FieldNames.LayoutOverride]; if (layoutOverrideField != null) { string layoutOverrideFieldValue = layoutOverrideField.Value; if (!String.IsNullOrEmpty(layoutOverrideFieldValue)) { Item overrideItem = currentItem.Database.GetItem(new ID(layoutOverrideFieldValue), currentItem.Language); if (overrideItem != null && overrideItem.Versions.Count > 0) { return overrideItem; } } } return null; } |
This is probably the simplest way of selecting the override item. You could easily introduce the rules engine to give even more flexibility when choosing to override the source items layout.
What I think is really neat about this solution is editors can page edit or preview variants and then assign to any language at any point in time. If you only wanted to override one language variant, you simply only build the relationship to the variant on that given language.
As an add-on we built some ui notifications to indicate if an item was being overwritten, or was a variant in use elsewhere. Because a link field was used to create the relationship, this data is all available in the links db.
Happy mole’ing
Very useful. Just forwarded this to someone who asked me this exact question yesterday. 🙂