Feature for adding a Icon to your SharePoint farm

8 minute read

On the internet you can find several articles about how to add an icon to the SharePoint farm.. Microsoft also has a KB article that describes how you can accomplish it.

The steps that have to be taken to add the a icon are (example with a pdf icon):

  • Copy the .gif file that you want to use for the icon to the following folder on the server:Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\Template\Images
  • Edit the Docicon.xml file to include the .pdf file name extension. To do so:a. Start Notepad, and then open the Docicon.xml file. The Docicon.xml file is located in one of the following folders on the server: Drive:\Program Files\Common Files\Microsoft Shared\Web server extensions\12\Template\Xml b. In the section of the Docicon.xml file, add an entry for the .pdf file name extension. To do so, add the following line, where NameofIconFile is the name of the .gif file:
<Mapping Key="pdf" Value="NameofIconFile.gif" />
  • Restart Microsoft Internet Information Services (IIS).

Reading those articles I thought by myself that there must be a way to take these steps without manually editing the files within the 12 hive. To accomplish you have to create a wsp package that exists out of an image and a feature. The feature will have a receiver that kicks off a timer job. The timer job will add or delete the mapping section depending on the activation or deactivation of the feature. The feature.xml will have to look something like this and has to have a scope of farm because you will change a file that is used by the complete farm.:

<?xml version="1.0" encoding="utf-8"?>
<Feature Id="5dfefa72-4a9b-4320-af45-03758c755079"
         Title="motion10 PDF Integration"
         Description="This feature will add the pdf icon to the farm."
         Version="12.0.0.0"
         Hidden="FALSE"
         Scope="Farm"
         DefaultResourceFile="core"
         ReceiverAssembly="Motion10.SharePoint.IconIntegration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d7298385728e744a" ReceiverClass="Motion10.SharePoint.IconIntegration.IconIntegration" xmlns="http://schemas.microsoft.com/sharepoint/"
         Creator="Maik van der Gaag"
         ImageUrl="motion10/FeaturesIcon.png"
         ImageUrlAltText="http://www.motion10.com">
 <Properties>
     <Property Key="IconExtension" Value="pdf"/> 
     <Property Key="IconUrl" Value="pdf_icon.png"/>
 </Properties> 
</Feature>

In the feature.xml file I defined two properties with these properties we create the mapping element in the docicon.xml file. Note: This feature can be used to add all kinds of different icons to the farm. If you would like to add another icon you have to edit the two properties. Like we discussed the feature will have a receiver that kicks of the timer job. To accomplish this we have to implement the FeatureActivated and FeatureDeactivating. On the activation we will start the timer job to add the mapping and on the deactivation we will delete it.

 using System;
 using Microsoft.SharePoint;
 using Microsoft.SharePoint.Administration;

 namespace Motion10.SharePoint.IconIntegration
 {
     class IconIntegration: SPFeatureReceiver
     {
         /// <summary>
         /// The name of the WSS Administration service
         /// </summary>
         private static readonly string serviceName = "WSS_Administration";

         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)
         {
         }

         private void RunJobNow(bool delete, SPFeatureReceiverProperties prop)
         {
            if (prop == null)
             {
                 throw new ArgumentNullException("prop", "Argument 'prop' cannot be 'null'");
             }

             SPFarm farm = prop.Definition.Farm;

             InstallIconTimerJob newJob = null;

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

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

                     SPJobDefinition def = service.GetJobDefinitionByName(newJob.Name);
                     if (def != null)
                     {
                         def.Delete();
                     }

                     break;
                 }
             }

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

In the RunJobNow() method we create a new instance of our timer job by creating it with two properties. These properties are our feature id and a boolean whether we want to delete the mapping or add it. We also created an extension method on the SPService for retrieving a job definition by name. Here we will retrieve our own job definition if it is available and delete it if it is because we can’t create a new instance of the timer job when it is already available.

using System;
 using System.Linq;
 using Microsoft.SharePoint.Administration;

 namespace Motion10.SharePoint.IconIntegration{
     /// <summary>
     /// SPService extension methods
     /// </summary>
     public static class SPServiceExtensions {

         /// <summary>;
         /// Gets the jobdefintions by name.
         /// </summary>;
         /// <param name="service">The service</param>;
         /// <param name="name">The name.</param>
         /// <returns>SPJobDefinition</returns>;
         /// <exception cref="System.ArgumentNullException">Exception is thrown when the service or name equal null</exception>
         public static SPJobDefinition GetJobDefinitionByName(this SPService service, string name) {
             if (service == null) {
                 throw new ArgumentNullException("service", "Argument 'service' cannot be 'null'");
             }

             if (String.IsNullOrEmpty(name)) {
                 throw new ArgumentNullException(&quot;name&quot;, &quot;Argument 'name' cannot be 'null' or 'String.Empty'&quot;);
             }

             var query = from SPJobDefinition job in service.JobDefinitions
                         where job.Name == name
                         select job;

             return query.FirstOrDefault();
         }
     }
 }

Now that the feature is finished we can create the timer job (if you want to read more about the creation of a timer job you can read the following article http://msdn.microsoft.com/en-us/library/cc406686.aspx). The timer job will have five properties.

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

         private Guid _featureID
         {
             get
             {
                 if (this.Properties.ContainsKey(featureKey))
                 {
                     return new Guid(this.Properties[featureKey].ToString());
                 }
                 else
                 {
                     return Guid.Empty;
                 }
             }
             set
             {
                 if (this.Properties.ContainsKey(featureKey))
                 {
                     this.Properties[featureKey] = value.ToString();
                 }
                 else
                 {
                     this.Properties.Add(featureKey, value.ToString());
                 }
             }

         }

         private string IconExtension
         {
             get
             {
                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
                 return def.Properties["IconExtension"].Value;
             }
         }

         private string IconUrl
         {
             get
             {
                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
                 return def.Properties["IconUrl"].Value;
             }
         }

         public string SPDocIconFile
         {
             get
             {
                 return SPUtility.GetGenericSetupPath(filePath);
             }
         }

We save the value of the _featureid and _delete property in the property bag of our timer job because we have to access the properties after we created a new instance of the timer job. The other properties will retrieve the attributes for the mapping from the feature definition based on the feature id we saved in the property bag. The SPDocIconFile will create the complete path to the file we need.

using System;
 using System.IO;
 using System.Linq;
 using System.Xml.Linq;
 using Microsoft.SharePoint.Administration;
 using Microsoft.SharePoint.Utilities;
 namespace Motion10.SharePoint.IconIntegration
 {
     public class InstallIconTimerJob : SPJobDefinition
     {
         /// <summary>
         /// Filepath for the spthemes.xml file
         /// </summary>
         private readonly string filePath = "TEMPLATE\\XML\\DOCICON.XML";
         /// <summary>
         /// The property key for the delete property
         /// </summary>
         private readonly string deleteKey = "Icon_Installation_Deletekey";
         /// <summary>
         /// The property key for the feature property
         /// </summary>
         private readonly string featureKey = "Icon_Installation_Featurekey";
         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());
                 }
             }
         }
         private Guid _featureID
         {
             get
             {
                 if (this.Properties.ContainsKey(featureKey))
                 {
                     return new Guid(this.Properties[featureKey].ToString());
                 }
                 else
                 {
                     return Guid.Empty;
                 }
             }
             set
             {
                 if (this.Properties.ContainsKey(featureKey))
                 {
                     this.Properties[featureKey] = value.ToString();
                 }
                 else
                 {
                     this.Properties.Add(featureKey, value.ToString());
                 }
             }
         }
         private string IconExtension
         {
             get
             {
                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
                 return def.Properties["IconExtension"].Value;
             }
         }
         private string IconUrl
         {
             get
             {
                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
                 return def.Properties["IconUrl"].Value;
             }
         }
         public string SPDocIconFile
         {
             get
             {
                 return SPUtility.GetGenericSetupPath(filePath);
             }
         }
         public InstallIconTimerJob()
             : base()
         {
         }
         public InstallIconTimerJob(SPService service, Guid featureID, bool delete)
             : base("Icon Installer", service, null, SPJobLockType.None)
         {
             this._delete = delete;
             this._featureID = featureID;
         }
         public override void Execute(Guid targetInstanceId)
         {
             base.Execute(targetInstanceId);
             this.ChangeDocIconFile();
         }
         private void ChangeDocIconFile()
         {
             string spThemesContent = string.Empty;
             if (File.Exists(SPDocIconFile))
             {
                 XDocument doc = XDocument.Load(SPDocIconFile);
                 var element = from b in doc.Element("DocIcons").Element("ByExtension").Elements("Mapping")
                               where b.Attribute("Key").Value.ToLower() == this.IconExtension.ToLower()
                               select b;
                 bool containsElement = (element != null && element.Count() > 0);
                 if (_delete)
                 {
                     if (containsElement)
                     {
                         element.Remove();
                         doc.Save(SPDocIconFile);
                     }