Last active
September 17, 2020 09:22
-
-
Save IvanLieckens/f99277e4ff99b8666cd2d34ca088eee9 to your computer and use it in GitHub Desktop.
Powershell to sync SXA themes with TDS serialized items
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Param( | |
[string]$ThemePath = "sitecore\media library\Themes\Project\Shared\Project", | |
[string]$FilesPath = ".\-\media\Themes\Project\Shared\Project", | |
[string]$TdsPath = "..\Project\TDS.Master", | |
[string]$TdsProject = "TDS.Master.scproj", | |
[string]$TypeLoadPath = ".\Binaries", | |
[string]$UpdateFile = "", | |
[string]$DeleteFile = "", | |
[string]$ItemId = "" | |
) | |
# Load Sitecore Kernel for Serialization | |
try | |
{ | |
Add-Type -Path (Join-Path $TypeLoadPath "HtmlAgilityPack.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "ITHit.WebDAV.Server.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Lucene.Net.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Microsoft.Extensions.DependencyInjection.Abstractions.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Mvp.Xml.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Newtonsoft.Json.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Sitecore.Logging.dll") | |
Add-Type -Path (Join-Path $TypeLoadPath "Sitecore.Kernel.dll") | |
# Load System.Xml.Linq for manipulating project XML | |
Add-Type -AssemblyName "System.Xml.Linq" | |
} | |
catch [System.Reflection.ReflectionTypeLoadException] | |
{ | |
Write-Verbose "Message: $($_.Exception.Message)" -Verbose | |
Write-Verbose "StackTrace: $($_.Exception.StackTrace)" -Verbose | |
Write-Debug "LoaderExceptions: $($_.Exception.LoaderExceptions)" -Debug | |
} | |
function Get-Base64FileContents([string]$Path) | |
{ | |
$stream = New-Object System.IO.FileStream $Path, ([System.IO.FileMode]::Open), ([System.IO.FileAccess]::Read), ([System.IO.FileShare]::Read) | |
$bytes = New-Object Byte[] $stream.Length | |
$stream.Read($bytes, 0, $stream.Length) | |
$stream.Dispose() | |
return [System.Convert]::ToBase64String($bytes, [System.Base64FormattingOptions]::InsertLineBreaks) | |
} | |
function Get-MimeType([string]$Extension) | |
{ | |
$mimeType = $null | |
if ($null -ne $Extension) | |
{ | |
$drive = Get-PSDrive HKCR -ErrorAction SilentlyContinue; | |
if ($null -eq $drive) | |
{ | |
$drive = New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | |
} | |
$mimeType = (Get-ItemProperty HKCR:$Extension)."Content Type" | |
if([string]::IsNullOrWhiteSpace($mimeType)) | |
{ | |
$mimeType = "application/octet-stream" | |
} | |
} | |
return $mimeType | |
} | |
function Get-SyncItem([string]$Path) | |
{ | |
$itemFile = [System.IO.File]::OpenText($Path) | |
$tokenizer = New-Object Sitecore.Data.Serialization.ObjectModel.Tokenizer $itemFile | |
[Sitecore.Data.Serialization.ObjectModel.SyncItem]$syncItem = [Sitecore.Data.Serialization.ObjectModel.SyncItem]::ReadItem($tokenizer) | |
$itemFile.Dispose() | |
return $syncItem | |
} | |
function Set-SharedSyncItemField([Sitecore.Data.Serialization.ObjectModel.SyncItem]$Item, [string]$Name, [string]$Value) | |
{ | |
[Sitecore.Data.Serialization.ObjectModel.SyncField]$sharedField = $Item.SharedFields.Find({ param($field) [String]::Equals($field.FieldName, $Name, [StringComparison]::OrdinalIgnoreCase) }) | |
$sharedField.FieldValue = $Value | |
} | |
function Get-SharedSyncItemField([Sitecore.Data.Serialization.ObjectModel.SyncItem]$Item, [string]$Name) | |
{ | |
[Sitecore.Data.Serialization.ObjectModel.SyncField]$sharedField = $Item.SharedFields.Find({ param($field) [String]::Equals($field.FieldName, $Name, [StringComparison]::OrdinalIgnoreCase) }) | |
return $sharedField.FieldValue | |
} | |
# /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// VALIDATE!!!!! | |
function Set-VersionedSynItemField([Sitecore.Data.Serialization.ObjectModel.SyncItem]$Item, [string]$Language, [int]$VersionNumber, [string]$Name, [string]$Value) | |
{ | |
[Sitecore.Data.Serialization.ObjectModel.SyncVersion]$version = $Item.Versions.Find({ param($version) [String]::Equals($version.Language, $Language, [StringComparison]::OrdinalIgnoreCase) -and $version.Version -eq $VersionNumber }) | |
[Sitecore.Data.Serialization.ObjectModel.SyncField]$versionedField = $version.Fields.Find({ param($field) [String]::Equals($field.FieldName, $Name, [StringComparison]::OrdinalIgnoreCase) }) | |
$versionedField.FieldValue = $Value | |
} | |
function Get-VersionedSynItemField([Sitecore.Data.Serialization.ObjectModel.SyncItem]$Item, [string]$Language, [int]$VersionNumber, [string]$Name) | |
{ | |
[Sitecore.Data.Serialization.ObjectModel.SyncVersion]$version = $Item.Versions.Find({ param($version) [String]::Equals($version.Language, $Language, [StringComparison]::OrdinalIgnoreCase) -and $version.Version -eq $VersionNumber }) | |
[Sitecore.Data.Serialization.ObjectModel.SyncField]$versionedField = $version.Fields.Find({ param($field) [String]::Equals($field.FieldName, $Name, [StringComparison]::OrdinalIgnoreCase) }) | |
return $versionedField.FieldValue | |
} | |
# /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
function Create-MediaFolder([string]$Path, [string]$Name) | |
{ | |
Write-Verbose "Creating folder $Name in $Path" -Verbose | |
# Load Parent | |
[Sitecore.Data.Serialization.ObjectModel.SyncItem]$parent = Get-SyncItem "$(Resolve-Path (Join-Path $Path "..")).item" | |
$sitecorePath = "$($parent.ItemPath)/$Name" | |
# Build new media folder | |
[Sitecore.Data.Serialization.ObjectModel.SyncItem]$mediaFolder = New-Object Sitecore.Data.Serialization.ObjectModel.SyncItem | |
$mediaFolder.ID = ([Guid]::NewGuid()).ToString("B").ToUpper() | |
$mediaFolder.DatabaseName = "master" | |
$mediaFolder.ItemPath = $sitecorePath | |
$mediaFolder.ParentID = $parent.ID | |
$mediaFolder.Name = $Name | |
$mediaFolder.MasterID = ([Guid]::Empty).ToString("B").ToUpper() | |
$mediaFolder.TemplateID = "{FE5DD826-48C6-436D-B87A-7C4210C7413B}" | |
$mediaFolder.TemplateName = "Media folder" | |
$isoNow = [Sitecore.DateUtil]::IsoNow | |
$mediaFolder.Created = $isoNow | |
$revision = [Guid]::NewGuid() | |
[Sitecore.Data.Serialization.ObjectModel.SyncVersion]$enVersion = $mediaFolder.AddVersion("en", "1", $revision) | |
$enVersion.AddField("{52807595-0F8F-4B20-8D2A-CB71D28C6103}", "__Owner", "__owner", "sitecore\syncscript", $true) | |
$enVersion.AddField("{25BED78C-4957-4165-998A-CA1B52F67497}", "__Created", "__created", $isoNow, $true) | |
$enVersion.AddField("{5DD74568-4D4B-44C1-B513-0AF5F4CDA34F}", "__Created by", "__created by", "sitecore\syncscript", $true) | |
$enVersion.AddField("{8CDC337E-A112-42FB-BBB4-4143751E123F}", "__Revision", "__revision", $revision, $true) | |
$enVersion.AddField("{D9CF14B1-FA16-4BA6-9288-E8A174D4D522}", "__Updated", "__updated", $isoNow, $true) | |
$enVersion.AddField("{BADD9CF9-53E0-4D0C-BCC0-2D784C282F6A}", "__Updated by", "__updated by", "sitecore\syncscript", $true) | |
# Create folder if needed and file | |
if(-not (Test-Path $Path)) | |
{ | |
New-Item -Path $Path -ItemType Directory | |
} | |
$itemWriter = [System.IO.File]::CreateText("$Path.item") | |
$mediaFolder.Serialize($itemWriter) | |
$itemWriter.Dispose() | |
} | |
function Update-MediaFile([string]$Path, [System.IO.FileInfo]$File) | |
{ | |
$syncItem = Get-SyncItem -Path $Path | |
$fileContent = Get-Base64FileContents -Path $File.FullName | |
$originalContent = Get-SharedSyncItemField -Item $syncItem -Name "Blob" | |
if() | |
{ | |
} | |
Set-SharedSyncItemField -Item $syncItem -Name "Blob" -Value $fileContent[1] | |
Set-SharedSyncItemField -Item $syncItem -Name "Size" -Value $fileContent[0] | |
$itemWriter = [System.IO.File]::CreateText($Path) | |
$syncItem.Serialize($itemWriter) | |
$itemWriter.Dispose() | |
} | |
function Create-MediaFile([string]$Path, [System.IO.FileInfo]$File, [string]$ItemId) | |
{ | |
Write-Verbose "Creating $($File.Name) in $Path" -Verbose | |
# Create clean sitecore name | |
$name = [System.IO.Path]::ChangeExtension($File.Name, [string]::Empty) | |
$name = $name -replace "\.", [string]::Empty | |
# Load Parent | |
[Sitecore.Data.Serialization.ObjectModel.SyncItem]$parent = Get-SyncItem "$(Resolve-Path (Join-Path $Path "..")).item" | |
$sitecorePath = "$($parent.ItemPath)/$name" | |
# Build new media file | |
[Sitecore.Data.Serialization.ObjectModel.SyncItem]$mediaFile = New-Object Sitecore.Data.Serialization.ObjectModel.SyncItem | |
$mediaFile.ID = $ItemId | |
$mediaFile.DatabaseName = "master" | |
$mediaFile.ItemPath = $sitecorePath | |
$mediaFile.ParentID = $parent.ID | |
$mediaFile.Name = $name | |
$mediaFile.MasterID = ([Guid]::Empty).ToString("B").ToUpper() | |
$mediaFile.TemplateID = "{962B53C4-F93B-4DF9-9821-415C867B8903}" | |
$mediaFile.TemplateName = "File" | |
$isoNow = [Sitecore.DateUtil]::IsoNow | |
$mediaFile.Created = $isoNow | |
$revision = [Guid]::NewGuid() | |
# Shared Fields | |
$fileContent = Get-Base64FileContents -Path $File.FullName | |
$mediaFile.AddSharedField("{40E50ED9-BA07-4702-992E-A912738D32DC}", "Blob", "blob", $fileContent[1], $true) | |
$extension = $File.Extension -replace "\.", [string]::Empty | |
$mediaFile.AddSharedField("{C06867FE-9A43-4C7D-B739-48780492D06F}", "Extension", "extension", $extension, $true) | |
$mediaFile.AddSharedField("{6F47A0A5-9C94-4B48-ABEB-42D38DEF6054}", "Mime Type", "mime type", (Get-MimeType -Extension ".$extension"), $true) | |
$mediaFile.AddSharedField("{6954B7C7-2487-423F-8600-436CB3B6DC0E}", "Size", "size", $fileContent[0], $true) | |
$mediaFile.AddSharedField("{06D5295C-ED2F-4A54-9BF2-26228D113318}", "__Icon", "__icon", "-/media/7EC7101143E14304933C27348BF70466.ashx?h=16&thn=1&w=16", $true) | |
# Version Fields | |
[Sitecore.Data.Serialization.ObjectModel.SyncVersion]$enVersion = $mediaFile.AddVersion("en", "1", $revision) | |
$enVersion.AddField("{52807595-0F8F-4B20-8D2A-CB71D28C6103}", "__Owner", "__owner", "sitecore\syncscript", $true) | |
$enVersion.AddField("{25BED78C-4957-4165-998A-CA1B52F67497}", "__Created", "__created", $isoNow, $true) | |
$enVersion.AddField("{5DD74568-4D4B-44C1-B513-0AF5F4CDA34F}", "__Created by", "__created by", "sitecore\syncscript", $true) | |
$enVersion.AddField("{8CDC337E-A112-42FB-BBB4-4143751E123F}", "__Revision", "__revision", $revision, $true) | |
$enVersion.AddField("{D9CF14B1-FA16-4BA6-9288-E8A174D4D522}", "__Updated", "__updated", $isoNow, $true) | |
$enVersion.AddField("{BADD9CF9-53E0-4D0C-BCC0-2D784C282F6A}", "__Updated by", "__updated by", "sitecore\syncscript", $true) | |
# Create file | |
$itemWriter = [System.IO.File]::CreateText($Path) | |
$mediaFile.Serialize($itemWriter) | |
$itemWriter.Dispose() | |
} | |
function Add-ScprojReference([string]$Path, [string]$ScprojPath) | |
{ | |
[System.Xml.Linq.XDocument]$ProjFile = [System.Xml.Linq.XDocument]::Load($ScprojPath) | |
$syncElementName = [System.Xml.Linq.XName]::Get("ChildItemSynchronization", "http://schemas.microsoft.com/developer/msbuild/2003") | |
$syncElement = New-Object System.Xml.Linq.XElement $syncElementName, "KeepAllChildrenSynchronized" | |
$deployElementName = [System.Xml.Linq.XName]::Get("ItemDeployment", "http://schemas.microsoft.com/developer/msbuild/2003") | |
$deployElement = New-Object System.Xml.Linq.XElement $deployElementName, "AlwaysUpdate" | |
$includeAttribute = New-Object System.Xml.Linq.XAttribute "Include", $Path | |
$sitecoreItemName = [System.Xml.Linq.XName]::Get("SitecoreItem", "http://schemas.microsoft.com/developer/msbuild/2003") | |
$sitecoreItem = New-Object System.Xml.Linq.XElement $sitecoreItemName, $includeAttribute | |
$sitecoreItem.Add($syncElement) | |
$sitecoreItem.Add($deployElement) | |
[System.Xml.Linq.XElement]$itemGroup = [System.Xml.XPath.Extensions]::XPathSelectElement($ProjFile, "./*[local-name()='Project']/*[local-name()='ItemGroup']/*[local-name()='SitecoreItem']/..") | |
$itemGroup.Add($sitecoreItem) | |
#Save scproj file | |
$ProjFile.Save($ScprojPath, [System.Xml.Linq.SaveOptions]::OmitDuplicateNamespaces) | |
} | |
function Remove-ScprojReference([string]$Path, [string]$ScprojPath) | |
{ | |
[System.Xml.Linq.XDocument]$ProjFile = [System.Xml.Linq.XDocument]::Load($ScprojPath) | |
[System.Xml.Linq.XElement]$sitecoreItem = [System.Xml.XPath.Extensions]::XPathSelectElement($ProjFile, "./*[local-name()='Project']/*[local-name()='ItemGroup']/*[local-name()='SitecoreItem' and @Include='$Path']") | |
$sitecoreItem.Remove() | |
$ProjFile.Save($ScprojPath, [System.Xml.Linq.SaveOptions]::OmitDuplicateNamespaces) | |
} | |
function Get-TdsFilePath([string]$FilePath, [string]$FileBasePath, [string]$TdsBasePath, [string]$ThemePath) | |
{ | |
$localPathLength = $FilePath.Length - $FileBasePath.Length | |
$tdsFilePath = $FilePath.Substring($FileBasePath.Length, $localPathLength) | |
# Dots in the path are actually removed so we need to strip the extension before clearing them and then add the item extension | |
# Also this needs to be done only to the part that is actually in Sitecore, not the base path | |
$tdsFilePath = [System.IO.Path]::ChangeExtension($tdsFilePath, [string]::Empty) | |
$tdsFilePath = $tdsFilePath -replace "\.", [string]::Empty | |
$tdsFilePath = "$tdsFilePath.item" | |
$tdsIncludePath = "$ThemePath$tdsFilePath" | |
$tdsFilePath = Join-Path $TdsBasePath $tdsFilePath | |
return New-Object PsObject -Property @{ FullPath = $tdsFilePath; IncludePath = $tdsIncludePath } | |
} | |
function Process-File([System.IO.FileInfo]$File, [string]$FileBasePath, [string]$TdsBasePath, [string]$ScprojPath, [string]$ThemePath, [string]$ItemId) | |
{ | |
$tdsPath = Get-TdsFilePath -FilePath $File.FullName -FileBasePath $FileBasePath -TdsBasePath $TdsBasePath -ThemePath $ThemePath | |
$checkPath = $tdsPath.FullPath | |
$itemPath = $tdsPath.IncludePath | |
if(Test-Path -Path $checkPath -PathType Leaf) | |
{ | |
Update-MediaFile -Path $checkPath -File $File | |
Write-Verbose "$File has been updated." -Verbose | |
} | |
else | |
{ | |
Create-MediaFile -Path $checkPath -File $File -ItemId $ItemId | |
Add-ScprojReference -Path $itemPath -ScprojPath $ScprojPath | |
Write-Verbose "$File has been created." -Verbose | |
} | |
} | |
function Process-Directory([System.IO.DirectoryInfo]$Directory, [string]$FileBasePath, [string]$TdsBasePath, [string]$ScprojPath, [string]$ThemePath) | |
{ | |
$localPathLength = $Directory.FullName.Length - $FileBasePath.Length | |
$checkPath = $Directory.FullName.Substring($FileBasePath.Length, $localPathLength) | |
$itemPath = "$ThemePath$checkPath.item" | |
$checkFolderItemPath = Join-Path $TdsBasePath "$checkPath.item" | |
$checkPath = Join-Path $TdsBasePath $checkPath | |
if((Test-Path $checkPath) -and (Test-Path $checkFolderItemPath)) | |
{ | |
Write-Verbose "$Directory exists already" -Verbose | |
} | |
else | |
{ | |
Create-MediaFolder -Path $checkPath -Name $Directory.Name | |
Add-ScprojReference -Path $itemPath -ScprojPath $ScprojPath | |
Write-Verbose "$Directory has been created." -Verbose | |
} | |
} | |
# ----- ----- ----- | |
# Main Code | |
# ----- ----- ----- | |
$scprojPath = (Resolve-Path (Join-Path $TdsPath $TdsProject)).Path.ToString() | |
$tdsBasePath = (Resolve-Path (Join-Path $TdsPath $ThemePath)).Path.ToString() | |
$fileBasePath = (Resolve-Path $FilesPath).Path.ToString() | |
if(-not [string]::IsNullOrWhiteSpace($UpdateFile)) | |
{ | |
$UpdateFile = Resolve-Path $UpdateFile | |
# Check parents | |
[System.IO.DirectoryInfo]$parent = (Get-Item -Path $UpdateFile).Parent | |
while($null -ne $parent) | |
{ | |
Process-Directory -Directory $file -FileBasePath $fileBasePath -TdsBasePath $tdsBasePath -ScprojPath $scprojPath -ThemePath $ThemePath | |
$parent = $parent.Parent | |
} | |
# Update 1 file | |
$file = New-Object System.IO.FileInfo $UpdateFile | |
Process-File -File $file -FileBasePath $fileBasePath -TdsBasePath $tdsBasePath -ScprojPath $scprojPath -ThemePath $ThemePath -ItemId $ItemId | |
} | |
elseif(-not [string]::IsNullOrWhiteSpace($DeleteFile)) | |
{ | |
$deleteTdsPath = Get-TdsFilePath -FilePath $DeleteFile -FileBasePath $fileBasePath -TdsBasePath $tdsBasePath -ThemePath $ThemePath | |
Remove-Item -Path $deleteTdsPath.FullPath | |
Remove-ScprojReference -Path $deleteTdsPath.IncludePath -ScprojPath $ScprojPath | |
Write-Verbose "Deleted from project" -Verbose | |
} | |
else | |
{ | |
# Load all directory and file information excluding the node_modules and process them | |
$files = Get-ChildItem $FilesPath -Recurse | Where-Object { $_.FullName -inotmatch 'node_modules' } | |
foreach($file in $files) | |
{ | |
if($file -is [System.IO.FileInfo]) | |
{ | |
Process-File -File $file -FileBasePath $fileBasePath -TdsBasePath $tdsBasePath -ScprojPath $scprojPath -ThemePath $ThemePath -ItemId ([Guid]::NewGuid()).ToString("B").ToUpper() | |
} | |
if($file -is [System.IO.DirectoryInfo]) | |
{ | |
Process-Directory -Directory $file -FileBasePath $fileBasePath -TdsBasePath $tdsBasePath -ScprojPath $scprojPath -ThemePath $ThemePath | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment