public void Hashtagify(Dictionary <string, List <ExtendedPost> > hashtags, ExtendedPost post)
        {
            Log.Debug(string.Format("Looking for hashtags in post {0}", post.Id), post);
            var matches = _hashtags.Matches(post.Content);

            foreach (Match match in matches)
            {
                var hashtag = match.Value.ToLower();
                Log.Debug(string.Format("Found hashtag {0} in post {1}", hashtag, post.Id));
                if (!hashtags.ContainsKey(hashtag))
                {
                    hashtags.Add(hashtag, new List <ExtendedPost>());
                }
                if (!hashtags[hashtag].Contains(post))
                {
                    hashtags[hashtag].Add(post);
                }

                post.Content = post.Content.Replace(string.Format("#{0}", match.Value), string.Format("<a href='{0}/{1}.html'>#{2}</a>", Config["siteUrl"], hashtag, match.Value));
            }
        }
        public override void Publish(IEnumerator <Post> posts, int count)
        {
            // git all hashtags from all posts to build recent/popular lists
            List <string>            recentHashtags  = null;
            Dictionary <string, int> popularHashtags = null;

            if (Config["hashtags"] == "true")
            {
                recentHashtags  = new List <string>();
                popularHashtags = new Dictionary <string, int>();
                while (posts.MoveNext())
                {
                    var matches = _hashtags.Matches(posts.Current.Content);
                    foreach (Match match in matches)
                    {
                        var hashtag = match.Value.ToLower();
                        if (!recentHashtags.Contains(hashtag))
                        {
                            recentHashtags.Add(hashtag);
                        }
                        if (!popularHashtags.ContainsKey(hashtag))
                        {
                            popularHashtags.Add(hashtag, 0);
                        }
                        popularHashtags[hashtag] += 1;
                    }
                }
            }

            posts.Reset();

            var invalidations = new List <string> {
                "/"
            };

            var pagePosts = new List <ExtendedPost>();
            var hashtags  = new Dictionary <string, List <ExtendedPost> >();
            var data      = new HandlebarsPageData
            {
                Config      = Config,
                TotalPosts  = count,
                MaxPage     = (int)Math.Ceiling((float)count / _postsPerPage) - 1,
                PageNum     = 0,
                Prefix      = "index",
                RecentTags  = recentHashtags.Take(10),
                PopularTags = popularHashtags != null
                    ? popularHashtags.OrderByDescending(t => t.Value).Take(10).Select(t => t.Key).ToArray()
                    : null
            };

            while (posts.MoveNext())
            {
                var clone = new ExtendedPost((Post)posts.Current.Clone())
                {
                    Config = Config
                };
                if (Config["hashtags"] == "true")
                {
                    Hashtagify(hashtags, clone);
                }

                data.Post = clone;
                invalidations.Add(SavePost(data));
                pagePosts.Add(clone);
                if (pagePosts.Count == _postsPerPage)
                {
                    data.Posts = pagePosts.ToArray();
                    invalidations.Add(SaveIndex(data));
                    if (data.PageNum == 0)
                    {
                        invalidations.Add(SaveFeed(data));
                    }
                    data.PageNum += 1;
                    pagePosts.Clear();
                }
            }
            if (pagePosts.Count > 0)
            {
                data.Posts = pagePosts.ToArray();
                invalidations.Add(SaveIndex(data));
                if (data.PageNum == 0)
                {
                    invalidations.Add(SaveFeed(data));
                }
            }

            foreach (var hashtag in hashtags.Keys)
            {
                var hashData = new HandlebarsPageData
                {
                    Config      = Config,
                    TotalPosts  = hashtags[hashtag].Count,
                    MaxPage     = (int)Math.Ceiling((float)hashtags[hashtag].Count / _postsPerPage) - 1,
                    PageNum     = 0,
                    Prefix      = hashtag,
                    RecentTags  = recentHashtags.Take(10),
                    PopularTags = popularHashtags != null
                        ? popularHashtags.OrderByDescending(t => t.Value).Take(10).Select(t => t.Key).ToArray()
                        : null
                };
                pagePosts.Clear();
                foreach (var post in hashtags[hashtag])
                {
                    pagePosts.Add(post);
                    if (pagePosts.Count == _postsPerPage)
                    {
                        hashData.Posts = pagePosts.ToArray();
                        invalidations.Add(SaveIndex(hashData));
                        hashData.PageNum += 1;
                        pagePosts.Clear();
                    }
                }
                if (pagePosts.Count > 0)
                {
                    hashData.Posts = pagePosts.ToArray();
                    invalidations.Add(SaveIndex(hashData));
                }
            }

            var invalidPaths = invalidations.Where(i => !string.IsNullOrEmpty(i)).ToList();

            invalidPaths.AddRange(UploadDirectory(_assetPath, "assets"));

            Log.Debug("S3 upload complete");
            if (invalidPaths.Count > 0)
            {
                var distId = Config["cloudfrontDistId"];
                if (distId != null)
                {
                    _client.InvalidateCloudFrontDistribution(distId, invalidPaths);
                }
            }
        }