By: Kevin Lin
One of SharePoint’s most prominent strengths lies in the fact that users can easily customize their own experience. In the case of master pages however, they can be a bit complicated for an end user to deploy and change. Custom master pages sometimes contain dependant image folders and also require administrator approval before they can be used. However by bundling the master page for a SharePoint site into a feature, usability is enhanced greatly. Not only will the feature bypass the administrator approval step and push all relevant files onto SharePoint with a click of a button, but by activating the feature, the master page will instantly be set. Furthermore, by deactivating the feature the master page will automatically revert to default.
Recently I encountered a situation where I had to programmatically set a master page through code in a programmatically provisioned site collection. I wasn’t able to find a comprehensive guide on how to create such a feature, especially when the master page also requires that an images folder be pushed to the root of the site collection, so I wanted to share my knowledge with the rest of the SharePoint community. In this post I will describe how to create a Site Collection level feature that will push the master page file as well as all other relevant files into the SharePoint site and set the master page. I found Paul Stork’s blog (http://mindsharpblogs.com/PaulS/archive/2007/06/18/1903.aspx) very helpful when coding this feature.
Creating a master page feature can be essentially broken down into four simple steps.
1) Create the Visual Studio project
2) Copy over the relevant files
3) Edit the elements.xml
4) Code the feature
Step 1: Creating the Visual Studio project
Open up Visual Studio and create a new WSPBuilder Project.

Then add a Feature with Receiver to the project. Remember to scope this feature to Site when prompted for it. This will put make the feature into a Site Collection level feature.

Step 2: Copy over relevant files
After the project is created and the Feature added to the project, copy over all relevant files into the feature folder. In this case custom.master, the master page file, requires a theme folder at the root of the site with style1.css and an images folder. This structure is duplicated under the Feature folder.
Step 3: Edit the elements.xml
Now that all the files are inside the solution, the next step would be to edit the elements.xml file to push all the files into the SharePoint site. A new Module will have to be created for each location files are being pushed into SharePoint. The two most important parameters to note are listed below:
-
Module Url specifies the location within SharePoint the file should go. In the code snippet below, the custom.master file needs to be pushed into _catalogs/MasterPage. It is possible to specify folders that do not exist; they will be automatically created by the Feature.
-
File Url specifies the location inside your solution in which the file is located relative to the Feature folder. In the code snippet below, the style1.css file is located at themes/style1.css relative to the Feature folder.
In this code snippet there are three Module’s which will push the master page into _catalogs/MasterPage/, the style1.css into themes/, and the images into themes/images/. All of the files are set to IgnoreIfAlreadyExists as if the file is already there, we do not want to copy over it. The master page must be set to type GhostableInLibrary and the content type must be set if publishing is enabled otherwise it will default to the content type Page Layout. Setting the content type will not break the feature even if publishing is not enabled, so I would recommend always having it there.
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="AddCustomMaster" Url="_catalogs/MasterPage">
<File Url="custom.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
<Property Name="ContentType" Value="$Resources:cmscore,contenttype_masterpage_name;"></Property>
</File>
</Module>
<Module Name="AddCSS" Url="themes">
<File Url="themes/style1.css" Name="style1.css" IgnoreIfAlreadyExists="TRUE"></File>
</Module>
<Module Name="AddImages" Url="themes/images">
<File Url="themes/images/bgx.jpg" Name="bgx.jpg" IgnoreIfAlreadyExists="TRUE"></File>
<File Url="themes/images/blk_band.jpg" Name="blk_band.jpg" IgnoreIfAlreadyExists="TRUE"></File>
<File Url="themes/images/blk_logo.jpg" Name="blk_logo.jpg" IgnoreIfAlreadyExists="TRUE"></File>
<File Url="themes/images/blk_lstbulet.gif" Name="blk_lstbulet.gif" IgnoreIfAlreadyExists="TRUE"></File>
. . .
<File Url="themes/images/sidemenutop.gif" Name="sidemenutop.gif" IgnoreIfAlreadyExists="TRUE"></File>
<File Url="themes/images/titlegraphic.gif" Name="titlegraphic.gif" IgnoreIfAlreadyExists="TRUE"></File>
</Module>
</Elements>
Note: Since there were around 30 images in the image folder, I did not include all of them in the code snippet, I put ellipses. However in the elements.xml file all images must be included.
Step 4: Code the feature
The code for this feature can be copied and pasted from the code snippet listed below. The only parameter that will need changing is the two strings newCustomMasterUrl and newMasterUrl. Basically all that is being done when the Feature is activated is that the master page will be set to the master page specified. When the feature is deactivated, the master page will be flipped to the default master page. The code basically opens the site collection that the feature currently resides in, then sets the master page to the one specified. The code that deals with relative URL needs to be there in order for this feature to work in site collections that are not at the root. There is no code that actually pushes the master page, images, and other related files into SharePoint. That is done through the elements.xml file.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
SPWeb web = site.OpenWeb();
string newCustomMasterUrl = "/_catalogs/masterpage/custom.master";
string newMasterUrl = "/_catalogs/masterpage/custom.master";
string relativeUrl = (site.ServerRelativeUrl);
if (relativeUrl == @"/")
relativeUrl = "";
newCustomMasterUrl = relativeUrl + newCustomMasterUrl;
newMasterUrl = relativeUrl + newMasterUrl;
if (!string.Equals(newCustomMasterUrl, web.CustomMasterUrl, StringComparison.OrdinalIgnoreCase)
&& !string.Equals(newMasterUrl, web.MasterUrl, StringComparison.OrdinalIgnoreCase))
{
web.MasterUrl = newMasterUrl;
web.CustomMasterUrl = newCustomMasterUrl;
web.Update();
}
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
SPWeb web = site.OpenWeb();
string newCustomMasterUrl = "/_catalogs/masterpage/default.master";
string newMasterUrl = "/_catalogs/masterpage/default.master";
string relativeUrl = (site.ServerRelativeUrl);
if (relativeUrl == @"/")
relativeUrl = "";
newCustomMasterUrl = relativeUrl + newCustomMasterUrl;
newMasterUrl = relativeUrl + newMasterUrl;
if (!string.Equals(newCustomMasterUrl, web.CustomMasterUrl, StringComparison.OrdinalIgnoreCase)
&& !string.Equals(newMasterUrl, web.MasterUrl, StringComparison.OrdinalIgnoreCase))
{
web.MasterUrl = newMasterUrl;
web.CustomMasterUrl = newCustomMasterUrl;
web.Update();
}
}
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
//throw new Exception("The method or operation is not implemented.");
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
//throw new Exception("The method or operation is not implemented.");
}
Now all that needs to be done is deploy the feature to your SharePoint environment and try it out. I hope you found this guide useful. If anything is unclear, or there are any issues feel free to post a comment or shoot me an email at klin@gig-werks.com
--By: Kevin Lin