Ejemplo n.º 1
0
        public RSSFeedModel(NonNullImmutableList <PostWithRelatedPostStubs> posts, IRetrievePostSlugs postSlugRetriever, ICache postContentCache)
        {
            if (posts == null)
            {
                throw new ArgumentNullException(nameof(posts));
            }
            if (!posts.Any())
            {
                throw new ArgumentException("posts may not be an empty list");
            }

            Posts             = posts;
            PostSlugRetriever = postSlugRetriever ?? throw new ArgumentNullException(nameof(postSlugRetriever));
            PostContentCache  = postContentCache ?? throw new ArgumentNullException(nameof(postContentCache));
        }
Ejemplo n.º 2
0
        public static async Task <HtmlString> RenderPost(
            this IHtmlHelper helper,
            PostWithRelatedPostStubs post,
            Post previousPostIfAny,
            Post nextPostIfAny,
            IRetrievePostSlugs postSlugRetriever,
            ICache cache)
        {
            if (helper == null)
            {
                throw new ArgumentNullException(nameof(helper));
            }
            if (post == null)
            {
                throw new ArgumentNullException(nameof(post));
            }
            if (postSlugRetriever == null)
            {
                throw new ArgumentNullException(nameof(postSlugRetriever));
            }
            if (cache == null)
            {
                throw new ArgumentNullException(nameof(cache));
            }

            var cacheKey = string.Format(
                "PostHelper-RenderPost-{0}-{1}-{2}",
                post.Id,
                (previousPostIfAny == null) ? 0 : previousPostIfAny.Id,
                (nextPostIfAny == null) ? 0 : nextPostIfAny.Id
                );
            var cachedData = cache[cacheKey];

            if (cachedData != null)
            {
                if ((cachedData is CachablePostContent cachedPostContent) && (cachedPostContent.LastModified >= post.LastModified))
                {
                    return(new HtmlString(cachedPostContent.RenderableContent));
                }
                cache.Remove(cacheKey);
            }

            var content = await GetRenderableContent(helper, post, previousPostIfAny, nextPostIfAny, includeTitle : true, includePostedDate : true, includeTags : true, postSlugRetriever);

            cache[cacheKey] = new CachablePostContent(content, post.LastModified);
            return(new HtmlString(content));
        }
Ejemplo n.º 3
0
 public PostListModel(
     string title,
     NonNullImmutableList <PostWithRelatedPostStubs> posts,
     Post previousPostIfAny,
     Post nextPostIfAny,
     NonNullImmutableList <PostStub> recent,
     NonNullImmutableList <PostStub> highlights,
     NonNullImmutableList <ArchiveMonthLink> archiveLinks,
     PostListDisplayOptions postListDisplay,
     string optionalCanonicalLinkBase,
     string optionalGoogleAnalyticsId,
     string optionalDisqusShortName,
     TwitterCardDetails optionalTwitterCardDetails,
     IRetrievePostSlugs postSlugRetriever,
     ICache postContentCache)
 {
     if (string.IsNullOrWhiteSpace(title))
     {
         throw new ArgumentException("Null/blank title specified");
     }
     if (recent == null)
     {
         throw new ArgumentNullException(nameof(recent));
     }
     if (archiveLinks == null)
     {
         throw new ArgumentNullException(nameof(archiveLinks));
     }
     if (!Enum.IsDefined(typeof(PostListDisplayOptions), postListDisplay))
     {
         throw new ArgumentOutOfRangeException(nameof(postListDisplay));
     }
     Title                      = title.Trim();
     Posts                      = posts ?? throw new ArgumentNullException(nameof(posts));
     PreviousPostIfAny          = previousPostIfAny;
     NextPostIfAny              = nextPostIfAny;
     MostRecent                 = recent.Sort((x, y) => - x.Posted.CompareTo(y.Posted));
     Highlights                 = highlights ?? throw new ArgumentNullException(nameof(highlights));
     ArchiveLinks               = archiveLinks.Sort((x, y) => - (new DateTime(x.Year, x.Month, 1)).CompareTo(new DateTime(y.Year, y.Month, 1)));
     PostListDisplay            = postListDisplay;
     OptionalCanonicalLinkBase  = string.IsNullOrWhiteSpace(optionalCanonicalLinkBase) ? null : optionalCanonicalLinkBase.Trim();
     OptionalGoogleAnalyticsId  = string.IsNullOrWhiteSpace(optionalGoogleAnalyticsId) ? null : optionalGoogleAnalyticsId.Trim();
     OptionalDisqusShortName    = string.IsNullOrWhiteSpace(optionalDisqusShortName) ? null : optionalDisqusShortName.Trim();
     OptionalTwitterCardDetails = optionalTwitterCardDetails;
     PostSlugRetriever          = postSlugRetriever ?? throw new ArgumentNullException(nameof(postSlugRetriever));
     PostContentCache           = postContentCache ?? throw new ArgumentNullException(nameof(postContentCache));
 }
