Monday, September 21, 2009

Copy web.config settings using a SharePoint Feature

Scenario:
Copy web.config settings using a SharePoint Feature

Explanation:
For most of the projects, it is usually required to add few web.config entries.
Adding them in the WFE servers manually is prone to mistakes.

Deploying them as a feature ensures that its not only clean but also eases the job of administrators if a new WFE server is added in the future.

Lets take the scenario where we had to update web.config with entries for Telerik Controls

Code:
//Feature.xml

<?xml version="1.0" encoding="utf-8"?>
<Feature
Id="[ID]"
Title="Telerik Controls"
Description="This feature adds the required Telerik Web.Config entries and assosiated dlls"
Version="1.0.0.0"
Scope="WebApplication"
Hidden="false"
ImageUrl="TelerikControls\img.jpg"
ReceiverAssembly="TelerikControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d6ff03c5a94c295d"
ReceiverClass="TelerikControls.TelerikControlsFeatureReceiver"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementFile Location="Web.Config" />
</ElementManifests>
</Feature>


//Web.Config

<configuration>
<!-- SafeControls for Telerik -->
<SharePoint>
<SafeControls>
<SafeControl Assembly="Telerik.Web.Design, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Namespace="Telerik.Web.Design" TypeName="*" Safe="True" />
<SafeControl Assembly="Telerik.Web.UI, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Namespace="Telerik.Web.UI" TypeName="*" Safe="True" />
</SafeControls>
</SharePoint>
<system.web>
<httpHandlers>
<add verb="*" validate="false" path="Telerik.Web.UI.DialogHandler.axd" type="Telerik.Web.UI.DialogHandler, Telerik.Web.UI, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" />
</httpHandlers>
</system.web>
</configuration>


//FeatureReceiver

using System.Xml;

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

namespace TelerikControls
{
public class TelerikControlsFeatureReceiver : SPFeatureReceiver
{

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
string xPath;
SPWebApplication webApp;
WebConfigModifications webConfigMods;
XmlDataDocument xmlDoc;

webApp = (SPWebApplication)properties.Feature.Parent;
webConfigMods = new WebConfigModifications();

//load the web.config settings from the feature root
xmlDoc = new XmlDataDocument();
xmlDoc.Load(string.Format("{0}\\{1}", properties.Feature.Definition.RootDirectory, "web.config"));

//configuration/SharePoint/SafeControls
xPath = "configuration/SharePoint/SafeControls";
AddWebConfigNodes(xPath, ref xmlDoc, ref webConfigMods, ref webApp);

//configuration/system.web/httpHandlers
xPath = "configuration/system.web/httpHandlers";
AddWebConfigNodes(xPath, ref xmlDoc, ref webConfigMods, ref webApp);

//set full trust
SPWebConfigModification modification = new SPWebConfigModification("level", "configuration/system.web/trust");
modification.Owner = "WebConfigTrust";
modification.Sequence = 0;
modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;
modification.Value = "Full";
webApp.WebConfigModifications.Add(modification);

webApp.Farm.Servers.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();

}


private void AddWebConfigNodes(string xPath, ref XmlDataDocument xmlDoc, ref WebConfigModifications webConfigMods, ref SPWebApplication webApp)
{
foreach (XmlNode node in xmlDoc.SelectSingleNode(xPath))
if (node.NodeType != XmlNodeType.Comment)
webConfigMods.AddWebConfigNode(webApp, xPath, node, node.Attributes);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
WebConfigModifications webConfigMods = new WebConfigModifications();
webConfigMods.RemoveWebConfigNodes(webApp);
webApp.Farm.Servers.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();
}

public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
/* no op */
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
/* no op */
}
}
}


//Helpers

using System.Xml;
using System.Text;
using Microsoft.SharePoint.Administration;
using System.Collections.ObjectModel;

namespace TelerikControls
{
public class WebConfigModifications
{

public string Owner
{
get
{
return this.GetType().FullName;
}
}

private string GetWebConfigModName(string nodeName, XmlAttributeCollection attributes)
{
StringBuilder webConfigModName = new StringBuilder(nodeName);


foreach (XmlAttribute attribute in attributes)
{
webConfigModName.Append(string.Format("[@{0}=\"{1}\"]", attribute.Name, attribute.Value));
}


return webConfigModName.ToString();
//return string.Format("add[@key=\"{0}\"]", key);

}

private string GetWebConfigModValue(string nodeName, XmlAttributeCollection attributes)
{
XmlDataDocument xDoc = new XmlDataDocument();
XmlAttribute newAttribute;
XmlNode modValueNode = xDoc.AppendChild(xDoc.CreateElement(nodeName));
foreach (XmlAttribute attribute in attributes)
{
newAttribute = xDoc.CreateAttribute(attribute.Name);
newAttribute.Value = attribute.Value;
modValueNode.Attributes.Append(newAttribute);
}
return string.Format("{0}\n", modValueNode.OuterXml);
}

private string GetFirstNodeName(ref XmlDataDocument xDoc)
{
string xPath = xDoc.FirstChild.Name;

//we don't want to process the xml declaration node
if (xPath == "xml")
{
if (xDoc.FirstChild.NextSibling != null)
{
xPath = xDoc.FirstChild.NextSibling.Name;
}
else
{
xPath = string.Empty;
}
}

return xPath;
}




/// <summary>
/// Adds the key/value pair as an appSettings entry in the web application's
/// SPWebConfigModification collection
/// </summary>
/// <param name="webApp">Current web application context</param>
/// <param name="key">appSettings node key</param>
/// <param name="value">appSettings node value</param>
public void AddWebConfigNode(SPWebApplication webApp, string webConfigModxPath, XmlNode node, XmlAttributeCollection attributes)
{
SPWebConfigModification webConfigMod;
string webConfigModName;

webConfigModName = GetWebConfigModName(node.Name, attributes);
webConfigMod = new SPWebConfigModification(webConfigModName, webConfigModxPath);
webConfigMod.Owner = this.Owner;
webConfigMod.Sequence = 0;
webConfigMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
webConfigMod.Value = node.OuterXml;

webApp.WebConfigModifications.Add(webConfigMod);
}

/// <summary>
/// Removes the key from the appSettings the web application's
/// SPWebConfigModification collection
/// </summary>
/// <param name="webApp">Current web application context</param>
/// <param name="key">appSettings node key</param>
public void RemoveWebConfigNodes(SPWebApplication webApp)
{
Collection<SPWebConfigModification> collection;
SPWebConfigModification[] tempCollection;
string webConfigModName;
int iStartCount;
SPWebConfigModification webConfigMod;

collection = webApp.WebConfigModifications;
tempCollection = new SPWebConfigModification[collection.Count];
collection.CopyTo(tempCollection, 0);
iStartCount = collection.Count;

// Remove any modifications that were originally created by the owner.
for (int c = iStartCount - 1; c >= 0; c--)
{
webConfigMod = collection[c];

if (webConfigMod.Owner == this.Owner)
collection.Remove(webConfigMod);
}
}
}
}

2 comments:

  1. What Telerik has to do with this ?

    ReplyDelete
  2. @ Sandeep K Nahta.. It is quite obvious that I just gave an example of a real time scenario. In this case, it happened to be the config entries that need to be added for Telerik controls

    ReplyDelete