RssFeed Retrieve: Difference between revisions

From ICO wiki
Jump to navigationJump to search
(New page: <source lang=csharp> </source>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.IO; using System.Collections; using Sys...)
(No difference)

Revision as of 14:01, 16 April 2010

using System;

using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.IO;

using System.Collections; using System.Xml;

namespace Scream.RSS {

   public class RssManager {
       public void GetRssItemList(string url) {
           HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
           HttpWebResponse response = (HttpWebResponse)request.GetResponse();
           Stream responseStream = response.GetResponseStream();
           Encoding BrowserEncoding = Encoding.ASCII;
           if (request.HaveResponse) {
               // Next is redirection
               if ((response.StatusCode == HttpStatusCode.Found) ||
                   (response.StatusCode == HttpStatusCode.Redirect) ||
                   (response.StatusCode == HttpStatusCode.Moved) ||
                   (response.StatusCode == HttpStatusCode.MovedPermanently)) {
                   // Get new location and call recursively
                   WebHeaderCollection responseHeaders = response.Headers;
                   request = (HttpWebRequest)WebRequest.Create(responseHeaders["Location"]);
                   HttpWebResponse redirectedResponse = (HttpWebResponse)request.GetResponse();
                   responseStream = redirectedResponse.GetResponseStream();
               }
           }
           StringBuilder stringBuilder = new StringBuilder();
           byte[] buffer = new byte[8192];
           string tempString = null;
           int count = 0;
           do {
               // fill the buffer with data
               count = responseStream.Read(buffer, 0, buffer.Length);
               // make sure we read some data
               if (count != 0) {
                   tempString = Encoding.UTF8.GetString(Encoding.Convert(BrowserEncoding, Encoding.UTF8, buffer, 0, count));
                   stringBuilder.Append(tempString);
               }
           }
           while (count > 0); // any more data to read?
           string html = stringBuilder.ToString();


       }
   }
   #region Event datatype/delegate
   /// <summary>
   /// Holds details about any errors that occured
   /// during the loading or parsing of the RSS feed.
   /// </summary>
   public class RssReaderErrorEventArgs : EventArgs {
       /// <summary>
       /// The details of the error.
       /// </summary>
       public string Message {
           get {
               return this.message;
           }
           set {
               this.message = value;
           }
       }
       private string message;
   }
   /// <summary>
   /// Represents the method that will handle the RssReader error event.
   /// </summary>
   public delegate void RssReaderErrorEventHandler(object sender, RssReaderErrorEventArgs e);
   #endregion
   #region RssReader class
   /// <summary>
   /// The RssReader class provides a number of static methods for easy
   /// 1 or 2 step retrieval of RSS feeds. RSS feeds can be downloaded from any
   /// URL, and are then parsed into an <see cref="RssFeed">RssFeed</see> data type,
   /// which contains properties representing most aspects of an RSS Feed. A number
   /// of events are available for the calling application to register at the various
   /// stages of the feed request and parsing.
   /// <example>
   /// The following example retrieves the RSS news feed for the BBC news website,
   /// and creates a HTML document from the feed's details. It saves the HTML document
   /// to disk, and launches the default browser with the document. The number of items
   /// displayed is limited to 5. If there is any error, a messagebox is displayed with
   /// the details of the error.
   /// 
   /// RssFeed feed = RssReader.GetFeed("http://www.bbc.co.uk/syndication/feeds/news/ukfs_news/front_page/rss091.xml");
   /// 
   /// if ( feed.ErrorMessage == null || feed.ErrorMessage == "" )
   /// {
   ///		string template = "<a href=\"%Link%>%Title%</a><br/>%Description%<br/><br/><ul>%Items%</ul>";
   ///		string itemTemplate = "<li><a href=\"%Link%>%Title%</a><br/>%Description%</li>";
   /// 	string html = RssReader.CreateHtml(feed,template,itemTemplate,"",5);
   /// 
   /// 	StreamWriter streamWriter = File.CreateText("c:\\rss.html");
   /// 	streamWriter.Write(html);
   /// 	streamWriter.Close();
   /// 
   /// 	System.Diagnostics.Process.Start("c:\\rss.html");
   /// }
   /// else
   /// {
   /// 	MessageBox.Show("Error getting feed:\r\n" +feed.ErrorMessage,"Rss Demo App",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
   /// }
   /// 
   /// </example>
   /// </summary>
   public class RssReader {
       // Events: XML document loaded, rss element found,
       // channel node found, item parsed, error
       /// <summary>
       /// This event is fired when the feed has finished loading from the URL
       /// provided, into the XML parser.
       /// </summary>
       public event EventHandler FeedLoaded;
       /// <summary>
       /// This event is fired when the root node (typically 'rss') has
       /// been found in the feed.
       /// </summary>
       public event EventHandler RssNodeFound;
       /// <summary>
       /// This event is fired when the channel/child node of the rss node
       /// (typically 'channel') has been found in the feed.
       /// </summary>
       public event EventHandler ChannelNodeFound;
       /// <summary>
       /// This event is fired when an item is added to the <see cref="RssFeed">RssFeed</see>'s
       /// collection of items.
       /// </summary>
       public event EventHandler ItemAdded;
       /// <summary>
       /// This event is fired when an error occurs in the loading or parsing
       /// of the feed. The same error message is also available in the ErrorMessage
       /// property of the <see cref="RssFeed">RssFeed</see> object that is returned
       /// by the <see cref="Retrieve">Retrieve</see> method.
       /// </summary>
       public event RssReaderErrorEventHandler Error;


