Creating an RSS Generator in SharePoint
I recently had to create an RSS feed generator of SharePoint content and required a bit more flexibility that the out of the box SharePoint RSS feeds. There may be a number of approaches to this but here is an outline of how I did this…
So what do you get out of the box?
You enable RSS within your site collection/site via site settings. You also need to enable RSS on the specific list or document library. You do this via the list settings.
When enabled users see a new link from the list ‘Actions’ menu…
This link calls the page listfeed.aspx which you can find in the LAYOUTS folder within the 12 HIVE. It’s worth taking a look at the code behind for this page which is contained within the Microsoft.SharePoint.ApplicationPages.dll which you can find in the _app_bin folder of any SharePoint site (And not the GAC). Using Reflector take a look at the class ListFeed and you will get an idea on how the RSS feed work.
There are a couple of query string fields which can be passed to this page. list and View the list is expecting the source list GUID and the view is looking for a lists view GUID. The thing I found odd was that even if you are in the a specific list view if you click the view RSS feed link you still only get of the an RSS feed back which is the default view i.e. the query string ‘View’ is not used? You can manually put this in an it works? Perhaps I’m missing something here?
The render method of this class calls the WriteRssMethod of the corresponding SPlist. This method is overloaded as follows
| Name | Description |
|---|---|
| SPList.WriteRssFeed (Stream) |
Writes the RSS feeds from the list to the specified document stream.
|
| SPList.WriteRssFeed (Stream, Int32) |
Writes the RSS feeds from the list that are associated with the specified meeting to the specified document stream.
|
| SPList.WriteRssFeed (Stream, Int32, SPView) |
Writes the RSS feeds from the list that are associated with the specified meeting and view to the specified document stream.
|
Building your own RSS generator
Anyway my requirements had to cater for
1. Return content which maps to specific content type fields
2. Return content from a specified view within a list – I used the view to filter and sort the content although I could have easily just done this in using SPQuery
I created a SharePoint solution with a new application page which would be mapped to the LAYOUTS folder within the 12 Hive
The application page, called rssFeed.aspx contains the following mark-up
<%
@ Assembly Name=”chrisforbesblogs.RSSTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8e473a8929984006″ %>
<%
@ Page Language=”C#” Inherits=”chrisforbesblogs.RSSTest.rssFeed” %>
<%
@ OutputCache Duration=”300″ VaryByParam=”xsl;web;page;wp” VaryByCustom=”rights;feedresults”%>
In the same solution I created a class file called rssFeed.aspx.cs with the following inheritance from LayoutsPageBase
using System;using System.Collections.Generic;using System.Text;using Microsoft.SharePoint;using Microsoft.SharePoint.WebControls;using System.Web.UI.WebControls;using System.Web.UI.HtmlControls;using System.Xml;using System.Globalization; namespace chrisforbesblogs.RSSTest{ public class rssFeed :LayoutsPageBase {
Fields to store relevant objects
protected SPList spList;protected SPWeb spWeb;protected SPView spView;protected string viewName;protected string listName;
protected override void OnLoad(EventArgs e){ base.OnLoad(e); // System.Diagnostics.Debugger.Break(); if (!Page.IsPostBack) { try { viewName = (base.Request.QueryString.GetValues("View")[0]); listName = (base.Request.QueryString.GetValues("List")[0]); spWeb = SPControl.GetContextWeb(this.Context); spList = spWeb.Lists[listName]; if (spList == null) throw new Exception("list not found"); spView = spList.Views[viewName]; if (spView == null) throw new Exception("View Not Found"); } catch (Exception ex) { throw new SPException(ex.Message); } }}
Then in the render method return the relevant xml using some SharePoint API to get the required records and a string builder to construct the XML. You can also create the xml file using System.XML.XMLDocument but I find this way easier to read the XML as I write it.
protected override void Render(System.Web.UI.HtmlTextWriter writer){ base.Render(writer); if (spList != null) { base.Response.AppendHeader("ETag" , spList.CurrentChangeToken.ToString()); base.Response.AppendHeader("Last-Modified" , spList.LastItemModifiedDate.ToString("r" , CultureInfo.InvariantCulture)); base.Response.ContentType = "text/xml"; if (spView != null) { base.Response.ContentEncoding = Encoding.UTF8; string channelTitle = "The RSS Channel title"; string channelDescription = "The RSS Description"; string channelLink = "http://www.chrisforbesblogs.net"; string language = "en-us"; int timeToLive = 60; string copyright = "Copyright 2009 - " + DateTime.Now.Year + " ChrisForbesBlogs.net"; StringBuilder sb = new StringBuilder(); sb.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"); sb.Append("<rss version=\"2.0\">\r\n"); sb.Append("<channel>\r\n"); sb.AppendFormat("<title>{0}</title>\r\n" , channelTitle); sb.AppendFormat("<link>{0}</link>\r\n" , channelLink); sb.AppendFormat("<description>{0}</description>\r\n" , channelDescription); sb.AppendFormat("<language>{0}</language>\r\n" , language); sb.AppendFormat("<ttl>{0}</ttl>\r\n", timeToLive); sb.AppendFormat("<copyright>{0}</copyright>\r\n" , copyright); string spTitle = ""; string spDescription = ""; string spDate = ""; string spAuthor = "Chris Forbes"; sb.AppendFormat( "<lastBuildDate>{0}</lastBuildDate>\r\n" , DateTime.Now.Date.ToShortDateString()); SPListItemCollection items = spList.GetItems(spView); foreach (SPListItem item in items) { //Make sure you are pulling back valid //SharePoint fields which are listed in the view if (item["Title"] != null) spTitle = item["Title"].ToString(); if (item["Description"] != null) spTitle = item["Description"].ToString(); if (item["PageDate"] != null) spTitle = item["PageDate"].ToString(); sb.AppendFormat("<item>\r\n"); sb.AppendFormat("<title>{0}</title>", spTitle); sb.AppendFormat("<link><![CDATA[{0}]]></link>\r\n" , item.Url); sb.AppendFormat( "<description><![CDATA[{0}]]></description>\r\n" , spDescription); sb.AppendFormat("<author>{0}</author>\r\n" , spAuthor); sb.AppendFormat("<pubDate>{0}</pubDate>\r\n" , spDate); sb.AppendFormat("</item>\r\n"); } sb.AppendFormat("</channel>\r\n"); sb.AppendFormat("</rss>\r\n"); base.Response.Write(sb.ToString()); } }}
I wrapped the returned field values in <![CDATA[]]> to ensure any illegal characters are ignored by the XML.
As I mentioned earlier you could easily extend this code to use SPQuery instead of relying on an existing view. You could also access a number of lists and aggregate the content into a single feed.
If your publishing this to an anonymous access site then you will have to add some additional methods to prevent an authentication challenge. If you look back the out of the box class file you can see some additional methods which handle this.
protected override bool AllowAnonymousAccess
{
get
{
return true;
}
}
protected override bool RequireDefaultLayoutsRights
{
get
{
return false;
}
}
protected override SPBasePermissions RightsRequired
{
get
{
return (SPBasePermissions.EmptyMask | SPBasePermissions.Open);
}
}
Anyway hope the above is of some help to you, happy coding


Another informative post. This is a very nice blog that I will definitively come back to several more times this year!
Thanks Genie, much appreciated. I hope to get more postings out soon but a bit max’d with projects at the moment.