Skip to content

Instantly share code, notes, and snippets.

@MartinMiles
Last active June 3, 2025 06:59
Show Gist options
  • Save MartinMiles/baca6f87af475c8508a4c6374db78683 to your computer and use it in GitHub Desktop.
Save MartinMiles/baca6f87af475c8508a4c6374db78683 to your computer and use it in GitHub Desktop.
Sets Layout item for the page's presentation details (on a shared layout field) without modifying/resetting the rest of presentation/renderings
param(
[string]$PageItemPath = "/sitecore/content/Zont/Habitat/Home/AAA",
[string]$LayoutItemPath = "/sitecore/layout/Layouts/Foundation/JSS Experience Accelerator/Presentation/JSS Layout"
)
# 1. Mount "master:" drive if missing
if (-not (Get-PSDrive -Name master -ErrorAction SilentlyContinue)) {
try {
New-PSDrive -Name master -PSProvider Sitecore -Root "/" -Database "master" -ErrorAction Stop | Out-Null
Write-Host "📁 Mounted master: drive."
}
catch {
Write-Host "❌ Could not mount master: $_.Exception.Message"
return
}
}
# 2. Get master database
$db = [Sitecore.Configuration.Factory]::GetDatabase("master")
if ($null -eq $db) {
Write-Host "❌ Unable to retrieve master database."
return
}
# 3. Load the page item
$pageItem = Get-Item -Path ("master:" + $PageItemPath) -ErrorAction SilentlyContinue
if ($null -eq $pageItem) {
Write-Host "❌ Page item not found at: master:$PageItemPath"
return
}
# 4. Load the layout item
$layoutItem = Get-Item -Path ("master:" + $LayoutItemPath) -ErrorAction SilentlyContinue
if ($null -eq $layoutItem) {
Write-Host "❌ Layout item not found at: master:$LayoutItemPath"
return
}
# 5. Read existing __Renderings XML
$existingXml = $pageItem.Fields[[Sitecore.FieldIDs]::LayoutField].Value
# 6. Parse (or create) XmlDocument for <r>...</r>
[xml]$xmlDoc = $null
if ([string]::IsNullOrWhiteSpace($existingXml)) {
# No current Shared Layout → new <r> root
$xmlDoc = New-Object System.Xml.XmlDocument
$root = $xmlDoc.CreateElement("r")
# Add namespace declarations exactly as Sitecore expects
$root.SetAttribute("xmlns:p", "p")
$root.SetAttribute("xmlns:s", "s")
$root.SetAttribute("p:p", "1")
$xmlDoc.AppendChild($root) | Out-Null
}
else {
try {
$xmlDoc = New-Object System.Xml.XmlDocument
$xmlDoc.LoadXml($existingXml)
}
catch {
Write-Host "❌ Failed to parse existing __Renderings XML: $_.Exception.Message"
return
}
}
# 7. Grab namespace URIs from the root (<r>) element
$root = $xmlDoc.DocumentElement
# Typically these return "p" and "s"
$nsP = $root.GetNamespaceOfPrefix("p")
$nsS = $root.GetNamespaceOfPrefix("s")
# 8. Find the single <d> under <r>
# (This is the device node where your renderings live.)
$dNode = $xmlDoc.SelectSingleNode("/r/d")
if ($null -eq $dNode) {
Write-Host "❌ No <d> device node found under <r> in Shared Layout. Cannot update layout."
return
}
# 9. Compute the new layout GUID (uppercase, with braces)
$layoutGuid = $layoutItem.ID.Guid.ToString("B").ToUpper()
# 10. Set or overwrite the "s:l" attribute on <d> to point to our new layout
# (This leaves all child <r>… elements intact.)
$dNode.SetAttribute("l", $nsS, $layoutGuid)
# 11. Also add "p:before"="*" so that Sitecore knows to insert this layout before existing renderings
$dNode.SetAttribute("before", $nsP, "*")
# 12. Ensure that <r> root still has the correct namespace declarations and p:p="1"
# (If they were missing initially, we already added them in step 6.)
# 13. Serialize back to a string
$updatedXml = $root.OuterXml
# 14. Write back into the __Renderings field (Shared Layout)
try {
$pageItem.Editing.BeginEdit()
$pageItem.Fields[[Sitecore.FieldIDs]::LayoutField].Value = $updatedXml
$pageItem.Editing.EndEdit()
Write-Host "✅ Shared Layout updated successfully."
Write-Host " Page: master:$PageItemPath (ID: $($pageItem.ID))"
Write-Host " Layout: master:$LayoutItemPath (ID: $($layoutItem.ID))"
}
catch {
if ($pageItem -and $pageItem.Editing.IsEditing) {
$pageItem.Editing.CancelEdit()
}
Write-Host "❌ Error writing Shared Layout: $_.Exception.Message"
throw
}
# 15. Verification: print out new __Renderings XML so you can confirm it kept your child <r> nodes
Write-Host "`n🔍 New __Renderings content (Shared Layout):"
Write-Host $updatedXml
You are a world-renowned Sitecore XM Cloud scripting expert. Generate a Sitecore PowerShell Extensions (SPE) script that does exactly the following, preserving every nuance:
1. **Parameter defaults**
- At the top, include:
```powershell
param(
[string]$PageItemPath = "/sitecore/content/Zont/Habitat/Home/AAA",
[string]$LayoutItemPath = "/sitecore/layout/Layouts/Foundation/JSS Experience Accelerator/Presentation/JSS Layout"
)
```
- These are the default paths to:
- The page item whose Shared Layout must be updated.
- The layout definition item to assign.
2. **Mount and database retrieval**
- If the “master:” PSDrive is not already mounted, create it using:
```powershell
New-PSDrive -Name master -PSProvider Sitecore -Root "/" -Database "master" -ErrorAction Stop | Out-Null
```
- Retrieve the master database with:
```powershell
$db = [Sitecore.Configuration.Factory]::GetDatabase("master")
```
- If mounting or getting the database fails, write a clear `Write-Host` error and `return`.
3. **Item lookups**
- Load the page item:
```powershell
$pageItem = Get-Item -Path ("master:" + $PageItemPath) -ErrorAction SilentlyContinue
```
If `$pageItem` is `$null`, write an error and `return`.
- Load the layout item:
```powershell
$layoutItem = Get-Item -Path ("master:" + $LayoutItemPath) -ErrorAction SilentlyContinue
```
If `$layoutItem` is `$null`, write an error and `return`.
4. **Read existing Shared Layout XML**
- Read the raw `__Renderings` field:
```powershell
$existingXml = $pageItem.Fields[[Sitecore.FieldIDs]::LayoutField].Value
```
- Initialize an `XmlDocument`:
- If `$existingXml` is empty or whitespace, create a new `<r>` root element with exactly these attributes:
```xml
<r xmlns:p="p" xmlns:s="s" p:p="1"></r>
```
- Otherwise, load `$existingXml` into `XmlDocument`. If parsing fails, write an error and `return`.
5. **Preserve existing child `<r uid="…">…</r>` nodes inside `<d>`**
- Under the root `<r>`, locate the single `<d>` element (device node). If none exists, write an error and `return`.
- Do **not** recreate or remove any child `<r uid="…"><p:d/></r>` nodes—leave them untouched.
- The `<d>` element’s `id` is the Default device GUID `{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}`.
6. **Update that `<d>` node**
- Compute the uppercase GUID of the layout item:
```powershell
$layoutGuid = $layoutItem.ID.Guid.ToString("B").ToUpper()
```
- Set or overwrite the `s:l` attribute on `<d>` (using the “s” namespace) to `$layoutGuid`.
- Also set `p:before="*"` on `<d>` (using the “p” namespace).
- Do **not** touch any other attributes or nested elements.
7. **Ensure namespace declarations remain**
- If you created a brand-new `<r>` root, add attributes:
```
xmlns:p="p" xmlns:s="s" p:p="1"
```
- If loading existing XML, do not remove or alter any other attributes or child elements in `<r>`—only update `<d>`.
8. **Serialize and save**
- Serialize **only** the `<r>…</r>` node’s `OuterXml` into a string.
- Write that string back into the `__Renderings` field:
```powershell
$pageItem.Editing.BeginEdit()
$pageItem.Fields[[Sitecore.FieldIDs]::LayoutField].Value = $updatedXml
$pageItem.Editing.EndEdit()
```
- If editing fails, cancel the edit and re-throw the exception.
9. **Confirm and report**
- After saving, output via `Write-Host` a success message including both item paths and IDs.
- Then print the new `__Renderings` XML so the user can verify it contains:
- The `<r xmlns:p="p" xmlns:s="s" p:p="1">` root.
- A single `<d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" p:before="*" s:l="{YourLayoutGUID}">` element.
- All original `<r uid="…"><p:d/></r>` child nodes preserved exactly.
Your script must run without errors, respect any renderings already removed from standard values, update only the Shared Layout’s `<d>` node, and leave everything else unchanged.
@MartinMiles
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment