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.

Wednesday 27 July 2011

SharePoint 2010 / Visual Studio 2010 - Error Occurred In Deployment Step - Access Denied

Occasionally, while developing a SharePoint 2010 Solution in Visual Studio 2010 I would be faced with the following error when hitting "Deploy":


Error occurred in deployment step '[various]': Access to the path '[path to a hive file]' is denied.


An IIS reset would fix the problem but I felt that this had to be just a workaround for a more specific issue - the error only occurred at certain times so there must had to be a specific cause for the file(s) being locked.


Short answer
The debugging web application that you chose when creating the SP2010 project in VS2010 is the only web application that is reset (well, its application pool is recycled, to be specific) when deploying a solution.  If any other web applications that use a different application pool lock any of the files that you are working on within your project then they will hold on to those files, causing the access denied error.



Long answer
When creating a SharePoint 2010 project within Visual Studio 2010 you are required to enter a URL that will be used for testing purposes.  This URL is then used to determine a number of things including: which web application to deploy to if/when the solution contains Web Parts, etc.; where to automatically activate features; and, crucially, which application pool to reset before carrying out the deploy.


If you choose a debugging web application that you then don't use for testing and instead use a different web application then you are at risk of seeing exactly this issue.  Bearing in mind this will only happen if the debugging web application has a different application pool to the one you are using for testing, when you start to make use of server templates, for example a site definition, the application pool will open one of the files (e.g. default.aspx) and keep it locked.  When you choose to deploy through Visual Studio it will recycle the application pool for the debugging web application but if this application pool is not being used for your testing, then the application pool for your test site will hang on to those files and you will receive the error.


Solutions
1) Update the debugging URL in your project properties to the web application that you are using.  Of course, if you are using more than one web application for testing purposes then if possible just re-use the same application pool and if this isn't possible then solution 3 may help


2) Ensure that the debugging web application's application pool is the same as the one you are using for testing.  I tend to create an application pool that I then use for as many web applications as possible as this is easier to manage and less intensive on memory usage.


3) Use WSPBuilder and use the context menu options to recycle the application pools.


4) IIS Reset (heavy handed but has the same effect as solution 3)