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 24 comment(s)
Filed under: ,

Comments

Links (1/22/2009) « Steve Pietrek - Everything SharePoint said:

Pingback from  Links (1/22/2009) &laquo; Steve Pietrek - Everything SharePoint

# January 22, 2009 7:57 PM

shan kane said:

well, good to do both, explain in detail and provide the code..?

# January 25, 2009 6:29 PM

BobC said:

Good example.  Like the generic implementation. The inline code is good too.

# April 8, 2009 3:33 PM

Bill Wilson said:

Including your code as you explain the process makes for a excellent learning experience. I am especially appreciative of your using short code segments (a by-product of your approach). It is incrediblly difficult to work with inbedded code that is longer than one's monitor is tall. You must continually scroll to the bottom to scroll right and then scroll back up. Thanks a million!

# April 10, 2009 12:07 PM

Martijn said:

I've got the following problem.

I want to add the Module as being the first http module.

I have no idea how I should do that with the SPWebConfigModification class.

Can you provide me some help?

If I add it to the httpModules section, it always appears to be the last element.

# June 16, 2009 10:38 AM

webbes said:

# June 16, 2009 11:47 AM

drew said:

Hey Wes, perhaps you can help me out quickly. I'm a bit of a newbie to SP, and my query is simple enough, but having probs finding a definitive set of things to check.

I’ve got a basic vs2008 app working under sharepoint, but once I try to use the Microsoft.sharepoint dll (or add code to connect to a sql db) I run into problems, ie Label1.Text = SPContext.Current.Web.CurrentUser.LoginName

This gives me a Security Exception error on the sharepoint site, and im not sure what to add where to let it run properly. I gather it has something to do with the ‘trust’ settings in the web.config, but im not sure where and how.

Any help would be gladly received!

Cheers,

Drew

# July 19, 2009 10:24 PM

webbes said:

@drew.. add your control - the one that contains Label1 - to the SafeControls list in your web.config.

Cheers,

Wes

# July 20, 2009 3:50 AM

Using SPWebConfigModification within a Feature Receiver « PANVEGA’s Blog said:

Pingback from  Using SPWebConfigModification within a Feature Receiver &laquo; PANVEGA&#8217;s Blog

# September 2, 2009 2:50 PM

anandsan said:

I've also got the following problem.

I want to add the Module as being the first http module.

I have used sequence as 0 hoping that my modification would put the httpmodule first, but it always appears to be the last element.

Does anybody know how to resolve this?

Thanks

# January 17, 2010 3:57 PM

webbes said:

@anandsan: I do not know how to solve the sequence issue for you so if anybody reads this... please feel free to leave a comment.

Another thing though... are you really really sure you want to place your module before the SPVirtualPathProvider? I would not recommend it unless you are really knowing what you are doing. HttpModules can be a blessing and a burden cause they get called on EVERY single request to your server. If you screw up there... everything can blow up quit easely...

# January 19, 2010 3:19 PM

carla said:

So helpful!!! Thanks.

# January 21, 2010 10:39 AM

sergey said:

regarding the sequence issue:

weblogs.asp.net/.../sharepoint-features-and-web-config-modifications-using-spwebconfigmodification.aspx

note in the comment in the source code:

"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-'."

# March 17, 2010 5:06 AM

Kirk Evans Blog said:

When I first started developing with SharePoint, I wanted to learn how to do the things that I did in

# June 28, 2010 7:30 PM

Tune Up Your PC » Post Topic » Creating a SharePoint Site Page With Code-Behind Using Visual Studio 2010 said:

Pingback from  Tune Up Your PC  &raquo; Post Topic   &raquo; Creating a SharePoint Site Page With Code-Behind Using Visual Studio 2010

# June 30, 2010 4:00 AM

Amol Pandit said:

hi i am new SP i had question?

Is it safe  if we apply the changes like --

webApp.WebService.ApplyWebConfigModifications();

webApp.WebService.Update();

i got problem while i was updating the web config for central admin web site this method solves my problem.

please tell if it is not proper way to do this

Thanks

silveramola230@gmail.com

# August 26, 2010 2:03 AM

Sharepoint Question | hardware wacool said:

Pingback from  Sharepoint Question | hardware wacool

# September 3, 2010 7:31 PM

Ronald Strous said:

Hello,

How can you add a key/value pair to the AppSettings section with the above solution?

# November 9, 2010 10:44 AM

Saurabh Kumar said:

Hi,

webApp.WebService.ApplyWebConfigModifications();

This piece of code doesn't work

I am getting "Access Denied" error on this line.

how can i get rid of this error ??

Kind Regards,

Saurabh Kumar

# July 21, 2011 2:43 AM

webbes said:

@Saurabh well the error says it all. I'm not 100% sure but I can imagine you need to be a Farm Admin to make web.config modifications.

# July 21, 2011 3:41 AM

Muhammad Kazim said:

Saurabh Kumar : I was in same situation , with some tries i figured it out that Access Denied option was escalating after configurations have been modified / applied.

So i just ignored that access denied. Caught that exception and ignored that. That might be not the best solution for you but was for me as i had to go with that.

# November 9, 2011 1:52 AM

Use a supplemental .config file just for a web application | web technical support said:

Pingback from  Use a supplemental .config file just for a web application | web technical support

# November 21, 2011 8:29 AM

Use a supplemental .config file just for a web application | Q&A System said:

Pingback from  Use a supplemental .config file just for a web application | Q&amp;A System

# January 17, 2012 9:09 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)