SharePoint, Features and web.config modifications using SPWebConfigModification
SharePoint has a great way for deploying content and functionality using Windows SharePoint Services Solution Packages (WSP's). While developing a powerful new feature for SharePoint Publishing sites I had to deploy a HttpModule "the SharePoint" way. Building a HttpModule , a corresponding feature and the resulting WSP package is easy with our Macaw Solutions Factory. The actual logic in the Http Module and the feature is the difficult part. One of the things I had to do was to create a feature that registers a HTTPModule on feature activation, and removes it from the web.config on the feature deactivation. You can do this using the SPWebConfigModification class.
A good article on this topic is http://www.crsw.com/mark/Lists/Posts/Post.aspx?ID=32. It contains links to other posts as well.
The Microsoft documentation can be found at SPWebConfigModification Class (Microsoft.SharePoint.Administration), I wished I scrolled down before, because a lot of valuable information can be found in the Community Content of this page (keep scrolling!).
Anyway, it took quite some time to get my HttpModule to register/unregister correctly on activation/deactivation of my web application level feature. I post the code below so you have a head-start if you have to do something similar yourself.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
// namespace must be in the form <Company>.<Product>.<FunctionalArea>.SharePoint.Features.<FeatureName>.FeatureReceiver
namespace Macaw.WcmRia.Moss2007.DualLayout.SharePoint.Features.DualLayoutSupport.FeatureReceiver
{
/// <summary>
/// Add HttpModule registration to web.config of the web application
/// </summary>
class DualLayoutSupportFeatureReceiver : SPFeatureReceiver
{
private const string WebConfigModificationOwner = "Macaw.WcmRia.Moss2007.DualLayout";
private static readonly SPWebConfigModification[] Modifications = {
// For not so obvious reasons web.config modifications inside collections
// are added based on the value of the key attribute in alphabetic order.
// Because we need to add the DualLayout module after the
// PublishingHttpModule, we prefix the name with 'Q-'.
new SPWebConfigModification()
{
// The owner of the web.config modification, useful for removing a
// group of modifications
Owner = WebConfigModificationOwner,
// Make sure that the name is a unique XPath selector for the element
// we are adding. This name is used for removing the element
Name = "add[@name='Q-Macaw.WcmRia.Moss2007.DualLayout']",
// We are going to add a new XML node to web.config
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode,
// The XPath to the location of the parent node in web.config
Path = "configuration/system.web/httpModules",
// Sequence is important if there are multiple equal nodes that
// can't be identified with an XPath expression
Sequence = 0,
// The XML to insert as child node, make sure that used names match the Name selector
Value = "<add name='Q-Macaw.WcmRia.Moss2007.DualLayout' type='Macaw.WcmRia.Moss2007.DualLayout.Business.Components.HttpModule, Macaw.WcmRia.Moss2007.DualLayout.Business.Components, Version=1.0.0.0, Culture=neutral, PublicKeyToken=077f92bbf864a536' />"
}
};
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
}
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
if (webApp != null)
{
AddWebConfigModifications(webApp, Modifications);
}
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
if (webApp != null)
{
RemoveWebConfigModificationsByOwner(webApp, WebConfigModificationOwner);
}
}
/// <summary>
/// Add a collection of web modifications to the web application
/// </summary>
/// <param name="webApp">The web application to add the modifications to</param>
/// <param name="modifications">The collection of modifications</param>
private void AddWebConfigModifications(SPWebApplication webApp, IEnumerable<SPWebConfigModification> modifications)
{
foreach (SPWebConfigModification modification in modifications)
{
webApp.WebConfigModifications.Add(modification);
}
// Commit modification additions to the specified web application
webApp.Update();
// Push modifications through the farm
webApp.WebService.ApplyWebConfigModifications();
}
/// <summary>
/// Remove modifications from the web application
/// </summary>
/// <param name="webApp">The web application to remove the modifications from</param>
/// <param name="owner"Remove all modifications that belong to the owner></param>
private void RemoveWebConfigModificationsByOwner(SPWebApplication webApp, string owner)
{
Collection<SPWebConfigModification> modificationCollection = webApp.WebConfigModifications;
Collection<SPWebConfigModification> removeCollection = new Collection<SPWebConfigModification>();
int count = modificationCollection.Count;
for (int i = 0; i < count; i++)
{
SPWebConfigModification modification = modificationCollection[i];
if (modification.Owner == owner)
{
// collect modifications to delete
removeCollection.Add(modification);
}
}
// now delete the modifications from the web application
if (removeCollection.Count > 0)
{
foreach (SPWebConfigModification modificationItem in removeCollection)
{
webApp.WebConfigModifications.Remove(modificationItem);
}
// Commit modification removals to the specified web application
webApp.Update();
// Push modifications through the farm
webApp.WebService.ApplyWebConfigModifications();
}
}
}
}