Skip to content

Instantly share code, notes, and snippets.

@nsturdivant-prft
Last active March 22, 2024 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nsturdivant-prft/228bd933915607f4b722d279eeb7bd3a to your computer and use it in GitHub Desktop.
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.
<#
.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/>&nbsp;&nbsp;- $($_.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