Saturday 8 October 2011

SharePoint 2010 Visual Upgrade Issue - v4.master could not be found

A problem I came across when carrying out a SharePoint 2007 to 2010 upgrade was that I wasn't able to carry out a visual upgrade without v4.master being present, generating the following error:

**********************************************
Visual Upgrade failed. The default master page for this user interface could not be found at "/search/_catalogs/masterpage/v4.master". Add the requested master page at this path and try again.
**********************************************

This, of course, makes sense and an easy solution is to copy the file into the master page gallery.  The problem is that when there are hundreds of subwebs in the site collection this becomes a little tricky and by copying the file into the library, any changes to the server side v4.master will not carry through as the copied version will be stored in the database rather than reference the file system version.

The solution to the problem is to deploy a module feature which pushes a reference to the server side v4.master into the master page gallery for each site.  This can be done using the following module definition:

**********************************************
<Module Name="OTBMasterPages" List="116" Url="_catalogs/masterpage" RootWebOnly="FALSE" SetupPath="GLOBAL">
  <File Url="v4.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
    <Property Name="UIVersion" Value="4" />
    <Property Name="ContentTypeId" Value="0x010105" />
  </File>
  <File Url="minimal.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
    <Property Name="UIVersion" Value="4" />
    <Property Name="ContentTypeId" Value="0x010105" />
  </File>
</Module>

**********************************************

The module directly references the TEMPLATE/GLOBAL folder so there is no need to include the files in the feature definition.  Once this module has been deployed in a feature, the next step is to add the feature to each web in the site collection.  This can easily be done with a PowerShell script:

**********************************************
cls
trap{break;}

# VARIABLES
$url = "http://[site url]/";
$featureId = New-Object Guid("1ba455df-5ea7-4c60-a90f-20b6ec3728d3");

# INITIALISE
$site = New-Object Microsoft.SharePoint.SPSite($url);
$rootWeb = $site.RootWeb;

# METHODS
function ProcessWeb($web)
{
 # If the feature has already been activated then deactivate
 if($web.Features[$featureId] -ne $null) {
  "Deleting from " + $web.Url;
  $web.Features.Remove($featureId);
 }
 
 # Activate the feature and then deactivate it.  No need for the dependency.
 $feature = $web.Features.Add($featureId);
 $web.Features.Remove($featureId);
 
 "Completed " + $web.Url;
 
 # Process each subweb
 foreach($subWeb in $web.Webs) {
  ProcessWeb $subWeb;
 }
 
 $web.Dispose();
}

# DO WORK
ProcessWeb $rootWeb;

# CLEAN UP
$rootWeb.Dispose();
$site.Dispose();
*********************************************

The script opens up the site collection and recursively processes the tree of webs.  For each web, the feature is checked for activation and deactivated if it is currently activated.  The feature is then activated - thereby pushing the master page references into the web - and then deactivated.  The reason for the deactivation is that once the references exist, the feature is no longer required - and any unnecessary dependencies should always be removed.  I would suggest that the feature either be configured as hidden or deployed as part of a temporary solution installation that is removed when the script has been successfully run.

If there are any meeting workspaces within the site collection then references to the mwsdefaultv4.master will be required.  When I carried out this work there were only three meeting workspace sites so I took the easy way out and just manually copied the file into each site.  Had there been a lot then I would have created a new feature which referenced the mws master page and then within the PowerShell script checked the web to see if it was a meeting workspace and activated the feature as needed.