Migrating Sitecore Media Library to Content Hub. Part 3: Repointing Image Fields to Content Hub

Introduction

This is the 3rd part of a 3-part series describing scripted content migration from Sitecore XM/XP’s Media Library to Sitecore Content Hub DAM, where I'll outline the process of repointing all image fields from Media Library to Content Hub.

Media fields: Media Library vs Content Hub

What does this mean to “repoint image fields”? A typical image field pointing to the “good old” Sitecore Media library looks like this:

 <image mediaid={..} alt=".." height=".." width="..".../>

For this type of field, the Sitecore field rendering pipeline will read the image from the Media Library, generate its media URL, and inject it into the resulting HTML

An image field pointing to Content Hub is different, and it looks like this:

<image width=".." height=".." alt=".." src=".." stylelabs-content-id=".." stylelabs-content-type=".." thumbnailsrc=".." media-id=".." sitecore-path=".." />

Here’s what field attributes mean in the above example

The Sitecore Connect for Content Hub needs to be installed in order for this type of fieldwork. Connect’s media rendering pipeline will interpret those Content Hub fields slightly differently. Instead of doing media lookup in the Media Library, it’ll simply output the image’s public URL, which comes from the Content Hub.

Repointing the media fields: replacing the Media Library references with Content Hub fields.

In essence, this update process comes down to iterating over all Media Library items (after the migration to Content Hub has been completed) and performing the following steps for each item:

  • Find item referrers (content items that have an image or file field pointing to a given item)
  • Change each referrer’s field value from reference to Media Library to Content Hub field.

Creating the Content Hub export file

The Content Hub export file will drive the update process. For this to work, the Sitecore IDs of the source items are

  1. Migrated to Content Hub. (see part 1 and part 2 for details on migration process)
  2. Included in the Content Hub export file (see this post for details on extending the export profile and this post for the details on creating the export file)
  3. Update Excel file to copy over the values
  4. Save 1st sheet of Excel file in CSV format - this will become an input file for the following script

“Update Referrers” script

$uploadPath = "C:\temp\upload"
# Content Hub field template for image and file fields
$chValueTemplate = "<image width=""{0}"" height=""{1}"" alt=""{2}"" src=""{3}"" stylelabs-content-id=""{4}"" stylelabs-content-type=""{5}"" thumbnailsrc=""https://mhc-p-001.sitecorecontenthub.cloud/api/gateway/{6}/thumbnail"" media-id=""{7}"" sitecore-path=""{8}"" />";
# Content Hub link template to replace media links in the rich text fields
$rteElementLinkTemplate = "<img alt=""{0}"" src=""{1}"" />"
# Content Hub links tempalte for link fields
$linkFieldLinkTemplate = "<link text=""{0}"" title=""{0}"" linktype=""external"" url=""{1}"" anchor="""" target="""" />"

# Upload file to Sitecore CM for processing...
function UploadFile($title, $description){
    $filePath = Receive-File -Path $uploadPath -Title $title -Description $description;
	return $filePath;
}

$importFilePath = UploadFile -title "Upload CSV File" -description "Upload the CSV file exported from ContentHub"
Write-Host "Imported file path: $importFilePath";

Write-Host "Loading file into memory. This may take a few minutes.."
$importList = Import-CSV $importFilePath #make sure this file is cleaned using the seperate .NET core app
Write-Host "Completed loading file into memory." 

