Recently I have involved in branding one of our clients sharepoint portal. As part of the branding process we needed to modify the application.master file, so even the layout pages will have the same look and feel. As part of the investigation, we come up with the best approach for doing this.
1. Create a custom master page, with all the placeholders which are available in the original application.master file. Its better to take a copy of the application.master file and add the styles/images etc as you wish
2. Create a custom HttpModule which will set the new custom master page if the current page is having the master page “application.master”. We preferred HttpModule on top of HttpHandler because HttpModule can be deployed only to the web applications we wants, while HttpHandler will hit across all web applications.
Below is the code i have created for the http module.
using System;
using System.Web;
using System.Web.UI;
using Microsoft.SharePoint;
namespace Company.HttpModules
{
/// <summary>
/// All the layout pages will be using application.master
/// inorder to make the application consistent. we have created a custom application master page
/// this module will check whether the current request is having the master page as application.master
/// then change it to our custom page
/// </summary>
public class ApplicationMasterModule : IHttpModule
{
public void Init(HttpApplication context)
{
if (context == null)
throw new ArgumentNullException("context");
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}
void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit += new EventHandler(page_PreInit);
}
}
void page_PreInit(object sender, EventArgs e)
{
Page page = sender as Page;
if (page != null)
{
if (page.MasterPageFile != null)
{
if (page.MasterPageFile.Contains("application.master"))
{
using (SPSite connectSite = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb currentWeb = connectSite.RootWeb)
{
string url = currentWeb.ServerRelativeUrl + "/_catalogs/masterpage/our_custom_app.master";
page.MasterPageFile = url;
}
}
}
}
}
}
public void Dispose()
{
}
}
}
Now comes the important aspect of deployment. Its better to create the master page as well as http module to be deployed as a feature. See the feature.xml
<?xml version="1.0" encoding="utf-8"?>
<Feature Id="b1531032-19b5-4fb1-87a6-6892b280c90d"
Title="Custom Application Master Page"
Description="Custom Application Master Page"
Version="12.0.0.0"
Hidden="FALSE"
Scope="Site"
DefaultResourceFile="core"
ReceiverAssembly="Company.MasterPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6c27a5016ad8e167"
ReceiverClass="Company.MasterPages.CustomAppMasterPage"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="elements.xml"/>
<ElementFile Location="our_custom_app.master" />
</ElementManifests>
</Feature>
and elements.xml file
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="CustomApplicationMasterPage"
Url="_catalogs/masterpage"
Path=""
RootWebOnly="False">
<File Url="our_custom_app.master"
Type="GhostableInLibrary" IgnoreIfAlreadyExists="FALSE">
<Property Name="ContentType"
Value="$Resources:cmscore,contenttype_-9masterpage_name;" />
<Property Name="PublishingPreviewImage"
Value="" />
<Property Name="Description"
Value="Custom Application Master Page"/>
</File>
</Module>
</Elements>
See the Feature Receiver assembly which will add the web.config entries so the HttpModule will work without manually modifying the web configuration file
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.Globalization;
using System.Diagnostics;
namespace Company.UIUpdates
{
/// <summary>
/// On Feature Activation => Add HttpModule Entry in Web.Config file
/// On Feature DeActivation => Remove the HttpModule Entry from Web.Config file
/// </summary>
class CustomAppMasterPage : SPFeatureReceiver
{
static SPWebConfigModification CreateModification(string Name, string XPath, string Value)
{
SPWebConfigModification modification = new SPWebConfigModification(Name, XPath);
modification.Owner = "Custom Application Master Page.wsp";
modification.Sequence = 0;
modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
modification.Value = Value;
return modification;
}
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
using (SPSite site = properties.Feature.Parent as SPSite)
{
SPWebApplication webApp = site.WebApplication;
string name = "add[@name='ApplicationMasterModule']";
string path = "configuration/system.web/httpModules";
string value = @"<add name=""ApplicationMasterModule"" type=""Company.HttpModules.ApplicationMasterModule,Company.HttpModules, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2391931b556f037"" />";
if (webApp != null)
{
SPWebConfigModification modification = CreateModification(name, path, value);
webApp.WebConfigModifications.Add(modification);
webApp.Update();
SPWebService.ContentService.ApplyWebConfigModifications();
SPWebService.ContentService.WebApplications[webApp.Id].Update();
//Applies the web config settings in all the web application in the farm
SPWebService.ContentService.WebApplications[webApp.Id].WebService.ApplyWebConfigModifications();
}
}
}
catch (Exception ex)
{
WriteMessageToEventLog(ex.ToString());
}
}
static private void WriteMessageToEventLog(string message)
{
string EVENT_SOURCE = "CustomAppMasterPage Feature Receiver";
SPSecurity.RunWithElevatedPrivileges(delegate()
{
if (!EventLog.SourceExists(EVENT_SOURCE))
{
EventLog.CreateEventSource(EVENT_SOURCE, "Application");
}
EventLog.WriteEntry(EVENT_SOURCE, message);
});
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
try
{
using (SPSite site = properties.Feature.Parent as SPSite)
{
SPWebApplication webApp = site.WebApplication;
string name = "add[@name='ApplicationMasterModule']";
string path = "configuration/system.web/httpModules";
string value = @"<add name=""ApplicationMasterModule"" type=""Company.HttpModules.ApplicationMasterModule,Company.HttpModules, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2391931b556f037"" />";
if (webApp != null)
{
SPWebConfigModification modification = CreateModification(name, path, value);
webApp.WebConfigModifications.Remove(modification);
webApp.Update();
SPWebService.ContentService.ApplyWebConfigModifications();
SPWebService.ContentService.WebApplications[webApp.Id].Update();
//Applies the web config settings in all the web application in the farm
SPWebService.ContentService.WebApplications[webApp.Id].WebService.ApplyWebConfigModifications();
}
}
}
catch (Exception ex)
{
WriteMessageToEventLog(ex.ToString());
}
}
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
}
}
}
As part of the WSP, we included the following
1. Custom Master Page
2. Http Module dll which will go into GAC
3. Feature Receiver
Hope this helps. Feel free to contact me if you have any queries.