Skip to content

Instantly share code, notes, and snippets.

@michaellwest
Last active April 5, 2020 23:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save michaellwest/add2fff8744dfa62ab77 to your computer and use it in GitHub Desktop.
Save michaellwest/add2fff8744dfa62ab77 to your computer and use it in GitHub Desktop.
Generates an anti-package from the selected package. Requires SPE 3.1.
$response = Show-ModalDialog -HandleParameters @{
"h"="Create an Anti-Package";
"t" = "Select a package that needs an anti-package";
"ic"="People/16x16/box.png";
"ok"="Pick";
"ask"="";
"path"= "packPath:$SitecorePackageFolder";
"mask"="*.zip";
} -Control "Installer.Browse"
if(!$response -or $response -eq "undetermined") {
Write-Verbose "Operation cancelled by user."
return $null
}
$path = Join-Path -Path $SitecorePackageFolder -ChildPath $response
Write-Verbose "Selected package at the path $($path) to process."
function Get-PackageProject {
param (
[Sitecore.Zip.ZipEntry]$Entry
)
[Sitecore.Install.Serialization.IOUtils]::LoadSolution([Sitecore.Install.StreamUtil]::LoadString($Entry.GetStream()))
}
function Get-PackageItem {
param(
[string]$Entry
)
$pi = @{}
$split = $Entry -split "/"
$pi["Database"] = $split[1]
$pi["Name"] = $split[$split.Length - 4]
$pi["ID"] = $split[$split.Length - 3]
$pi["ItemPath"] = ({
$path = ""
for ($index = 2; $index -lt $split.Length - 3; ++$index) {
$path = $path + "/" + $split[$index]
}
$path
}.Invoke())
[pscustomobject]$pi
}
function Get-PackageFile {
param (
[string]$Entry
)
$pf = @{}
$filename = $Entry
$pf["FileName"] = $filename
[pscustomobject]$pf
}
function New-PackagePostStep {
param(
$PackageItems,
$PackageFiles
)
$writer = New-Object System.IO.StringWriter
$output = New-Object System.Xml.XmlTextWriter([System.IO.TextWriter]$writer)
$output.Formatting = [System.Xml.Formatting]::Indented
$output.WriteStartElement("uninstall")
if($PackageItems) {
$output.WriteStartElement("items")
foreach($packageItem in $PackageItems) {
$output.WriteStartElement("item")
$output.WriteAttributeString("database", $packageItem.Database)
$output.WriteAttributeString("id", $packageItem.ID.ToString())
$output.WriteEndElement()
}
$output.WriteEndElement()
}
if($PackageFiles) {
$output.WriteStartElement("files")
foreach($packageFile in $PackageFiles) {
$output.WriteStartElement("file")
$output.WriteAttributeString("filename", $packageFile.FileName)
$output.WriteEndElement()
}
$output.WriteEndElement()
}
$output.WriteEndElement()
$writer.ToString()
}
$packageItems = @()
$packageFiles = @()
$name = [System.IO.Path]::GetFileNameWithoutExtension($path)
$packageReader = New-Object Sitecore.Zip.ZipReader $path
$entry = $packageReader.GetEntry("package.zip")
$ms = New-Object System.IO.MemoryStream
[Sitecore.Zip.Utils.StreamUtils]::CopyStream($entry.GetStream(), $ms, 0x4000)
$ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null
$reader = New-Object Sitecore.Zip.ZipReader $ms
foreach($entry in $reader.Entries) {
$name = $entry.Name
if($name -like "installer/project") {
$project = (Get-PackageProject -Entry $entry)
}
}
$reader.Dispose()
$ms.Dispose()
$packageReader.Dispose()
# Create the new anti-package
$packagePath = [System.IO.Path]::GetFileNameWithoutExtension($path) + ".anti" + [System.IO.Path]::GetExtension($path)
$package = New-Package -Name ("Anti Package for " + $project.Metadata.PackageName)
$package.Sources.Clear()
$readMe = [string]::Format("Anti Package for {0}. Created {1} by {2}.", $project.Metadata.PackageName, [datetime]::Now.ToString(), [Sitecore.Context]::GetUserName())
if($project.Metadata.PostStep) {
$readMe += "$([Environment]::NewLine + [Environment]::NewLine)WARNING:$([Environment]::NewLine)The original package contains a poststep which may have made modifications that is not known to this antipackage."
}
$package.Metadata.ReadMe = $readMe
$package.Metadata.Publisher = $project.Metadata.Publisher
# Sources
$newPackageItems = @()
$newPackageFiles = @()
foreach($source in $project.Sources) {
$items = @()
$files = @()
$installMode = [Sitecore.Install.Utils.InstallMode]::Undefined
$mergeMode = [Sitecore.Install.Utils.MergeMode]::Undefined
$sourceTypeName = $source.GetType().Name
$isItemSource = $sourceTypeName -like "*ItemSource"
$isFileSource = $sourceTypeName -like "*FileSource"
$isExplicit = "ExplicitItemSource","ExplicitFileSource" -contains $sourceTypeName
if([string]::IsNullOrEmpty($source.Name)) {
$guid = [guid]::NewGuid().ToString()
Write-Verbose "Changing the source name to $($guid) because it's missing."
$source.Name = $guid
}
Write-Verbose "Processing $($sourceTypeName) : $($source.Name)"
if($isExplicit) {
foreach($entry in $source.Entries) {
Write-Verbose "Processing entry : $($entry)"
if($isItemSource) {
$packageItem = Get-PackageItem -Entry $entry
$database = Get-Database -Name $packageItem.Database
if($database) {
$item = $database.GetItem($packageItem.ID)
if(!$item) {
$newPackageItems += $packageItem
} else {
$items += $item
}
} else {
Write-Verbose "Skipping item because the database $($packageItem.Database) does not exist."
}
} elseif ($isFileSource) {
$packageFile = Get-PackageFile -Entry $entry
$filename = $packageFile.FileName
$path = Join-Path -Path $AppPath -ChildPath $filename
if(!(Test-Path -Path $path)) {
$newPackageFiles += $packageFile
} else {
$files += Get-Item -Path $path
}
}
}
} else {
if($isItemSource) {
Write-Verbose "Processing item $($source.Root) from the $($source.Database) database."
$itemRoot = & {
if([Sitecore.Data.ID]::IsID($source.Root)) {
Get-Item -Path "$($source.Database):" -ID $source.Root
} else {
Get-Item -Path "$($source.Database):$($source.Root)"
}
}
if($itemRoot) {
Write-Verbose "Processing items at the path $($itemRoot)."
$items = @($itemRoot) + (Get-ChildItem -Path $itemRoot.PSPath -Recurse)
} else {
Write-Verbose "No items found at that path. This is likely because it was removed after installation."
}
} elseif($isFileSource) {
$fileRoot = Get-Item -Path "$AppPath\$($source.Root)"
$files = @($fileRoot)
}
}
if($source.TransForms) {
$installMode = $source.TransForms.Options.ItemMode
$mergeMode = $source.TransForms.Options.ItemMergeMode
}
$props = @{
"Name" = $source.Name
"InstallMode" = $installMode
}
if($isItemSource -and $items) {
$props["MergeMode"] = $mergeMode
if($source.SkipVersions) {
$props["SkipVersions"] = $source.SkipVersions
}
if($isExplicit) {
$package.Sources.Add(($items | New-ExplicitItemSource @props))
} else {
$package.Sources.Add(($items | New-ItemSource @props))
}
} elseif($isFileSource -and $files) {
if($isExplicit) {
$package.Sources.Add(($files | New-ExplicitFileSource @props))
} else {
$package.Sources.Add(($files | New-FileSource @props))
}
}
}
$source = Get-Item "$AppPath\bin\Cognifide.PowerShell.Package.dll" | New-ExplicitFileSource -Name "PowerShell PostStep Binary"
$package.Sources.Add($source)
$package.Metadata.PostStep = "Cognifide.PowerShell.Package.Install.PackagePostStep, Cognifide.PowerShell.Package"
$package.Metadata.Comment = New-PackagePostStep -PackageItems $newPackageItems -PackageFiles $newPackageFiles
# TODO: I think SkipVersions is never set in the UI. Should we still handle it?
# TODO: Handle security.
Export-Package -Project $package -Path $packagePath -Zip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment