Content migration - every sitecore developer will stumble upon this topic sooner or later in his career.
It is impossible to give step-by-step instructions for this, as the requirements vary for each use case, but I will do my best to point out what to look for during a content migration.
Make a list of all the items on the old instance. For each element, check whether it is still in use. If not, you don't have to map it either, so you can cross it off the list. Now try to find a counterpart among the new modules for each old module on the list and map the properties.
If a module is only used a few times, you should weigh up whether it makes sense to include the module in the content migration or not. Sometimes it is more efficient to map rarely occurring modules manually.
Always try to get everyone involved on board and let people from the content area support you with the content migration. You will get to a point where it takes expertise to map the properties properly.
A content migration is always a good opportunity to get rid of unnecessary content and and get the project to a proper state.
In the case of multilingual applications, do not forget to map the items in all required languages.
Clarify whether you actually need all versions of an item. In most cases it is enough if you map the latest published version and any newer drafts.
Make sure that the user you are performing the content migration with has all the necessary permissions.
If it is a requirement, that the content migration does not affect the processing history, you can switch user for each item to the user from the "__Updated by" field. In addition, I would recommend creating a fallback user with all possible permissions, which will be used if anything goes wrong.
I'm sure, there are a lot of different ways to migrate content, but i will introduce you to the approach that I have had the best experience with - Sitecore PowerShell Extension (SPE).
Below you can find a few code snippets which roughly illustrate the content migration process.
Start by getting all the pages you want to map from the old system and passing them on to the associated method. I didn't check for versions here to keep the sample compact.
$pagesToMap = Get-ChildItem -Path "/sitecore/content/DemoOld/Pages" -Language "en" -Recurse | Where-Object {$_.TemplateName -eq 'ContentPage'}
$pagesToMap | ForEach-Object {
MapPage($_)
}
Now map the page, get the right version for every module on the placehoder and pass it on to the associated method again.
Function MapPage($oldDatasource){
# Get the user who last updated the datasource item
$user = Get-User -Identity $oldDatasource.Fields["__Updated by"].Value
# Use the fallback user, in case something went wrong
if(-Not $user){
$user = Get-User -Identity "sitecore\contentmigration"
}
# Use SecurityDisabler
New-UsingBlock(New-Object -TypeName "Sitecore.SecurityModel.SecurityDisabler"){
# Use UserSwitcher
New-UsingBlock (New-Object Sitecore.Security.Accounts.UserSwitcher $user) {
$newDatasource = New-Item -Path "sitecore/content/Demo/Home/$($oldDatasource.Name)" -ItemType "/sitecore/templates/Demo/PageTemplates/ContentPage" -Language "en"
# Create datasource folder under the page
$ModulesDatasourcePath = "sitecore/content/Demo/Home/$($oldDatasource.Name)/Content"
New-Item -Path "$($ModulesDatasourcePath)" -ItemType "/sitecore/templates/Demo/FolderTemplates/ContentFolder" -Language "en" | Out-Null
# Map properties
$newDatasource.Editing.BeginEdit()
$newDatasource["PageTitle"] = $oldDatasource["PageHeadline"]
$newDatasource["PageSubTitle"] = $oldDatasource["PageSubheadline"]
$newDatasource["PageIntro"] = $oldDatasource["IntroText"]
$newDatasource.Editing.EndEdit() | Out-Null
# Get every rendering from the placeholder
Get-Rendering -Item $oldDatasource -FinalLayout -Language "en" |Where-Object{$_.Placeholder -eq "/phContent/phPageContent_123a45b-9ffd-4ac6-bbad-6dff506fecc0"} | ForEach-Object {
# Check if the rendering has a datasource
if ($_.Datasource){
# Get all versions of the datasource
[array]$versions = Get-Item -Path "master:" -ID $_.Datasource -Version *
# Check if the datasource has versions
if($versions){
# Start with the last (newest) version
For ($i = $versions.Count-1; $i -ge 0; $i--) {
$module = $versions[$i]
# Check if the Workflow state is "published"
if((($module)["__Workflow state"] -eq "{5EA2EE09-394C-4CEC-8E94-CEE6535B094F}")){
# Check the template of the module in order to call the associated method
if($module.TemplateName -eq 'ImageParagraph'){
MapImageParagraphModuleToImageTextModule $module $ModulesDatasourcePath $newDatasource
}
elseif($module.TemplateName -eq 'AnotherModule'){
MapAnotherModule $module $ModulesDatasourcePath $newDatasource
}
# else if...
# Leave the loop as soon as one version got mapped
break
}
}
}
}
}
}
}
}
Last but not least map the actual module and add it's rendering to the new pages placeholder.
Function MapImageParagraphModuleToImageTextModule($ImageParagraphDatasource, $ImageTextModulesDatasourcePath, $TargetPage){
# Get the user who last updated the datasource item
$user = Get-User -Identity $ImageParagraphDatasource.Fields["__Updated By"].Value
# Use the fallback user, in case something went wrong
if(-Not $user){
$user = Get-User -Identity "sitecore\contentmigration"
}
# Use SecurityDisabler
New-UsingBlock(New-Object -TypeName "Sitecore.SecurityModel.SecurityDisabler"){
# Use UserSwitcher
New-UsingBlock (New-Object Sitecore.Security.Accounts.UserSwitcher $user) {
# Create a new datasource item
$ImageTextDatasource = New-Item -Path "$($ImageTextModulesDatasourcePath)/$($ImageParagraphDatasource.Name)" -ItemType "/sitecore/templates/Demo/ModuleTemplates/ImageTextModule" -Language "en"
# Switch to editing mode
$ImageTextDatasource.Editing.BeginEdit()
# Map custom properties
$ImageTextDatasource["Headline"] = $ImageParagraphDatasource["Topline"] # 1:1
$ImageTextDatasource["MainText"] = $ImageParagraphDatasource["Paragraph"] # 1:1
$ImageTextDatasource["Image"] = $ImageParagraphDatasource["Image"] # 1:1
$ImageTextDatasource["ImagePosition"] = "F102189B-5PW2-46C2-9FV2-8K310D09478A" # Set fallback value
if($ImageParagraphDatasource["ImageLeftHandSide"] -eq 1) {$ImageTextDatasource["ImagePosition"] = "D202232A-5CB2-46C2-9AD2-8C310D07188A"} # Checkbox => Droplink
# Map Sitecore properties
$ImageTextDatasource.Fields["__Lock"].Value = $ImageParagraphDatasource.Fields["__Lock"].Value
$ImageTextDatasource.Fields["__Workflow state"].Value = $ImageParagraphDatasource.Fields["__Workflow state"].Value
$ImageTextDatasource.Fields["__Default workflow"].Value = $ImageParagraphDatasource.Fields["__Default workflow"].Value
$ImageTextDatasource.Fields["__Workflow"].Value = $ImageParagraphDatasource.Fields["__Workflow"].Value
$ImageTextDatasource.Fields["__Created"].Value = $ImageParagraphDatasource.Fields["__Created"].Value
# Leave editing mode
$ImageTextDatasource.Editing.EndEdit() | Out-Null
# Create a new rendering
$ImageTextRendering = Get-Item -Database "master" -Path "/sitecore/layout/Renderings/Demo/ModuleRenderings/ImageTextModule" | New-Rendering
# Add the created rendering to the placeholder
Add-Rendering -Item $TargetPage -FinalLayout -Instance $ImageTextRendering -DataSource $ImageTextDatasource.ID -Placeholder "phContent" -Language "en"
}
}
}
Now your content is in the right place!
P.S.: For more informations about Sitecore and our Services, please visit www.cyber-solutions.at