Automatically Applying SharePoint Themes
A recent issue I encountered is that SharePoint 2007 does not, by default, apply a parent site's theme to any newly created child sites. While this can be effective for a small departmental intranet, it is not effective for a highly governed enterprise intranet. When a user creates a site, it should automatically have the standard corporate theme applied to it.
So, where does that leave us? Thankfully you can wire in all sorts of custom code with SharePoint Features and Site Template Associations. In the rest of this post I will outline the steps required to solve the problem I mentioned, and you will find that these steps can apply to a multitude of related scenarios.
Step 1: The Theme Feature
Beause the SharePoint theme is not automatically propogated to any child sites, you will need to leverage custom code based on the SharePoint API to set the theme. To do this we will create a SharePoint Feature with an Event Receiver class that will perform the setting of the theme when the Feature is activated.
<Feature Id="477770BB-C970-41af-8374-56BEA2DC2245"
Title="My Corporate Theme"
Description="Applies the Corporate Theme to this site."
Version="1.0.0.0"
Scope="Web"
Hidden="false"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/"
ActivateOnDefault="true"
ReceiverAssembly="MyCorp.SharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d245b3ed42dababd"
ReceiverClass="MyCorp.SharePoint.MyCorpThemeEventReceiver"
>
<Properties>
<Property Key="DefaultTheme" Value="none" />
<Property Key="ThemeToApply" Value="Obsidian" />
</Properties>
</Feature>
The Feature element for this Feature is set to a Web scope. This is done because we want this Feature to be activatable with each site that is created, rather than only once as part of the site collection, web application, or farm.
An Event Receiver is also specified for this Feature. The Event Receiver is where the real action takes place, and I have outlined those details below.
You will notice that I have specified some properties for the Feature element. This allows me to dynamically provide the theme value instead of hardcoding it. Feature Properties can be used for any sort of dynamic Feature configuration, so make sure to keep them in mind when designing your Features.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
namespace MyCorp.SharePoint
{
public class MyCorpThemeEventReceiver : SPFeatureReceiver
{
#region Constants
private const string Property_DefaultTheme = "DefaultTheme";
private const string Property_ThemeToApply = "ThemeToApply";
#endregion
#region Abstract Methods
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
//throw new Exception("The method or operation is not implemented.");
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
//throw new Exception("The method or operation is not implemented.");
}
#endregion
#region Feature Receiver Methods
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
SPWeb web = properties.Feature.Parent as SPWeb;
if (site != null)
{
web = site.RootWeb;
}
if (web != null)
{
if (properties.Feature.Properties[Property_ThemeToApply] != null)
{
string theme = properties.Feature.Properties[Property_ThemeToApply].Value;
if (!string.IsNullOrEmpty(theme))
{
web.ApplyTheme(theme);
}
}
}
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
SPWeb web = properties.Feature.Parent as SPWeb;
if (site != null)
{
web = site.RootWeb;
}
if (web != null)
{
if (properties.Feature.Properties[Property_DefaultTheme] != null)
{
string theme = properties.Feature.Properties[Property_DefaultTheme].Value;
if (!string.IsNullOrEmpty(theme))
{
web.ApplyTheme(theme);
}
}
}
}
#endregion
}
}
The Event Receiver class outlined above performs a simple task. During activation it determines the Feature's parent (either an SPSite or SPWeb) and then sets a new theme for the associated SPWeb instance. Simple enough, right? Notice that on deactivation the reverse is performed. This provides for a clean removal of features, without leaving any orphaned settings.
At this point you have a SharePoint Feature that will apply a dynamically specified theme upon activation. This in itself isn't any more sophisticated than having a user select a theme themselves after creating a new site. Where it really becomes powerful is when we utilize Site Template Associations for activating this Feature when sites are created based on a specified site template.
Step 2: The Site Template Association Feature
Now that we have a Feature that will automatically apply a specified theme, what we need to do now is make sure that Feature is activated each time a site is created. To do this we will create another Feature for associating our Theme Feature with a few specific site templates.
<Feature Id="A138151F-2AF5-4a4d-890B-DD46B98302A3"
Title="My Corporate Theme Stapling"
Description="Staples the My Corporate Theme Feature to all sites."
Version="1.0.0.0"
Scope="Farm"
Hidden="true"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/"
ActivateOnDefault="true"
>
<ElementManifests>
<ElementManifest Location="SiteAssociations.xml" />
</ElementManifests>
</Feature>
The Feature element for this Feature is set to Farm scope. This is done because we want to associate our Theme Feature with all site templates across all web applications and site collections. This can be particularly powerful if you have multiple SharePoint applications running on the same infrastructure and you want them all to have the same theme applied to newly created sites.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- <FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="GLOBAL#0" /> -->
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSMSITE#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSPERS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="OSRV#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="STS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="STS#2" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="MPS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="MPS#1" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="MPS#2" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="MPS#3" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="MPS#4" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="WIKI#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="BLOG#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="BDR#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="EAWF#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="OFFILE#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="OFFILE#1" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="PWA#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="PWS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSMSITE#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSTOC#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSTOPIC#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSNEWS#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSNHOME#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSSITES#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSBWEB#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSCOMMU#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSREPORTCENTER#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SPSPORTAL#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="SRCHCEN#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="PROFILES#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="CMSPUBLISHING#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="BLANKINTERNET#0" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="BLANKINTERNET#1" />
<FeatureSiteTemplateAssociation Id="477770BB-C970-41af-8374-56BEA2DC2245" TemplateName="BLANKINTERNET#2" />
</Elements>
Notice that I have specified which site templates I would like to associate my Theme Feature with (the Id value is the Feature Id value of the Theme Feature). You can optionally provide the site template "GLOBAL#0" (commented out), which will then associate the Theme Feature with all site templates, rather than listing them all out. However, the downside with this approach is that the Theme Feature will *not* be automatically activated. SharePoint seems to only activate the Feature if you specify the site template specifically.
Step 3: Deploy!
I won't go into the details of deploying SharePoint Features as there are lots of articles based on that. Suffice to say, once you deploy these two new Features you will notice that your newly created sites have the specified theme applied to them.
Good luck!
Hi,
I've tried feature stapling to GLOBAL (and GLOBAL#0) but my feature is not activated automatically on new site creation?? The feature works when manually activated and has scope=Site. I've tried other template types such as BDR#0, STS#0 and MPS#0
Posted by: David | August 02, 2007 at 04:41 AM
Features cannot be automatically activated at Site/Web scope.
Which basically means that if you dont manually activate the feature when a site is created, your going to have to ask your users to do it for you.
Posted by: Tobias Andersen | September 28, 2007 at 07:08 AM
Graham,
thanks for your great article! I enjoyed reading it and had the features implemented in a couple of hours. Works like a charm!
Alex
Posted by: Alexander Groß | November 06, 2007 at 03:23 PM
excellent post, very useful
Posted by: sri | July 27, 2008 at 05:52 AM
Nice article, thanks for sharing!
Posted by: Dennis | October 09, 2008 at 08:20 AM
Thanks Dennis, you may also want to check out the follow-up article located at http://www.grahamzero.com/blog/2008/05/dynamic-staplin.html
Posted by: Graham Sibley | October 09, 2008 at 12:48 PM
The Feature is working fine. but when i create a new site in teh same site collection, the theme is not effected for the newly created site.
Can any one find out what have i missed?
Posted by: Anu | March 23, 2009 at 02:54 AM