From 83eb1bc565dfb4bdb38d3c5f0cbfbc21b05ad4b2 Mon Sep 17 00:00:00 2001 From: Tommy Parnell Date: Fri, 4 Mar 2022 15:06:22 -0500 Subject: [PATCH] amp --- .../Controllers/HomeController.cs | 14 ++- .../Controllers/SeoController.cs | 1 + .../Factories/BlogFactory.cs | 7 ++ src/TerribleDev.Blog.Web/Models/IPost.cs | 1 + .../Models/IPostContent.cs | 1 + src/TerribleDev.Blog.Web/Models/Post.cs | 1 + .../Models/PostContent.cs | 1 + .../Models/PostViewModel.cs | 9 ++ .../Taghelpers/InlineCss.cs | 30 +++-- .../Taghelpers/InlineJS.cs | 79 +++++++++++++ .../Views/Home/Post.cshtml | 35 +++--- .../Views/Shared/DisplayTemplates/Post.cshtml | 13 ++- .../Views/Shared/Gtm.cshtml | 43 ++++++-- .../Views/Shared/Nav.cshtml | 15 ++- .../Views/Shared/_Layout.cshtml | 104 ++++++++++++------ 15 files changed, 274 insertions(+), 80 deletions(-) create mode 100644 src/TerribleDev.Blog.Web/Models/PostViewModel.cs create mode 100644 src/TerribleDev.Blog.Web/Taghelpers/InlineJS.cs diff --git a/src/TerribleDev.Blog.Web/Controllers/HomeController.cs b/src/TerribleDev.Blog.Web/Controllers/HomeController.cs index fa73d0d..f0f6fcf 100644 --- a/src/TerribleDev.Blog.Web/Controllers/HomeController.cs +++ b/src/TerribleDev.Blog.Web/Controllers/HomeController.cs @@ -66,7 +66,19 @@ namespace TerribleDev.Blog.Web.Controllers this.StatusCode(404); return View(nameof(FourOhFour)); } - return View(model: currentPost); + return View("Post", model: new PostViewModel() { Post = currentPost, IsAmp = false }); + } + [Route("{postUrl}/amp")] + [OutputCache(Duration = 31536000, VaryByParam = "postUrl")] + [ResponseCache(Duration = 900)] + public IActionResult PostAmp(string postUrl) + { + if(!postCache.UrlToPost.TryGetValue(postUrl, out var currentPost)) + { + this.StatusCode(404); + return View(nameof(FourOhFour)); + } + return View("Post", model: new PostViewModel() { Post = currentPost, IsAmp = true }); } [Route("/Error")] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] diff --git a/src/TerribleDev.Blog.Web/Controllers/SeoController.cs b/src/TerribleDev.Blog.Web/Controllers/SeoController.cs index e56874c..fdd7c7a 100644 --- a/src/TerribleDev.Blog.Web/Controllers/SeoController.cs +++ b/src/TerribleDev.Blog.Web/Controllers/SeoController.cs @@ -65,6 +65,7 @@ namespace TerribleDev.Blog.Web.Controllers Urls = postCache.PostsAsLists.Select(a => new SiteMapItem() { LastModified = DateTime.UtcNow, Location = a.CanonicalUrl }).ToList() }; sitemap.Urls.AddRange(sitewideLinks); + sitemap.Urls.AddRange(postCache.PostsAsLists.Select(a => new SiteMapItem() { LastModified = DateTime.UtcNow, Location = a.AMPUrl }).ToList()); ser.Serialize(this.Response.Body, sitemap); } } diff --git a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs index 6e0d7ce..78b9211 100644 --- a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs +++ b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Hosting; using System.Diagnostics; using System.Collections.Concurrent; using Schema.NET; +using System.Text.RegularExpressions; namespace TerribleDev.Blog.Web { @@ -69,6 +70,7 @@ namespace TerribleDev.Blog.Web var postSettings = ParseYaml(ymlRaw); var resolvedUrl = !string.IsNullOrWhiteSpace(postSettings.permalink) ? postSettings.permalink : fileName.Split('.')[0].Replace(' ', '-').WithoutSpecialCharacters(); var canonicalUrl = $"https://blog.terrible.dev/{resolvedUrl}/"; + var ampUrl = $"https://blog.terrible.dev/{resolvedUrl}/amp/"; return new Post() { PublishDate = postSettings.date.ToUniversalTime(), @@ -77,6 +79,7 @@ namespace TerribleDev.Blog.Web Title = postSettings.title, RelativeUrl = $"/{resolvedUrl}/", CanonicalUrl = canonicalUrl, + AMPUrl = ampUrl, UrlWithoutPath = resolvedUrl, Content = new Lazy(() => { @@ -107,8 +110,12 @@ namespace TerribleDev.Blog.Web }, }, }; + // regex remove picture and source tags + var regex = new Regex(@"]*>||]*>|", RegexOptions.IgnoreCase); + var ampContent = regex.Replace(postContent, ""); return new PostContent() { + AmpContent = new HtmlString(ampContent), Content = new HtmlString(postContent), Images = postImages, ContentPlain = postContentPlain, diff --git a/src/TerribleDev.Blog.Web/Models/IPost.cs b/src/TerribleDev.Blog.Web/Models/IPost.cs index 39afe50..1d6752b 100644 --- a/src/TerribleDev.Blog.Web/Models/IPost.cs +++ b/src/TerribleDev.Blog.Web/Models/IPost.cs @@ -9,6 +9,7 @@ namespace TerribleDev.Blog.Web.Models { public interface IPost { + string AMPUrl { get; set; } string CanonicalUrl { get; set; } string UrlWithoutPath { get; set; } string RelativeUrl { get; set; } diff --git a/src/TerribleDev.Blog.Web/Models/IPostContent.cs b/src/TerribleDev.Blog.Web/Models/IPostContent.cs index 1008f34..9c22b34 100644 --- a/src/TerribleDev.Blog.Web/Models/IPostContent.cs +++ b/src/TerribleDev.Blog.Web/Models/IPostContent.cs @@ -7,6 +7,7 @@ namespace TerribleDev.Blog.Web.Models { public interface IPostContent { + public HtmlString AmpContent { get; set; } HtmlString Content { get; set; } HtmlString Summary { get; set; } string ContentPlain { get; set; } diff --git a/src/TerribleDev.Blog.Web/Models/Post.cs b/src/TerribleDev.Blog.Web/Models/Post.cs index c0025a2..c26a451 100644 --- a/src/TerribleDev.Blog.Web/Models/Post.cs +++ b/src/TerribleDev.Blog.Web/Models/Post.cs @@ -9,6 +9,7 @@ namespace TerribleDev.Blog.Web.Models [DebuggerDisplay("{Title}")] public class Post : IPost { + public string AMPUrl { get; set; } public string CanonicalUrl { get; set; } public string UrlWithoutPath { get; set; } public string RelativeUrl { get; set; } diff --git a/src/TerribleDev.Blog.Web/Models/PostContent.cs b/src/TerribleDev.Blog.Web/Models/PostContent.cs index 0f2e214..905b408 100644 --- a/src/TerribleDev.Blog.Web/Models/PostContent.cs +++ b/src/TerribleDev.Blog.Web/Models/PostContent.cs @@ -8,6 +8,7 @@ namespace TerribleDev.Blog.Web.Models public class PostContent : IPostContent { + public HtmlString AmpContent { get; set; } public HtmlString Content { get; set; } public HtmlString Summary { get; set; } public string ContentPlain { get; set; } diff --git a/src/TerribleDev.Blog.Web/Models/PostViewModel.cs b/src/TerribleDev.Blog.Web/Models/PostViewModel.cs new file mode 100644 index 0000000..7b42aab --- /dev/null +++ b/src/TerribleDev.Blog.Web/Models/PostViewModel.cs @@ -0,0 +1,9 @@ +namespace TerribleDev.Blog.Web.Models +{ + public class PostViewModel + { + public IPost Post { get; set; } + public bool IsAmp { get; set; } = false; + + } +} \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Taghelpers/InlineCss.cs b/src/TerribleDev.Blog.Web/Taghelpers/InlineCss.cs index c9fc108..82ec05f 100644 --- a/src/TerribleDev.Blog.Web/Taghelpers/InlineCss.cs +++ b/src/TerribleDev.Blog.Web/Taghelpers/InlineCss.cs @@ -4,7 +4,9 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; namespace TerribleDev.Blog.Web.Taghelpers @@ -29,25 +31,29 @@ namespace TerribleDev.Blog.Web.Taghelpers public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - var path = Href; + var paths = Href.Split(','); // Get the value from the cache, or compute the value and add it to the cache - var fileContent = await Cache.GetOrCreateAsync("InlineStyleTagHelper-" + path, async entry => + var fileContent = await Cache.GetOrCreateAsync("InlineStyleTagHelper-" + paths, async entry => { var fileProvider = HostingEnvironment.WebRootFileProvider; - if(HostingEnvironment.IsDevelopment()) - { - var changeToken = fileProvider.Watch(path); - entry.AddExpirationToken(changeToken); - } + var result = paths.Select(async path => { + if(HostingEnvironment.IsDevelopment()) + { + var changeToken = fileProvider.Watch(path); + entry.AddExpirationToken(changeToken); + } - entry.SetPriority(CacheItemPriority.NeverRemove); + entry.SetPriority(CacheItemPriority.NeverRemove); - var file = fileProvider.GetFileInfo(path); - if (file == null || !file.Exists) - return null; + var file = fileProvider.GetFileInfo(path); + if (file == null || !file.Exists) + return null; - return await ReadFileContent(file); + return await ReadFileContent(file); + }); + var allFinished = await Task.WhenAll(result); + return string.Join("\n", allFinished); }); if (fileContent == null) diff --git a/src/TerribleDev.Blog.Web/Taghelpers/InlineJS.cs b/src/TerribleDev.Blog.Web/Taghelpers/InlineJS.cs new file mode 100644 index 0000000..72e6b0c --- /dev/null +++ b/src/TerribleDev.Blog.Web/Taghelpers/InlineJS.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace TerribleDev.Blog.Web.Taghelpers +{ + [HtmlTargetElement("inline-script")] + public class InlineScriptTagHelper : TagHelper + { + [HtmlAttributeName("src")] + public string Src { get; set; } + + private IWebHostEnvironment HostingEnvironment { get; } + private IMemoryCache Cache { get; } + + + + public InlineScriptTagHelper(IWebHostEnvironment hostingEnvironment, IMemoryCache cache) + { + HostingEnvironment = hostingEnvironment; + Cache = cache; + } + + + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var paths = Src.Split(','); + + // Get the value from the cache, or compute the value and add it to the cache + var fileContent = await Cache.GetOrCreateAsync("InlineScriptTagHelper-" + paths, async entry => + { + var fileProvider = HostingEnvironment.WebRootFileProvider; + var result = paths.Select(async path => { + if(HostingEnvironment.IsDevelopment()) + { + var changeToken = fileProvider.Watch(path); + entry.AddExpirationToken(changeToken); + } + + entry.SetPriority(CacheItemPriority.NeverRemove); + + var file = fileProvider.GetFileInfo(path); + if (file == null || !file.Exists) + return null; + + return await ReadFileContent(file); + }); + var allFinished = await Task.WhenAll(result); + return string.Join("\n", allFinished); + }); + + if (fileContent == null) + { + output.SuppressOutput(); + return; + } + + output.TagName = "script"; + output.Attributes.RemoveAll("href"); + output.Content.AppendHtml(fileContent); + } + + private static async Task ReadFileContent(IFileInfo file) + { + using (var stream = file.CreateReadStream()) + using (var textReader = new StreamReader(stream)) + { + return await textReader.ReadToEndAsync(); + } + } + } +} diff --git a/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml b/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml index 944f76a..aeaddc3 100644 --- a/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml @@ -1,40 +1,43 @@ @inject BlogConfiguration config -@model IPost +@model PostViewModel @{ - ViewData["Title"] = @Model.Title; + ViewData["Title"] = Model.Post.Title; + ViewData["amp"] = Model.IsAmp; } - @Html.DisplayForModel() + @Html.DisplayFor(m => m.Post, "Post") @section Head { - + - - + + - - + + - - + + - - @foreach(var image in Model.Content.Value.Images.Take(6)) + + + @foreach(var image in Model.Post.Content.Value.Images.Take(6)) { } - @if(Model.Content.Value.Images.Count > 0) + @if(Model.Post.Content.Value.Images.Count > 0) { - + } } + diff --git a/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml b/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml index 25a4256..fc73a4d 100644 --- a/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml @@ -1,9 +1,18 @@ @model IPost - +@{ + var amp = ViewData["amp"] as bool? ?? false; +}

@Model.Title

- @Model.Content.Value.Content + @if(amp) + { + @Model.Content.Value.AmpContent + } + else + { + @Model.Content.Value.Content + } @if (Model.tags.Count > 0) {
diff --git a/src/TerribleDev.Blog.Web/Views/Shared/Gtm.cshtml b/src/TerribleDev.Blog.Web/Views/Shared/Gtm.cshtml index c0b5e57..d41ed2d 100644 --- a/src/TerribleDev.Blog.Web/Views/Shared/Gtm.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Shared/Gtm.cshtml @@ -1,17 +1,36 @@  @{ Layout = null; + var amp = ViewData["amp"] as bool? ?? false; } - +@if(amp) +{ + +} +else +{ + + + +} \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Views/Shared/Nav.cshtml b/src/TerribleDev.Blog.Web/Views/Shared/Nav.cshtml index 577bc50..6c71a96 100644 --- a/src/TerribleDev.Blog.Web/Views/Shared/Nav.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Shared/Nav.cshtml @@ -1,9 +1,20 @@ -
- @RenderSection("Scripts", required: false) - - - - - - + @if(amp) + { + + + + } + else + { + @RenderSection("Scripts", required: false) + + + + + + + } +