1

Feature for adding a Icon to your SharePoint farm

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);
                     }
                 }
                 else
                 {
                     if (!containsElement)
                     {
                         XElement iconElement = new XElement("Mapping");
                         iconElement.SetAttributeValue("Key", IconExtension);
                         iconElement.SetAttributeValue("Value", IconUrl);
                         doc.Element("DocIcons").Element("ByExtension").Add(iconElement);
                         doc.Save(SPDocIconFile);
                     }
                 }
             }
             else
             {
                 throw new FileNotFoundException("The DocIcon file does not exist on the server");
             }
         }
     }
 }

This Timer Job looks a lot like the Timer Job I created for installing a theme in the SharePoint farm (here). It basically does the same only with another file. In the ChangeDocIconFile() we load the document if it exists and search for the mapping we want by using a linq query. If the linq query has more than zero items we can delete it or we do not have to add it. When we are adding the section and the Linq query does not return any value we create a XElement mapping and add two attributes based on the properties in the feature definition. If you would like to try this out you can download the wsp package with a installer. Download: PDF Icon Integration

Related Posts

SharePoint Rest API Handler SharePoint contains a lot of Rest API’s that can be used for many scenario’s. You could use them for example in desktop and windows phone applications...
User automatically removed from SharePoint Group During my current project we received an access request from a user. We did what we normally do, we checked the request and added the user in the appr...
Warm Up Form Based Web Application Normal warm up scripts for SharePoint open a site object or web object and make a request to that specific site. When you use form based authenticatio...
Load modules by default when opening PowerShell At the moment we are working a lot with PowerShell in combination with SharePoint. Over time we created a lot of usable functions that were not availi...
PowerShell Scripts During my current project we are using a lot of PowerShell scripts to administrate the environment and make development easier for us. In this post...
TFS and SharePoint Builds including Automatic deployments – Part 1 Part 1 of this series will instruct how to configure your build server for building SharePoint projects. In Part 2 we will discuss the automatic deplo...

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.