Customizing application.master Without Breaking Things

In this post I want to illustrate how we can create a custom application.master without putting our farm into an unsupported state by incorporating a completely unobtrusive HTTP module.

What Are We Trying To Achieve?

Whenever we visit an administrative page like the List Settings page we notice that it looks like SharePoint OOB pages although we have put a great deal into creating a branded master page for our content pages. This happens because administrative pages, also known as application pages, refer to their own set of master pages in the layouts folder of the 12 Hive (%COMMONFILES%\Microsoft Shared\web server extensions\12).

In order to customize the appearance of those application pages we must not touch any of the OOB files in the 12 Hive. So what else can we do? Luckily, HTTP modules come to the rescue. Properly coded such a module will intercept each request to an application page and replace the reference to application.master to our custom master page that can be heavily modified or simply reference a specially crafted style sheet.

First, I want to provide some background on why we have to resort to HTTP modules. Then we will take a look at the solution. If you want to skip the appetizer feel free to jump right to the entré.

Background

SharePoint comes with a rich set of application pages in the /_layouts directory which are primarily used for administrative purposes, i.e. when switching to the Site Settings page. The /_layouts directory is a virtual directory being mapped into each web site, thus all web wsites in a farm refer to the same application pages.

For example, you might have a web site at http://dev.local/training. Selecting the Site Settings menu from Site Actions will redirect to http://dev.local/training/_layouts/settings.aspx. This particular web site may have a sub-web site at http://dev.local/training/moss that has a Site Settings page at http://dev.local/training/moss/_layouts/settings.aspx. The directory is made available to each web site by dynamically mapping it from the filesystem (the 12 Hive).

Additionally, almost all application pages refer to a single master page file namely application.master located in /_layouts as well. Unfortunately, in a branded environment the application pages just do not fit into the overall design and customizing the application.master is a bit different from creating custom master pages for content pages.

The Problem and the Solution

There is one main problem with application.master: customizing it puts our environment into an unsupported state and updates may simply overwrite the masterpage file.

Currently, there are four available solutions to our problem the first two “supported” methods are documented at http://support.microsoft.com/kb/944105:

  1. Customize application.master in the 12 Hive – Generally, modifying files in the 12 Hive is a big no-no, unless you’ve created them by yourself. However, doing so will lead to quick results but may be lost with the next update.
  2. Make a copy of the /_layouts folder and instruct IIS to map this directory instead of the original one – This idea is even worse than the first one since this method may cause excessive manageability and maintenance issues for very obvious reasons.
  3. Modify all application pages to point to your custom masterpages file – Besides being a very tedious task this method suffers from the same problem as in (1): changes will be overwritten with updates made to that folder. And again, OOB application files should never be touched.
  4. Create HTTP module that injects a custom master page on a per-request basis – This approach currently is the only viable option considering the fact that you neither have to touch any of the files in the 12 Hive and that you can configure different master pages for each web application.

In the next few sections we will create a custom HTTP module and setup one of our web applications accordingly. Creating that module does not hurt, so don’t be afraid.

Creating the HTTP Module

I first stumbled about this method on David Wise’ SharePoint Blog and decided to give it a try. It is by far the most sophisticated and least intrusive (not to say dangerous) approach to provide custom master pages for application pages.

How Does It Work?

HTTP handlers (not HTTP modules) have access to the application context, including the requesting user’s identity (if known), application state, and session information. When an HTTP handler is requested, ASP.NET calls the ProcessRequest method on the appropriate handler. The handler’s ProcessRequest method creates a response, which is sent back to the requesting browser. As with any page request, the response goes through any HTTP modules that have subscribed to events that occur after the handler has run. What we’re going to do is to subscribe our own module to such events.

After an HttpApplication object has been assigned to the HttpRequest it is process by the HttpApplication pipeline which involves about 22 steps until the page content is finally delivered to the client application.

Now here’s the plan: we want to capture the right moment of the page processing in order to change the page’s reference to application.master into custom.master. To do that we hook up with the PreRequestHandlerExecute event, which allows us to retrieve a reference to the System.Web.UI.Page instance, and in turn registers with the page’s PreInit event that is the correct moment to inject our custom master page reference. While the first event is scoped to the HTTP application the latter in the first stage of the page life cycle. To read more about the System.Web.UI.Page life cycle please refer to http://msdn.microsoft.com/en-us/library/system.web.ui.page(VS.80).aspx.

Let’s Code Our HTTP Module

