Automating Tag Update in Sitecore XM Cloud with PowerShell - A Use Case

Managing tags across different environments in a Sitecore setup can be a challenging task, especially when dealing with a large number of items and multiple folders. Automating the synchronization or updating of these tags can save time and reduce errors. In this blog post, we'll explore a practical use case of using Sitecore PowerShell Extensions (SPE) to automate the synchronization of tags between shared and global environments in a Sitecore instance

Use Case

Imagine you are managing a global website for a large corporation, such as ABC company. Your content is distributed across various regional sites, and each region uses a different set of tags stored in different folders.The respective page content are tagged with site specific tags. However, for consistency,The business ask you to ensure that tags from the shared environment should be used from now on. But there is catch while you make the changes. You need to make sure that all existing website content pages should be tagged with tags coming from Shared Site only.

When this kind of require come, we think the best way to update content is using PowerShell script and yes you are correct and we will follow the same to achieve it.

Solution

  • Retrieve tags from the shared and global environments
  • Identify matching and unmatching tags based on their names.
  • Update the tag references in the specified content items with the corresponding tags from the shared environment
       
# Function to find shared website corresponding tag id
function Get-MatchingItemValue {
    param (
        [string]$id
    )
	Write-Host "The parameter id: "  $id
    foreach ($item in $global:matchingItems) {
        if ($item.Item2ID -eq $id) {
            return $item.Item1ID
        }
    }
    return $null
}

# Script with WhatIf support
$WhatIf = $true

# Define both source and destination tags folders
$folderPath1 = "{Shared Site Tags Folder Path}"
$folderPath2 = "{Current Site Tags Folder Path}"

# Define the path of the location where we need to update the tags
$locationPath = "{Location of the folder under whose item should be updated}"

# Define the language to consider
$language = "en" 

# Define the template name to filter by (Template name of the tag items)
$templateName = "Tag"


# Define the Treelist field name
$treelistFieldName = "Tags" (Field name of the tag)
$pagetemplateName = "App Route" (Template name of the page items)


# Get the items from the folders in the specified language and template
$folder1Items = Get-ChildItem -Path $folderPath1 -Recurse | Where-Object { $_.Language.Name -eq $language -and $_.TemplateName -eq $templateName }
$folder2Items = Get-ChildItem -Path $folderPath2 -Recurse | Where-Object { $_.Language.Name -eq $language -and $_.TemplateName -eq $templateName }

# Create a list to store the matching items
$global:matchingItems = @()

# Create a hash table to store the items from folder 1 for quick lookup
$folder1ItemsHashTable = @{}
foreach ($item in $folder1Items) {
    $folder1ItemsHashTable[$item.Name] = $item
}


# Create a list to store the unmatching items
$unmatchingItems = @()

# Compare items based on their names, language, and template
foreach ($item2 in $folder2Items) {
    if ($folder1ItemsHashTable.ContainsKey($item2.Name)) {
        $item1 = $folder1ItemsHashTable[$item2.Name]
        # Add matching items to the list
        $global:matchingItems += @{
            "Item1ID" = $item1.ID
            "Item1Name" = $item1.Name
            "Item1Path" = $item1.Paths.FullPath
            "Item2ID" = $item2.ID
            "Item2Name" = $item2.Name
            "Item2Path" = $item2.Paths.FullPath
            "Status" = "Matched"
        }
    }
    else{
        $unmatchingItems += @{
            "Item2ID" = $item2.ID
            "Item2Name" = $item2.Name
            "Item2Path" = $item2.Paths.FullPath
            "Status" = "UnMatched"
        }
    }
}

# Output the matching items
$matchingItems | ForEach-Object {
    Write-Output "Item from Folder1: ID = $($_.Item1ID), Name = $($_.Item1Name), Path = $($_.Item1Path)"
    Write-Output "Item from Folder2: ID = $($_.Item2ID), Name = $($_.Item2Name), Path = $($_.Item2Path)"
    Write-Output "Matching Status: Status = $($_.Status)"
    Write-Output "----------------------------------------"
}

