Category Archives: Branding

Branding a SharePoint site – Part 2

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("}");

             this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "ReplaceSearchImage", javascript.ToString(), true);
         }
     }

Note: If you want to use jQuery in a production environment you have to think about a method for adding the jQuery Library : “ ”. Because when someone creates a webpart that uses jQuery they will insert the declaration into the page. When another user also uses jQuery they will do the same and then you will have two declaration of the jQuery library on the page.

To solve this you can create a feature with a delegate control that inserts the jQuery library into the page. And let the feature you are creating that uses jQuery have an activation dependency to the jQuery feature. If there is enough interest in an article that will explain the above in detail I will write a Branding your SharePoint site – Part 3 so let me know.

Branding a SharePoint site – Part 1

Within SharePoint and WSS you have the possibility to brand your site. You can do this on several ways:

  • A custom style sheet.
  • A custom theme.

I think the best way to brand your SharePoint site is to create a custom theme.

You can create a theme by copying one of the themes folders in the “C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\THEMES” directory. When you have a copy of a directory you have to rename it to whatever you like.

In this article we will use as example a copy of the “Simple” theme and rename it to “motion10”.

In the directory you have three files that are necessary to create a theme. The images can be deleted.

INF file
In this file you can find information about your theme. The file contains two sections the [info] section and the [titles] section.

In the [info] section information is given about the theme. In this section we have to change the title from “simple” to “motion10”.

In the [titles] section the title of the theme is given for each language code besides the title for the language code 1033. This title is saved into SPThemes.xml file (This file will be discussed later in this article).

After we made the changes to the file we save and rename it. You have to give it the same name as your folder. We will rename it from “Simple.inf” to “motion10.inf”.

Theme.css
The theme.css file is the style sheet file off the theme. In this file you will have to define your own styling.

mossExtension.css
The mossExtension.css is an extension file for the Theme.css file. When the Theme.css file is loaded it will be extended with the content of this file.

When you want to create a custom theme you can use several tools to help you edit the theme.css file. One of my favorites is the Web Developers tool in Internet Explorer 8. You can use this tool by opening your SharePoint site and selecting: “Tools – Developer Tools” or just press F12.

This will open a window like below. If you select the arrow at the top of the page you are able to select a section of your site and see the css styling of it.

developertoolsselected

On my SharePoint site I have selected the site title. On the right side of the developer tool you can see the styling of the site title. Every item that is stripped away will not be used so you can see that the colour of the title is changes to #0066a4 in moti1011-65001.css. This file is the theme.css file in our motion10 theme folder.

When you are done with the creation of the theme you have to make it available within the SharePoint farm. The theme can be added by updating the “SPThemes.xml” file within the following directory:

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\1033

If you examine the file you can see that every theme has its own section. You have to add a section for your theme. You can do this by copying one of the Templates nodes and pasting it into the file and change the following properties:

  • TemplateID: The ID of your template. This has be the same name as your folder. In our example it will be “motion10”.
  • DisplayName: This is the display name for your theme.
  • Description: This is the description of your theme.
  • Thumbnail: This is the URL to a picture that represents your theme. This will be shown on the themes page. Example: “images/motion10/theme/theme.jpg”.
  • Preview: This is the URL to a picture that represents your theme. Example: “images/motion10/theme/theme.jpg”.

When you save the file the theme will be available in the SharePoint Farm.

Important: When you use multiple servers in the SharePoint farm you will have to change the SPThemes file on every server. You also have to create the themes folder on every server.”

Manually editing of files in the farm is not desired and support. To solve this you have to create a solution using WSP Builder and add your Theme folder to the right path in your solution. You also have to make changes to the “SPThemes.xml” so you also add this file to your solution. You will have a problem with this because you have the complete file and extend it with you section. When you deploy your solution the out of the box file will be overwritten. But what happens when another SharePoint developer also makes a theme en deploys it to the server. He will also overwrite the file with the result that your theme will be deleted.

To work around this problem you have to create a farm feature that will kick off a timer job to alter the SPThemes file.

First we have to create a farm scoped feature that has a feature receiver.

 <?xml version="1.0" encoding="utf-8"?>
 <Feature  Id="e0c634c2-5378-4fe6-927b-03aa895b5d8d"
               Title="motion10 Theme Installer : motion10"
               Description="Feature for adding the motion10 theme to the SharePoint Farm."
               Version="12.0.0.0"
               Hidden="FALSE"
               Scope="Farm"
               DefaultResourceFile="core"
               ReceiverAssembly="Motion10.SharePoint.Theme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=09e87b786333535e"
               ReceiverClass="Motion10.SharePoint.Theme.ThemeInstaller"
               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" />
     <Property Key="Theme:DisplayName" Value="motion10" />
     <Property Key="Theme:Description" Value="Theme for enabling the motion10 styling." />
     <Property Key="Theme:Thumbnail" Value="images/motion10/theme/theme.jpg" />
     <Property Key="Theme:Preview" Value="images/motion10/theme/theme.jpg" />
   </Properties>
 </Feature> 

