Branding Mark II: Refining the HttpModule-based Approach

So we've got another MOSS environment to brand for my customer. This time it's an extranet environment for partner collaboration that has some very minor changes from the Intranet branding implementation I've described in the initial series of posts. Changes are pretty superficial - swap the logo, remove certain components that are only applicable to the Intranet environment, and update the header and footer.

Implementing the actual branding tasks isn't anything new. What is new is that I've taken a different approach this time around to ease configuration management headaches - mostly due to having adequate time in the project schedule and complete ownership of the development tasks. The implementation design changes are based on two major changes:

  • Use of the ResourceRedirect module
  • Packaging of all branding components in the SharePoint Solution framework

I'll post the gory details in the next few weeks, once I've finished prepping for delivering SharePoint Academy classes, but below is a quick summary of the tactics and some lessons learned.

Using the ResourceRedirect Module

This is basically a revisit of the tactics described in part 3 of my initial series of branding posts. We used KB944105 method 1 (taking a backup copy of the LAYOUTS directory) for a handful of files that needed updating, like CORE.CSS, SPTHEMES.XML, some IMAGES files, and the various WEBTEMP*.xml files (there were a few sitedefs we wanted to hide from end users), but the branding of the LAYOUTS pages was accomplished via redirects.

We used the following set of redirects (read my previous posts and you'll see why these were selected):

<Branding>

      <pageRedirects/>

      <destinationRedirects>

            <redirect pattern="/_layouts/SiteManager.aspx" destinationPageUrl="/_layouts/customizations/SiteManager.aspx" />

            <redirect pattern="/_layouts/templatepick.aspx" destinationPageUrl="/_layouts/customizations/templatepick.aspx" />

      </destinationRedirects>

      <pathRedirects/>

      <comboRedirects/>

      <masterRedirects>

            <redirect masterPageUrl="~/_layouts/customizations/NEW_simple.master" originalMaster="simple.master"/>

            <redirect masterPageUrl="~/_layouts/customizations/NEW_application.master" originalMaster="application.master"/>

      </masterRedirects>

</Branding>

While unit-testing the ResourceRedirect, I found a couple of bugs and flaws in the approach:

  • The <destinationRedirect/> elements can only be used for ASPX pages. You can't use this for components that don't have a PreInit event, like CSS files, images, etc. You'll need something lower-level - like an ISAPI filter - for those. This revision to the approach has been noted in the original post.
  • The original code for ResourceRedirect.cs bombed when a page didn't have any master page. Forgot to check for nulls (DUH!), so pages like SiteManager.aspx caused ResourceRedirect to choke. The updated version of the code is now in the original post.
  • Templatepick.aspx and the ResourceRedirect don't play well together. The jury's out on how best to handle this issue for now. Try to use the ResourceRedirect module on templatepick.aspx, and although site provisioning seems to work fine, you'll get a "Vaule does not fall within the expected range" error upon site creation. The bug doesn't seem to be in the page itself, as following KB944105 method 1 with the updated page works fine. I'm hopeful that we can chase down the source of the issue, but the fallback is replacing the page and keeping the backup of LAYOUTS.

Deploying the Branding as a Solution

Important Update: Contrary to the contents of this post, you'll likely need to deploy your branding code as a combination of a Web Front End solution and an App Server solution. I'll have an updated post on this topic by late April.

I can't overstate how much easier this makes the deployment process, or how much I wish I would have had the time to implement this approach with the Intranet branding before handing off the prototype to the dev team... :( Anyways, I used WSPBuilder to prepare the solution, which saved many many hours of tweaking a solution manifest and reduced the activity of prepping a solution to copying a bunch of files to a temporary directory. It's my new hero.

You'll still need to perform a backup of any "12" directory files that you might customize. Beyond that, you've got several options for how you structure, deploy, and undeploy the solution. They differ in how you handle NEW components and REPLACEMENT components (overwriting OOTB files in the "12" directory):

  • Option 1: Only include NEW components in the solution. In this case, you'll have to create a backup of the "12" hive and update/restore the replacement components manually on each server.
  • Option 2: Include new AND replacement components in the solution. As with option 1, you'll have to create a backup of the "12" hive and update/restore the replacement components manually on each server. Here, you must create the backup before deploying the solution!!!
  • Option 3: Include new, replacement, AND backup components in the solution.  To make this one work well, you perform two steps in structuring the solution to include the backups: add a ".bak" to each file's name AND store the backups in a new subdirectory (i.e. store a custom version of LAYOUTS\error.aspx as LAYOUTS\originals\error.aspx.bak. When it comes time to retract the solution, it's now a 3-phase process:
    • On all servers - Copy the *.bak files to their original locations (but don't change the names)
    • Retract the solution
    • On all servers - Rename the *.bak files to their original names

I'm sticking with Option 3 for now, since it puts the "12" hive originals in source control, spells out very clearly to the development team which components have been customized, and minimizes installation effort. Option 2 sounds a little dangerous, since you could destroy "12" hive files if you didn't capture the original backup.

Along with the solution, I wrote a simple batch file to install the solution and activate the feature stapler. There's still some manual effort involved in updating the web.config to include the ResourceRedirect configuraiton, but it's a very minimal amount of low-level configuration work. There's also an uninstall script that's step 2 from Option 3. Note that both include PAUSEs, since it's essential that you wait for the solution deployment/retraction to complete before the script executes all tasks. The install script also uses the BrandingUpdate application from my previous posts.

Install Script

The install script also uses the BrandingUpdate application from my previous posts. Note that you'll need to execute the IISRESET on ALL farm servers, so you might want to include another PAUSE there to allow admins to complete that step on all servers.

echo off

echo Adding solution to farm...

stsadm -o addsolution -filename customizations.wsp

echo Deploying solution...

stsadm -o deploysolution -name customizations.wsp -immediate -allowgacdeployment -allowcaspolicies -force -allcontenturls

echo Please navigate to "Central Administration > Operations > Solution Management" to confirm deployment.

echo Once Customizations.wsp indicates a status of 'Deployed', hit any key to proceed with the installation.

pause

echo Activating feature stapler...

stsadm -o activatefeature -name BrandingStapler -url https://sites.contoso.com

echo Resetting IIS...

iisreset /noforce

echo Updating branding on existing sites...

brandingupdate.exe

echo Branding installation complete. Please update web.config settings for branding redirects in all applicable web applications.

Uninstall Script

Note that you'll need to execute the IISRESET on ALL farm servers, so you'll need to perform that on any other WFEs after the script completes. You still need to manage the deployment of "12" directory replacements as described above, either manually or through other scripts.

echo off

echo Retracting solution...

stsadm -o retractsolution -name customizations.wsp -immediate -allcontenturls

echo Please navigate to "Central Administration > Operations > Solution Management" to confirm retraction.

echo Once Customizations.wsp indicates a status of 'Not Deployed', hit any key to proceed with the uninstall.

pause

echo Deleting solution...

stsadm -o deletesolution -name customizations.wsp

echo Resetting IIS...

iisreset /noforce

echo Branding uninstall complete.

echo Please update web.config on all applicable web applications to remove branding redirects.

echo Please restore all original "12" hive files from backup copies.