$unmatchingItems | ForEach-Object {
    Write-Output "Item from Folder2: ID = $($_.Item2ID), Name = $($_.Item2Name), Path = $($_.Item2Path)"
    Write-Output "Matching Status: Status = $($_.Status)"
    Write-Output "----------------------------------------"
}


# Get the items from the specified location
$items = Get-ChildItem -Path $locationPath -Recurse | Where-Object { $_.Language.Name -eq $language -and $_.TemplateName -eq $pagetemplateName}

# Iterate through the items
foreach ($item in $items) {
    # Get the treelist field value
    $treelistValue = $item.Fields[$treelistFieldName].Value
    
    # Check if the multilist field is not empty
    if (![string]::IsNullOrEmpty($treelistValue)) {
        Write-Host "The original treelistvalue before updating: " $treelistValue
		
        # Split the multilist field value into individual IDs
        $ids = $treelistValue -split '\|'
		
		
		# Iterate through the array and replace the value using for loop
		for ($i = 0; $i -lt $ids.Length; $i++) {
			$correspondingitemId = Get-MatchingItemValue -id $ids[$i]
			Write-Host "Output from Function : " $correspondingitemId
			if(![string]::IsNullOrEmpty($correspondingitemId)){
			
				Write-Host "The tag id is not empty from shared site : " $correspondingitemId
				$ids[$i] = $correspondingitemId
				
			}else{
				Write-Host "The corresponding tag id is empty: " $ids[$i]
			}
		}
		
		$newTreelistValue = $ids -join '|'
		Write-Host "The new treelistvalue after updating " $newTreelistValue
		
		if ($WhatIf) {
			Write-Host "The new treelistvalue after updating - Dry Run: " $newTreelistValue
			Write-Host "Updated item - Dry Run: " $($item.Paths.FullPath)
		}else{
			# Begin editing the item
			Write-Host "item path for updating: " $($item.Paths.FullPath)
			$item.Editing.BeginEdit()
			try {
					# Set the new value for the multilist field
					$item.Fields[$treelistFieldName].Value = $newTreelistValue

					# End editing the item
					$item.Editing.EndEdit()
					Write-Host "Updated item: " $($item.Paths.FullPath)
			}
			catch{
					# Cancel the edit if something goes wrong
					$item.Editing.CancelEdit()
					Write-Error "Failed to update item: $($item.Paths.FullPath)"
			}
		}			
    }
}

	   

Script Breakdown

1. Function to Find Matching Item Value

The Get-MatchingItemValue function takes an ID and checks if it exists in the list of matching items. If found, it returns the corresponding ID from the shared environment.

2. Script Initialization

  • WhatIf is set to $true to enable a dry run mode, preventing actual changes during the test.
  • Paths for both source (shared) and destination (global) tags folders are defined.
  • The path of the location where tags need to be updated is specified.

3. Retrieve and Compare Tags

  • Tags from both folders are retrieved based on language and template.
  • Tags are compared based on their names, and matching tags are stored in a global list.
  • Unmatching tags are logged for review

4. Update Content Items

  • Content items in the specified location are retrieved.
  • For each item, the script checks if the tags field is not empty.
  • It then updates the tag references with the corresponding IDs from the shared environment.
  • If WhatIf is true, changes are only logged. Otherwise, the items are updated in Sitecore.

Feel free to customize and run this script in your Sitecore environment, and share your experiences and any improvements you make!

You can check my other blogs too if interested. Blog Website

Comments

Popular posts from this blog

Sitecore XM Cloud Form Integration with Azure Function as Webhook

Automate RSS Feed to Sitecore XM Cloud: Logic App, Next.js API & Authoring API Integration

Create and Fetch Content From Sitecore Content Hub One using GraphQL and React