if($importList) {
	$totalCount = $importList.count;
    $index = 0;
	
	Write-Host "Total Count: $totalCount"
	# Disable indexing and Sitecore events for faster processing during the mass update
	Suspend-SearchIndex -Name sitecore_master_index
	
	New-UsingBlock (New-Object Sitecore.SecurityModel.SecurityDisabler) {
		New-UsingBlock (New-Object Sitecore.Data.DatabaseCacheDisabler){
			New-UsingBlock (New-Object Sitecore.Data.BulkUpdateContext) {
				New-UsingBlock (New-Object Sitecore.Data.Events.EventDisabler){
					foreach ( $row in $importList ) {
						$index++;
						
						$width = 0;
						$height = 0;
						$contentType = "";
						# Write-Host 'master file json' $row.MasterFileJson
      						# Read item attributes from line in a given CSV file 
						if($row.MasterFileJson.Length -gt 10) {
							$json = $row.MasterFileJson | ConvertFrom-Json;
							$width = $json.properties.width;
							$height = $json.properties.height;
							$contentType = $json.properties.group;
						}
						
						# Write-Host 'Processing CH Item: ' $row.id $row.SitecoreItemId 
						$mediaItem = $null;
						$mediaItem = Get-Item -Path "master:" -ID $row.SitecoreItemId;
					   
						
						if($null -eq $mediaItem) {continue; }
						# Write-Host 'Found item: ' $row.SitecoreItemId;
						# $mediaLibraryId = $sourceItem.Fields[$sourceField.Name].Value.Replace("<image mediaid=", "").Replace("/>", "").Replace("""", "").Replace(" ", "");
						$mediaId = $row.id;
						$stylelabsContentId = "";
						$thumbnailSrc = "";
						$src = $row.AssetPublicLinks;
						if($src.Length -gt 0) {
							$src = ([String]$src).split("|")[0]
						}
						$stylelabsContentType = "";
						$alt = "";
						$stylelabsContentType = "Image";
						
						$encodedTitle = [System.Net.WebUtility]::HtmlEncode($row.title)
      						# Create Content Hub field value from field template & values from the CSV file 
						$newFieldValue = $chValueTemplate -f $width, $height, $encodedTitle, $src, $row.id, $stylelabsContentType, $row.id,  $row.SitecoreItemId, $row.SitecorePath;
						# Find all items, referring the original media item
						$referrers = $mediaItem | Get-ItemReferrer -ItemLink;
						$referrersMeasure = $referrers | measure;
						Write-Host 'found referrers: ' $referrersMeasure.Count
						# Update referrers: change Media Library fields to Content Hub fields...
						if ($referrersMeasure.Count -gt 0) {
							$referrers | ForEach-Object {
								
								$sourceFieldId = $_.SourceFieldID;
								$sourceItem = Get-Item -Path "master:" -ID $_.SourceItemID;
								
								$sourceVersion = $sourceItem.Version.ToString()
								$referenceItemVersion = $_.SourceItemVersion.ToString()
								
								if($sourceVersion -eq $referenceItemVersion)
								{
									$itemId = $sourceItem.ID
									$sourceField = Get-Item -Path "master:" -ID $_.SourceFieldID;
									$itemField = $sourceItem | Get-ItemField -ReturnType TemplateField -Name $sourceField.Name;
									# Write-Host $itemField.type
									
									# Overwrite field value for Rich Text and General Link field types
									if($itemField.type -eq "Rich Text")
									{
									    $richTextValue = $sourceItem.Fields[$sourceField.Name].Value
									    if($richTextValue)
									    {
    									    $mediaImgStartIndex = $richTextValue.IndexOf("<img src=""-/media/");
    									    if($mediaImgStartIndex -ge 0)
    									    {
    											$mediaImgEndIndex = $richTextValue.IndexOf("/>", $mediaImgStartIndex);
    											if($mediaImgEndIndex -ge 0)
    											{
    											    $newRichTextElementValue = $rteElementLinkTemplate -f $encodedTitle, $src;
    											    $newFieldValue = $richTextValue.remove($mediaImgStartIndex,$mediaImgEndIndex-$mediaImgStartIndex+2).insert($mediaImgStartIndex, $newRichTextElementValue);
    											    
    											    $sourceItem.Editing.BeginEdit();
                									$sourceItem.Fields[$sourceField.Name].Value = $newFieldValue;
                									$sourceItem.Editing.EndEdit();
    											}
    									    }
									    }
									}
									elseif ($itemField.type -eq "General Link")
									{
									    $sourceItem.Editing.BeginEdit()

										[Sitecore.Data.Fields.LinkField]$fieldValueLink = $sourceItem.Fields[$sourceField.Name]

										$fieldValueLink.Clear()
										$fieldValueLink.Text = "Link"
										$fieldValueLink.Title = $encodedTitle
										$fieldValueLink.LinkType = "external"
										$fieldValueLink.Url = $src

										$sourceItem.Editing.EndEdit() #| Out-Null     
									}
									else
									{
    									$sourceItem.Editing.BeginEdit();
    									$sourceItem.Fields[$sourceField.Name].Value = $newFieldValue;
    									$sourceItem.Editing.EndEdit();
									}
								}	
							}
								
						} else {
							Write-Host $mediaItem.ID 'no referrers'
						# I commented this out just in case someone run this accidentally. I suggest to test run and checking log output before uncommenting the following line 
							# $_ | Remove-Item;
						}
						Write-Host "totalCount", $totalCount
						if(!$totalCount)
						{
						    $totalCount = 1
						}
						$percentComplete = ($index/$totalCount) * 100;
						Write-Progress -Activity 'Processing CH Assets' -Status "Processing CH asset $index out of $totalCount" -PercentComplete $percentComplete
					}
				}
			}
		}
	}
	
    
	Resume-SearchIndex -Name sitecore_master_index
}

Useful Links