Archive

Posts Tagged ‘RSS’

Creating an RSS Generator in SharePoint

January 18, 2010 3 comments

imageI 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…

image

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;
 
In the class I override the OnLoad method. I get the query string values for the required  list and view name and store these in class fields
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 :)

Follow

Get every new post delivered to your Inbox.