Skip to content

Instantly share code, notes, and snippets.

@merkle-sitecore-gists
Last active July 21, 2020 14:39
Show Gist options
  • Save merkle-sitecore-gists/a701bd3c5f37316f9d0770310ac00f83 to your computer and use it in GitHub Desktop.
Save merkle-sitecore-gists/a701bd3c5f37316f9d0770310ac00f83 to your computer and use it in GitHub Desktop.
Export Sitecore item data in json to compare it with your preferred comparison tool
############################################################
# This Script can be used for export Sitecore Item tree data
# This can be used for comparison scenarios like
# 1. Compare content of different databases eg: "master and web"
# 2. Compare content between environments eg: "local, dev, staging, prod"
# 3. Versions or languages
# Benefints: only export field you want to compare/inspect (less noise, like update fields and so on)
# Note: the scripts only deliverys the export. Comparision have to be done with a different tool (winmerge, baretail, kdiff etc.
# Preconditions: Powerhshell Sitecore Extensions installed
############################################################
$helixCustomSolution = ("/sitecore/templates/Project/*", "/sitecore/templates/Feature/*", "/sitecore/templates/Foundation/*", "/sitecore/layout/Layouts/Foundation/*", "/sitecore/layout/Layouts/Feature/*", "/sitecore/layout/Layouts/Project/*", "/sitecore/layout/Models/Foundation/*", "/sitecore/layout/Models/Feature/*", "/sitecore/layout/Models/Project/*", "/sitecore/layout/Renderings/Foundation/*", "/sitecore/layout/Renderings/Feature/*", "/sitecore/layout/Renderings/Project/*", "/sitecore/layout/Placeholder Settings/Foundation/*", "/sitecore/layout/Placeholer Settings/Feature/*", "/sitecore/layout/Placeholder Settings/Project/*" );
# Sitecore specific field lists
$standardTemplateFields = ("__Enable item fallback", "__Tracking", "__Source", "__Source Item", "__Enforce version presence", "__Standard values", "__Read Only", "__Sortorder", "__Editor", "__Originator", "__Style", "__Subitems Sorting", "__Preview", "__Hidden", "__Context Menu", "__Thumbnail", "__Skin", "__Editors", "__Display name", "__Ribbon", "__Icon", "__Long description", "__Short description", "__Help link", "__Final Renderings", "__Presets", "__Controller", "__Renderings", "__Page Level Test Set Definition", "__Content Test", "Controller Action", "__Hide Version", "__Valid from", "__Valid to", "__Facets", "__Boosting Rules", "__Boost", "__Masters", "__Insert Rules", "__Bucket Parent Reference", "__Bucketable", "__Quick Actions", "__Persist Bucket Filter", "__Should Not Organize In Bucket", "__Enable Views", "__Default Bucket Query", "__Is Bucket", "__Publish", "__Unpublish", "__Never publish", "__Publishing groups", "__Owner", "__Security", "__Created by", "__Created", "__Updated", "__Revision", "__Updated by", "__Semantics", "__Archive date", "__Reminder date", "_Archive Version date", "__Reminder recipients", "__Reminder text", "__Workflow Validation Rules", "__Suppressed Validation Rules", "__Validator Bar Validation Rules", "__Validate Button Validation Rules", "__Quick Action Bar Validation Rules", "__Workflow state", "__Workflow state", "__Workflow", "__Lock", "__Default workflow")
$presentationFields = ("__Renderings", "__Final Renderings");
$publishingFields = ("__Publish", "__Unpublish", "__Never publish", "__Publishing groups", "__Workflow Validation Rules", "__Suppressed Validation Rules", "__Validator Bar Validation Rules", "__Validate Button Validation Rules", "__Quick Action Bar Validation Rules", "__Workflow state", "__Workflow state", "__Workflow", "__Lock", "__Default workflow")
$excludedFieldNames = $standardTemplateFields;
#if includedFieldNames are used excluded will be ignored
$includedFieldNames = $null
# Global configurations which can be activated or deactivated for your needs but please make sure if you use export for comparition that you use the same settings
$globalConfig = @{};
$globalConfig.UseFullPath = $true;
$globalConfig.UseName = $true;
$globalConfig.UseId = $false;
$globalConfig.UseTemplate = $false;
$globalConfig.UseLanguage = $true;
$globalConfig.UseVersion = $true;
# The extraction methods
function Get-ItemRepresentation($item, $excludedFields, $includedFields)
{
# Global configuration intepretation
Write-Host "Process item: " + $item.Name
$representation = @{};
if ($globalConfig.UseName -eq $true)
{
$representation.Add("Name", $item.Name)
}
if ($globalConfig.UseId -eq $true)
{
$representation.Add("ID", $item.ID.Guid)
}
if ($globalConfig.UseFullPath -eq $true)
{
$representation.Add("Path", $item.FullPath)
}
if ($globalConfig.UseTemplate -eq $true)
{
$representation.Add("Template", $item.TemplateID.Guid)
}
$version = [ordered]@{};
if ($globalConfig.UseVersion -eq $true)
{
$version.Add("Version", $item.Version)
}
if ($globalConfig.UseLanguage -eq $true)
{
$version.Add("Language", $item.Language)
}
$fullpath = $item.FullPath
Write-Host "Fields on item: $fullpath"
$orderedFields = $item.Fields | Sort-Object -Property Key
foreach($field in $orderedFields)
{
if($includedFields -ne $null)
{
if ($includedFields -contains $field.Name)
{
#Write-Host "Process field: " + $field.Name
$version.Add($field.Name, $field.Value)
}
}
else
{
if ($excludedFields -cnotcontains $field.Name)
{
#Write-Host "Process field: " + $field.Name
$version.Add($field.Name, $field.Value)
}
}
}
$representation.Add("Version", $version)
return $representation;
}
function Export-Trees($paths, $excludedFieldNames, $includedFieldNames, $db, $exportFileName)
{
if ($exportFileName -eq $null)
{
$exportFileName = "export.json"
}
$results = @();
foreach ($path in $paths)
{
if ($db -eq $null)
{
$db = "master"
}
Write-Host "Path will be executed: $path"-ForegroundColor Green
$pathWithDatabase = $db + ":" + $path;
if ($path.StartsWith("query:"))
{
$querypath = $db + ":"
$items = Get-Item -Path $querypath -Query $path.TrimStart("query:") -Language * -Version *
foreach ( $queryItem in $items)
{
$results += Get-ItemRepresentation $queryItem $excludedFieldNames $includedFieldNames
}
}
else
{
$recurse = $path.StartsWith("!") -or $path.EndsWith("*")
$path = $path.TrimStart("!")
$path = $path.TrimEnd("*")
$self = Get-Item $path -Language * -Version * -Database $db
foreach ( $selfversion in $self)
{
$results += Get-ItemRepresentation $selfversion $excludedFieldNames $includedFieldNames
}
# definition to also process subitems recursivly
if ($recurse)
{
$children = Get-ChildItem $pathWithDatabase -Language * -Version * -Recurse;
foreach ( $child in $children)
{
$child
$results += Get-ItemRepresentation $child $excludedFieldNames $includedFieldNames
}
}
}
}
Write-Host "Browser download startet. If you receive no download check if browser is blocking your download." -ForegroundColor Yellow
ConvertTo-Json $results -Depth 2 | Out-Download -Name $exportFileName
}
$fieldNameTemplatePaths = ("/sitecore/templates/Foundation", "/sitecore/templates/Feature", "/sitecore/templates/Project");
function Get-TemplatesFieldNamesFromTemplateRoot($paths)
{
$collectedFieldNames = @();
[string]$collectedFieldNamesString = "("
foreach ($path in $paths)
{
# templates must be created in english
$children = Get-ChildItem $path -Language "en" -Recurse;
foreach ( $child in $children)
{
if ($child.TemplateId.Guid -eq "{455A3E98-A627-4B40-8035-E683A0331AC7}")
{
$fieldName = $child.Name
$collectedFieldNames += $fieldName;
$collectedFieldNamesString += ", '$fieldName'"
}
}
}
$collectedFieldNamesString += ")"
$collectedFieldNamesString = $collectedFieldNamesString.Replace("(,", "(")
Write-Host $collectedFieldNamesString
return $collectedFieldNames;
}
# Extract from windows powershell console the flat item structure to separated language version files in filesystem structure
function Create-FolderStructure($inputFilePath, $folderDestination){
if (Test-Path $inputFilePath)
{
if (Test-Path $folderDestination)
{
$json = Get-Content $inputFilePath | Out-String | ConvertFrom-Json
for ($i; $i -lt $json.Count; $i++)
{
$fileObject = $json.Item($i)
$path = $fileObject.Path.Replace("/", "\")
$name = $fileObject.Name
$content = ConvertTo-Json $fileObject
$folder = $folderDestination.TrimEnd("\") + $path
if (Test-Path $folder)
{
}
else
{
New-Item -ItemType "directory" -Path $folder -Force
}
# include name if you want + "\" + $name + "_"
$filePath = $folder.TrimEnd("\") + "\" + $fileObject.Version.Language + "_" + "v" + $fileObject.Version.Version + ".json"
New-Item -ItemType "file" -Path $filePath -Value $content -Force
}
}
}
else
{
Write-Host "InputFile Path not valid" -ForegroundColor Red
}
}
# Export all data for a path
$pathsWithChildren = ("/sitecore/content/home/*", "/sitecore/content/system/languges/*")
Export-Trees $pathsWithChildren $excludedFieldNames $null "master" "export_master.json"
# Export only paths without children
$pathsWithoutChildren = ("/sitecore/content/home/", "/sitecore/content/system/languges/")
Export-Trees $pathsWithoutChildren $excludedFieldNames $null "master" "export_master.json"
# Export items by query
$pathsQuery = ("/sitecore/content/home//*[@@templatename='Sample Page']")
Export-Trees $pathsQuery $excludedFieldNames $null "master" "export_master.json"
# Compare custom data of the project only cases (to find missconfiguration) only appears on custom data fields of items
$customFields = Get-TemplatesFieldNamesFromTemplateRoot $fieldNameTemplatePaths # this searches for template fields under helix folder paths
Export-Trees $mypaths $excludedFieldNames $customFields "master" "customfieldsexport_master.json"
Export-Trees $mypaths $excludedFieldNames $customFields "web" "customfieldsexport_web.json"
# Compare files
# Publish database case
Export-Trees $mypaths $excludedFieldNames $includedFieldNames "master" "master.json"
Export-Trees $mypaths $excludedFieldNames $includedFieldNames "web" "web.json"
# Compare files
# Environment comparision case
# 1. Execute script on environment 1
Export-Trees $mypaths $excludedFieldNames $includedFieldNames "master" "env1.json"
# 2. Execute script on environment 2
Export-Trees $mypaths $excludedFieldNames $includedFieldNames "master" "env2.json"
# Compare files
# You can generate a filesystem folder structure form the flat exported file
Create-FolderStructure "export.json" "C:\temp\"
# Compare folders afterwards
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment