Hey folks!
Moving items on the Content Tree of Sitecore XM/XP is something frequently asked by customers, and although it’s covered by many other blog posts — and you can easily write an SPE script to do it — I found a scenario where nothing fit my solution:
What happens if you want to keep the folder tree structure and move an item to a location where the structure doesn’t exist?
It will not work.
When moving items from one location to another, Sitecore doesn’t replicate the same structure and will throw an error if it doesn’t identify a pre-existing folder.
For example, imagine your Content Tree looks like this::
/sitecore
/media library
/Project
/Folder1
ItemA
ItemB
/Folder2
ItemC
/Web
/Project
Now, if you want to move all items under /media library/Project/
to /media library/Web/Project/
, the desired outcome should be:
/sitecore
/media library
/Project
/Web
/Project
/Folder1
ItemA
ItemB
/Folder2
ItemC
But Sitecore doesn’t automatically create Folder1
and Folder2
under /Web/Project/
. Instead, it will throw an error when trying to move ItemA
, ItemB
, or ItemC
, because their parent folders don’t exist in the target location.
Based on this challenge, I decided to create a PowerShell script to handle the job. In my case, I applied it to move media items within the Media Library, but you can adapt it to any other situation.
Let’s take a look.
What Does the Script Do?
The script’s purpose is to move items from a source folder to a target folder in the Media Library. It also ensures that the target folder exists before moving any items. Let’s break down how each part of the code works and how it comes together.
Initial Variables
The script starts with some configuration variables:
$sourcePathRoot = "/sitecore/media library/Project/"
$targetPathRoot = "/sitecore/media library/Web/"
$folderMediaTemplateId= "{FE5DD826-48C6-436D-B87A-7C4210C7413B}"
$global:counter = 0
$maxItemsToMove = 100
- $sourcePathRoot and $targetPathRoot define the source and target paths. These are the directories the script will use to look for and move the items.
- $folderMediaTemplateId is the ID of the folder template.
- $global:counter is a global counter that will be used to track how many items have been moved. If you are concerned about moving thousands of items, use it to move by batches.
Function: EnsureFolderExists
This function checks if the path for the target folder exists. If the folder doesn’t exist, it will be created. The cool part here is that it will create any intermediate folders that don’t exist, ensuring the final path is intact.
function EnsureFolderExists {
param (
[string]$folderPath
)
# Check if the folder already exists
$folderItem = Get-Item -Path $folderPath -ErrorAction SilentlyContinue
if (-not $folderItem) {
# Get the parent path
$parentPath = Split-Path -Path $folderPath -Parent
EnsureFolderExists -folderPath $parentPath # Ensures parent exists
# Create the current folder
$folderName = Split-Path -Path $folderPath -Leaf
Write-Host "Creating folder: $folderPath" -ForegroundColor Yellow
New-Item -Path $parentPath -Name $folderName -ItemType $folderMediaTemplateId
}
}
The function first checks if the folder exists. If not, it recursively calls the function to create any parent folders and finally creates the target folder. No matter how deep the target path is, the script guarantees everything will be created properly.
Function: MoveItems
Now comes the part that actually does the trick. The MoveItems function takes the source and target paths and begins moving the media items.
function MoveItems {
param (
[string]$sourcePath,
[string]$targetPath
)
# Get the source folder
$sourceItem = Get-Item -Path $sourcePath -ErrorAction SilentlyContinue
if (-not $sourceItem) {
Write-Host "Source path not found: $sourcePath" -ForegroundColor Red
return
}
# Ensure the destination exists
EnsureFolderExists -folderPath $targetPath
# Iterate over the child items in the source folder
Get-ChildItem -Path $sourcePath | ForEach-Object {
$child = $_
$childTargetPath = Join-Path $targetPath $child.Name
if ($child.TemplateID -eq $folderMediaTemplateId) {
# If it's a folder, process it recursively
MoveItems -sourcePath $child.ItemPath -targetPath $childTargetPath
} else {
# If it's a media item, move it to the target folder
if ($global:counter -lt $maxItemsToMove) {
Write-Host "Moving item: $($child.ItemPath) -> $childTargetPath" -ForegroundColor Green
Move-Item -Path $child.ItemPath -Destination $targetPath -Force
$global:counter++
} else {
Write-Host "Item limit reached. Stopping further moves." -ForegroundColor Red
return
}
}
}
}
How It Works:
- Getting the Source Item: First, it checks if the source folder exists. If not, it displays an error message.
- Ensuring the Destination: Before moving anything, it calls the
EnsureFolderExists
function to ensure that the target folder is ready to receive the items. - Moving the Items: It then loops through all the items in the source folder and checks whether they are folders or files. If it’s a folder, it calls the MoveItems function recursively to move its contents. If it’s a media item, it moves it to the target folder, as long as the item limit hasn’t been reached.
- Item Count: The global $global:counter keeps track of how many items have been moved. Once the limit is reached, the script stops moving items.
Calling the Main Function
At the end, the MoveItems function is called with the source and target paths as parameters, kicking off the entire item-moving process.
Write-Host "Operation started..." -ForegroundColor Cyan
MoveItems -sourcePath $sourcePathRoot -targetPath $targetPathRoot
Write-Host "Operation completed. Total items moved: $global:counter" -ForegroundColor Cyan
It’s always a good idea to test this in a dev environment before running it in production, but this script can save you a lot of time by automating the process.
The Final Code :
$sourcePathRoot = "/sitecore/media library/Project/"
$targetPathRoot = "/sitecore/media library/Web/"
$folderMediaTemplateId = "{FE5DD826-48C6-436D-B87A-7C4210C7413B}"
$global:counter = 0
$maxItemsToMove = 100
function EnsureFolderExists {
param (
[string]$folderPath
)
$folderItem = Get-Item -Path $folderPath -ErrorAction SilentlyContinue
if (-not $folderItem) {
$parentPath = Split-Path -Path $folderPath -Parent
EnsureFolderExists -folderPath $parentPath
$folderName = Split-Path -Path $folderPath -Leaf
Write-Host "Creating folder: $folderPath" -ForegroundColor Yellow
New-Item -Path $parentPath -Name $folderName -ItemType $folderMediaTemplateId
}
}
function MoveItems {
param (
[string]$sourcePath,
[string]$targetPath
)
$sourceItem = Get-Item -Path $sourcePath -ErrorAction SilentlyContinue
if (-not $sourceItem) {
Write-Host "Source path not found: $sourcePath" -ForegroundColor Red
return
}
EnsureFolderExists -folderPath $targetPath
Get-ChildItem -Path $sourcePath | ForEach-Object {
$child = $_
$childTargetPath = Join-Path $targetPath $child.Name
if ($child.TemplateID -eq $folderMediaTemplateId) {
# If it is a folder, process recursively
MoveItems -sourcePath $child.ItemPath -targetPath $childTargetPath
} else {
if ($global:counter -lt $maxItemsToMove) {
Write-Host "Moving item: $($child.ItemPath) -> $childTargetPath" -ForegroundColor Green
Move-Item -Path $child.ItemPath -Destination $targetPath -Force
$global:counter++
} else {
Write-Host "Item limit reached. Stopping further moves." -ForegroundColor Red
return
}
}
}
}
Write-Host "Operation started..." -ForegroundColor Cyan
MoveItems -sourcePath $sourcePathRoot -targetPath $targetPathRoot
Write-Host "Operation completed. Total items moved: $global:counter" -ForegroundColor Cyan
Feel free to modify it as needed, and if you find any error or improvement, let me know!
That’s all, keep Sitecoring!
Leave a Reply