Ejemplo n.º 4
0
        public static async Task <string> RenderPostForRSS(this IHtmlHelper helper, PostWithRelatedPostStubs post, string scheme, HostString host, IRetrievePostSlugs postSlugRetriever, ICache cache)
        {
            if (helper == null)
            {
                throw new ArgumentNullException(nameof(helper));
            }
            if (post == null)
            {
                throw new ArgumentNullException(nameof(post));
            }
            if (string.IsNullOrWhiteSpace(scheme))
            {
                throw new ArgumentException("Null/blank/whitespace-only scheme");
            }
            if (!host.HasValue)
            {
                throw new ArgumentException("No host value");
            }
            if (postSlugRetriever == null)
            {
                throw new ArgumentNullException(nameof(postSlugRetriever));
            }
            if (cache == null)
            {
                throw new ArgumentNullException(nameof(cache));
            }

            var cacheKey   = "PostHelper-RenderPostForRSS-" + post.Id;
            var cachedData = cache[cacheKey];

            if (cachedData != null)
            {
                if ((cachedData is CachablePostContent cachedPostContent) && (cachedPostContent.LastModified >= post.LastModified))
                {
                    return(cachedPostContent.RenderableContent);
                }
                cache.Remove(cacheKey);
            }

            var content = await GetRenderableContent(helper, post, previousPostIfAny : null, nextPostIfAny : null, includeTitle : false, includePostedDate : false, includeTags : false, postSlugRetriever);

            var doc = new HtmlDocument();

            doc.LoadHtml(content);
            MakeUrlsAbsolute(doc, "a", "href", scheme, host);
            MakeUrlsAbsolute(doc, "img", "src", scheme, host);
            using (var writer = new StringWriter())
            {
                doc.Save(writer);
                content = writer.ToString();
            }

            cache[cacheKey] = new CachablePostContent(content, post.LastModified);
            return(content);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Update any markdown links where the target is of the form "Post{0}" to replace with the real url
        /// </summary>
        private static async Task <string> HandlePostLinks(IHtmlHelper helper, string content, IRetrievePostSlugs postSlugRetriever)
        {
            if (helper == null)
            {
                throw new ArgumentNullException(nameof(helper));
            }
            if (string.IsNullOrEmpty(content))
            {
                return(content);
            }
            if (postSlugRetriever == null)
            {
                throw new ArgumentNullException(nameof(postSlugRetriever));
            }

            var rewrittenContent = new StringBuilder();
            var lastIndex        = 0;

            foreach (Match match in Regex.Matches(content, @"\[([^\]]*?)\]\(Post(\d+)\)", RegexOptions.Multiline))
            {
                var text = match.Groups[1].Value;
                var id   = int.Parse(match.Groups[2].Value);

                rewrittenContent
                .Append(content, lastIndex, match.Index - lastIndex)
                .Append(helper.RenderedActionLink(text, "ArchiveBySlug", "ViewPost", new { Slug = await postSlugRetriever.GetSlug(id) }));

                lastIndex = match.Index + match.Length;
            }
            rewrittenContent.Append(content, lastIndex, content.Length - lastIndex);
            return(rewrittenContent.ToString());
        }
Ejemplo n.º 6
0
        private static async Task <string> GetRenderableContent(
            IHtmlHelper helper,
            PostWithRelatedPostStubs post,
            Post previousPostIfAny,
            Post nextPostIfAny,
            bool includeTitle,
            bool includePostedDate,
            bool includeTags,
            IRetrievePostSlugs postSlugRetriever)
        {
            if (helper == null)
            {
                throw new ArgumentNullException(nameof(helper));
            }
            if (post == null)
            {
                throw new ArgumentNullException(nameof(post));
            }
            if (postSlugRetriever == null)
            {
                throw new ArgumentNullException(nameof(postSlugRetriever));
            }

            var markdownContent = await HandlePostLinks(
                helper,
                includeTitle?(await ReplaceFirstLineHashHeaderWithPostLink(post, postSlugRetriever)) : RemoveTitle(post.MarkdownContent),
                postSlugRetriever
                );

            var content = new StringBuilder();

            if (includePostedDate)
            {
                content.AppendFormat("<h3 class=\"PostDate\">{0}</h3>", post.Posted.ToString("d MMMM yyyy"));
            }
            content.Append(
                MarkdownTransformations.ToHtml(markdownContent)
                );
            if (includePostedDate)
            {
                content.AppendFormat("<p class=\"PostTime\">Posted at {0}</p>", post.Posted.ToString("HH:mm"));
            }
            if ((previousPostIfAny != null) || (nextPostIfAny != null))
            {
                content.Append("<div class=\"PreviousAndNext\">");
                if (previousPostIfAny != null)
                {
                    content.Append("<div class=\"Previous\">");
                    content.Append("<h3>Last time:</h3>");
                    content.Append(
                        helper.RenderedActionLink(previousPostIfAny.Title, "ArchiveBySlug", "ViewPost", new { previousPostIfAny.Slug }, new { @class = "Previous" })
                        );
                    content.Append("</div>");
                }
                if (nextPostIfAny != null)
                {
                    content.Append("<div class=\"Next\">");
                    content.Append("<h3>Next:</h3>");
                    content.Append(
                        helper.RenderedActionLink(nextPostIfAny.Title, "ArchiveBySlug", "ViewPost", new { nextPostIfAny.Slug }, new { @class = "Next" })
                        );
                    content.Append("</div>");
                }
                content.Append("</div>");
            }
            if (post.RelatedPosts.Any())
            {
                content.Append("<div class=\"Related\">");
                content.Append("<h3>You may also be interested in:</h3>");
                content.Append("<ul>");
                foreach (var relatedPost in post.RelatedPosts)
                {
                    content.AppendFormat(
                        "<li>{0}</li>",
                        helper.RenderedActionLink(relatedPost.Title, "ArchiveBySlug", "ViewPost", new { relatedPost.Slug }, null)
                        );
                }
                content.Append("</ul>");
                content.Append("</div>");
            }
            else if (post.AutoSuggestedRelatedPosts.Any())
            {
                // Only display the auto-suggested related posts if there are no manually-picked related posts
                content.Append("<div class=\"Related\">");
                content.AppendFormat(
                    "<h3>You may also be interested in (see {0} for information about how these are generated):</h3>",
                    helper.RenderedActionLink("here", "ArchiveBySlug", "ViewPost", new { Slug = "automating-suggested-related-posts-links-for-my-blog-posts" }, null)
                    );
                content.Append("<ul>");
                foreach (var relatedPost in post.AutoSuggestedRelatedPosts)
                {
                    content.AppendFormat(
                        "<li>{0}</li>",
                        helper.RenderedActionLink(relatedPost.Title, "ArchiveBySlug", "ViewPost", new { relatedPost.Slug }, null)
                        );
                }
                content.Append("</ul>");
                content.Append("</div>");
            }
            if (includeTags)
            {
                content.Append(GetTagLinksContent(helper, post.Tags));
            }
            return(content.ToString());
        }