In order to get a consistent look and feel across all the pages, we had to brand the LAYOUTS pages as well. Since we needed webapp-specific branding and didn’t want to deal with the additional effort involved in branding & testing SSPAdmin pages, I started out following what eventually came to be known as Method 2 from KB944105. After a bit of prototyping, it looks like we’ll be going with the HttpModule-based approach, plus a few exceptions handled by Method 1 from KB944105. Performance testing is coming up in the next couple of weeks, so I’ll be sure to share how that shakes out (Method 2 is our fallback if performance is a big drag).
Thanks to Liam Cleary, whose code gave me a huge kickstart on this solution.
The BrandingModule project consists of three components:
· ResourceRedirect Class – HTTPModule used to execute master page and other resource redirects
· RedirectSectionHandler Class – Configuration file section handler for processing ResourceRediect configuration settings
· Redirect Structure – Structure used to capture individual configuration setting elements for ResourceRedirect
The Redirect Structure is a simple structure for capturing configuration settings for all five types of redirects. Usage by type is detailed in the description.
Member
Type
Inheritance
Description
pattern
Property
n/a
· pageRedirects -Captures the CONTAINS pattern-match filter
· destinationRedirects - Captures the CONTAINS pattern-match filter
· pathRedirects - Captures the STARTS WITH pattern-match filter
· comboRedirects - Captures the STARTS WITH pattern-match filter
· masterRedirects – not used
masterPageUrl
· pageRedirects -identifies replacement master page
· destinationRedirects – not used
· pathRedirects - identifies replacement master page
· comboRedirects - identifies replacement master page
· masterRedirects – identifies replacement master page
originalMaster
· pageRedirects - identifies original master page used to identify a class of pages
· pathRedirects - not used
· comboRedirects - not used
· masterRedirects – identifies original master page used to identify a class of pages
destinationPageUrl
· pageRedirects - not used
· destinationRedirects – identifies redirect page
The RedirectSectionHandler is a custom implementation of the System.Configuration.IConfigurationSectionHandler class.
Create
Object
IConfigurationSectionHandler
Returns a collection of Redirect objects and performs minimal validation on entries. Throws a ConfigurationErrorsException if either of the following conditions is true:
· masterPageUrl and destinationPageUrl are blank/missing
· pattern and originalMaster are blank/missing
RecourceRedirect is a custom implementation of System.Web.IHttpModule.
Init
Method
IHttpModule
Adds the context_PreRequestHandlerExecute event handler to the PreRequestHandlerExecute event handler.
Dispose
Non-implemented stub.
context_PreRequestHandlerExecute
Adds the page_PreInit event handler to the PreInit event handler if the current handler is a page.
page_PreInit
Processes redirect instructions as specified in the configuration file. Redirects are processed in the following order:
1. Destination Redirects identified in the Branding/destinationRedirects configuration section.
2. Combination Redirects identified in the Branding/comboRedirects configuration section.
3. Path Redirects identified in the Branding/pathRedirects configuration section.
4. PageRedirects identified in the Branding/pageRedirects configuration section.
5. Master Page Redirects identified in the Branding/masterRedirects configuration section.
UpdateLog
Writes event log entries. Not used – intended for diagnostic purposes only.
The ResourceRedirect requires three sets of configuration settings to be registered in a web application:
· SectionGroup configuration
· HttpModule configuration
· Branding configuration
The custom section group and sections for the Branding configuration require registration in the web.config file. The <configSection/> element REQUIRES the following:
<sectionGroup name="Branding">
<section name="pageRedirects"
type="MOSS.Branding.RedirectSectionHandler,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee"/>
<section name="pathRedirects"
<section name="comboRedirects"
<section name="masterRedirects"
<section name="destinationRedirects"
</sectionGroup>
The ResourceRedirect module must be added to the <httpModules/> element to be registered with the web application. This element must contain the following entry:
<add name="ResourceRedirect" type="MOSS.Branding.ResourceRedirect,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee" />
The <Branding/> section group and all of its subsections are REQUIRED. Any or all of the subsections can be empty. A sample <Branding/> section group appears below:
<Branding>
<pageRedirects>
<redirect pattern="settings.aspx" masterPageUrl="~/_layouts/customizations/newapplication.master" />
<redirect pattern="error.aspx" masterPageUrl="~/_layouts/customizations/newsimple.master" />
</pageRedirects>
<destinationRedirects>
<redirect pattern="/_layouts/AccessDenied.aspx" destinationPageUrl="/_layouts/customizations/AccessDenied.aspx" />
</destinationRedirects>
<pathRedirects>
<redirect pattern="/sites/branding/_layouts/create.aspx" masterPageUrl="~/_layouts/customizations/newapplication2.master" originalMaster=""/>
</pathRedirects>
<comboRedirects>
<redirect pattern="/sites/branding/_layouts/" masterPageUrl="~/_layouts/customizations/newsimple2.master" originalMaster="simple.master"/>
</comboRedirects>
<masterRedirects>
<redirect masterPageUrl="~/_layouts/customizations/customsimple.master" originalMaster="simple.master"/>
<redirect masterPageUrl="~/_layouts/customizations/customapplication.master" originalMaster="application.master"/>
</masterRedirects>
</Branding>
We had three types of customizations to the “12” directory: modifications, new additions, and redirect additions.
The files listed below were actual customizations, subject to KB944105 Method 1.
Component
Location
Modification Purpose
SPTHEMES.XML
Defines the manifest of themes installed with SharePoint.
TEMPLATE\LAYOUTS\1033
Added a custom theme to the themes manifest.
NAVSHAPE.GIFFORMTITLEGRAD.GIFPAGETITLEBKGD.GIF
Background images typically appearing in the left and header areas of SharePoint content pages.
TEMPLATE\IMAGES
Replaced with images adhering to the branding look & feel guidelines to impose branding on non-customizable pages (specifically the “Operation in Progress” page).
ADDITION to this list as of 26JAN2008:
CORE.CSS
Core stylesheet for WSS 3.0.
LAYOUTS\1033\STYLES
Includes customizations to the search control styles, which cannot be overridden by a theme.
Take special note on the middle three files – you can’t directly modify the “Gears” page, so the only way of customizing it is indirectly via styles and changes to the graphics files it uses.
These are the all-new files that had no corresponding file in the OOTB “12” directory.
File name
Installed Location
Purpose
Notes
company.gif
IMAGES
Company standard logo (referenced in the custom default.master & MWSdefault.master).
Appears on all branded master pages.
themes.css
THEMES\CONTOSO
Defines the custom styles for the company theme.
Customized from the SharePoint standard “simple” theme.
mossExtension.css
???
Copied from the SharePoint standard “simple” theme.
CONTOSO.INF
Information file for the company standard theme.
navBullet_contoso.gif
Bullet icon for left navigation items.
Recolored version of navBullet_simple.gif from the SharePoint standard “simple” theme.
alldayOver_simple.gif
allday_simple.gif
ApplyFiltersActive.gif
ApplyFiltersHoverOver.gif
ApplyFiltersInactive.gif
formtitlegrad_simple.gif
linksectiongrad_simple.gif
listheadergrad_simple.gif
navBullet_simple.gif
pageTitleBKGD_simple.gif
partgrad_simple.gif
portaltabhover.gif
portaltabselected.gif
portraitbackground.gif
quickLaunchHeader_simple.gif
toolgrad_simple.gif
topnavhover_simple.gif
topnavselected_simple.gif
viewheadergrad_simple.gif
weekbox_simple.gif
Icons for the company theme.
theme.css
LAYOUTS\customizations
Duplicate of themes.css from THEMES\CONTOSO
Used to apply theme styles to pages that can't reference/access the theme.
Replacements/Redirects for the “12” Directory
These are all the files that required ResourceRedirect configurations.
Items in red font were added/updated 26JAN2008.
Original Location
Redirect Location
LAYOUTS\1033\STYLES\Customizations
NEW_application.master
LAYOUTS
LAYOUTS\Customizations
Master page for most non-dialog system pages.
NEW_simple.master
Non-themed master page for all nonsecured system pages.
Used to apply styles of company standard theme to simple.master, which cannot retrieve site theme due to security restrictions.
Duplicate of themes.css from THEMES\CONTOSO .
templatepick.aspx
Site Template selection page
Added reference to LAYOUTS\Customizations\theme.css
SiteManager.aspx
Site Content & Structure page
Added custom header and footer
The associated <Branding/> configuration for applying the appropriate redirects for these files appears below. We installed the ResourceRedirect on all of our end user-facing web applications, thus leaving Central Admin and SSP Admin as-is. This also gives us the flexibility to have different branding for our webapps down the road if necessary, since both the feature stapling and the HttpModule are configured at the WebApp level.
<pageRedirects/>
<redirect pattern="/_layouts/AdminRecycleBin.aspx" destinationPageUrl="/_layouts/customizations/AdminRecycleBin.aspx" />
<redirect pattern="/_layouts/osssearchresults.aspx" destinationPageUrl="/_layouts/customizations/osssearchresults.aspx" />
<redirect pattern="/_layouts/SiteManager.aspx" destinationPageUrl="/_layouts/customizations/SiteManager.aspx" />
<redirect pattern="/_layouts/templatepick.aspx" destinationPageUrl="/_layouts/customizations/templatepick.aspx" />
<pathRedirects/>
<comboRedirects/>
Updated 26JAN2006
The ResourceRedirect code appears below. Most of this is self-explanatory – especially if you read Liam Cleary’s article – but there are a few points worth calling out:
· The HttpContext.Current.Request.RawUrl is used for pattern matches. This is the only property that includes the relative site URL for LAYOUTS files (i.e. /sites/test/_layouts/settings.aspx instead of /_layouts/settings.aspx). Without this, site & site collection-specific LAYOUTS customizations would be impossible.
· The matchFound variable is essential for enforcing bailouts once a match is found, as per the “precedence” logic described earlier.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Configuration;
using System.Diagnostics;
namespace MOSS.Branding
{
public class ResourceRedirect : IHttpModule
public void Init(HttpApplication context)
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}
void context_PreRequestHandlerExecute(object sender, EventArgs e)
HttpApplication httpApp = sender as HttpApplication;
if (httpApp != null)
Page page = httpApp.Context.CurrentHandler as Page;
if (page != null)
page.PreInit += new EventHandler(page_PreInit);
void page_PreInit(object sender, EventArgs e)
Page page = sender as Page;
string currentMaster = String.Empty;
string currentPath = HttpContext.Current.Request.RawUrl.ToLower();
ArrayList masterRedirects = null;
ArrayList pageRedirects = null;
ArrayList pathRedirects = null;
ArrayList comboRedirects = null;
ArrayList destinationRedirects = null;
//retrieve remappings by type to enable enforcing precedence
masterRedirects = (ArrayList)ConfigurationManager.GetSection("Branding/masterRedirects");
pageRedirects = (ArrayList)ConfigurationManager.GetSection("Branding/pageRedirects");
pathRedirects = (ArrayList)ConfigurationManager.GetSection("Branding/pathRedirects");
comboRedirects = (ArrayList)ConfigurationManager.GetSection("Branding/comboRedirects");
destinationRedirects = (ArrayList)ConfigurationManager.GetSection("Branding/destinationRedirects");
bool matchFound = false;
IEnumerator redirectEnum = null;
if (destinationRedirects.Count > 0) //check for full redirect
redirectEnum = destinationRedirects.GetEnumerator();
while (redirectEnum.MoveNext())
Redirect destinationRedirect = (Redirect)redirectEnum.Current;
if (currentPath.Contains(destinationRedirect.pattern))
HttpContext.Current.Response.Redirect(currentPath.Replace(destinationRedirect.pattern,
destinationRedirect.destinationPageUrl));
matchFound = true;
if (page.MasterPageFile != null)
currentMaster = page.MasterPageFile.ToLower();
if ((comboRedirects.Count > 0) && (!matchFound))//check for combo remaps first
redirectEnum = comboRedirects.GetEnumerator();
Redirect comboRedirect = (Redirect)redirectEnum.Current;
if ((currentPath.StartsWith(comboRedirect.pattern)) && (currentMaster.Contains(comboRedirect.originalMaster)))
page.MasterPageFile = comboRedirect.masterPageUrl;
if ((pathRedirects.Count > 0) && (!matchFound)) //check for path-based remaps second
redirectEnum = pathRedirects.GetEnumerator();
Redirect pathRedirect = (Redirect)redirectEnum.Current;
if (currentPath.StartsWith(pathRedirect.pattern))
page.MasterPageFile = pathRedirect.masterPageUrl;
if ((pageRedirects.Count > 0) && (!matchFound)) //check for page-based remaps third
redirectEnum = pageRedirects.GetEnumerator();
Redirect pageRedirect = (Redirect)redirectEnum.Current;
if (currentPath.Contains(pageRedirect.pattern))
page.MasterPageFile = pageRedirect.masterPageUrl;
if ((masterRedirects.Count > 0) && (!matchFound)) //check for master page remaps last
redirectEnum = masterRedirects.GetEnumerator();
Redirect masterRedirect = (Redirect)redirectEnum.Current;
if (currentMaster.Contains(masterRedirect.originalMaster))
page.MasterPageFile = masterRedirect.masterPageUrl;
private void UpdateLog(string Message, EventLogEntryType msgType)
try
System.Diagnostics.EventLog.WriteEntry("ResourceRedirect", Message, msgType);
catch
//ignore
public void Dispose()