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.

2 comments:

  1. Great post, and even better solution :-) thanks

    that is until I get about half way through....

    Exception calling "Add" with "1" argument(s): "Failed to instantiate file "v4.master" from module "OTBMasterPages": The specified list does not exist."

    At C:\xxx.ps1:22 char:30
    + $feature = $web.Features.Add <<<< ($featureId);
    + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : DotNetMethodException

    ReplyDelete
    Replies
    1. Hi Shaun. Off the top of my head this sounds like it might have been run against a site that has not had a template applied yet. Did it fail on the first site or in a subsite? Try adding in "$web.Url" at the top of the ProcessWeb function and then when it fails, try going to that URL and see what's there. You could try going into SharePoint Designer or a UNC path to have a look in more detail (if you are using UNC don't forget to turn on view all system files and folders). Let me know how you get on, I have no doubt there will be a way to get it going.

      Delete