Wednesday, August 13, 2008

Programatically removing an HttpModule from SharePoint Web.config (2)

In one of my previous posts (that you can find here) I described how I managed to add a HttpModule tag to the web.config. The issue still remaining was the removal of that tag from the web.config, that I needed to do in the uninstallation process (in my case feature deactivating). At that time I used a remove tag. 

Although web.config file sees no problem in duplicate add tags, the same thing does NOT happen with the remove tags. If two or more remove tags of the same module are present in the web.config file, an error is generated - so down the drain goes our solution if two or more install-uninstall sequences occur on the same web application.

What is the solution to this problem? I found it in one of Andrew Connell’s books - “Professional SharePoint 2007 Web Content Management Development” (find it in my Books sidebar). It is a fairly simple solution: at Http Module installation in a SharePoint site you add a property to the site to signal that the Http Module should be applied there. In the Http Module code behind you check if that property exists before letting the Http Module do what it is meant to do. At unistallation you remove the property, or make it null. And Voila! - although the tag in the web.config stands, the module will not act on sites unless they have the property it is looking for.


 

Posted by Madalina at 12:12:14 | Permalink | No Comments »

Tuesday, June 17, 2008

Programatically add (and remove) an HttpModule to SharePoint Web.config

Sometimes you need to use an HttpHandler or HttpModule in a SharePoint application - I am using one to handle the requests to the system pages and to replace the application.master on them with my own master. To install this module I need to register the dll in the GAC and to add a HttpModule declaration in the web.config.

To modify the web.config programatically in SharePoint you can use the SPWebConfigModification class. There are many examples on the net about using this class, and the one I started with is this one. Unfortunately, this example, along with all the others on the subject are healing with adding HttpHandler elements, while I needed to play with an HttpModule element. The example does apply to me for adding an element, but the issue arises when I need to remove it.

The SPWebConfigModification class has a Name property. This property should be set to the relative XPath to the modification and maps to the Path attribute of an HttpHandler element. The property is used to locate the element when you need to remove it. Unfortunately, the HttpModule element does not have the Path attribute. So no matter how I tried it, I could not remove the element from the web.config.

The solution came while I was reading a different post about web.config inheritance: the tag. If you want to remove the HttpModule element, add a tag to the web.config.

Posted by Madalina at 09:34:33 | Permalink | Comments (5)

Thursday, October 4, 2007

Http Handlers applied – Rss Feed

RSS Feeds are very popular these days.

Recently, I found myself in the need of using a rss feed for a “Joke of the day” web part. The tricky side of the business was that I had to provide only one like in the rss feed – the joke for the current day. Many options came into my mind, but the most elegant was by far the HttpHandler one.

An HttpHandler is an independent unit that manages the server response for a given situation: for example, a specific file extension. In my case, what I need was for the web application to create a rss file “on the fly” when a user requested a certain url. The url was http://mycompany.com/RssFeed/jokes.rss and the trick - the file didn’t even exist :) . When the url was requested, I had to tell the application that a rss file (stream) had to be generated and passed to the client.

How is this done? First of all, you need a class that generated the feed. This class needs to implement the IhttpHandler interface. What this class does is to read one joke from a data source (xml, database etc) and output it to the browser as a rss file. You can put this class in a ClassLibrary project, because you will need the dll. Here is the code:

namespace MDL.RSSLibDLL

{

    public class RssHandler : IHttpHandler

    {

        public void ProcessRequest(HttpContext context) // this is the function that needs to be implemented. It comes, by dfault, with the Context of the user in the application that it handles

        {

            // Get the file name from the requested url

            string fileName = Path.GetFileName(context.Request.Path); // the rss file that the user wants

            //generally, you want to cache your rss files

string cachedChan = context.Cache[fileName] as String;

            if (cachedChan == null)

            {

                // The file is not in the cache

                string profileFolder = ConfigurationSettings.AppSettings["rssProfileFolder"];

//get the rss profile – the phyisical structure

                if (profileFolder == null)

                {

                    context.Response.StatusCode = 404;

                    context.Response.End();

                    return;

                }

                // Verify that you actually have a profile for the requested feed(a source rss file to read the structure from )

                string profilePath = profileFolder + “” + fileName;

                if (!File.Exists(profilePath))

                {

                    context.Response.StatusCode = 404;

                    context.Response.End();

                    return;

                }

   

   

                // create the rss response

                RssChannel chan = new RssChannel(profilePath, DateTime.Now);

                cachedChan = chan.GetResponse();

                double cacheTimeout;

                string appCacheTimeout = ConfigurationSettings.AppSettings["cacheTimeout"];

                if (appCacheTimeout == null)

                    cacheTimeout = 30;

                else

                    cacheTimeout = double.Parse(appCacheTimeout);

                context.Cache.Insert(fileName, cachedChan, null, DateTime.Now.AddMinutes(cacheTimeout), TimeSpan.Zero);

            }

            context.Response.ContentType = “text/xml”;

            context.Response.Write(cachedChan); // output the rss to the browser

   

           

        }

   

        public bool IsReusable