       /// <summary>
       /// The node name for the channel element
       /// in the RSS feed. This will rarely ever to be
       /// changed. Default is 'channel'.
       /// </summary>
       public string RootNodeName {
           get {
               return this.rootNodeName;
           }
           set {
               this.rootNodeName = value;
           }
       }
       /// <summary>
       /// The node name for the root rss element
       /// in the RSS feed. This is altered automatically to 'rdf:RDF'
       /// when RdfMode is set to true. Default is 'rss'.
       /// </summary>
       public string ChannelNodeName {
           get {
               return this.channelNodeName;
           }
           set {
               this.channelNodeName = value;
           }
       }


       /// <summary>
       /// If this is set to true, then the XML document
       /// is parsed slightly different, to cater sites with RDF feeds (such as
       /// slashdot.org and register.com). The whole RDF format is not supported,
       /// but those items in RSS which have a corresponding RDF property, such
       /// as description,title for the channel, and title,description for each
       /// item, are matched.
       /// </summary>
       public bool RdfMode {
           get {
               return this.rdfMode;
           }
           set {
               if (value) {
                   this.rootNodeName = "rdf:RDF";
               }
               else {
                   this.rootNodeName = "rss";
               }
               this.rdfMode = value;
           }
       }
       /// <summary>
       /// Member for the public property.
       /// </summary>
       private string rootNodeName = "rss";
       /// <summary>
       /// Member for the public property.
       /// </summary>
       private string channelNodeName = "channel";
       /// <summary>
       /// Member for the public property.
       /// </summary>
       private bool rdfMode = false;
       /// <summary>
       /// Retrieves a <see cref="RssFeed">RssFeed</see> object using
       /// the url provided as the source of the Feed.
       /// </summary>
       /// <param name="Url">The url to retrieve the RSS feed from, this can
       /// be in the format of http:// and also file://.. (ftp?)</param>
       /// <param name="RdfFormat">If this is set to true, then the XML document
       /// is parsed slightly different, to cater sites with RDF feeds (such as
       /// slashdot.org and register.com). The whole RDF format is not supported,
       /// but those items in RSS which have a corresponding RDF property, such
       /// as description,title for the channel, and title,description for each
       /// item, are matched.</param>
       /// <returns>A <see cref="RssFeed">RssFeed</see> object from the
       /// RSS feed's details.</returns>
       public static RssFeed GetFeed(string Url, bool RdfFormat) {
           RssReader rssReader = new RssReader();
           rssReader.RdfMode = RdfFormat;
           return rssReader.Retrieve(Url);
       }
       /// <summary>
       /// Retrieves a <see cref="RssFeed">RssFeed</see> object using
       /// the url provided as the source of the Feed.
       /// </summary>
       /// <param name="Url">The url to retrieve the RSS feed from, this can
       /// be in the format of http:// and also file://.. (ftp?)</param>
       /// <returns>A <see cref="RssFeed">RssFeed</see> object from the
       /// RSS feed's details.</returns>
       public static RssFeed GetFeed(string Url) {
           RssReader rssReader = new RssReader();
           return rssReader.Retrieve(Url);
       }
       /// <summary>
       /// A simplified method of creating a HTML (or any document) from an
       /// RSS Feed. See <see cref="RssHtmlMaker">RssHtmlMaker</see>
       /// </summary>
       /// <param name="Feed">The <see cref="RssFeed">RssFeed</see> object to
       /// get the tokens' data from.</param>
       /// <param name="Template">The overall HTML template (or any other format)
       /// to replace the tokens in.</param>
       /// <param name="ItemPrefix">A string template that is prepended to the beginning
       /// of each RSS item.</param>
       /// <param name="ItemSuffix">A string template that is apppended to the end
       /// of each RSS item.</param>
       /// <returns>A string with the templates provided parsed of their tokens, with
       /// the data values in their place.</returns>
       public static string CreateHtml(RssFeed Feed, string Template, string ItemPrefix, string ItemSuffix) {
           return new RssHtmlMaker().GetHtmlContents(Feed, Template, ItemPrefix, ItemSuffix);
       }
       /// <summary>
       /// A simplified method of creating a HTML (or any document) from an
       /// RSS Feed. See <see cref="RssHtmlMaker">RssHtmlMaker</see>
       /// </summary>
       /// <param name="Feed">The <see cref="RssFeed">RssFeed</see> object to
       /// get the tokens' data from.</param>
       /// <param name="Template">The overall HTML template (or any other format)
       /// to replace the tokens in.</param>
       /// <param name="ItemPrefix">A string template that is prepended to the beginning
       /// of each RSS item.</param>
       /// <param name="ItemSuffix">A string template that is apppended to the end
       /// of each RSS item.</param>
       /// <param name="MaxItems">The maximum number of RSS items to display.</param>
       /// <returns>A string with the templates provided parsed of their tokens, with
       /// the data values in their place.</returns>
       public static string CreateHtml(RssFeed Feed, string Template, string ItemPrefix, string ItemSuffix, int MaxItems) {
           RssHtmlMaker rssHtmlMaker = new RssHtmlMaker();
           rssHtmlMaker.MaxItems = MaxItems;
           return rssHtmlMaker.GetHtmlContents(Feed, Template, ItemPrefix, ItemSuffix);
       }
       /// <summary>
       /// Retrieves an RSS feed using the given Url, parses it and
       /// creates and new <see cref="RssFeed">RssFeed</see> object with the information.
       /// If an error occurs in the XML loading of the document, or parsing of
       /// the RSS feed, the error is trapped and stored inside the RssFeed's
       /// ErrorMessage property.
       /// </summary>
       /// <param name="Url">The url to retrieve the RSS feed from, this can
       /// be in the format of http:// and also file://.. (ftp?)</param>
       /// <returns>An <see cref="RssFeed">RssFeed</see> object with information
       /// retrieved from the feed.</returns>
       public RssFeed Retrieve(string Url) {
           RssFeed rssFeed = new RssFeed();
           rssFeed.Items = new RssItems();
           XmlTextReader xmlTextReader = new XmlTextReader(Url);
           XmlValidatingReader xmlValidatingReader = new XmlValidatingReader(xmlTextReader);
           xmlValidatingReader.ValidationType = ValidationType.None;
           XmlDocument xmlDoc = new XmlDocument();
           try {
               xmlDoc.Load(xmlTextReader);
               // Fire the load event
               if (this.FeedLoaded != null) {
                   this.FeedLoaded(this, new EventArgs());
               }
               XmlNode rssXmlNode = null;
               // Loop child nodes till we find the rss one
               for (int i = 0; i < xmlDoc.ChildNodes.Count; i++) {
                   System.Diagnostics.Debug.Write("Child: " + xmlDoc.ChildNodes[i].Name);
                   System.Diagnostics.Debug.WriteLine(" has " + xmlDoc.ChildNodes[i].ChildNodes.Count + " children");
                   if (xmlDoc.ChildNodes[i].Name == this.rootNodeName && xmlDoc.ChildNodes[i].ChildNodes.Count > 0) {
                       rssXmlNode = xmlDoc.ChildNodes[i];
                       // Fire the found event
                       if (this.RssNodeFound != null) {
                           this.RssNodeFound(this, new EventArgs());
                       }
                       break;
                   }
               }
               if (rssXmlNode != null) {
                   XmlNode channelXmlNode = null;
                   // Loop through the rss node till we find the channel
                   for (int i = 0; i < rssXmlNode.ChildNodes.Count; i++) {
                       System.Diagnostics.Debug.WriteLine("Rss child: " + rssXmlNode.ChildNodes[i].Name);
                       if (rssXmlNode.ChildNodes[i].Name == this.channelNodeName && rssXmlNode.ChildNodes[i].ChildNodes.Count > 0) {
                           channelXmlNode = rssXmlNode.ChildNodes[i];
                           // Fire the found event
                           if (this.ChannelNodeFound != null) {
                               this.ChannelNodeFound(this, new EventArgs());
                           }
                           break;
                       }
                   }
                   // Found the channel node
                   if (channelXmlNode != null) {
                       // Loop through its children, copying details to the
                       // RssFeed struct, and parsing the items
                       for (int i = 0; i < channelXmlNode.ChildNodes.Count; i++) {
                           System.Diagnostics.Debug.WriteLine(channelXmlNode.ChildNodes[i].Name);
                           switch (channelXmlNode.ChildNodes[i].Name) {
                               case "title": {
                                       rssFeed.Title = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "description": {
                                       rssFeed.Description = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "language": {
                                       rssFeed.Language = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "copyright": {
                                       rssFeed.Copyright = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "webmaster": {
                                       rssFeed.Webmaster = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "pubDate": {
                                       rssFeed.PubDate = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "lastBuildDate": {
                                       rssFeed.LastBuildDate = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "category": {
                                       rssFeed.Category = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "generator": {
                                       rssFeed.Generator = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "ttl": {
                                       rssFeed.Ttl = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "rating": {
                                       rssFeed.Rating = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "skipHours": {
                                       rssFeed.Skiphours = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "skipDays": {
                                       rssFeed.Skipdays = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "managingEditor": {
                                       rssFeed.ManagingEditor = channelXmlNode.ChildNodes[i].InnerText;
                                       break;
                                   }
                               case "item": {
                                       rssFeed.Items.Add(this.getRssItem(channelXmlNode.ChildNodes[i]));
                                       // Fire the found event
                                       if (this.ItemAdded != null) {
                                           this.ItemAdded(this, new EventArgs());
                                       }
                                       break;
                                   }
                           }
                       }
                       // If rdf mode is set, then the channel node only contains
                       // information about the channel, it doesn't hold the item
                       // nodes. The item nodes are children of the root node in
                       // an RDF document, so we use this instead.
                       if (this.RdfMode) {
                           for (int i = 0; i < rssXmlNode.ChildNodes.Count; i++) {
                               switch (rssXmlNode.ChildNodes[i].Name) {
                                   case "item": {
                                           rssFeed.Items.Add(this.getRssItem(rssXmlNode.ChildNodes[i]));
                                           // Fire the found event
                                           if (this.ItemAdded != null) {
                                               this.ItemAdded(this, new EventArgs());
                                           }
                                           break;
                                       }
                               }
                           }
                       }
                   }
                   else {
                       rssFeed.ErrorMessage = "Unable to find rss <seehannel> node";
                       // Fire the error event
                       if (this.Error != null) {
                           RssReaderErrorEventArgs args = new RssReaderErrorEventArgs();
                           args.Message = rssFeed.ErrorMessage;
                           this.Error(this, args);
                       }
                   }
               }
               else {
                   rssFeed.ErrorMessage = "Unable to find root <rss> node";
                   // Fire the error event
                   if (this.Error != null) {
                       RssReaderErrorEventArgs args = new RssReaderErrorEventArgs();
                       args.Message = rssFeed.ErrorMessage;
                       this.Error(this, args);
                   }
               }
           }
           catch (XmlException err) {
               //
               rssFeed.ErrorMessage = "Xml error: " + err.Message;
               // Fire the error event
               if (this.Error != null) {
                   RssReaderErrorEventArgs args = new RssReaderErrorEventArgs();
                   args.Message = rssFeed.ErrorMessage;
                   this.Error(this, args);
               }
               return rssFeed;
           }
           return rssFeed;
       }
       /// <summary>
       /// Creates an RSS item from an XML node with the 
       /// corresponding child nodes (title,description etc.)
       /// </summary>
       /// <param name="xmlNode">The node to extract the details from</param>
       /// <returns>An RssItem object with details taken from the item node.</returns>
       private RssItem getRssItem(XmlNode xmlNode) {
           RssItem rssItem = new RssItem();
           for (int i = 0; i < xmlNode.ChildNodes.Count; i++) {
               switch (xmlNode.ChildNodes[i].Name.ToLower()) {
                   case "title": {
                           rssItem.Title = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "description": {
                           rssItem.Description = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "link": {
                           rssItem.Link = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "author": {
                           rssItem.Author = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "comments": {
                           rssItem.Comments = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "pubdate": {
                           rssItem.Pubdate = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
                   case "guid": {
                           rssItem.Guid = xmlNode.ChildNodes[i].InnerText;
                           break;
                       }
               }
           }
           return rssItem;
       }
   }
   #endregion
   #region Html creator class
   /// <summary>
   /// This class provides an easy method of converting a <see cref="RssFeed">RssFeed</see>
   /// object into a simple HTML document. This document can then be written to
   /// file, where it can be stored in a cached state (saving a feed request each
   /// time the feed is required). 
   /// </summary>
   public class RssHtmlMaker {
       /// <summary>
       /// Restricts the number of items that are displayed and replaced
       /// using the %Items% token in the HTML template.
       /// </summary>
       public int MaxItems {
           get {
               return this.maxItems;
           }
           set {
               this.maxItems = value;
           }
       }
       /// <summary>
       /// Member for the public property.
       /// </summary>
       private int maxItems = 0;
       /// <summary>
       /// Creates a HTML document, or any format - this is only limited by
       /// the template you provide - from the provided
       /// <see cref="RssFeed">RssFeed</see> object. The tokens described in the
       /// remarks section are replaced with their values inside the template.
       /// The items in the RSS feed are replaced using the ItemPrefix and ItemSuffix
       /// templates, where the suffix is placed face, and the suffix is appended on the end.
       /// </summary>
       /// <remarks>
       /// The following are a list of tokens which are replaced inside the main Template,
       /// with their corresponding values from the provided <see cref="RssFeed">RssFeed</see> 
       /// object. For details on each token, see its corresponding property in the 
       /// <see cref="RssFeed">RssFeed</see> object.
       /// <list type="bullet">
       /// <item>%Title%</item>
       /// <item>%Description%</item>
       /// <item>%Link%</item>
       /// <item>%Language%</item>
       /// <item>%Copyright%</item>
       /// <item>%Webmaster%</item>
       /// <item>%PubDate%</item>
       /// <item>%LastBuildDate%</item>
       /// <item>%Category%</item>
       /// <item>%Generator%</item>
       /// <item>%Ttl%</item>
       /// <item>%Rating%</item>
       /// <item>%Skiphours%</item>
       /// <item>%Skipdays%</item>
       /// <item>%Skipdays%</item>
       /// <item>%Items% - This is replaced by the parsed template of the items</item>
       /// </list>
       /// The following are a list of tokens which are replaced inside the ItemPrefix
       /// and ItemSuffix templates, with their corresponding values from the 
       /// provided <see cref="RssItem">RssItem</see> object. For details
       /// on each token, see its corresponding property in 
       /// the <see cref="RssItem">RssItem</see> object.
       /// <list type="bullet">
       /// <item>%Title%</item>
       /// <item>%Description%</item>
       /// <item>%Link%</item>
       /// <item>%Author%</item>
       /// <item>%Comments%</item>
       /// <item>%Pubdate%</item>
       /// <item>%Guid%</item>
       /// </list>
       /// </remarks>
       /// <param name="Feed">The <see cref="RssFeed">RssFeed</see> object to
       /// get the tokens' data from.</param>
       /// <param name="Template">The overall HTML template (or any other format)
       /// to replace the tokens in.</param>
       /// <param name="ItemPrefix">A string template that is prepended to the beginning
       /// of each RSS item.</param>
       /// <param name="ItemSuffix">A string template that is apppended to the end
       /// of each RSS item.</param>
       /// <returns>A string with the templates provided parsed of their tokens, with
       /// the data values in their place.</returns>
       public string GetHtmlContents(RssFeed Feed, string Template, string ItemPrefix, string ItemSuffix) {
           string result = Template;
           // Replace all template tokens
           result = result.Replace("%Title%", Feed.Title);
           result = result.Replace("%Description%", Feed.Description);
           result = result.Replace("%Link%", Feed.Link);
           result = result.Replace("%Language%", Feed.Language);
           result = result.Replace("%Copyright%", Feed.Copyright);
           result = result.Replace("%Webmaster%", Feed.Webmaster);
           result = result.Replace("%PubDate%", Feed.PubDate);
           result = result.Replace("%LastBuildDate%", Feed.LastBuildDate);
           result = result.Replace("%Category%", Feed.Category);
           result = result.Replace("%Generator%", Feed.Generator);
           result = result.Replace("%Ttl%", Feed.Ttl);
           result = result.Replace("%Rating%", Feed.Rating);
           result = result.Replace("%Skiphours%", Feed.Skiphours);
           result = result.Replace("%Skipdays%", Feed.Skipdays);
           result = result.Replace("%Skipdays%", Feed.ManagingEditor);
           // Parse item template
           string itemsContent = "";
           string tempContent = "";
           if (maxItems == 0 || maxItems > Feed.Items.Count) {
               maxItems = Feed.Items.Count;
           }
           for (int i = 0; i < maxItems; i++) {
               // Parse prefix template
               tempContent = ItemPrefix;
               tempContent = tempContent.Replace("%Title%", Feed.Items[i].Title);
               tempContent = tempContent.Replace("%Description%", Feed.Items[i].Description);
               tempContent = tempContent.Replace("%Link%", Feed.Items[i].Link);
               tempContent = tempContent.Replace("%Author%", Feed.Items[i].Author);
               tempContent = tempContent.Replace("%Comments%", Feed.Items[i].Comments);
               tempContent = tempContent.Replace("%Pubdate%", Feed.Items[i].Pubdate);
               tempContent = tempContent.Replace("%Guid%", Feed.Items[i].Guid);
               itemsContent += tempContent;
               // Parse suffix template
               tempContent = ItemSuffix;
               tempContent = tempContent.Replace("%Title%", Feed.Items[i].Title);
               tempContent = tempContent.Replace("%Description%", Feed.Items[i].Description);
               tempContent = tempContent.Replace("%Link%", Feed.Items[i].Link);
               tempContent = tempContent.Replace("%Author%", Feed.Items[i].Author);
               tempContent = tempContent.Replace("%Comments%", Feed.Items[i].Comments);
               tempContent = tempContent.Replace("%Pubdate%", Feed.Items[i].Pubdate);
               tempContent = tempContent.Replace("%Guid%", Feed.Items[i].Guid);
               itemsContent += tempContent;
           }
           // Replace %items% with items
           result = result.Replace("%Items%", itemsContent);
           return result;
       }
   }
   #endregion
   #region Data structures
   /// <summary>
   /// A data type to represent all properties of single RSS feed.
   /// (one XML document). The descriptions for
   /// the properties of RssItem are para-phrased from the 
   /// <see href="http://blogs.law.harvard.edu/tech/rss">RSS 2 specification</see>.
   /// See <see cref="RssReader">RssReader</see> for properties which 
   /// have not yet been implemented in this version of the
   /// the RssReader class.
   /// </summary>
   /// <remarks>
   /// The following elements of the RSS <channel> node aren't
   /// supported by this version of RssReader:
   /// <list type="bullet">
   /// <item>image (has subelements: image,url,title,link)</item>
   /// <item>cloud (has attributes: domain,port,path,registerProcedure,protocol)</item>
   /// <item>textInput (has subelements: title,description,name,link)</item>
   /// </list>
   /// </remarks>
   [Serializable()]
   public struct RssFeed {
       /// <summary>
       /// The name of the channel.
       /// </summary>
       public string Title;
       /// <summary>
       /// Phrase or sentence describing the channel.
       /// </summary>
       public string Description;
       /// <summary>
       /// The URL to the HTML website corresponding to the channel.
       /// </summary>
       public string Link;
       // Optional items
       /// <summary>
       /// The language the channel is written in. This allows 
       /// aggregators to group all Italian language sites, for example, on a single page. 
       /// </summary>
       public string Language;
       /// <summary>
       /// Copyright notice for content in the channel.
       /// </summary>
       public string Copyright;
       /// <summary>
       /// Email address for person responsible for technical issues relating to channel.
       /// </summary>
       public string Webmaster;
       /// <summary>
       /// The publication date for the content in the channel. 
       /// </summary>
       public string PubDate;
       /// <summary>
       /// The last time the content of the channel changed.
       /// </summary>
       public string LastBuildDate;
       /// <summary>
       /// Specify one or more categories that the channel belongs to.
       /// </summary>
       public string Category;
       /// <summary>
       /// A string indicating the program used to generate the channel.
       /// </summary>
       public string Generator;
       /// <summary>
       /// ttl stands for time to live. It's a number of minutes 
       /// that indicates how long a channel can be cached before 
       /// refreshing from the source
       /// </summary>
       public string Ttl;
       /// <summary>
       /// The <see href="http://www.w3.org/PICS/">PICS</see> rating for the channel.
       /// </summary>
       public string Rating;
       /// <summary>
       /// A hint for aggregators telling them which hours they can skip. 
       /// </summary>
       public string Skiphours;
       /// <summary>
       /// A hint for aggregators telling them which days they can skip. 
       /// </summary>
       public string Skipdays;
       /// <summary>
       /// Email address for person responsible for editorial content.
       /// </summary>
       public string ManagingEditor;
       /// <summary>
       /// A collection of RssItem datatypes, representing each
       /// item for the RSS feed.
       /// </summary>
       public RssItems Items;
       /// <summary>
       /// Contains any errors that occured during the loading or
       /// parsing of the XML document. Compare this to a blank string
       /// to see if any errors occured.
       /// </summary>
       public string ErrorMessage;
   }
   /// <summary>
   /// A data type to represent a single
   /// RSS item in a RSS feed. See <see cref="RssReader">RssReader</see> for
   /// properties of a RSS item which have not yet been implemented 
   /// in this version of the the RssReader class. The descriptions for
   /// the properties of RssItem are para-phrased from the 
   /// <see href="http://blogs.law.harvard.edu/tech/rss">RSS 2 specification.</see>
   /// </summary>
   /// <remarks>
   /// The following elements of a RSS item aren't
   /// supported by this version of RssReader:
   /// <list type="bullet">
   /// <item>category (can have domain attribute)</item>
   /// <item>enclosure ( has attributes: url,length,type )</item>
   /// <item>source (has attributes: url)</item>
   /// </list>
   /// </remarks>
   [Serializable()]
   public struct RssItem {
       /// <summary>
       /// The title of the item.
       /// </summary>
       public string Title;
       /// <summary>
       /// The item synopsis.
       /// </summary>
       public string Description;
       /// <summary>
       /// The URL of the item.
       /// </summary>
       public string Link;
       /// <summary>
       /// Email address of the author of the item. 
       /// </summary>
       public string Author;
       /// <summary>
       /// URL of a page for comments relating to the item
       /// </summary>
       public string Comments;
       /// <summary>
       /// Indicates when the item was published. 
       /// </summary>
       public string Pubdate;
       /// <summary>
       /// A string that uniquely identifies the item.
       /// </summary>
       public string Guid;
   }
   /// <summary>
   /// Represents a collection of RSS items for
   /// the RSS feed.
   /// </summary>
   [Serializable()]
   public class RssItems : CollectionBase {
       public RssItem this[int item] {
           get {
               return this.getItem(item);
           }
       }
       public void Add(RssItem rssItem) {
           List.Add(rssItem);
       }
       public bool Remove(int index) {
           if (index > Count - 1 || index < 0) {
               return false;
           }
           else {
               List.RemoveAt(index);
               return true;
           }
       }
       private RssItem getItem(int Index) {
           return (RssItem)List[Index];
       }
   }
   #endregion

}