In the properties section of the feature we have defined 5 properties. These properties represent the values that you have to fill into the SPThemes.xml.

In the SPFeatureReceiver we will start the timer job to insert the values into the file.

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
         RunJobNow(false, properties);
 }

 public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
         RunJobNow(true, properties);
 }

 public override void FeatureInstalled(SPFeatureReceiverProperties properties) {
 }

 public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {
 } 

In the FeatureActivated and FeatureDeactivating event we call a method called “RunJobNow” with two properties. These properties represent a boolean if the section must be deleted and the property bag of the feature for retrieving the property values.

public void RunJobNow(bool delete, SPFeatureReceiverProperties prop) {

    SPFarm farm = prop.Definition.Farm;

    InstallThemeTimerJob newJob = null;

    foreach (SPService service in farm.Services) {
       if (service.Name == serviceName) {

          newJob = new InstallThemeTimerJob(service, prop.Definition.Id, delete);

          foreach (SPJobDefinition def in service.JobDefinitions) {
             if (def.Name == newJob.Name) {
                def.Delete();
             }
          }
         break;
       }
    }

    newJob.Schedule = new SPOneTimeSchedule(DateTime.Now.AddHours(-2.0));
    newJob.Title = string.Format("{0} Theme {1} for Feature {2}", delete ? "Delete" : "Install", prop.Definition.Properties["Theme:TemplateID"], prop.Definition.Id);
    newJob.Update();
 }

The RunJobNow method will find a service called “WSSAdministration” on the farm. We need to find a service because the timer job needs to be attached to a service. By creating a new InstallThemeTimerJob object you create a new timer job in SharePoint. By adding the Schedule to the InstallThemeTimerJob object you can ensure that the job will run immediately.

When we create a new InstallThemeTimerJob object you can see that we have to define three properties. These properties must be defined to let the timer job know what to do.

When we have finished the feature it is time to create the timer job. In the SPJobDefinition we have four read only strings that define some values we need:

  • filePath: The path to the SPThemes.xml file : “TEMPLATE\\LAYOUTS\\1033\\SPTHEMES.XML”
  • deleteKey: The key of the property in the property bag.
  • featureKey: The key of the property in the property bag.
  • documentNameSpace: The namespace of the xml document.

Within the timer job we have five properties to retrieve the properties from the feature definition. These properties need to be retrieved for adding our Templates section to the SPThemes file.

 public string TemplateID {
    get {
       SPFeatureDefinition def = Farm.FeatureDefinitions[this._featureID];

       SPFeatureProperty prop = def.Properties["Theme:TemplateID"];

       return prop.Value;
    }
 }

For the initiation of our timer job we will have to insert two properties in the property bag. These properties are “_delete” and “_featureid”.

private readonly string deleteKey = "C11B44D604004a1d9CD9D0CAB326373F_Delete";

 private bool _delete {
    get {
       if (this.Properties.ContainsKey(deleteKey)) {
          return Convert.ToBoolean(this.Properties[deleteKey]);
       } else {
          return false;
       }
    }
    set {
       if (this.Properties.ContainsKey(deleteKey)) {
          this.Properties[deleteKey] = value.ToString();
       } else {
          this.Properties.Add(deleteKey, value.ToString());
       }
    }
 } 

