From 7bc2a10fd2702e8b647da000a796c5b45f2e1f4d Mon Sep 17 00:00:00 2001 From: simonjfirth Date: Wed, 31 Jul 2024 13:56:53 +0100 Subject: [PATCH 1/8] Added default page to handle base route --- .../Controllers/SitemapController.cs | 26 ++++++++++++++++++- src/Dfe.ContentSupport.Web/Program.cs | 5 +--- .../Views/Sitemap/Index.cshtml | 2 ++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 src/Dfe.ContentSupport.Web/Views/Sitemap/Index.cshtml diff --git a/src/Dfe.ContentSupport.Web/Controllers/SitemapController.cs b/src/Dfe.ContentSupport.Web/Controllers/SitemapController.cs index fe6530d..d57b001 100644 --- a/src/Dfe.ContentSupport.Web/Controllers/SitemapController.cs +++ b/src/Dfe.ContentSupport.Web/Controllers/SitemapController.cs @@ -1,11 +1,35 @@ -using Dfe.ContentSupport.Web.Services; +using Dfe.ContentSupport.Web.Models.Mapped; +using Dfe.ContentSupport.Web.Services; +using Dfe.ContentSupport.Web.ViewModels; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Dfe.ContentSupport.Web.Controllers; +[Route("/sitemap")] +[AllowAnonymous] public class SitemapController(IContentService contentfulService) : Controller { + [HttpGet] + [Route("/")] public async Task Index() + { + var defaultModel = new CsPage + { + Heading = new Models.Heading + { + Title = "Department for Education", + Subtitle = "Content and Support" + } + }; + + + return View(defaultModel); + } + + [HttpGet] + [Route("/sitemap.xml")] + public async Task Sitemap() { var baseUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/"; var sitemap = await contentfulService.GenerateSitemap(baseUrl); diff --git a/src/Dfe.ContentSupport.Web/Program.cs b/src/Dfe.ContentSupport.Web/Program.cs index f2d3870..62b2e40 100644 --- a/src/Dfe.ContentSupport.Web/Program.cs +++ b/src/Dfe.ContentSupport.Web/Program.cs @@ -41,7 +41,7 @@ public static void Main(string[] args) app.UseCookiePolicy(); app.MapControllerRoute( - "sitemap", + "Default", "sitemap.xml", new { controller = "Sitemap", action = "Index" } ); @@ -52,9 +52,6 @@ public static void Main(string[] args) pattern: "{controller=Cache}/{action=Clear}" ); - app.MapControllerRoute( - name: "home", - pattern: "{controller=Home}/{action=Home}"); app.MapControllerRoute( name: "slug", diff --git a/src/Dfe.ContentSupport.Web/Views/Sitemap/Index.cshtml b/src/Dfe.ContentSupport.Web/Views/Sitemap/Index.cshtml new file mode 100644 index 0000000..33c6f37 --- /dev/null +++ b/src/Dfe.ContentSupport.Web/Views/Sitemap/Index.cshtml @@ -0,0 +1,2 @@ +@model CsPage + From 849cf2e83601a0402eaf66fe33d47c1209e0dc9b Mon Sep 17 00:00:00 2001 From: simonjfirth Date: Tue, 6 Aug 2024 15:06:24 +0100 Subject: [PATCH 2/8] Created the ability to filter content by tags --- .../Controllers/ContentController.cs | 4 +- .../Dfe.ContentSupport.Web.csproj | 26 ++++-------- .../Models/ContentBase.cs | 4 +- .../Models/ContentItemBase.cs | 4 +- .../Models/Mapped/CsPage.cs | 2 + .../Models/Mapped/RichTextContentItem.cs | 1 + .../Services/ModelMapper.cs | 38 +++++++++++------ .../Views/Shared/RichText/_RichText.cshtml | 41 +++++++++++-------- src/Dfe.ContentSupport.Web/appsettings.json | 20 --------- 9 files changed, 67 insertions(+), 73 deletions(-) delete mode 100644 src/Dfe.ContentSupport.Web/appsettings.json diff --git a/src/Dfe.ContentSupport.Web/Controllers/ContentController.cs b/src/Dfe.ContentSupport.Web/Controllers/ContentController.cs index d9c4ec0..d025e74 100644 --- a/src/Dfe.ContentSupport.Web/Controllers/ContentController.cs +++ b/src/Dfe.ContentSupport.Web/Controllers/ContentController.cs @@ -29,13 +29,15 @@ public async Task Home() } [HttpGet("{slug}")] - public async Task Index(string slug, bool isPreview = false) + public async Task Index(string slug, bool isPreview = false, [FromQuery]List? tags = null) { if (!ModelState.IsValid) return RedirectToAction("error"); if (string.IsNullOrEmpty(slug)) return RedirectToAction("error"); var resp = await contentService.GetContent(slug, isPreview); if (resp is null) return RedirectToAction("error"); + + ViewBag.tags = tags; return View("CsIndex", resp); } diff --git a/src/Dfe.ContentSupport.Web/Dfe.ContentSupport.Web.csproj b/src/Dfe.ContentSupport.Web/Dfe.ContentSupport.Web.csproj index 056b476..c927a0e 100644 --- a/src/Dfe.ContentSupport.Web/Dfe.ContentSupport.Web.csproj +++ b/src/Dfe.ContentSupport.Web/Dfe.ContentSupport.Web.csproj @@ -8,24 +8,12 @@ - - - - - - Never - true - Never - - - - - - - - - - - + + + + + + + diff --git a/src/Dfe.ContentSupport.Web/Models/ContentBase.cs b/src/Dfe.ContentSupport.Web/Models/ContentBase.cs index 9379dcf..d7d2bc0 100644 --- a/src/Dfe.ContentSupport.Web/Models/ContentBase.cs +++ b/src/Dfe.ContentSupport.Web/Models/ContentBase.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; - namespace Dfe.ContentSupport.Web.Models; [ExcludeFromCodeCoverage] -public class ContentBase +public class ContentBase : Contentful.Core.Models.Entry { public string InternalName { get; set; } = null!; + } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Models/ContentItemBase.cs b/src/Dfe.ContentSupport.Web/Models/ContentItemBase.cs index 475a159..b1e8ea6 100644 --- a/src/Dfe.ContentSupport.Web/Models/ContentItemBase.cs +++ b/src/Dfe.ContentSupport.Web/Models/ContentItemBase.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using Contentful.Core.Models; +using System.Diagnostics.CodeAnalysis; namespace Dfe.ContentSupport.Web.Models; @@ -9,4 +10,5 @@ public class ContentItemBase public string NodeType { get; set; } = null!; public Data Data { get; set; } = null!; public List Content { get; set; } = []; + public ContentfulMetadata Metadata { get; set; } = null!; } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Models/Mapped/CsPage.cs b/src/Dfe.ContentSupport.Web/Models/Mapped/CsPage.cs index f449fce..1196268 100644 --- a/src/Dfe.ContentSupport.Web/Models/Mapped/CsPage.cs +++ b/src/Dfe.ContentSupport.Web/Models/Mapped/CsPage.cs @@ -9,4 +9,6 @@ public class CsPage public string Slug { get; set; } = null!; public bool IsSitemap { get; set; } public List Content { get; set; } = null!; + + public List Tags { get;set; } = null!; } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Models/Mapped/RichTextContentItem.cs b/src/Dfe.ContentSupport.Web/Models/Mapped/RichTextContentItem.cs index c38f1a5..d65671d 100644 --- a/src/Dfe.ContentSupport.Web/Models/Mapped/RichTextContentItem.cs +++ b/src/Dfe.ContentSupport.Web/Models/Mapped/RichTextContentItem.cs @@ -9,4 +9,5 @@ public class RichTextContentItem : CsContentItem public List Content { get; set; } = null!; public RichTextNodeType NodeType { get; set; } = RichTextNodeType.Unknown; public string Value { get; set; } = null!; + public List Tags { get; set; } = []; } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Services/ModelMapper.cs b/src/Dfe.ContentSupport.Web/Services/ModelMapper.cs index 1341d97..3e5434c 100644 --- a/src/Dfe.ContentSupport.Web/Services/ModelMapper.cs +++ b/src/Dfe.ContentSupport.Web/Services/ModelMapper.cs @@ -1,4 +1,5 @@ -using Dfe.ContentSupport.Web.Common; +using Contentful.Core.Models; +using Dfe.ContentSupport.Web.Common; using Dfe.ContentSupport.Web.Configuration; using Dfe.ContentSupport.Web.Models; using Dfe.ContentSupport.Web.Models.Mapped; @@ -23,11 +24,19 @@ public CsPage MapToCsPage(ContentSupportPage incoming) Heading = incoming.Heading, Slug = incoming.Slug, IsSitemap = incoming.IsSitemap, - Content = MapEntriesToContent(incoming.Content) + Content = MapEntriesToContent(incoming.Content), + Tags = FlattenMetadata(incoming.Metadata) }; return result; } + private List FlattenMetadata(ContentfulMetadata item) + { + if (item is null) return new(); + + return item.Tags.Select(_ => _.Sys.Id).ToList(); + } + private List MapEntriesToContent(List entries) { return entries.Select(ConvertEntryToContentItem).ToList(); @@ -36,12 +45,12 @@ private List MapEntriesToContent(List entries) public CsContentItem ConvertEntryToContentItem(Entry entry) { CsContentItem item = entry.RichText is not null - ? MapRichTextContent(entry.RichText)! + ? MapRichTextContent(entry.RichText, entry.Metadata)! : new CsContentItem { InternalName = entry.InternalName }; return item; } - public RichTextContentItem? MapRichTextContent(ContentItemBase? richText) + public RichTextContentItem? MapRichTextContent(ContentItemBase? richText, ContentfulMetadata metaData) { if (richText is null) return null; RichTextContentItem item = @@ -50,6 +59,7 @@ public CsContentItem ConvertEntryToContentItem(Entry entry) InternalName = richText.InternalName, NodeType = ConvertToRichTextNodeType(richText.NodeType), Content = MapRichTextNodes(richText.Content), + Tags = FlattenMetadata(metaData) }; return item; } @@ -57,14 +67,17 @@ public CsContentItem ConvertEntryToContentItem(Entry entry) public List MapRichTextNodes(List nodes) { return nodes.Select(node => MapContent(node) ?? new RichTextContentItem - { NodeType = RichTextNodeType.Unknown, InternalName = node.InternalName }).ToList(); + { NodeType = RichTextNodeType.Unknown, InternalName = node.InternalName }).ToList(); } + public RichTextContentItem? MapContent(ContentItem contentItem) { RichTextContentItem? item; var nodeType = ConvertToRichTextNodeType(contentItem.NodeType); var internalName = contentItem.InternalName; + + switch (nodeType) { case RichTextNodeType.Text: @@ -75,7 +88,7 @@ public List MapRichTextNodes(List nodes) break; case RichTextNodeType.Hyperlink: var uri = contentItem.Data.Uri.ToString(); - item = new Hyperlink + item = new Models.Mapped.Standard.Hyperlink { Uri = uri, IsVimeo = uri.Contains("vimeo.com") @@ -97,7 +110,7 @@ public List MapRichTextNodes(List nodes) item = new EmbeddedEntry { JumpIdentifier = target.JumpIdentifier, - RichText = MapRichTextContent(target.RichText), + RichText = MapRichTextContent(target.RichText, target.Metadata), CustomComponent = GenerateCustomComponent(target) }; break; @@ -124,16 +137,17 @@ public List MapRichTextNodes(List nodes) default: return null; } - + item.Content = MapRichTextNodes(contentItem.Content); item.Value = contentItem.Value; item.InternalName = internalName; + item.Tags = FlattenMetadata(contentItem.Metadata); return item; } public CustomComponent? GenerateCustomComponent(Target target) { - var contentType = target.Sys.ContentType?.Sys.Id; + var contentType = target.SystemProperties.ContentType?.SystemProperties.Id; if (contentType is null) return null; return contentType switch { @@ -178,7 +192,7 @@ private CustomCard GenerateCustomCard(Target target) Uri = target.Uri, Description = target.Description, ImageAlt = target.ImageAlt, - ImageUri = target.Image.Fields.File.Url, + ImageUri = target?.Image?.Fields.File.Url, Meta = target.Meta }; return card; @@ -220,8 +234,8 @@ public RichTextNodeType ConvertToRichTextNodeType(string str) _ => RichTextNodeType.Unknown }; } - - + + public AssetContentType ConvertToAssetContentType(string str) { if (supportedAssetTypes.ImageTypes.Contains(str)) return AssetContentType.Image; diff --git a/src/Dfe.ContentSupport.Web/Views/Shared/RichText/_RichText.cshtml b/src/Dfe.ContentSupport.Web/Views/Shared/RichText/_RichText.cshtml index 5133429..08a44ca 100644 --- a/src/Dfe.ContentSupport.Web/Views/Shared/RichText/_RichText.cshtml +++ b/src/Dfe.ContentSupport.Web/Views/Shared/RichText/_RichText.cshtml @@ -1,61 +1,66 @@ @model RichTextContentItem - @{ + var tags = ViewBag.tags ?? new List(); +} + + +@if (Model.Tags.Count == 0 || Model.Tags.Exists(el => tags.Contains(el))) +{ var nodeType = Model.NodeType; switch (nodeType) { case RichTextNodeType.Document: - + break; case RichTextNodeType.Paragraph: - + break; case RichTextNodeType.Heading2: case RichTextNodeType.Heading3: case RichTextNodeType.Heading4: case RichTextNodeType.Heading5: case RichTextNodeType.Heading6: - + break; case RichTextNodeType.UnorderedList: - + break; case RichTextNodeType.OrderedList: - + break; case RichTextNodeType.ListItem: - + break; case RichTextNodeType.Hyperlink: - + break; case RichTextNodeType.Table: - + break; case RichTextNodeType.TableRow: - + break; case RichTextNodeType.TableHeaderCell: - + break; case RichTextNodeType.TableCell: - + break; case RichTextNodeType.Hr: -
+
break; case RichTextNodeType.EmbeddedAsset: - + break; case RichTextNodeType.Text: - + break; case RichTextNodeType.EmbeddedEntry: - + break; default: - + break; } -} \ No newline at end of file +} diff --git a/src/Dfe.ContentSupport.Web/appsettings.json b/src/Dfe.ContentSupport.Web/appsettings.json deleted file mode 100644 index 169b3df..0000000 --- a/src/Dfe.ContentSupport.Web/appsettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "CacheTimeOutMs": 30000, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "cs:supportedAssetTypes": { - "ImageTypes": [ - "image/jpeg", - "image/png" - ], - "VideoTypes": [ - "video/mp4", - "video/quicktime" - ] - } -} \ No newline at end of file From 0c20f397c812d71304448788e71b6e9ffacc1bfe Mon Sep 17 00:00:00 2001 From: simonjfirth Date: Thu, 15 Aug 2024 14:42:41 +0100 Subject: [PATCH 3/8] Fixed empty reference aria label --- .../Views/Shared/RichText/Custom/_Attachment.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dfe.ContentSupport.Web/Views/Shared/RichText/Custom/_Attachment.cshtml b/src/Dfe.ContentSupport.Web/Views/Shared/RichText/Custom/_Attachment.cshtml index 1b528f2..e7076ef 100644 --- a/src/Dfe.ContentSupport.Web/Views/Shared/RichText/Custom/_Attachment.cshtml +++ b/src/Dfe.ContentSupport.Web/Views/Shared/RichText/Custom/_Attachment.cshtml @@ -44,7 +44,7 @@ -