Microsoft Playground

A Playground for Microsoft Technologies
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:

  1: <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.:

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

  1: //-----------------------------------------------------------------------
  2: // <copyright file="IconIntegration.cs" company="motion10">
  3: //     Copyright (c) motion10. All rights reserved.
  4: // </copyright>
  5: //----------------------------------------------------------------------- 
  6:  
  7: using System;
  8: using Microsoft.SharePoint;
  9: using Microsoft.SharePoint.Administration;
 10:  
 11: namespace Motion10.SharePoint.IconIntegration
 12: {
 13:     class IconIntegration: SPFeatureReceiver
 14:     {
 15:         /// <summary>
 16:         /// The name of the WSS Administration service
 17:         /// </summary>
 18:         private static readonly string serviceName = "WSS_Administration";
 19:  
 20:         public override void FeatureActivated(SPFeatureReceiverProperties properties)
 21:         {
 22:             RunJobNow(false, properties);
 23:         }
 24:  
 25:         public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
 26:         {
 27:             RunJobNow(true, properties);
 28:         }
 29:  
 30:         public override void FeatureInstalled(SPFeatureReceiverProperties properties)
 31:         {
 32:         }
 33:  
 34:         public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
 35:         {
 36:         }
 37:  
 38:         private void RunJobNow(bool delete, SPFeatureReceiverProperties prop)
 39:         {
 40:             if (prop == null)
 41:             {
 42:                 throw new ArgumentNullException("prop", "Argument 'prop' cannot be 'null'");
 43:             }
 44:  
 45:             SPFarm farm = prop.Definition.Farm;
 46:  
 47:             InstallIconTimerJob newJob = null;
 48:  
 49:             foreach (SPService service in farm.Services)
 50:             {
 51:                 if (service.Name == serviceName)
 52:                 {
 53:  
 54:                     newJob = new InstallIconTimerJob(service, prop.Definition.Id, delete);
 55:  
 56:                     SPJobDefinition def = service.GetJobDefinitionByName(newJob.Name);
 57:                     if (def != null)
 58:                     {
 59:                         def.Delete();
 60:                     }
 61:  
 62:                     break;
 63:                 }
 64:             }
 65:  
 66:             newJob.Schedule = new SPOneTimeSchedule(DateTime.Now.AddHours(-2.0));
 67:             newJob.Title = string.Format("{0} {1} Icon for Feature {2}", delete ? "Delete" : "Install", prop.Definition.Properties["IconExtension"], prop.Definition.Id);
 68:             newJob.Update();
 69:         }
 70:     }
 71: } 

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.

  1: //-----------------------------------------------------------------------
  2: // <copyright file="SPServiceExtensions.cs" company="motion10">
  3: //     Copyright (c) motion10. All rights reserved.
  4: // </copyright>
  5: //----------------------------------------------------------------------- 
  6:  
  7: using System;
  8: using System.Linq;
  9: using Microsoft.SharePoint.Administration;
 10:  
 11: namespace Motion10.SharePoint.IconIntegration{
 12:     /// <summary>
 13:     /// SPService extension methods
 14:     /// </summary>
 15:     public static class SPServiceExtensions {
 16:  
 17:         /// <summary>
 18:         /// Gets the jobdefintions by name.
 19:         /// </summary>
 20:         /// <param name="service">The service.</param>
 21:         /// <param name="name">The name.</param>
 22:         /// <returns>SPJobDefinition</returns>
 23:         /// <exception cref="System.ArgumentNullException">Exception is thrown when the service or name equal null</exception>
 24:         public static SPJobDefinition GetJobDefinitionByName(this SPService service, string name) {
 25:             if (service == null) {
 26:                 throw new ArgumentNullException("service", "Argument 'service' cannot be 'null'");
 27:             }
 28:  
 29:             if (String.IsNullOrEmpty(name)) {
 30:                 throw new ArgumentNullException("name", "Argument 'name' cannot be 'null' or 'String.Empty'");
 31:             }
 32:  
 33:             var query = from SPJobDefinition job in service.JobDefinitions
 34:                         where job.Name == name
 35:                         select job;
 36:  
 37:             return query.FirstOrDefault();
 38:         }
 39:     }
 40: } 

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.

  1:         private bool _delete
  2:         {
  3:             get
  4:             {
  5:                 if (this.Properties.ContainsKey(deleteKey))
  6:                 {
  7:                     return Convert.ToBoolean(this.Properties[deleteKey]);
  8:                 }
  9:                 else
 10:                 {
 11:                     return false;
 12:                 }
 13:             }
 14:             set
 15:             {
 16:                 if (this.Properties.ContainsKey(deleteKey))
 17:                 {
 18:                     this.Properties[deleteKey] = value.ToString();
 19:                 }
 20:                 else
 21:                 {
 22:                     this.Properties.Add(deleteKey, value.ToString());
 23:                 }
 24:             }
 25:         }
 26:  
 27:         private Guid _featureID
 28:         {
 29:             get
 30:             {
 31:                 if (this.Properties.ContainsKey(featureKey))
 32:                 {
 33:                     return new Guid(this.Properties[featureKey].ToString());
 34:                 }
 35:                 else
 36:                 {
 37:                     return Guid.Empty;
 38:                 }
 39:             }
 40:             set
 41:             {
 42:                 if (this.Properties.ContainsKey(featureKey))
 43:                 {
 44:                     this.Properties[featureKey] = value.ToString();
 45:                 }
 46:                 else
 47:                 {
 48:                     this.Properties.Add(featureKey, value.ToString());
 49:                 }
 50:             }
 51:  
 52:         }
 53:  
 54:         private string IconExtension
 55:         {
 56:             get
 57:             {
 58:                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
 59:                 return def.Properties["IconExtension"].Value;
 60:             }
 61:         }
 62:  
 63:         private string IconUrl
 64:         {
 65:             get
 66:             {
 67:                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
 68:                 return def.Properties["IconUrl"].Value;
 69:             }
 70:         }
 71:  
 72:  
 73:         public string SPDocIconFile
 74:         {
 75:             get
 76:             {
 77:                 return SPUtility.GetGenericSetupPath(filePath);
 78:             }
 79:         } 

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.

  1: //-----------------------------------------------------------------------
  2: // <copyright file="InstallIconTimerJob.cs" company="motion10">
  3: //     Copyright (c) motion10. All rights reserved.
  4: // </copyright>
  5: //----------------------------------------------------------------------- 
  6:  
  7: using System;
  8: using System.IO;
  9: using System.Linq;
 10: using System.Xml.Linq;
 11: using Microsoft.SharePoint.Administration;
 12: using Microsoft.SharePoint.Utilities;
 13:  
 14: namespace Motion10.SharePoint.IconIntegration
 15: {
 16:     public class InstallIconTimerJob : SPJobDefinition
 17:     {
 18:         /// <summary>
 19:         /// Filepath for the spthemes.xml file
 20:         /// </summary>
 21:         private readonly string filePath = "TEMPLATE\\XML\\DOCICON.XML";
 22:  
 23:         /// <summary>
 24:         /// The property key for the delete property
 25:         /// </summary>
 26:         private readonly string deleteKey = "Icon_Installation_Deletekey";
 27:  
 28:         /// <summary>
 29:         /// The property key for the feature property
 30:         /// </summary>
 31:         private readonly string featureKey = "Icon_Installation_Featurekey";
 32:  
 33:         private bool _delete
 34:         {
 35:             get
 36:             {
 37:                 if (this.Properties.ContainsKey(deleteKey))
 38:                 {
 39:                     return Convert.ToBoolean(this.Properties[deleteKey]);
 40:                 }
 41:                 else
 42:                 {
 43:                     return false;
 44:                 }
 45:             }
 46:             set
 47:             {
 48:                 if (this.Properties.ContainsKey(deleteKey))
 49:                 {
 50:                     this.Properties[deleteKey] = value.ToString();
 51:                 }
 52:                 else
 53:                 {
 54:                     this.Properties.Add(deleteKey, value.ToString());
 55:                 }
 56:             }
 57:         }
 58:  
 59:         private Guid _featureID
 60:         {
 61:             get
 62:             {
 63:                 if (this.Properties.ContainsKey(featureKey))
 64:                 {
 65:                     return new Guid(this.Properties[featureKey].ToString());
 66:                 }
 67:                 else
 68:                 {
 69:                     return Guid.Empty;
 70:                 }
 71:             }
 72:             set
 73:             {
 74:                 if (this.Properties.ContainsKey(featureKey))
 75:                 {
 76:                     this.Properties[featureKey] = value.ToString();
 77:                 }
 78:                 else
 79:                 {
 80:                     this.Properties.Add(featureKey, value.ToString());
 81:                 }
 82:             }
 83:  
 84:         }
 85:  
 86:         private string IconExtension
 87:         {
 88:             get
 89:             {
 90:                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
 91:                 return def.Properties["IconExtension"].Value;
 92:             }
 93:         }
 94:  
 95:         private string IconUrl
 96:         {
 97:             get
 98:             {
 99:                 SPFeatureDefinition def = Farm.FeatureDefinitions[_featureID];
100:                 return def.Properties["IconUrl"].Value;
101:             }
102:         }
103:  
104:  
105:         public string SPDocIconFile
106:         {
107:             get
108:             {
109:                 return SPUtility.GetGenericSetupPath(filePath);
110:             }
111:         }
112:  
113:         public InstallIconTimerJob()
114:             : base()
115:         {
116:         }
117:  
118:         public InstallIconTimerJob(SPService service, Guid featureID, bool delete)
119:             : base("Icon Installer", service, null, SPJobLockType.None)
120:         {
121:             this._delete = delete;
122:             this._featureID = featureID;
123:         }
124:  
125:         public override void Execute(Guid targetInstanceId)
126:         {
127:             base.Execute(targetInstanceId);
128:  
129:             this.ChangeDocIconFile();
130:         }
131:  
132:         private void ChangeDocIconFile()
133:         {
134:             string spThemesContent = string.Empty;
135:  
136:             if (File.Exists(SPDocIconFile))
137:             {
138:                 XDocument doc = XDocument.Load(SPDocIconFile);
139:  
140:                 var element = from b in doc.Element("DocIcons").Element("ByExtension").Elements("Mapping")
141:                               where b.Attribute("Key").Value.ToLower() == this.IconExtension.ToLower()
142:                               select b;
143:  
144:                 bool containsElement = (element != null && element.Count() > 0);
145:  
146:                 if (_delete)
147:                 {
148:  
149:                     if (containsElement)
150:                     {
151:                         element.Remove();
152:                         doc.Save(SPDocIconFile);
153:                     }
154:  
155:                 }
156:                 else
157:                 {
158:                     if (!containsElement)
159:                     {
160:                         XElement iconElement = new XElement("Mapping");
161:                         iconElement.SetAttributeValue("Key", IconExtension);
162:                         iconElement.SetAttributeValue("Value", IconUrl);
163:  
164:  
165:                         doc.Element("DocIcons").Element("ByExtension").Add(iconElement);
166:                         doc.Save(SPDocIconFile);
167:                     }
168:                 }
169:             }
170:             else
171:             {
172:                 throw new FileNotFoundException("The DocIcon file does not exist on the server");
173:             }
174:         }
175:     }
176: } 

This TimerJob looks a lot like the TimerJob 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:

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: (Required)

Maik van der Gaag

Maik van der Gaag
MCPD