Wesley Bakker

Interesting things I encounter doing my job...

Sponsors

News

Wesley Bakker
motion10
Rivium Quadrant 151
2909 LC Capelle aan den IJssel
Region of Rotterdam
The Netherlands
Phone: +31 10 2351035

(feel free to chat with me)
 

Add to Technorati Favorites

Web.config modifications with a SharePoint feature

How to modify your web.config in SharePoint.

As with many things the modification of your web.config on a SharePoint webfarm is a little bit more daunting than in a standard ASP.Net web application. There are some interesting cases where you would like to modify your web.config simply by a feature. In this post I'll make your life a little easier by showing you the code for an abstract base class that can be used for your feature recievers and two cases where I used this technique.

Feature Recievers

First I'll explain what a feature reciever is. A feature reciever is a class that gets called by a SharePoint feature on 4 specific events. These events are:

  • FeatureInstalled
  • FeatureUninstalling
  • FeatureActivated
  • FeatureDeactivating

This class must inherit from the base class: SPFeatureReceiver. You override 4 methods which gives you the power to react on feature events. In our case we'll concentrate on the FeatureActivated and FeatureDeactivating. On FeatureActivated we'll modify the web.config and on FeatureDeactivating we'll revert our changes.

Debug Switch

Each and every SharePoint developer knows that if you want to be able to debug your code you must make some modifications to your web.config.

  • configuration/SharePoint/SafeMode[@CallStack]="true"
  • configuration/system.web/customErrors[@mode]="Off"
  • configuration/system.web/compilation[@debug]="true"

We can modify this manually each and every time but would it be nice if we could simply activate our Debug Switch web application feature? Fortunately we can by writing just a few lines of code.

SPWebConfigModification

A simple way of modifying your SharePoint web.config is by using the SPWebConfigModification class. We can add SPWebConfigModifications to the WebConfigModifications of a SPWebApplication and call ApplyWebConfigModifications of the WebService property of SPWebApplication. If we then call Update() on the SPWebApplication our changes are persisted. Just like this:

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
    SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;

    foreach (SPWebConfigModification modification in this.Modifications) {
        webApp.WebConfigModifications.Add(modification);
    }

    webApp.WebService.ApplyWebConfigModifications();
    webApp.Update();
}

In this code sample the 'this.Modification' is a property that returns an array of SPWebConfigModification. The array is declared like this:

private static SPWebConfigModification[] modifications = {
            new SPWebConfigModification("CallStack", "configuration/SharePoint/SafeMode")
                { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" },
            new SPWebConfigModification("mode", "configuration/system.web/customErrors")
                { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Off" },
            new SPWebConfigModification("debug", "configuration/system.web/compilation")
                { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" }
};

All these modifications are added to the 'WebConfigModifications' collection and persisted by calling 'ApplyWebConfigModifications()' and 'Update()'.

Revert modifications

As you can see an SPWebConfigModification has an owner defined and this enables rollback of YOUR changes. This is great news since we need to have that option on FeatureDeactivating. The code again is very straightforward:

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
    SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
    
    foreach (SPWebConfigModification modification in this.Modifications.Reverse()) {
        webApp.WebConfigModifications.Remove(modification);
    }

    webApp.WebService.ApplyWebConfigModifications();
    webApp.Update();
}

We simply reverse the order of the modifications and instead of adding the modifications, we remove them. That's sounds pretty logical doesn't it?

Generic

This code can be used for other features as well. How about a Strict xHtmlConformance feature?

private static SPWebConfigModification[] modifications = {
            new SPWebConfigModification("xhtmlConformance", "configuration/system.web")
                { Owner = "motion10XhtmlConformance", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode, Value = "<xhtmlConformance />" },
            new SPWebConfigModification("mode", "configuration/system.web/xhtmlConformance")
                { Owner = "motion10XhtmlConformance", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Strict" }
};

So we need our code to be a little more generic. The only thing that changes is the modifications array. So I came up with the following abstract class.

//-----------------------------------------------------------------------
// <copyright file="WebConfigModificationFeatureReciever.cs" company="motion10">
//     Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

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

namespace Motion10.SharePoint2007 {
    /// <summary>
    /// The WebConfigModificationFeatureReceiver class is the abstract base class for Feature Recievers that modify the web.config
    /// </summary>
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    public abstract class WebConfigModificationFeatureReceiver : SPFeatureReceiver {
        /// <summary>
        /// Initializes a new instance of the <see cref="WebConfigModificationFeatureReceiver"/> class.
        /// </summary>
        protected WebConfigModificationFeatureReceiver() : base() { }

        /// <summary>
        /// Gets the modifications to apply to the web.config.
        /// </summary>
        /// <value>The modifications.</value>
        protected abstract SPWebConfigModification[] GetModifications();

        private SPWebConfigModification[] Modifications {
            get {
                return this.GetModifications();
            }
        }

        /// <summary>
        /// Occurs after a Feature is activated.
        /// </summary>
        /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties) {
            SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;

            foreach (SPWebConfigModification modification in this.Modifications) {
                webApp.WebConfigModifications.Add(modification);
            }

            webApp.WebService.ApplyWebConfigModifications();
            webApp.Update();
        }

        /// <summary>
        /// Occurs when a Feature is deactivated.
        /// </summary>
        /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
            SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
            
            foreach (SPWebConfigModification modification in this.Modifications.Reverse()) {
                webApp.WebConfigModifications.Remove(modification);
            }

            webApp.WebService.ApplyWebConfigModifications();
            webApp.Update();
        }

        /// <summary>
        /// Occurs after a Feature is installed.
        /// </summary>
        /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
        public override void FeatureInstalled(SPFeatureReceiverProperties properties) {
            //throw new Exception("The method or operation is not implemented.");
        }

        /// <summary>
        /// Occurs when a Feature is uninstalled.
        /// </summary>
        /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"></see> object that represents the properties of the event.</param>
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {
            //throw new Exception("The method or operation is not implemented.");
        }
    }
}

Implementation

With our abstract base class in place it's a piece of cake to implement both our Strict xHtmlConformance Feature and our Debug Switch.

//-----------------------------------------------------------------------
// <copyright file="XhtmlConformance.cs" company="motion10">
//     Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Security.Permissions;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Security;

namespace Motion10.SharePoint2007 {
    /// <summary>
    /// The XhtmlConformance class is the Feature Reciever for the motion10 strict xHtmlConformance feature
    /// </summary>
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    public class XhtmlConformance : WebConfigModificationFeatureReceiver {
        private static SPWebConfigModification[] modifications = {
                    new SPWebConfigModification("xhtmlConformance", "configuration/system.web")
                        { Owner = "motion10XhtmlConformance", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode, Value = "<xhtmlConformance />" },
                    new SPWebConfigModification("mode", "configuration/system.web/xhtmlConformance")
                        { Owner = "motion10XhtmlConformance", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Strict" }
        };

        /// <summary>
        /// Initializes a new instance of the <see cref="XhtmlConformance"/> class.
        /// </summary>
        public XhtmlConformance() : base() { }

        /// <summary>
        /// Gets the modifications to apply to the web.config.
        /// </summary>
        /// <returns></returns>
        /// <value>The modifications.</value>
        protected override SPWebConfigModification[] GetModifications(){
            return (SPWebConfigModification[])modifications.Clone();
        }
    }
}

 

//-----------------------------------------------------------------------
// <copyright file="DebugSwitch.cs" company="motion10">
//     Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Security.Permissions;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Security;

namespace Motion10.SharePoint2007 {
    /// <summary>
    /// The DebugSwitch class is the Feature Reciever for the motion10 Debug Switch feature
    /// </summary>
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    public class DebugSwitch : WebConfigModificationFeatureReceiver {
        private static SPWebConfigModification[] modifications = {
                    new SPWebConfigModification("CallStack", "configuration/SharePoint/SafeMode")
                        { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" },
                    new SPWebConfigModification("mode", "configuration/system.web/customErrors")
                        { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Off" },
                    new SPWebConfigModification("debug", "configuration/system.web/compilation")
                        { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" }
        };

        /// <summary>
        /// Initializes a new instance of the <see cref="DebugSwitch"/> class.
        /// </summary>
        public DebugSwitch() : base() { }

        /// <summary>
        /// Gets the modifications to apply to the web.config.
        /// </summary>
        /// <returns></returns>
        /// <value>The modifications.</value>
        protected override SPWebConfigModification[] GetModifications() {
            return (SPWebConfigModification[])modifications.Clone();
        }
    }
}

Feature.xml

With our classes ready we need to create two feature.xml files. Both are not to difficult. They need an Id, Title, Description and the RecieverAssembly and RecieverClass.

<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="8404bc89-27c0-487f-9346-58f9e68a7d82"
          Title="motion10 strict xHtmlConformance"
          Description="By activating the motion10 strict xHtmlConformance feature the web.config will be changed in order for the asp.net controls to render valid xHtml to the clients."
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="WebApplication"
          DefaultResourceFile="core"
          ReceiverAssembly="SharePointSolutionPack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a7cd02bdf107f7a"
          ReceiverClass="Motion10.SharePoint2007.XhtmlConformance"
          Creator="Wesley Bakker"
          ImageUrl="motion10/FeaturesIcon.png"
          ImageUrlAltText="http://www.motion10.com"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests />
</Feature>

 

<?xml version="1.0" encoding="utf-8"?>
<Feature  Id="d728e7a5-a794-4f3b-9664-8d96dfccfff2"
          Title="motion10 Debug Switch"
          Description="Activate this feature to make the necessary changes to the web.config to enable debugging. Deactivate this feature to revert the changed web.config settings to the state they were at activation."
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="WebApplication"
          DefaultResourceFile="core"
          ReceiverAssembly="SharePointSolutionPack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a7cd02bdf107f7a"
          ReceiverClass="Motion10.SharePoint2007.DebugSwitch"
          Creator="Wesley Bakker"
          ImageUrl="motion10/FeaturesIcon.png"
          ImageUrlAltText="http://www.motion10.com"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests />
</Feature>

Conclusion

It's not that hard to modify the web.config of your SharePoint application. Especially with the given abstract base class, you guys should be able to create some new feautures yourselfs. Let me know if you can think of something.

I'm experimenting with my blog. In this article I've insert all the code in-line instead of making it available as a download. I think it's a little bit long to read in this way but it enables you to see what I'm talking about right away. What do you think? Is this better or worse? Please leave me a comment on this.

Cheers,

Wes

Posted: Jan 21 2009, 05:33 PM by webbes | with 22 comment(s)
Filed under: ,

Comments

No Comments