private static async Task GetFavoriteIconAsync(Settings settings, TemplatingOptions options) { var iconUrl = settings.Icon; if (string.IsNullOrWhiteSpace(settings.Icon)) { iconUrl = "favicon.ico"; } var httpClient = new HttpClient(); var response = await httpClient.GetAsync(new Uri(options.GhostUrl, iconUrl.Replace(settings.Url, string.Empty))); if (response.IsSuccessStatusCode) { var iconBytes = await response.Content.ReadAsByteArrayAsync(); var path = iconUrl.Replace(settings.Url, string.Empty); path = Path.Combine(options.OutputDirectory, path); var fileInfo = new System.IO.FileInfo(path); var directory = fileInfo.Directory; if (!directory.Exists) { directory.Create(); } File.WriteAllBytes(path, iconBytes); } }
private static async Task HandlePostAsync(Post post, Settings settings, TemplatingOptions options, IServiceProvider services, IRazorViewEngine engine, string type) { var httpContext = new DefaultHttpContext { RequestServices = services }; var routeData = new RouteData(); var actionDescriptor = new ActionDescriptor(); var modelStateDictionary = new ModelStateDictionary(); var modelMetadataProvider = new EmptyModelMetadataProvider(); var tempDataProvider = new VirtualTempDataProvider(); var htmlHelperOptions = new HtmlHelperOptions(); var actionContext = new ActionContext(httpContext, routeData, actionDescriptor, modelStateDictionary); var viewDataDictionary = new ViewDataDictionary(modelMetadataProvider, modelStateDictionary); var tempDataDictionary = new TempDataDictionary(httpContext, tempDataProvider); viewDataDictionary.Model = post; using (var stringWriter = new StringWriter()) { var view = engine.GetView(".", $"/Templates/{type}.cshtml", true); var viewContext = new ViewContext(actionContext, view.View, viewDataDictionary, tempDataDictionary, stringWriter, htmlHelperOptions); await view.View.RenderAsync(viewContext); var result = stringWriter.ToString(); var directory = Path.Combine(options.OutputDirectory, post.Slug); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(Path.Combine(directory, "index.html"), result); await DownloadImagesFromPostAsync(post.HTML, settings, options); if (type == "post") { //amp it view = engine.GetView(".", $"/Templates/post-amp.cshtml", true); viewContext = new ViewContext(actionContext, view.View, viewDataDictionary, tempDataDictionary, stringWriter, htmlHelperOptions); await view.View.RenderAsync(viewContext); result = stringWriter.ToString(); directory = Path.Combine(directory, "amp"); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(Path.Combine(directory, "index.html"), result); } } }
private static void BuildSitemap(Page[] pages, Post[] posts, Author[] authors, Tag[] tags, Settings settings, TemplatingOptions options) { //build the index that ties the maps together var siteIndex = new Sitemap.sitemapindex { sitemap = GetArray( new Sitemap.tSitemap { lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "sitemaps/pages.xml" }, new Sitemap.tSitemap { lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "sitemaps/posts.xml" }, new Sitemap.tSitemap { lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "sitemaps/authors.xml" }, new Sitemap.tSitemap { lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "sitemaps/tags.xml" }) }; File.WriteAllBytes(Path.Combine(options.OutputDirectory, "sitemap.xml"), XmlSerialize(siteIndex)); var sitemapDirectory = Path.Combine(options.OutputDirectory, "sitemaps"); if (!Directory.Exists(sitemapDirectory)) { Directory.CreateDirectory(sitemapDirectory); } var map = new Sitemap.urlset { url = pages.Select(page => new Sitemap.tUrl { changefreq = Sitemap.tChangeFreq.daily, changefreqSpecified = true, lastmod = page.UpdatedAt.ToString("O"), loc = settings.Url + page.Slug + "/" }).ToArray() }; File.WriteAllBytes(Path.Combine(sitemapDirectory, "pages.xml"), XmlSerialize(map)); map = new Sitemap.urlset { url = posts.Select(post => new Sitemap.tUrl { changefreq = Sitemap.tChangeFreq.daily, changefreqSpecified = true, lastmod = post.UpdatedAt.ToString("O"), loc = settings.Url + post.Slug + "/" }).ToArray() }; File.WriteAllBytes(Path.Combine(sitemapDirectory, "posts.xml"), XmlSerialize(map)); map = new Sitemap.urlset { url = authors.Select(author => new Sitemap.tUrl { changefreq = Sitemap.tChangeFreq.daily, changefreqSpecified = true, lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "authors/" + author.Slug + "/" }).ToArray() }; File.WriteAllBytes(Path.Combine(sitemapDirectory, "authors.xml"), XmlSerialize(map)); map = new Sitemap.urlset { url = tags.Select(tag => new Sitemap.tUrl { changefreq = Sitemap.tChangeFreq.daily, changefreqSpecified = true, lastmod = DateTime.UtcNow.ToString("O"), loc = settings.Url + "tag/" + tag.Slug + "/" }).ToArray() }; File.WriteAllBytes(Path.Combine(sitemapDirectory, "tags.xml"), XmlSerialize(map)); }
private static void BuildRssFeed(Post[] posts, Author[] authors, Tag[] tags, Settings settings, TemplatingOptions options, IServiceProvider services, IRazorViewEngine engine) { var targetDirectory = Path.Combine(options.OutputDirectory, "rss"); if (!Directory.Exists(targetDirectory)) { Directory.CreateDirectory(targetDirectory); } var oldestPost = DateTime.Now.AddMonths(-6); var items = new List <RssFeed.Item>(); foreach (var post in posts.Where(x => x.PublishedAt > oldestPost)) { var item = new RssFeed.Item(); item.Authors = post.Authors.Select(author => new RssFeed.ItemAuthor { Name = author.Name, Uri = settings.Url + "author/" + author.Slug }).ToArray(); item.Categories = post.Tags.Select(tag => tag.Name).ToArray(); item.Content = post.HTML; item.Creator = post.PrimaryAuthor.Name; item.Description = post.MetaDescription ?? post.CustomExcerpt ?? post.Excerpt; item.Guid = new RssFeed.ItemGuid { Value = post.UUID }; item.Link = post.Settings.Url + post.Slug + "/"; item.PublishedDate = post.PublishedAt.Value.ToString("R"); item.Title = post.Title; items.Add(item); } var feed = new RssFeed.Feed(); var channel = new RssFeed.Channel(); channel.AtomLink = new RssFeed.AtomLink { HRef = options.RssFeed, Relationship = "self", Type = "application/rss+xml" }; channel.Categories = tags.Select(tag => tag.Name).ToArray(); channel.Contributors = authors.Select(author => new RssFeed.Contributor { Name = author.Name, Uri = settings.Url + "author/" + author.Slug + "/" }).ToArray(); channel.Copyright = options.FormattedCopyright; channel.Description = settings.Description; channel.Generator = options.GeneratorName; channel.Image = new RssFeed.Image { Link = settings.Url, Title = settings.Title, Url = settings.Url + "favicon.png" }; channel.Items = items.ToArray(); channel.LastBuildDate = DateTime.UtcNow.ToString("R"); channel.Link = settings.Url; channel.Title = settings.Title; channel.TTL = 60; feed.Channels = new[] { channel }; feed.Version = "2.0"; using var memoryStream = new MemoryStream(); using var xmlWriter = XmlWriter.Create(memoryStream, new XmlWriterSettings { Indent = true, }); var namespaces = new XmlSerializerNamespaces(); namespaces.Add("dc", "http://purl.org/dc/elements/1.1/"); namespaces.Add("content", "http://purl.org/rss/1.0/modules/content/"); namespaces.Add("atom", "http://www.w3.org/2005/Atom"); namespaces.Add("media", "http://search.yahoo.com/mrss/"); var serializer = new XmlSerializer(typeof(RssFeed.Feed)); serializer.Serialize(xmlWriter, feed, namespaces); xmlWriter.Flush(); File.WriteAllBytes(Path.Combine(targetDirectory, "index.html"), memoryStream.ToArray()); }
private static async Task DownloadImagesFromPostAsync(string post, Settings settings, TemplatingOptions options) { //this straight sucks var imageRegex = new Regex("<img.+?>"); var tagMatches = imageRegex.Matches(post); if (tagMatches.Count > 0) { foreach (Match tagMatch in tagMatches) { var quoteRegex = new Regex("\".+?\""); var quoteMatches = quoteRegex.Matches(tagMatch.Value); foreach (Match quoteMatch in quoteMatches) { var splits = quoteMatch.Value.Split(' '); foreach (var split in splits) { var item = split.Trim('\"'); if (item.StartsWith(settings.Url)) { //we have an image, download it. var imageResult = await _imageClient.GetAsync(new Uri(options.GhostUrl, item.Replace(settings.Url, ""))); if (imageResult.IsSuccessStatusCode) { var filePath = item.Replace(settings.Url, string.Empty); var destinationFile = Path.Combine(options.OutputDirectory, filePath); var destination = new System.IO.FileInfo(destinationFile); var destinationDirectory = destination.Directory.FullName; if (!Directory.Exists(destinationDirectory)) { Directory.CreateDirectory(destinationDirectory); } var image = await imageResult.Content.ReadAsByteArrayAsync(); if (File.Exists(destinationFile)) { File.Delete(destinationFile); } File.WriteAllBytes(destinationFile, image); } } } } } } }
private static async Task HandleIndexPageAsync(Post[] posts, Settings settings, TemplatingOptions options, IServiceProvider services, IRazorViewEngine engine) { var postsToDisplay = 27; var httpContext = new DefaultHttpContext { RequestServices = services }; var routeData = new RouteData(); var actionDescriptor = new ActionDescriptor(); var modelStateDictionary = new ModelStateDictionary(); var modelMetadataProvider = new EmptyModelMetadataProvider(); var tempDataProvider = new VirtualTempDataProvider(); var htmlHelperOptions = new HtmlHelperOptions(); var actionContext = new ActionContext(httpContext, routeData, actionDescriptor, modelStateDictionary); var viewDataDictionary = new ViewDataDictionary(modelMetadataProvider, modelStateDictionary); var tempDataDictionary = new TempDataDictionary(httpContext, tempDataProvider); var imageDirectory = Path.Combine(options.OutputDirectory, "content", "images", "index"); var index = new IndexPage { Options = options, Settings = settings }; posts = posts.OrderByDescending(x => x.PublishedAt).ToArray(); if (!Directory.Exists(imageDirectory)) { Directory.CreateDirectory(imageDirectory); } if (!string.IsNullOrWhiteSpace(settings.CoverImage)) { using var client = new HttpClient(); var imageResult = await client.GetAsync(new Uri(options.GhostUrl, settings.CoverImage)); if (imageResult.IsSuccessStatusCode) { using var imageStream = await imageResult.Content.ReadAsStreamAsync(); using var memoryStream = new MemoryStream(); await imageStream.CopyToAsync(memoryStream); var widths = new[] { 600, 1000, 2000 }; foreach (var width in widths) { memoryStream.Position = 0; using var image = await Image.LoadAsync(memoryStream); index.ImageHeight = image.Height; index.ImageWidth = image.Width; image.Mutate(context => context.Resize(width, 0)); await image.SaveAsPngAsync(Path.Combine(imageDirectory, width + ".png")); } } } viewDataDictionary.Model = index; var batch = 1; while (posts.Length > 0) { var directory = options.OutputDirectory; if (batch > 1) { directory = Path.Combine(directory, "index", batch.ToString()); } if (batch == 1) { index.Previous = null; } else if (batch == 2) { index.Previous = "/"; } else { index.Previous = "/index/" + (batch - 1) + "/"; } if (posts.Length > postsToDisplay) { index.Next = "/index/" + (batch + 1) + "/"; } else { index.Next = null; } using (var stringWriter = new StringWriter()) { var view = engine.GetView(".", $"/Templates/index.cshtml", true); var viewContext = new ViewContext(actionContext, view.View, viewDataDictionary, tempDataDictionary, stringWriter, htmlHelperOptions); index.PostsToDisplay = posts.Take(postsToDisplay).ToArray(); await view.View.RenderAsync(viewContext); var result = stringWriter.ToString(); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(Path.Combine(directory, "index.html"), result); } posts = posts.Skip(postsToDisplay).ToArray(); batch++; } }
private static async Task HandleTagAsync(Tag tag, Post[] posts, Settings settings, TemplatingOptions options, IServiceProvider services, IRazorViewEngine engine) { var postsToDisplay = 27; var httpContext = new DefaultHttpContext { RequestServices = services }; var routeData = new RouteData(); var actionDescriptor = new ActionDescriptor(); var modelStateDictionary = new ModelStateDictionary(); var modelMetadataProvider = new EmptyModelMetadataProvider(); var tempDataProvider = new VirtualTempDataProvider(); var htmlHelperOptions = new HtmlHelperOptions(); posts = posts.Where(x => x.Tags.Any(x => x.Id == tag.Id)).OrderByDescending(x => x.PublishedAt).ToArray(); tag.PostCount = posts.Length; var actionContext = new ActionContext(httpContext, routeData, actionDescriptor, modelStateDictionary); var viewDataDictionary = new ViewDataDictionary(modelMetadataProvider, modelStateDictionary); var tempDataDictionary = new TempDataDictionary(httpContext, tempDataProvider); viewDataDictionary.Model = tag; var batch = 1; while (posts.Length > 0) { var directory = Path.Combine(options.OutputDirectory, "tag", tag.Slug); if (batch > 1) { directory = Path.Combine(directory, batch.ToString()); } if (batch == 1) { tag.Previous = null; } else if (batch == 2) { tag.Previous = tag.Url; } else { tag.Previous = tag.Url + (batch - 1) + "/"; } if (posts.Length > postsToDisplay) { tag.Next = tag.Url + (batch + 1) + "/"; } else { tag.Next = null; } using (var stringWriter = new StringWriter()) { var view = engine.GetView(".", $"/Templates/tag.cshtml", true); var viewContext = new ViewContext(actionContext, view.View, viewDataDictionary, tempDataDictionary, stringWriter, htmlHelperOptions); tag.PostsToDisplay = posts.Take(postsToDisplay).ToArray(); await view.View.RenderAsync(viewContext); var result = stringWriter.ToString(); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(Path.Combine(directory, "index.html"), result); } posts = posts.Skip(postsToDisplay).ToArray(); batch++; } }