RssFeed Retrieve

From ICO wiki
Revision as of 14:10, 16 April 2010 by Ajastrem (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

RSS voogu uudiste nimekiri kätte saamine (abi klass)

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.
    /// <code>
    /// 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 = "&lt;a href=\"%Link%&gt;%Title%&lt;/a&gt;&lt;br/&gt;%Description%&lt;br/&gt;&lt;br/&gt;&lt;ul&gt;%Items%&lt;/ul&gt;";
    ///		string itemTemplate = "&lt;li&gt;&lt;a href=\"%Link%&gt;%Title%&lt;/a&gt;&lt;br/&gt;%Description%&lt;/li&gt;";
    /// 	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);
    /// }
    /// </code>
    /// </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 &lt;channel&gt; 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
}