For the creation of a timer job you also need to define a LockType. This LockType is very important because the timer job has to be run on every server in the farm. For accomplishing this we will have to use SPLockType.None ( http://www.shillier.com/archive/2009/05/01/where-is-my-timer-job.aspx)

 public InstallThemeTimerJob() {
 }

 public InstallThemeTimerJob(SPService service, Guid featureID, bool delete)
    : base("Themes Installer TimerJob", service, null, SPJobLockType.None) {

    //save the properties
    this._delete = delete;
    this._featureID = featureID;
 }

On the execution of the timer job we will execute a method called ChangeThemesFile(). Within this method we will add or delete a section in the SPThemes file that is defined in the properties of the feature definition.

private readonly string filePath = "TEMPLATE\\LAYOUTS\\1033\\SPTHEMES.XML";
 private readonly string documentNameSpace = "http://tempuri.org/SPThemes.xsd";

 public override void Execute(Guid targetInstanceId) {
       base.Execute(targetInstanceId);

       this.ChangeThemesFile();
 }

  private void ChangeThemesFile() {

    XNamespace ns = documentNameSpace;
    string spThemesContent = string.Empty;

    XDocument doc = XDocument.Load(SPThemesFile);

    var element = from b in doc.Element(ns + "SPThemes").Elements(ns + "Templates")
                       where b.Element(ns + "TemplateID").Value == this.TemplateID
                       select b;

    bool containsElement = (element != null && element.Count() > 0);

    if (_delete) {
       if (containsElement) {
         element.Remove();
         doc.Save(SPThemesFile);
       }
    } else {
       if (!containsElement) {
          XElement spThemesElement = new XElement(ns + "Templates", new XElement(ns + "TemplateID", this.TemplateID),
                                             new XElement(ns + "DisplayName", this.ThemeDisplayName),
                                             new XElement(ns + "Description", this.Description),
                                             new XElement(ns + "Thumbnail", this.Thumbnail),
                                             new XElement(ns + "Preview", this.Preview));

           doc.Element(ns + "SPThemes").Add(spThemesElement);
           doc.Save(SPThemesFile);
      }
    }
 } 

In the method we use Linq to find an element based on the TemplateID we have defined in the feature.xml. If the element count is 0 the element is not in the SPThemes file and can be added to the file by creating a new XElement with the properties from the feature.

In part 2 we will discuss the point of activating the theme by default and creating a feature to activate the theme. Besides that we will also discuss a way to replace all the out of the box search images without replacing the default one in the 12 hive.

ASP:Menu in IE8

Since a few days I have installed Internet Explorer 8 on my computer and I was curious how it would display certain sites. Till a few hours ago everything looked nice.

When navigating to an internet site we had just developed for one of our clients I noticed a strange thing. On the default page we added a custom control that inherits from the ASP Menu. The hover of that menu wasn’t working anymore in IE8.

After searching on Google I found an solution for this problem ( here ) and I also found another possible solution (here tanks to Mark Hildreth’s). The problem is caused by IE8 because it does not handle the z-index off the page right. You can fix this problem by doing the following:

Create a new class in your CSS file or place it in your web page:

 .aspmenufix
 {
     z-index: 1;
 }

In the ASP menu control you have to add the following:

<asp:Menu ID="Menu1" runat="server">
    <DynamicMenuStyle CssClass="aspmenufix" />
</asp:Menu>

I hope this will help everyone who uses the ASP Menu in his website!

Inserting initial information in a site definition

For a current project I had to figure out how to place information in a list in SharePoint when you are creating a new site from a site template and place documents in a document library.

After a lot of searching on the internet I found some helpful stuff. Placing initial information in a list can also be found on the MSDN site.

First step is to create a new site definition. This can be done by copying an out of the box Template and adjusting it the way you would like it to be. More information about creating a site definition you can find at:

http://www.sharepointblogs.com/tbaginski/archive/2007/08/16/creating-a-custom-site-definition-in-wss-v3-moss.aspx

This can also be a helpful link:

http://blah.winsmarts.com/2006-12-Registering_your_custom_site_definitions_in_SharePoint_2007.aspx

The way for placing initial information in a SharePoint list is done by finding the right list in the onet.xml were in you would like to place the information.

For example you have to find a tag in your ONET.xml like the one below:

 <!--List information-->
 <List FeatureId="1BFC6CD6-4941-4964-A143-25E62AAEF642" Type="107" Title="Taken" Url="Lists/Taken" QuickLaunchUrl="Lists/Taken/AllItems.aspx" ></List>

For placing information in that list you have to know the columns that that list uses and place it between the <list> and </list> with the <data> tag.

<!--Inserting initial information-->
 <List FeatureId="1BFC6CD6-4941-4964-A143-25E62AAEF642" Type="107" Title="Taken" Url="Lists/Taken" QuickLaunchUrl="Lists/Taken/AllItems.aspx" >
 <Data>
  <Rows>
   <Row>
    <Field Name="Title">Task 1</Field>
   </Row>
   <Row>
    <Field Name="Title">Task 2</Field>
   </Row>
   <Row>
    <Field Name="Title">Task 3</Field>
   </Row>
   <Row>
    <Field Name="Title">Task 4</Field>
   </Row>
   <Row>
    <Field Name="Title">Task 5</Field>
   </Row>
  </Rows>
 </Data>
 </List>

This example will place five new lines in the Task list with all different titles. If you would like to fill in more items for a task, you would just have to add more field items between a row tag. Placing documents into a site is done a little bit different.

For placing these documents in the right document library you have to add a folder to you site definition with a name you are just thinking off. For this example I choice the folder name doctemp. To place the documents into a document library you have to make a module in the onet.xml file. And add it to the right configuration.

WordDocuments is the name I created for my module. You have to specify the module at the end of the onet.xml between the <modules> tag just before the end tag of projects. The module you could enter there could be something like this:

 <!--Inplementing module-->
 <Module Name="WordDocuments" List="101" Url="Documents" Path="docTemp">
 <File Name="Test.doc"  Url="Test.doc" Type="GhostableInLibrary" />
 </Module>

The explanation of the used properties:

  • Name: The name of the module.
  • List: The list type of the list were you would like to insert the documents.
  • Url: The URL to the list were you would like to insert the documents.
  • Path: The path to the files
  • File Name: The file name you would like to use.
  • File Url: The file name you would like to insert.
  • Type GhostableInLibrary: tells SharePoint to put an entry in the document library for this file, but this file will not be stored in the database it will stay on the file system.

So now you are ready to make a site template with initial information.