private static string MakeLinksAbsolute(HtmlParser parser, HtmlMarkupFormatter formatter, IExecutionContext context, string value)
 {
     if (!string.IsNullOrEmpty(value))
     {
         IHtmlDocument          dom      = parser.Parse(string.Empty);
         INodeList              nodes    = parser.ParseFragment(value, dom.Body);
         IEnumerable <IElement> elements = nodes.SelectMany(x => x.Descendents <IElement>().Where(y => y.HasAttribute("href") || y.HasAttribute("src")));
         bool replaced = false;
         foreach (IElement element in elements)
         {
             replaced = MakeLinkAbsolute(element, "href", context) || replaced;
             replaced = MakeLinkAbsolute(element, "src", context) || replaced;
         }
         if (replaced)
         {
             using (StringWriter writer = new StringWriter())
             {
                 nodes.ToHtml(writer, formatter);
                 return(writer.ToString());
             }
         }
     }
     return(null);
 }
        /// <inheritdoc />
        protected override async Task <IEnumerable <IDocument> > ExecuteContextAsync(IExecutionContext context)
        {
            // Get the feed
            Feed feed = new Feed
            {
                Id          = _feedId ?? context.GetLink(),
                Title       = _feedTitle ?? context.Settings.GetString(FeedKeys.Title),
                Description = _feedDescription ?? context.Settings.GetString(FeedKeys.Description),
                Author      = _feedAuthor ?? context.Settings.GetString(FeedKeys.Author),
                Published   = _feedPublished ?? DateTime.UtcNow,
                Updated     = _feedUpdated ?? DateTime.UtcNow,
                Link        = _feedLink ?? TypeHelper.Convert <Uri>(context.GetLink()),
                ImageLink   = _feedImageLink ?? TypeHelper.Convert <Uri>(context.GetLink(context.Settings, FeedKeys.Image, true)),
                Copyright   = _feedCopyright ?? context.Settings.GetString(FeedKeys.Copyright) ?? DateTime.UtcNow.Year.ToString()
            };

            // Sort the items and take the maximum items
            List <(IDocument, DateTime?)> items = new List <(IDocument, DateTime?)>();

            if (_preserveOrdering)
            {
                // Preserve the input order, take first so we don't calculate unnecessary published dates
                foreach (IDocument input in _maximumItems <= 0 ? context.Inputs : context.Inputs.Take(_maximumItems))
                {
                    items.Add((input, await _itemPublished.GetValueAsync(input, context)));
                }
            }
            else
            {
                // Order by published date then take the maximum items
                foreach (IDocument input in context.Inputs)
                {
                    items.Add((input, await _itemPublished.GetValueAsync(input, context)));
                }
                IEnumerable <(IDocument, DateTime?)> orderedItems = items.OrderByDescending(x => x.Item2);
                items = _maximumItems <= 0 ? orderedItems.ToList() : orderedItems.Take(_maximumItems).ToList();
            }

            // Add items
            foreach ((IDocument, DateTime?)item in items)
            {
                feed.Items.Add(new FeedItem
                {
                    Id            = await _itemId.GetValueAsync(item.Item1, context),
                    Title         = await _itemTitle.GetValueAsync(item.Item1, context),
                    Description   = await _itemDescription.GetValueAsync(item.Item1, context),
                    Author        = await _itemAuthor.GetValueAsync(item.Item1, context),
                    Published     = item.Item2,
                    Updated       = await _itemUpdated.GetValueAsync(item.Item1, context),
                    Link          = await _itemLink.GetValueAsync(item.Item1, context),
                    ImageLink     = await _itemImageLink.GetValueAsync(item.Item1, context),
                    Content       = await _itemContent.GetValueAsync(item.Item1, context),
                    ThreadLink    = await _itemThreadLink.GetValueAsync(item.Item1, context),
                    ThreadCount   = await _itemThreadCount.GetValueAsync(item.Item1, context),
                    ThreadUpdated = await _itemThreadUpdated.GetValueAsync(item.Item1, context)
                });
            }

            // Make description and content links absolute
            if (_absolutizeLinks)
            {
                HtmlParser          parser    = new HtmlParser();
                HtmlMarkupFormatter formatter = new HtmlMarkupFormatter();
                foreach (FeedItem feedItem in feed.Items)
                {
                    string description = MakeLinksAbsolute(parser, formatter, context, feedItem.Description);
                    if (description != null)
                    {
                        feedItem.Description = description;
                    }

                    string content = MakeLinksAbsolute(parser, formatter, context, feedItem.Content);
                    if (content != null)
                    {
                        feedItem.Content = content;
                    }
                }
            }

            // Generate the feeds
            return(new[]
            {
                await GenerateFeedAsync(FeedType.Rss, feed, _rssPath, context),
                await GenerateFeedAsync(FeedType.Atom, feed, _atomPath, context),
                await GenerateFeedAsync(FeedType.Rdf, feed, _rdfPath, context)
            }.Where(x => x != null));
        }