Accessing SXA Partial Design Datasources using Powershell
![Accessing SXA Partial Design Datasources using Powershell](/content/images/size/w1200/2023/05/SXA-Powershell-5.jpg)
Page created using Sitecore SXA page designs and partial designs does not have all rendering information on its layout which is the purpose of the partial design to have more flexibility.
Page Composition in SXA
As you may know, in Sitecore SXA we can create multiple partial designs, for example, Header, Footer, etc. Afterward we need to create a page design and on the page design we define which partials should be referenced on that page design.
Taking a look at the mvc.getXmlBasedLayoutDefinition pipeline, you will notice the AddPartialDesignsRenderings and ResolveLocalDatasources processors:
<processor type="Sitecore.XA.Foundation.Presentation.Pipelines.GetXmlBasedLayoutDefinition.AddPartialDesignsRenderings, Sitecore.XA.Foundation.Presentation"
resolve="true" patch:source="Sitecore.XA.Foundation.Presentation.config" />
<processor type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.GetXmlBasedLayoutDefinition.ResolveLocalDatasources, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
Basically the AddPartialDesignsRenderings does following:
- find the page desing using IPresentationContext.GetDesignItem(contextItem)
- Merge the partial design's rendering to the current page using ILayoutXmlService.MergePartialDesignsRenderings()
After page composition, the datasources needs to be resolved using ResolveLocalDatasources, and it is worth to have a look at resolveRenderingDatasource pipeline:
<resolveRenderingDatasource>
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.GetFromCache, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.QueryableDatasource, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.DatasourceFromField, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.CodeDatasource, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.PageRelativeDatasource, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor
type="Sitecore.XA.Foundation.Search.Pipelines.ResolveRenderingDatasource.SearchDatasource, Sitecore.XA.Foundation.Search"
resolve="true" patch:source="Sitecore.XA.Foundation.Search.config" />
<processor
type="Sitecore.XA.Foundation.LocalDatasources.Pipelines.ResolveRenderingDatasource.SetCache, Sitecore.XA.Foundation.LocalDatasources"
resolve="true" patch:source="Sitecore.XA.Foundation.LocalDatasources.config" />
<processor type="Spe.Integrations.Processors.ScriptedRenderingDataSourceResolve, Spe" patch:source="Spe.config" />
</resolveRenderingDatasource>
Getting partial designs using powershell
To access the rendering and datasource of the page, first we need to get all partial designs inlcuded on that specific page.
To access the partial designs of the page we need to use IPresentationContext.GetDesignItem like this:
# Returns the partial design items of the page
function Get-PagePartialDesigns {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[Item]$RootItem # Page Item
)
begin {
Write-Verbose "Cmdlet Get-PagePartialDesigns - Begin"
Import-Function Test-InDelegatedArea
}
process {
Write-Verbose "Cmdlet Get-PagePartialDesigns - Process"
$datasourceItems = New-Object System.Collections.ArrayList
if ((Test-InDelegatedArea $RootItem)) {
Write-Host "You can not access the delegated page"
retrun $datasourceItems;
}
$instance = [Sitecore.DependencyInjection.ServiceLocator]::ServiceProvider
$presentation = $instance.GetType().GetMethod('GetService').Invoke($instance, [Sitecore.XA.Foundation.Presentation.IPresentationContext])
$partialDesigns = $presentation.GetDesignItem($RootItem)["PartialDesigns"].Split("|", [System.StringSplitOptions]::RemoveEmptyEntries)
$lang = $RootItem.Language.name
foreach ($partial in $partialDesigns) {
if (-not([string]::IsNullOrWhiteSpace($partial))) {
$partialPageItem = Get-Item -Id $partial -Path master: -lang $lang
if ($null -ne $partialPageItem) {
[void]$datasourceItems.Add($partialPageItem)
}
}
}
return , $datasourceItems
}
end {
Write-Verbose "Cmdlet Get-PagePartialDesigns - End"
}
}
This function will return the partial design items used on the page.
Resolve Rendering datasources using Powershell
Using Get-LayoutDevice and Get-Rendering, we can access the datasources like this:
# Returns the Renderings of the given root item
function Get-RenderingDatasources {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[Item]$RootItem
)
begin {
Write-Verbose "Cmdlet Add-PageLanguageVersion - Begin"
Import-Function Test-InDelegatedArea
}
process {
Write-Verbose "Cmdlet Add-PageLanguageVersion - Process"
$datasourceItems = New-Object System.Collections.ArrayList
if ((Test-InDelegatedArea $RootItem)) {
Write-Host "You can not access the RenderingDatasources of delegated page"
retrun $datasourceItems;
}
$instance = [Sitecore.DependencyInjection.ServiceLocator]::ServiceProvider
$defaultLayout = Get-LayoutDevice -Default
$pageDataSources = Get-Rendering -Item $rootItem -Device $defaultLayout -FinalLayout
foreach ($datasource in $pageDataSources) {
if ($datasource.Datasource) {
$rds = $datasource.Datasource
# Return the raw Datasource to be parsed against the correct item
[void]$datasourceItems.Add($rds)
}
}
return , $datasourceItems
}
end {
Write-Verbose "Cmdlet Add-PageLanguageVersion - End"
}
}
And now we can bring all together:
# Returns the datasources used on the page
function Get-PageDatasources {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[Item]$RootItem,
[Parameter(Mandatory = $false, Position = 1)]
[switch]$includePartialDatasources
)
begin {
Write-Verbose "Cmdlet Get-PageDatasources - Begin"
Import-Function Test-InDelegatedArea
Import-Function Get-PagePartialDesigns
Import-Function Get-RenderingDatasources
}
process {
Write-Verbose "Cmdlet Get-PageDatasources - Process"
$datasourceItems = New-Object System.Collections.ArrayList
if ((Test-InDelegatedArea $RootItem)) {
Write-Host "You can not access the delegated page"
retrun $datasourceItems;
}
# Get renderings that directly are on the page
$directRenderings = Get-RenderingDatasources -RootItem $RootItem
$instance = [Sitecore.DependencyInjection.ServiceLocator]::ServiceProvider
if ($directRenderings.length) {
foreach ($raw in $directRenderings) {
$context = $instance.GetType().GetMethod('GetService').Invoke($instance, [Sitecore.XA.Foundation.Abstractions.IContext])
$context.Item = $rootItem
# Resolving the raw datasource based on the current page
$ds = [Sitecore.Pipelines.ParseDataSource.ParseDataSourcePipeline]::Run($rootItem.Database, $raw, $rootItem)
[void]$datasourceItems.Add($ds)
}
}
if ($includePartialDatasources) {
$partials = Get-PagePartialDesigns -RootItem $RootItem
foreach ($partial in $partials) {
$renderings = Get-RenderingDatasources -RootItem $partial
foreach ($raw in $renderings) {
$context = $instance.GetType().GetMethod('GetService').Invoke($instance, [Sitecore.XA.Foundation.Abstractions.IContext])
$context.Item = $rootItem
# Resolving the raw datasource of the partial designs based on the current page
$ds = [Sitecore.Pipelines.ParseDataSource.ParseDataSourcePipeline]::Run($rootItem.Database, $raw, $rootItem)
[void]$datasourceItems.Add($ds)
}
}
}
return $datasourceItems
}
end {
Write-Verbose "Cmdlet Get-PageDatasources - End"
}
}
The trick is, when you want to solve the datasources of the partial desins, you will treat them as a normal sitecore page, and change the context to the partial design:
$renderings = Get-RenderingDatasources -RootItem $partial
$context = $instance.GetType().GetMethod('GetService').Invoke($instance, [Sitecore.XA.Foundation.Abstractions.IContext])
$context.Item = $rootItem
# Resolving the raw datasource of the partial designs based on the current page
$ds = [Sitecore.Pipelines.ParseDataSource.ParseDataSourcePipeline]::Run($rootItem.Database, $raw, $rootItem)