public IEnumerable <IDocument> Execute(IReadOnlyList <IDocument> inputs, IExecutionContext context) { XDocument rss = new XDocument(new XDeclaration("1.0", "UTF-8", null)); FilePath rssPath = new DirectoryPath(_siteUri.ToString()).CombineFile(_rssPath); XElement rssRoot = new XElement("rss", new XAttribute("version", "2.0"), new XAttribute(XNamespace.Xmlns + "atom", AtomNamespace) ); XElement channel = new XElement("channel", new XElement("title", _feedTitle), new XElement("description", _feedDescription), new XElement("link", _siteUri.ToString()), new XElement(AtomNamespace + "link", new XAttribute("href", rssPath.FullPath), new XAttribute("rel", "self") ), new XElement("lastBuildDate", DateTimeHelper.ToRssDate(DateTime.Now)) ); if (_language != null) { channel.Add(new XElement("language", _language)); } foreach (IDocument input in inputs) { XElement item = new XElement("item"); object title; bool hasTitle = input.TryGetValue(_titleMetaKey, out title); item.Add(new XElement("title", hasTitle ? title : "Untitled")); // Get the item link FilePath link = input.FilePath(Keys.RssItemUrl) ?? input.FilePath(Keys.RelativeFilePath); if (link != null) { if (link.IsAbsolute) { link = new DirectoryPath("/").GetRelativePath(link); } if (_linkCustomizerDelegate != null) { link = _linkCustomizerDelegate(link); // Check for absolute one more time in case the customizer returned an absolute path if (link.IsAbsolute) { link = new DirectoryPath("/").GetRelativePath(link); } } } if (link == null) { throw new Exception($"Required metadata keys {Keys.RssItemUrl} or {Keys.RelativeFilePath} were not found in the document {input.SourceString()}"); } FilePath itemLink = new DirectoryPath(_siteUri.ToString()).CombineFile(link); item.Add(new XElement("link", itemLink.FullPath)); object description = null; bool hasDescription = input.TryGetValue(_descriptionMetaKey, out description); item.Add(new XElement("description", hasDescription ? description : "No description")); object pubDate = null; if (input.TryGetValue(_pubDateMetaKey, out pubDate)) { item.Add(new XElement("pubDate", DateTimeHelper.ToRssDate(DateTime.Parse((string)pubDate)))); } if (_appendGuid) { object guid; bool hasGuid = input.TryGetValue(_guidMetaKey, out guid); if (hasGuid) { item.Add(new XElement("guid", guid, new XAttribute("isPermaLink", false))); } else { if (_assumePermalinks) { item.Add(new XElement("guid", link)); } else if (input.Source == null) { Trace.Warning($"Cannot generate RSS item guid for document {input.SourceString()} because document source is not valid"); } else { item.Add(new XElement("guid", GuidHelper.ToGuid(input.Source.FullPath).ToString(), new XAttribute("isPermaLink", false))); } } } channel.Add(item); } rss.Add(rssRoot); rssRoot.Add(channel); // rss.ToString() doesn't return XML declaration StringBuilder outText = new StringBuilder(); using (UTF8StringWriter stream = new UTF8StringWriter(outText)) { rss.Save(stream); } return(new IDocument[] { context.GetDocument(outText.ToString(), new KeyValuePair <string, object>[] { new KeyValuePair <string, object>(Keys.IsRssFeed, true), new KeyValuePair <string, object>(Keys.RelativeFilePath, _rssPath), new KeyValuePair <string, object>(Keys.WritePath, _rssPath) }) }); }