Fire up Visual Studio (2005 at least) and create a new Class Library project:

  1. Configure the project to emit an assembly with the name CustomHttpModule
  2. Add a reference to the System.Web assembly.
  3. Create a class in the namespace CustomHttpModule and with the name ApplicationMasterModule that inherits from IHttpModule.

The complete module code resembles to this:

using System;
using System.Web;
using System.Web.UI;
using System.IO;
 
namespace CustomHttpModule {
  public class ApplicationMasterModule : IHttpModule {
    /*
     * This is from the HttpApplication processing pipeline.
     */
    public void Init(HttpApplication context) {
      context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
    }
 
    /*
     * Still in the pipeline, registers the page's PreInit event
     * with an event handler part of the System.Web.UI.Page
     * life cycle.
     */
    void context_PreRequestHandlerExecute(object sender, EventArgs e) {
      Page page = HttpContext.Current.CurrentHandler as Page;
 
      if (page != null) {
        page.PreInit += new EventHandler(page_PreInit);
      }
    }
 
    /*
     * Does the magic: replaces the default master page with
     * a custom master page.
     */
    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")) {
            // change to match your custom master page name
            page.MasterPageFile = "/_layouts/Custom.master";
          }
        }
      }
    }
 
    public void Dispose() { }
  }
}

Sign and compile the assembly and copy it into the GAC. Next we need to register the module with the desired web applications.

Registering the Module

Chose a web application that is supposed to use the module and edit the web.config accordingly:

    1. Locate the <httpModules> section and add the following entry:
 

In case your wondering what the type attribute means, the syntax is TypeName, AssemblyName, Version, Culture, PublicKeyToken. Consider you have an assembly called MyAssembly that defines the namespace CustomHttpModule which in turn has a definition for the class MyHttpMasterModule then the first two entries of the type attribute would be CustomHttpModule.MyHttpMasterModule, MyAssembly, ....

  1. Place your custom master page layout in /_layouts and name it Custom.master or whatever you called it in the module code.

Fire up a browser and navigate to the Site Settings page to verify that the module works as expected.

Final Remarks

In order to be able to apply a different style sheet on a per web site basis you may want to add the following code to the <head> section of your custom master file:

<script type="text/javascript">// <![CDATA[
  protected override void OnLoad(EventArgs e) {
    switch(Request.ServerVariables["HTTP_HOST"].ToString())
    {
        case "SiteName1":
            this.MyCSSLiteral.Text = "	<link rel=stylesheet type=text/css href=/_layouts/1033/styles/styles1.css />";
            break;
        case "SiteName2":
            this.MyCSSLiteral.Text = "	<link rel=stylesheet type=text/css href=/_layouts/1033/styles/styles2.css />";
            break;
        default:
            break;
    }
  }
// ]]></script>

That way you’re maximizing the effectiveness of this approach.







5 Responses to “Customizing application.master Without Breaking Things”

Hi Steve Graegert,
Excellent posting..
It’s really made my day..
Thanks a lot

ramesh added these pithy words on Jun 09 09 at 15:10

Hey Steve,

Thanks for posting this here. This post and code worked and helped me implement and complete the Sharepoint 2007 project I am doing now.

I’ve got the basics now and I will complete the configuration later on when moving to production server. I am using different folder for the custom application master. I prefer to use the “Styles” with the web application as than I can move the config easily without touching the file system.

I had issues with “copy it into the GAC” but I figured it out how to get through this issue. Non programmers like me are struggling with this and there is not easy information on google. I will blog my steps on for others.

Ivan Versluis added these pithy words on Nov 16 09 at 10:36

hi Steve

i have one doubt. why you are using IHttpModule instead of httphandler?

Navaneeth added these pithy words on Dec 16 09 at 07:49

Navaneeth – A HttpHandler is an extension-based pre-processor that is suitable to intercept request for a particular resource, i.e. HTML or GIF files. A HttpModule is an event-based preprocessor that allows to intercept events that occur in the pipeline and to inject custom logic. Since we want to to hook up with the page’s PreInit event we need to use an HttpHandler.

Essentially, handlers and modules can be combined so that modules are called before and after a handler executes. You may find the article KB307985 in Microsofts knowledge base interesting.

Hope that was helpful.

Steve Graegert added these pithy words on Dec 16 09 at 08:30

asp.net, c#,javascript…

[...]Steve Graegert » Blog Archive » Customizing application.master Without Breaking Things[...]…

asp.net, c#,javascript added these pithy words on Sep 30 11 at 12:05

Leave a Reply