SharePoint AJAX RSS Aggregator WebPart
Ok so I thought to myself.... what if I make the RSS Aggregator (http://dotnet.org.za/zlatan/archive/2008/11/28/updated-sharepoint-rss-aggregator-web-part.aspx) AJAX enabled.
Some of you might even remember me blogging about developing AJAX web parts for SharePoint last year some time (http://dotnet.org.za/zlatan/archive/2007/10/12/developing-ajax-web-parts-in-sharepoint-2007.aspx).
Well here's what came out of this experiment, although this is more for messing around, to see how it works and for an odd chance that someone is permanently displaying a screen with aggregated RSS feeds that needs smooth updates (every 30 seconds or so). It uses my favourite UpdatePanel and also Ticker control.
Download the source code here:
http://www.codeplex.com/AJAXRSSAggregator
Don't forget to follow this (http://msdn.microsoft.com/en-us/library/bb861898.aspx) to configure your SharePoint for AJAX, unless you did that already.
Here's the code as well:
using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Collections.Generic;
using System.Xml;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Xml.Linq;
using System.Net;
using System.Text;
namespace AJAXRSSAggregator
{
[Guid("5905eac7-df55-4529-8d7a-f6f7ff9b6fa3")]
public class AJAXRSSAggregator : System.Web.UI.WebControls.WebParts.WebPart
{
private string rssUrl;
private string feedName;
private const string constDefFeedCriteriaValue = "SAArchitect";
private string filterCriteria = constDefFeedCriteriaValue;
private const string constDefFeedLimitValue = "5";
private string feedLimit = constDefFeedLimitValue;
private int feedLmt;
private string tagValue;
private Label displayName;
private int refreshInterval = 30;
private string htmlstuff = null;
public AJAXRSSAggregator()
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//Fix for the UpdatePanel postback behaviour.
EnsurePanelFix();
UpdatePanel refreshName = new UpdatePanel();
ScriptManager scriptHandler = new ScriptManager();
displayName = new Label();
scriptHandler.ID = "scriptHandler";
refreshName.ID = "refreshName";
refreshName.UpdateMode = UpdatePanelUpdateMode.Conditional;
refreshName.ChildrenAsTriggers = true;
Timer timer = new Timer();
timer.Interval = refreshInterval * 1000;
timer.Tick += new EventHandler<EventArgs>(TimerHandler);
refreshName.ContentTemplateContainer.Controls.Add(timer);
refreshName.ContentTemplateContainer.Controls.Add(this.displayName);
//The ScriptManager control must be added first.
this.Controls.Add(scriptHandler);
this.Controls.Add(refreshName);
}
private void TimerHandler(object sender, EventArgs args)
{
htmlstuff = null;
try
{
feedLmt = Convert.ToInt32(feedLimit);
}
catch (Exception)
{
feedLmt = 0;
}
RssFeed feed = new RssFeed(rssUrl);
feed.Sort(delegate(RssItem r1, RssItem r2)
{
return r2.PubDate.CompareTo(r1.PubDate);
});
int i = 0;
foreach (RssItem singleRssItem in feed)
{
if (i < feedLmt)
{
if (singleRssItem.Tags != null)
{
if (filterCriteria != null)
tagValue = singleRssItem.Tags.ToLower();
if (tagValue.Contains(filterCriteria) == true || filterCriteria == null)
{
htmlstuff += "<div class=\"container\">";
htmlstuff += "<font size=\"4\" face=\"Verdana\"><b><a href =\"";
htmlstuff += singleRssItem.Href;
htmlstuff += "\"><font size=\"2\" face=\"Verdana\">";
htmlstuff += singleRssItem.Title;
htmlstuff += "</a></b></font>";
htmlstuff +="<div class=\"module-content\"><table width=\"100%\" border=\"0\"><tr><td><font size=\"2\" face=\"Verdana\">";
htmlstuff +=singleRssItem.Body;
htmlstuff +="</font></td></tr></table></div>";
htmlstuff +="</div>";
htmlstuff +="<div class=\"module-content\"><table width=\"100%\" border=\"0\"><tr><td><font size=\"2\" face=\"Verdana\">";
htmlstuff +=singleRssItem.Tags;
htmlstuff +="</font></td></tr></table></div>";
htmlstuff +="</div>";
htmlstuff +="<div class=\"module-content\"><table width=\"100%\" border=\"0\"><tr><td><font size=\"2\" face=\"Verdana\">";
htmlstuff +=singleRssItem.PubDate.ToString();
htmlstuff +="</font></td></tr></table></div>";
htmlstuff +="</div>";
i++;
}
}
}
}
this.displayName.Text = htmlstuff;
}
private void EnsurePanelFix()
{
if (this.Page.Form != null)
{
string fixupScript = @"_spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
function _initFormActionAjax()
{
if (_spEscapedFormAction == document.forms[0].action)
{
document.forms[0]._initialAction = document.forms[0].action;
}
}
var RestoreToOriginalFormActionCore = RestoreToOriginalFormAction;
RestoreToOriginalFormAction = function()
{
if (_spOriginalFormAction != null)
{
RestoreToOriginalFormActionCore();
document.forms[0]._initialAction = document.forms[0].action;
}
}";
ScriptManager.RegisterStartupScript(this, typeof(AJAXRSSAggregator), "UpdatePanelFixup", fixupScript, true);
}
}
public string FeedName
{
get
{
return feedName;
}
set
{
feedName = value;
}
}
[WebBrowsable(true),
Personalizable(true),
Category("RSS Aggregator Web Part"),
DisplayName("URLs of the Feed"),
WebDisplayName("URLs of the Feed"),
Description("Please enter the URLs of the feed separated by ';' character.")]
public string FeedURL
{
get
{
return rssUrl;
}
set
{
rssUrl = value;
}
}
[WebBrowsable(true),
Personalizable(true),
Category("RSS Aggregator Web Part"),
DisplayName("Filter Criteria"),
WebDisplayName("Filter Criteria"),
Description("Please enter the tag value you wish to filter on for desired RSS feeds."),
DefaultValue(constDefFeedCriteriaValue)]
public string FilterOn
{
get
{
return filterCriteria.ToLower();
}
set
{
filterCriteria = value.ToLower();
}
}
[WebBrowsable(true)]
[Personalizable(true),
Category("RSS Aggregator Web Part"),
DisplayName("Feed Limit"),
WebDisplayName("Feed Limit"),
Description("Defines the list the web part will read from."),
DefaultValue(constDefFeedLimitValue)]
public string FeedLimit
{
get
{
return feedLimit;
}
set
{
feedLimit = value;
}
}
}
internal class RssFeed : List<RssItem>
{
private XElement rssDoc;
internal RssFeed(string RssURL)
{
if (RssURL == null)
{
this.Add(new RssItem());
}
else
{
string[] rssUrlsArray = RssURL.Split(new char[] { ';' });
foreach (string url in rssUrlsArray)
{
try
{
WebClient client = new WebClient();
byte[] byteData = client.DownloadData(url);
string strData = Encoding.UTF8.GetString(byteData);
rssDoc = XElement.Parse(strData);
foreach (var elm in rssDoc.Descendants("item"))
{
this.Add(new RssItem(elm));
}
}
catch (Exception)
{
this.Add(new RssItem());
}
}
}
}
}
internal class RssItem
{
private string title;
private string href;
private string body;
private string tags;
private DateTime pubDate;
public string Href
{
get { return href; }
}
public string Title
{
get { return title; }
}
internal RssItem()
{
title = "Feed not available at this time";
href = "~";
}
public string Body
{
get { return body; }
set { body = value; }
}
public string Tags
{
get { return tags; }
set { tags = value; }
}
public DateTime PubDate
{
get { return pubDate; }
set { pubDate = value; }
}
internal RssItem(XElement xNode)
{
if (xNode.NodeType == XmlNodeType.CDATA)
{
XCData titleCData = xNode.FirstNode as XCData;
title = titleCData.Value;
}
else
title = (string)xNode.Element("title").Value;
if (xNode.NodeType == XmlNodeType.CDATA)
{
XCData linkCData = xNode.FirstNode as XCData;
href = linkCData.Value;
}
else
href = (string)xNode.Element("link").Value;
if (xNode.NodeType == XmlNodeType.CDATA)
{
XCData bodyCData = xNode.FirstNode as XCData;
body = FixDesc(bodyCData.Value, href);
}
else
body = FixDesc((string)xNode.Element("description").Value, href);
pubDate = Convert.ToDateTime((string)xNode.Element("pubDate").Value);
foreach (XElement node in xNode.Elements("category"))
{
if (node.NodeType == XmlNodeType.CDATA)
{
XCData categoryCData = node.FirstNode as XCData;
if (tags == null)
tags = (string)categoryCData.Value;
else
tags = tags + ", " + (string)categoryCData.Value;
}
else
{
if (tags == null)
tags = (string)node.Value;
else
tags = tags + ", " + (string)node.Value;
}
}
}
public string FixDesc(object desc, object link)
{
if (link == null || desc == null) return String.Empty;
string description = desc.ToString();
//Replace all HTML tags so none get cut-off and screw up the page
Regex reg = new Regex("<.*?>", RegexOptions.Compiled);
string stripDesc = reg.Replace(description, String.Empty);
if (stripDesc.Length > 250)
{
int startPos = 250;
char[] chars = stripDesc.ToCharArray();
char c = chars[startPos];
while (!Char.IsWhiteSpace(c) && startPos < chars.Length)
{
startPos++;
c = chars[startPos];
}
stripDesc = new String(chars, 0, startPos) + " ... <a href='" + link.ToString() + "'> More</a>";
}
return stripDesc;
}
}
}