Last active
March 22, 2024 21:08
-
-
Save nsturdivant-prft/228bd933915607f4b722d279eeb7bd3a to your computer and use it in GitHub Desktop.
A PowerShell script that uses Sitecore PowerShell Extensions to enumerate and generate content packages for media items.
This file contains 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
<# | |
.SYNOPSIS | |
Creates content packages for media items matching certain criteria. | |
By default, packages are saved to disk at C:\inetpub\wwwroot\App_Data\packages. | |
.NOTES | |
Original "Unused media items" report (/sitecore/system/Modules/PowerShell/Script Library/SPE/Reporting/Content Reports/Reports/Media Audit/Unused media items) written by Michael West. | |
Additional parameters, filtering, content package creation, etc. written by Nick Sturdivant. | |
This script requires that Sitecore PowerShell Extensions be installed. | |
#> | |
$reportName = "Export Media Items" | |
$extensionOptions = [ordered]@{ "bmp" = "bmp"; "gif" = "gif"; "jpg" = "jpg"; "jpeg" = "jpeg"; "pdf" = "pdf"; "png" = "png"; "svg" = "svg"; } | |
$maxPackageSizeOptions = [ordered]@{ "5 MB" = 5000000; "10 MB" = 10000000; "25 MB" = 25000000; "50 MB" = 50000000; "100 MB" = 100000000; "250 MB" = 250000000 } | |
$usedMediaItemOptions = [ordered]@{ "Both" = "both"; "Used" = "used"; "Unused" = "unused" } | |
$props = @{ | |
Parameters = @( | |
@{ | |
Name = "usedMediaMode" | |
Title = "Media to Include" | |
Tooltip = "Determines if the script processes used, unused, or both used and unused media items (where ""used"" is defined as having at least one entry in the link database)." | |
Value = "used" | |
Options = $usedMediaItemOptions | |
} | |
@{ | |
Name = "selectedMediaFolders" | |
Title = "Media Library Folders" | |
Tooltip = "The media library folders from which to include items." | |
Value = @() | |
Editor = "treelist" | |
} | |
@{ | |
Name = "selectedExtensions" | |
Title = "Extensions to Include" | |
Tooltip = "The file extension(s) for the media items to process and include in the package(s)." | |
Value = @() | |
Options = $extensionOptions | |
Editor = "check" | |
} | |
@{ | |
Name = "cutoffDate" | |
Title = "Cutoff Date" | |
Tooltip = "If set, causes the script to only process media items that were created or updated after this date." | |
Value = [datetime]::MinValue | |
Editor = "date" | |
} | |
@{ | |
Name = "selectedMaxPackageSize" | |
Title = "Maximum Package Size" | |
Tooltip = "The maximum size package the script will generate. If the total size of the media items to be packaged exceeds this limit, then multiple packages are created until all items have been packaged." | |
Value = 25000000 | |
Options = $maxPackageSizeOptions | |
} | |
@{ | |
Name = "excludeSystemFolders" | |
Title = "Exclude System Folders" | |
Tooltip = "If checked, any media items with ""/System/"" anywhere in their path are ignored." | |
Value = $true | |
Editor = "check" | |
} | |
@{ | |
Name = "verboseOutput" | |
Title = "Verbose Console Output" | |
Tooltip = "If checked, additional output will be written to the console." | |
Value = $true | |
Editor = "check" | |
} | |
@{ | |
Name = "debugMode" | |
Title = "Debug Mode" | |
Tooltip = "If checked, no packages will be saved to disk." | |
Value = $true | |
Editor = "check" | |
} | |
) | |
Title = " $reportName" | |
Icon = "OfficeWhite/32x32/box_into.png" | |
Description = "This script queries for used and/or unused (referenced) media items and generates content packages containing those items." | |
Width = 600 | |
ShowHints = $true | |
} | |
$result = Read-Variable @props | |
$items = @() | |
$itemsReport = @() | |
$timestamp = (Get-Date -Format FileDateTimeUniversal) | |
if ($result -eq "cancel") { | |
exit | |
} | |
function HasReference { | |
param( | |
$Item | |
) | |
$linkDb = [Sitecore.Globals]::LinkDatabase | |
$linkDb.GetReferrerCount($Item) -gt 0 | |
} | |
function Get-MediaItemWithReference { | |
param( | |
[string]$Path, | |
[string[]]$Extensions | |
) | |
$mediaItemContainer = Get-Item ("master:" + $Path) | |
$excludedTemplates = @([Sitecore.TemplateIDs]::MediaFolder, [Sitecore.TemplateIDs]::Node) | |
$items = $mediaItemContainer.Axes.GetDescendants() | | |
Where-Object { $excludedTemplates -notcontains $_.TemplateID } | Initialize-Item | | |
Where-Object { -not $excludeSystemFolders -or ( -not ($_.FullPath -like "*/System/*") ) } | | |
Where-Object { $cutoffDate -eq [datetime]::MinValue -or ( $_.__Created -gt $cutoffDate -or $_.__Updated -gt $cutoffDate ) } | | |
Where-Object { $Extensions.Count -eq 0 -or $Extensions -contains $_.Fields["Extension"].Value } | |
# filter based on usage (links) | |
foreach ($item in $items) { | |
if ($usedMediaMode -eq "both") { | |
$item | |
} | |
if ($usedMediaMode -eq "used" -and (HasReference -Item $item)) { | |
$item | |
} | |
if ($usedMediaMode -eq "unused" -and (-not (HasReference -Item $item))) { | |
$item | |
} | |
} | |
} | |
function Build-Package { | |
param( | |
[Sitecore.Data.Items.Item[]]$Items, | |
[int]$Size, | |
[int]$PackageNumber, | |
[ref]$ItemsReport | |
) | |
if ($verboseOutput) { | |
Write-Host "" | |
Write-Host "Building package $PackageNumber..." -ForegroundColor Green | |
Write-Host "Total items: $($Items.Count)" -ForegroundColor Green | |
Write-Host "Total size: $Size bytes" -ForegroundColor Green | |
Write-Host "" | |
} | |
$package = New-Package -Name "Export Media Items" | |
$package.Sources.Clear() | |
$package.Metadata.Author = "SPE" | |
$package.Metadata.Version = $timestamp | |
$package.Metadata.Readme = "A package containing media items; generated by a Sitecore PowerShell Extensions script." | |
$packageZipFileName = "$( $package.Metadata.Version ) - $( $package.Name ) $PackageNumber.zip" | |
foreach ($itemToPackage in $Items) { | |
if ($verboseOutput) { | |
Write-Host "`t+ $($itemToPackage.FullPath)` ($($itemToPackage.Fields["Size"].Value -as [int]) bytes)" -ForegroundColor Magenta | |
} | |
$source = Get-Item $itemToPackage.FullPath | New-ExplicitItemSource -Name "$($itemToPackage.ID)" -InstallMode Overwrite | |
$package.Sources.Add($source) | |
$ItemsReport.Value += @{ | |
ID = $itemToPackage.ID | |
FullPath = $itemToPackage.FullPath | |
Package = $packageZipFileName | |
} | |
} | |
if (-not $debugMode) { | |
Export-Package -Project $package -Path $packageZipFileName -Zip | |
} | |
} | |
foreach ($selectedMediaFolder in $selectedMediaFolders) { | |
# ensure selected media folder is the media library itself or a folder within the media library | |
if ($selectedMediaFolder.FullPath -ne "/sitecore/media library" -and $selectedMediaFolder.TemplateID -ne [Sitecore.TemplateIDs]::MediaFolder) { | |
Write-Host "Selected folder $($selectedMediaFolder.FullPath) is neither the media library root nor a media folder within the media library and will be ignored." -ForegroundColor Yellow | |
continue | |
} | |
$itemsFromPath = Get-MediaItemWithReference -Path $selectedMediaFolder.FullPath -Extensions $selectedExtensions | |
# prevent duplicate items if overlapping media folders are selected | |
foreach ($itemFromPath in $itemsFromPath) { | |
$existingItem = $items | Where-Object { $_.ID -eq $itemFromPath.ID } | |
if ($null -eq $existingItem) { | |
$items += $itemFromPath | |
} | |
} | |
} | |
if ($items.Count -eq 0) { | |
Show-Alert "There are no media items matching the specified parameters." | |
} | |
else { | |
Write-Host "Total media items to be processed and packaged: $($items.Count)" -ForegroundColor Cyan | |
$packageSize = 0 | |
$itemsInPackage = @() | |
$itemsProcessed = 0 | |
$packageCount = 0 | |
foreach ($itemToPackage in $items) { | |
$itemsInPackage += $itemToPackage | |
$packageSize += $itemToPackage.Fields["Size"].Value -as [int] | |
$itemsProcessed++ | |
if ($packageSize -ge $selectedMaxPackageSize -or $itemsProcessed -eq $items.Count) { | |
$packageCount++ | |
Build-Package -Items $itemsInPackage -Size $packageSize -PackageNumber $packageCount -ItemsReport ([ref]$itemsReport) | |
$packageSize = 0 | |
$itemsInPackage = @() | |
} | |
} | |
# report output | |
$mediaToInclude = "" | |
if ($usedMediaMode -eq "both") { | |
$mediaToInclude = "Both (used and unused)" | |
} else { | |
$mediaToInclude = ($usedMediaMode.Substring(0, 1).ToUpper() + $usedMediaMode.Substring(1)) | |
} | |
$mediaFolders = "" | |
$selectedMediaFolders | ForEach-Object { $mediaFolders += "<br/> - $($_.FullPath)" } | |
$extensions = $selectedExtensions -join ", " | |
if ($extensions -eq "") { | |
$extensions = "(all)" | |
} | |
$infoDescription = "List of the media items matching the specified criteria that are contained within the generated content packages.<br/><br/>" + | |
"Media to Include: $mediaToInclude<br/>" + | |
"Media Library Folders: $mediaFolders<br/>" + | |
"Extensions to Include: $extensions<br/>" + | |
"Cutoff Date: " | |
if ($cutoffDate -eq [datetime]::MinValue) { | |
$infoDescription += "(none)<br/>" | |
} | |
else { | |
$infoDescription += "$($cutoffDate.ToShortDateString())<br/>" | |
} | |
$infoDescription += "Maximum Package Size: $($selectedMaxPackageSize / 1000000) MB<br/>" + | |
"Exclude System Folders: $excludeSystemFolders<br/>" + | |
"Verbose: $verboseOutput<br/>" + | |
"Debug: $debugMode" | |
$reportProps = @{ | |
InfoTitle = $reportName | |
InfoDescription = $infoDescription | |
PageSize = 25 | |
Title = $reportName | |
} | |
Write-Host "" | |
Write-Host "Finished! 🎉" -ForegroundColor Cyan | |
if ($verboseOutput) { | |
Write-Host "" | |
$itemsReport | | |
ForEach-Object { $_.Package } | | |
Select-Object -Unique | | |
ForEach-Object { Write-Host ($SitecorePackageFolder + "\" + $_) -ForegroundColor Gray } | |
} | |
# display report output | |
$itemsReport | | |
Show-ListView @reportProps -Property @{ Label = "ID"; Expression = { $_.ID } }, | |
@{Label = "Full Path"; Expression = { $_.FullPath } }, | |
@{Label = "Package"; Expression = { $_.Package } } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment