SharePoint migration is always a challenge. And the more customizations you have the more challenging it is.
In this post I want to describe the solution to fix "404 Page not found" issue that may occur after migrating sites based on custom web templates and custom features.
Let's say you have a product that works both in SharePoint 2010 and 2013.
- backup content database on 2010
- restore it on 2013
- install the product's solution with CompatibilityMode 14, 15
- mount the database
- proceed Site Collection Upgrade (either from UI or with PowerShell script)
- happily use migrated content in SharePoint 2013.
protected override void DoUpgrade()
{
if (!this.NeedsUpgrade)
{
base.Log.InfoTag(0x258845, string.Format(CultureInfo.InvariantCulture, this.LogPreamble + "Web template upgrade for web/site {0} returned NeedsUpgrade false. Not upgrading. Web template: {1}, web template version: {2}, target web template version: {3}", new object[] { this.Web.Url, this.WebTemplate.Name, this.WebTemplateVersion, this.TargetWebTemplateVersion }));
}
// rest of the method
}
public override bool NeedsUpgrade
{
get
{
bool flag = false;
if (((this.WebTemplate != null) && (this.WebTemplateVersion < this.TargetWebTemplateVersion)) && (this.XmlConfiguration != null))
{
flag = true;
}
// Logging goes here
return flag;
}
}
WebTemplateVersion contains '4' as major revision for SharePoint 2010-based content, TargetWebTemplateVersion contains '15' as major revision as we're upgrading to SharePoint 2013.
So, first 2 conditions are true.
The last condition is the most interesting and complicated.
XmlConfiguration getter implementation looks like that:
internal SPXmlWebTemplateConfiguration XmlConfiguration
{
get
{
if (this.m_xwtcWebTemplate == null)
{
foreach (SPXmlConfiguration configuration in SPXmlConfigurationManager.GetInstanceByCompatibilityLevel(this.TargetMajorVersion).SelectXmlConfigurations(SPXmlConfiguration.WebTemplateUpgradeXPath))
{
List<spxmlwebtemplateconfiguration> webTemplateConfigurations = configuration.GetWebTemplateConfigurations(this.WebTemplateID, this.LCID);
int revision = this.TargetWebTemplateVersion.Revision;
foreach (SPXmlWebTemplateConfiguration configuration2 in webTemplateConfigurations)
{
if (configuration2.IsApplicable(this))
{
this.m_xwtcWebTemplate = configuration2;
break;
}
}
}
}
return this.m_xwtcWebTemplate;
}
}
- SPXmlConfigurationManager loads xml configuration files from c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\CONFIG\UPGRADE\ folder
- Each file can contain multiple (or none) WebTemplate elements (Upgrade Definition schema is described here)
- Each found WebTemplate is tested if it is applicable to the Web Template that is being processed by Site Upgrade action.
- If it is applicable then the Web Template (and the site) will be upgraded.
flag = ((this.FromProductVersion == webTemplateVersion.Major)
&& (this.ToSchemaVersion == targetWebTemplateVersion.Revision))
&& ((this.BeginFromSchemaVersion <= webTemplateVersion.Revision)
&& (webTemplateVersion.Revision <= this.EndFromSchemaVersion));
ToSchemaVersion should be equal to the Revision number from the site definition onet.xml file applied to SharePoint 2013.
BeginFromSchemaVersion and EndFromSchemaVersion should form a range that contain the Revision from the site definition onet.xml file applied to SharePoint 2010. I would suggest to set BeginFromSchemaVersion to 0 and EndFromSchemaVersion to some large number, for example, 10.
In my case resulting XML looked like that:
<Config xmlns="urn:Microsoft.SharePoint.Upgrade">
<WebTemplate
ID="<template_id>"
LocaleId="*"
FromProductVersion="4"
BeginFromSchemaVersion="0"
EndFromSchemaVersion="10"
ToSchemaVersion="2">
</WebTemplate>
</Config>
</template_id>
Either you can just copy the file to needed location as it is one-time operation, or you can include mapped folder to your Visual Studio project and deploy the file during solution installation.
This solution works right at the moment of the migration and Site Collection Upgrade. But if some customer has already migrated the content and upgraded the site collection then the only way to fix the issue is to execute SQL script to update SetupPathVersion value for all the items in the site collection:
UPDATE [dbo.AllDocs]
SET SetupPathVersion = '15'
WHERE SiteId = '<site_collection_id>' AND 'SetupPathVersion' = '4'
UPDATE
- If you update from 2007 to 2010 and then to 2013 SetupPathVersion will contain '3' instead of '4'. It means that above script should be modified to update entries that have '3' as SetupPathVersion:
UPDATE [dbo.AllDocs] SET SetupPathVersion = '15' WHERE SiteId = '<site_collection_id>' AND ('SetupPathVersion' = '4' OR 'SetupPathVersion' = '3')
- If your Feature for some reason had different name in previous versions you should also update 'SetupPath' with script like:
UPDATE [dbo.AllDocs] SET SetupPath = CAST(REPLACE(CAST(SetupPath as nvarchar(MAX)), 'old_feature_name', 'new_feature_name') as nvarchar(255)) WHERE SiteId = '<site_collection_id>' AND 'SetupPath' LIKE 'Features\old_feature_nane%'
Please, feel free to leave a comment if you have any questions.
Have fun!
Comments