I came across a Sitemap generation code), the idea interested me enough to do a bit more research into the topic, and try to work out the details.
So here's some notes on the hows and whys of using code to change layout details. (I'm working on Sitecore 6.6 here, but this information is relevant to many versions of Sitecore)
The information describing the presentation to use for an item is stored in the
__Renderings
field on each item. The field stores a blob of XML that describes presentation the settings. However, on the "page" item you don't necessarily see all of the data, as Sitecore uses what it calls "Layout Deltas" – the layout information on a page stores only the changes to the layout data on the Template Standard Values for the item.
You can view the layout details by enabling both the "Standard Fields" and "Raw Values" view of an item in Content Editor, and then looking at the Renderings field in the Layout region:
To read meaningful data from the Renderings field, you have to take into account the Layout Delta behaviour. The XML in the Renderings field for a particular item is not necessarily all the data you need to process. Hence you need to make sure you use the correct Sitecore classes to read this data. To extract the full XML, you can use:
var page = Sitecore.Context.Database.GetItem("/sitecore/content/Home"); var field = page.Fields[Sitecore.FieldIDs.LayoutField]; string xml = LayoutField.GetFieldValue(field);
First we load a page, and then extract the Renderings field from it. The constant
Sitecore.FieldIDs.LayoutField
represents the name of the correct field. Finally the
LayoutField
class provides the
GetFieldValue()
method, which knows how to extract the data correctly.
If you look inside the code for this,
GetFieldValue()
uses another class called
XmlDeltas
to deal with the process of managing the deltas.
Once you have the XML, you have two choices about how to work with it. You could just manipulate the XML manually if you wanted. However the more supportable approach (which I'm interested here) is to use the API objects that Sitecore provides. And the class
LayoutDefinitions
manages this for us, and provides a method to parse the XML into objects:
var details = Sitecore.Layouts.LayoutDefinition.Parse(xml);
To access and modify the data in your
LayoutDefinition
you need to navigate the structure of the object. It represents a similar model to that which you see in the Layout Details dialog:
So in the same way this bit of UI shows, you start traversing these objects by finding the appropriate
Device
. You can either iterate them:
foreach(DeviceDefinition device in details.Devices) { // do something with device }
or you can find a specific one by ID (The ID is the GUID of the Device item under
/sitecore/layout/Devices
):
var device = details.GetDevice("{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}");
Each of these devices then includes a series of Rendering Definitions - the things which need to be displayed when this device is being used. Again you can either iterate these, or extract specific ones. To iterate through the things that will be rendered, you can:
foreach(RenderingDefinition rendering in device.Renderings) { // do something with a rendering }
Each of these renderings have properties for things like the Datasource, the cache settings etc. They also have a unique ID for each individual rendering, as well as the ID of the Rendering / Sublayout that is being used for presentation. You can fetch specific renderings with:
RenderingDefinition r = device.GetRenderingByUniqueId("{43222D12-08C9-453B-AE96-D406EBB95126}");
(I don't believe these IDs are presented in the UI anywhere – so they make most sense in combination with iterating the collection)
Alternatively you can fetch by the UI component's ID. If you want to find exactly one rendering definition based on a specific control ID you can call:
RenderingDefinition r = device.GetRendering("{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}");
or if you expect to get back more than one rendering definition based on a specific component you could call:
foreach(RenderingDefinition r = device.GetRenderings("{43222D12-08C9-453B-AE96-D406EBB95126}")) { // do something with a rendering }
In all of these cases, you can then update the
RenderingDefinition
object's properties in the usual way. For example, if you wanted to set a data source:
RenderingDefinition r = device.GetRenderingByUniqueId("{43222D12-08C9-453B-AE96-D406EBB95126}"); r.Datasource = "/somewhere/thing/stuff";
You can also create new Rendering Definitions and add them to the Device object, or remove Rendering Definitions from the device using the
device.AddRendering()
and
device.Renderings.Remove()
methods.
After you've made your changes you need to save your data. As above, you need to ensure that you deal with the fact that you're saving a Layout Delta. And again this is done via the
LayoutField
class. For example:
string newXml = details.ToXml(); var field = page.Fields[Sitecore.FieldIDs.LayoutField]; using (new Sitecore.Data.Items.EditContext(page)) { LayoutField.SetFieldValue(field, newXml); }
The Layout Details object knows how to serialise itself to XML via the
ToXml()
method, but we need to use the
LayoutField.SetFieldValue()
method to store this data to ensure that we save a delta rather than the whole layout definition.
[NB: After I wrote this, I realised that John West had already posted some of this detail on his blog. So if you're interested in his take on this, you may wish to read that post as well]
↑ Back to top