Branding a SharePoint site – Part 2

5 minute read

Last week I wrote the first article in a series of articles about branding your SharePoint site. In the first article we discussed how to brand your site and make it available within SharePoint with a Timer Job. In the following article we will make our theme the default theme by using Feature stapling and we will discuss a way to replace all the out of the box search images without replacing the default one in the 12 hive. To make our theme the default theme we will have to create two features. One feature (feature stapling) will activate a feature that activates our theme when a site is created. First we will create a feature to activate our theme. The feature.xml will look like this:

 <?xml version="1.0" encoding="utf-8"?>
 <Feature  Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e"
           Title="motion10 Theme Activator : motion10"
           Description="Feature that will activate the motion10 theme for the web. It will also replace the out of the box search images if you have jQuery enabled."
           Version="12.0.0.0"
           Hidden="FALSE"
           Scope="Web"
           DefaultResourceFile="core"
           ReceiverAssembly="Motion10.SharePoint.Theme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=09e87b786333535e"
           ReceiverClass="Motion10.SharePoint.Theme.ThemeActivator"
           Creator="Maik van der Gaag"
           ImageUrl="motion10/FeaturesIcon.png"
           ImageUrlAltText="http://www.motion10.com"
           xmlns="http://schemas.microsoft.com/sharepoint/">
   <Properties>
     <Property Key="Theme:TemplateID" Value="motion10"/>
   </Properties>
 </Feature>

The feature has a feature receiver that activates the theme that is defined in the feature property called “Theme:TemplateID”. The receiver will activate the theme on the web wherefore the feature is activated. On the deactivation of the feature it will deactivate our theme and activate the default theme.

public static readonly string keyTheme = "Theme:TemplateID";
 public override void FeatureActivated(SPFeatureReceiverProperties properties) {
    try {
         SPWeb web = (SPWeb)properties.Feature.Parent;
         string theme = properties.Definition.Properties[keyTheme].Value;

         if (web.Theme != theme) {
             web.ApplyTheme(theme);
             web.Update();
         }
    } catch {
          throw;
    }
 }

 public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
     try {
          SPWeb web = (SPWeb)properties.Feature.Parent;
          string theme = properties.Definition.Properties[keyTheme].Value;

          if (web.Theme == theme) {
              web.ApplyTheme("none");
              web.Update();
          }
     } catch {
        throw;
     }
 }

As you can see in the code the theme is activated with the ApplyTheme() method on the Web object that we retrieve from the feature properties. When we deactivate the feature we check if the current theme is our theme because we do not want to change the theme if the current theme is not the one we created. When we have created the feature it is time to create a feature with feature stapling. Feature stapling is a way to activate a feature when a site is created with a certain site template. First we create a feature.xml file with a scope of web application.

 <?xml version="1.0" encoding="utf-8"?>
 <Feature  Id="b84r13fd-b1ab-4d90-a2c1-9701g732cb5e"
           Title="motion10 Theme Activator by defailt : motion10"
           Description="Feature that will activate the motion10 theme by default when a site is created."
           Version="12.0.0.0"
           Hidden="FALSE"
           Scope="WebApplication"
           Creator="Maik van der Gaag"
           ImageUrl="motion10/FeaturesIcon.png"
           ImageUrlAltText="http://www.motion10.com"
           xmlns="http://schemas.microsoft.com/sharepoint/">
   <ElementManifests>
     <ElementManifest Location="elements.xml" />
    </ElementManifests>
 </Feature>

In the feature you can see that we defined an elements manifest. In the elements manifest we will define the feature stapling like this:

 <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <FeatureSiteTemplateAssociation Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e" TemplateName="STS#0" />
    <FeatureSiteTemplateAssociation Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e" TemplateName="STS#1" />
    <FeatureSiteTemplateAssociation Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e" TemplateName="STS#2" />
 </Elements>

The Id represents the feature Id of the feature that activates our theme. If you would like to make your theme the default theme for every site that is created within your web application you will have to insert a FeatureSiteTemplateAssociation with the template name “GLOBAL”:

 <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <FeatureSiteTemplateAssociation Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e" TemplateName="GLOBAL" />
 </Elements>

Note: If you use GLOBAL make sure you explicitly make a FeatureSiteTemplateAssociation for STS#1 because the blank site has an attribute “AllowGlobalFeatureAssociations” which is set to false.

Now we have discussed a way to make the theme the default theme we want the search images to be replaced with our own images without replacing the search image in the 12 hive. To accomplish this we will create a jQuery method that replaces the standard gosearch.gif with our own search image. For adding the jQuery method to all of the pages we will use the delegate control AdditionalPageHead to load our jQuery that we add trough a user control. All these components have to be packed into a feature, because we want a minimal count of features we extend our theme activation feature with an elements manifest.

 <?xml version="1.0" encoding="utf-8"?>
 <Feature  Id="b84c13fd-b1ab-4d90-a2c1-9701c732cb5e"
           Title="motion10 Theme Activator : motion10"
           Description="Feature that will activate the motion10 theme for the web. It will also replace the out of the box search images if you have jQuery enabled."
           Version="12.0.0.0"
           Hidden="FALSE"
           Scope="Web"
           DefaultResourceFile="core"
           ReceiverAssembly="Motion10.SharePoint.Theme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=09e87b786333535e"
           ReceiverClass="Motion10.SharePoint.Theme.ThemeActivator"
           Creator="Maik van der Gaag"
           ImageUrl="motion10/FeaturesIcon.png"
           ImageUrlAltText="http://www.motion10.com"
           xmlns="http://schemas.microsoft.com/sharepoint/">
   <ElementManifests>
     <ElementManifest Location="elements.xml"/>
   </ElementManifests>
   <Properties>
     <Property Key="Theme:TemplateID" Value="motion10"/>
   </Properties>
  </Feature>

In the elements file we insert our delegate control.

<?xml version="1.0" encoding="utf-8" ?>
 <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   <Control Id="AdditionalPageHead"
            ControlClass="Motion10.SharePoint.Theme.SearchImageReplacer"
            ControlAssembly="Motion10.SharePoint.Theme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=09e87b786333535e">
   </Control>
 </Elements>

In the control we place the library off jQuery on the page and also insert our own method. It will search for tags with the attribute src that end with gosearch.gif (default image) and replace the src attribute with our own ImageUrl. Besides that we remove the onmouseover and onmouseout attribute.

public class SearchImageReplacer : UserControl {

         public static readonly string ImageUrl = "/_layouts/images/motion10/Search/gosearch.gif";
         public static readonly string DefaultSearchImageUrl = "/_layouts/images/gosearch.gif";

         protected override void OnPreRender(EventArgs e) {
             base.OnPreRender(e);

             StringBuilder javascript = new StringBuilder();

             javascript.Append("if(typeof jQuery != \"undefined\") {");
             javascript.Append("$(function(){");
             javascript.Append("$(\"[src$='gosearch.gif']\").attr(\"src\", \" " + ImageUrl + " \").removeAttr(\"onmouseover\").removeAttr(\"onmouseout\")");
             javascript.Append("});");
             javascript.Append("}");