Branding a MOSS Corporate Intranet Portal, Part 3: LAYOUTS Pages
Introduction
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.
BrandingModule Project
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
Redirect Structure
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.
Public Members
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 |
Property |
n/a |
· pageRedirects -identifies replacement master page · destinationRedirects – not used · pathRedirects - identifies replacement master page · comboRedirects - identifies replacement master page · masterRedirects – identifies replacement master page |
originalMaster |
Property |
n/a |
· pageRedirects - identifies original master page used to identify a class of pages · destinationRedirects – not used · pathRedirects - not used · comboRedirects - not used · masterRedirects – identifies original master page used to identify a class of pages |
destinationPageUrl |
Property |
n/a |
· pageRedirects - not used · destinationRedirects – identifies redirect page · pathRedirects - not used · comboRedirects - not used · masterRedirects – not used |
RedirectSectionHandler class
The RedirectSectionHandler is a custom implementation of the System.Configuration.IConfigurationSectionHandler class.
Public Members
Member |
Type |
Inheritance |
Description |
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 |
ResourceRedirect Class
RecourceRedirect is a custom implementation of System.Web.IHttpModule.
Public Members
Member |
Type |
Inheritance |
Description |
Init |
Method |
IHttpModule |
Adds the context_PreRequestHandlerExecute event handler to the PreRequestHandlerExecute event handler. |
Dispose |
Method |
IHttpModule |
Non-implemented stub. |
context_PreRequestHandlerExecute |
Method |
n/a |
Adds the page_PreInit event handler to the PreInit event handler if the current handler is a page. |
page_PreInit |
Method |
n/a |
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.
|
Private Members
Member |
Type |
Inheritance |
Description |
UpdateLog |
Method |
n/a |
Writes event log entries. Not used – intended for diagnostic purposes only. |
Configuration Settings
The ResourceRedirect requires three sets of configuration settings to be registered in a web application:
· SectionGroup configuration
· HttpModule configuration
· Branding configuration
SectionGroup 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"
type="MOSS.Branding.RedirectSectionHandler,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee"/>
<section name="comboRedirects"
type="MOSS.Branding.RedirectSectionHandler,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee"/>
<section name="masterRedirects"
type="MOSS.Branding.RedirectSectionHandler,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee"/>
<section name="destinationRedirects"
type="MOSS.Branding.RedirectSectionHandler,MOSS.Branding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbbbccccddddeeee"/>
</sectionGroup>
HttpModule Configuration
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" />
Branding Configuration
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>
“12” Customizations and Branding Redirects
We had three types of customizations to the “12” directory: modifications, new additions, and redirect additions.
Changes to the “12” Directory
The files listed below were actual customizations, subject to KB944105 Method 1.
Component |
Description |
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.
NEW Additions to the “12” Directory
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 |
THEMES\CONTOSO |
??? |
Copied from the SharePoint standard “simple” theme. |
CONTOSO.INF |
THEMES\CONTOSO |
Information file for the company standard theme. |
Customized from the SharePoint standard “simple” theme. |
navBullet_contoso.gif |
THEMES\CONTOSO |
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 |
THEMES\CONTOSO |
Icons for the company theme. |
Copied from the SharePoint standard “simple” theme. |
ADDITION to this list as of 26JAN2008:
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<o:p></o:p>
These are all the files that required ResourceRedirect configurations.<o:p></o:p>
Items in red font were added/updated 26JAN2008.<o:p></o:p>
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.
<Branding>
<pageRedirects/>
<destinationRedirects>
<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" />
</destinationRedirects>
<pathRedirects/>
<comboRedirects/>
<masterRedirects>
<redirect masterPageUrl="~/_layouts/customizations/customsimple.master" originalMaster="simple.master"/>
<redirect masterPageUrl="~/_layouts/customizations/customapplication.master" originalMaster="application.master"/>
</masterRedirects>
</Branding>
Code Snippets and Additional Comments
ResourceRedirect.cs
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 (page != 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();
while (redirectEnum.MoveNext())
{
Redirect comboRedirect = (Redirect)redirectEnum.Current;
if ((currentPath.StartsWith(comboRedirect.pattern)) && (currentMaster.Contains(comboRedirect.originalMaster)))
{
page.MasterPageFile = comboRedirect.masterPageUrl;
matchFound = true;
}
}
}
if ((pathRedirects.Count > 0) && (!matchFound)) //check for path-based remaps second
{
redirectEnum = pathRedirects.GetEnumerator();
while (redirectEnum.MoveNext())
{
Redirect pathRedirect = (Redirect)redirectEnum.Current;
if (currentPath.StartsWith(pathRedirect.pattern))
{
page.MasterPageFile = pathRedirect.masterPageUrl;
matchFound = true;
}
}
}
if ((pageRedirects.Count > 0) && (!matchFound)) //check for page-based remaps third
{
redirectEnum = pageRedirects.GetEnumerator();
while (redirectEnum.MoveNext())
{
Redirect pageRedirect = (Redirect)redirectEnum.Current;
if (currentPath.Contains(pageRedirect.pattern))
{
page.MasterPageFile = pageRedirect.masterPageUrl;
matchFound = true;
}
}
}
if ((masterRedirects.Count > 0) && (!matchFound)) //check for master page remaps last
{
redirectEnum = masterRedirects.GetEnumerator();
while (redirectEnum.MoveNext())
{
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()
{
}
}
}
Comments
- Anonymous
November 13, 2007
If you're into MOSS customizations, check out Brett Geoffroy's MSDN blog. He's done a sweet job organizing - Anonymous
November 13, 2007
If you're into MOSS customizations, check out Brett Geoffroy's MSDN blog . He's done a sweet job organizing - Anonymous
December 16, 2007
Ein sehr ausführliche Best-Practice Anleitung zum Theme SharePoint Anpassung mit vielen Hintergrundinformation - Anonymous
December 16, 2007
Eine sehr ausführliche Best-Practice Anleitung zum Theme SharePoint Anpassung mit vielen Hintergrundinformationen - Anonymous
December 24, 2007
The comment has been removed - Anonymous
January 09, 2008
Eine sehr ausführliche Best-Practice Anleitung zum Theme SharePoint Anpassung mit vielen Hintergrundinformationen - Anonymous
February 27, 2008
Several folks have asked for this, so here it is... the two other pieces of the ResourceRedirect solution - Anonymous
February 27, 2008
Several folks have asked for this, so here it is... the two other pieces of the ResourceRedirect solution