        {

            get

            {

                return true;

            }

        }

   

    }

   

    internal class RssChannel //my class that implements a Rss feed item

    {

        private string _title;

        private string _link;

        private string _description;

        private string _language;

        private string _date;

   

        private string _profilePath; //resulting RSS structure

        private ArrayList _items;

   

        public RssChannel(string profilePath, DateTime date)

        {

            _profilePath = profilePath;

   

            _date = date.Month.ToString() + “/” + date.Day.ToString();

            Load();

        }

   

   

        public string Title

        {

            get { return (_title); }

            set { _title = value; }

        }

   

        public string Link

        {

            get { return (_link); }

            set { _link = value; }

        }

   

        public string Description

        {

            get { return (_description); }

            set { _description = value; }

        }

   

        public string Language

        {

            get { return (_language); }

            set { _language = value; }

        }

   

        public RssItem this[int index]

        {

            get

            {

                if (index < _items.Count)

                    return ((RssItem)_items[index]);

                return (null);

            }

        }

   

        public string GetResponse()

        {

            XmlElement workNode;

            XmlElement itemNode;

            RssItem currentItem;

            XmlDocument rssDocument = new XmlDocument();

            // Create the rss document node and set the its attributes

            XmlElement docNode = rssDoc.CreateElement(“rss”);

            XmlAttribute attr = rssDoc.CreateAttribute(“version”);

            attr.Value = “2.0″;

            docNode.Attributes.Append(attr);

            rssDoc.AppendChild(docNode);

            // Create the channel element and its children

            XmlElement chanNode = rssDoc.CreateElement(“channel”);

            docNode.AppendChild(chanNode);

            workNode = rssDoc.CreateElement(“title”);

            workNode.InnerText = _title;

            chanNode.AppendChild(workNode);

            workNode = rssDoc.CreateElement(“link”);

            workNode.InnerText = _link;

            chanNode.AppendChild(workNode);

            workNode = rssDoc.CreateElement(“description”);

            workNode.InnerText = _description;

            chanNode.AppendChild(workNode);

            workNode = rssDoc.CreateElement(“language”);

            workNode.InnerText = _language;

            chanNode.AppendChild(workNode);

   

            // Add item nodes to the channel

            for (int index = 0; index < _items.Count; index++)

            {

                currentItem = (RssItem)_items[index];

                itemNode = rssDoc.CreateElement(“item”);

                chanNode.AppendChild(itemNode);

                workNode = rssDoc.CreateElement(“joke”);

                workNode.InnerText = currentItem.Joke;

                itemNode.AppendChild(workNode);

                workNode = rssDoc.CreateElement(“date”);

                workNode.InnerText = currentItem.Date;

                itemNode.AppendChild(workNode);

   

            }

            return (rssDoc.OuterXml);

        }

   

        private void Load()

        {

            _items = new ArrayList();

            

            // Set channel properties from the profile:

            _title = profile.SelectSingleNode(“rss/channel/title”).InnerText;

            _link = profile.SelectSingleNode(“rss/channel/link”).InnerText;

            _description = profile.SelectSingleNode(“rss/channel/description”).InnerText;

            _language = profile.SelectSingleNode(“rss/channel/language”).InnerText;

            string _source = profile.SelectSingleNode(“rss/channel/source”).InnerText;

   

            // Retrieve items from the joke source

// input your code depending on the datasource

           

            RssItem currentItem = new RssItem( /*joke text field*/,/*joke date field*/);

            _items.Add(currentItem);

           

                             

        }

            // internal class that implements the Joke object

        internal class RssItem

        {

            private string _joke;

            private string _date;

   

            internal RssItem(string joke, string date)

            {

                _joke = joke;

                _date = date;

            }

   

            public string Joke

            {

                get { return (_joke); }

                set { _joke = value; }

            }

   

            public string Date

            {

                get { return (_date); }

                set { _date = value; }

            }

   

        }

   

    }

This class uses 3 (or more) pieces of information that you need to provide from an external source:

  1. The rss profile file – the file with the rss structure
  2. The cache timeout value
  3. The data information – xml file path, database connection string, select query etc

My code used an xml file that had jokes for all the days of the year. What I did was to get the current date and then read from the xml file the corresponding joke.

You can pass this information in a very elegant way – the web.config file. The web config file of the web application that will serve the rss. This web application can have no pages at all – that is the beauty of it J. What you need to do for the web application is:

  1. Put this in the web.config:

The HttpHandlers section tells the application that the handler for the rss files is the RssHandler class.

  1. Add the Classlibrary application dll in the bin directory
  2. Tell IIS that you want to handle the rss file requests differently.

 

You can copy the executable path from another extension mapping.

Now all the requests for a rss file will be handled by the RssHandler class. The beauty of it is that you can particularize the class for different rss files.

Here is a sample structure for the rss file:

 

If you build this solution and install it on your localhost, if you then go to http://localhost/my_app_name/jokes.rss you should be shown a feed with one item for the corresponding date.

Posted by Madalina at 16:00:17 | Permalink | No Comments »