From 03dc289991335023aa318dec7aa18d0814adf42f Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Fri, 17 Jul 2026 22:23:39 -0400 Subject: [PATCH] Use favicon if OG image URL is not specified (#52) --- src/MyWebLog.Domain/SupportTypes.fs | 10 ++-- .../Domain/SupportTypesTests.fs | 50 +++++++++++++------ src/MyWebLog/Template.fs | 5 +- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs index 7b78260..de6917e 100644 --- a/src/MyWebLog.Domain/SupportTypes.fs +++ b/src/MyWebLog.Domain/SupportTypes.fs @@ -508,9 +508,10 @@ type OpenGraphImage = { /// The meta properties for this image /// A function to convert relative URLs to absolute URLs + /// A relative URL to use for the image if the image property is blank /// A sequence of key/value pairs for this OpenGraph image file - member this.ToProperties(urlTransform: string -> string) = seq { - let url = urlTransform this.Url + member this.ToProperties(urlTransform: string -> string, fallbackImage: string option) = seq { + let url = urlTransform (if this.Url = "" then defaultArg fallbackImage "" else this.Url) yield "og:image", url if url.StartsWith "https:" then yield "og:image:secure_url", url match this.Type with @@ -691,10 +692,11 @@ type OpenGraphProperties = { /// The meta properties for this page or post /// A function to convert relative URLs to absolute URLs + /// A relative URL to use for the image if the image property is blank /// A sequence of key/value pairs for this set of OpenGraph properties - member this.ToProperties urlTransform = seq { + member this.ToProperties urlTransform fallbackImage = seq { yield "og:type", string this.Type - yield! this.Image.ToProperties urlTransform + yield! this.Image.ToProperties(urlTransform, fallbackImage) match this.Description with Some desc -> yield "og:description", desc | None -> () match this.Determiner with Some det -> yield "og:determiner", det | None -> () match this.Locale with Some loc -> yield "og:locale", loc | None -> () diff --git a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs index 05a9965..01bb40c 100644 --- a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs +++ b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs @@ -356,12 +356,24 @@ let openGraphImageTests = testList "OpenGraphImage" [ let transform = webLog.UrlToAbsolute testList "ToProperties" [ test "succeeds with minimum required" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "http://test.url" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "http://test.url" }.ToProperties(transform, None)) Expect.hasLength props 1 "There should be one property" Expect.equal props[0] ("og:image", "http://test.url") "The URL was not written correctly" } + test "succeeds with fallback URL" { + let props = Array.ofSeq (OpenGraphImage.Empty.ToProperties(transform, Some "theme/it/favicon.ico")) + Expect.hasLength props 2 "There should be one property" + Expect.equal + props[0] ("og:image", "https://unit.test/taco/theme/it/favicon.ico") "The URL was not written correctly" + Expect.equal + props[1] + ("og:image:secure_url", "https://unit.test/taco/theme/it/favicon.ico") + "The secure URL was not written correctly" + } test "succeeds with secure URL" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "https://secure.url" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "https://secure.url" }.ToProperties(transform, None)) Expect.hasLength props 2 "There should be two properties" Expect.equal props[0] ("og:image", "https://secure.url") "The URL was not written correctly" Expect.equal @@ -373,7 +385,7 @@ let openGraphImageTests = testList "OpenGraphImage" [ Type = Some "image/jpeg" Width = Some 400 Height = Some 600 - Alt = Some "This ought to be good" }.ToProperties transform + Alt = Some "This ought to be good" }.ToProperties(transform, None) |> Array.ofSeq Expect.hasLength props 5 "There should be five properties" Expect.equal props[0] ("og:image", "http://test.this") "The URL was not written correctly" @@ -383,7 +395,8 @@ let openGraphImageTests = testList "OpenGraphImage" [ Expect.equal props[4] ("og:image:alt", "This ought to be good") "The alt text was not written correctly" } test "succeeds when deriving BMP and transforming URL" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "old/windows.bmp" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "old/windows.bmp" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[0] ("og:image", "https://unit.test/taco/old/windows.bmp") "The URL was not transformed correctly" @@ -394,48 +407,55 @@ let openGraphImageTests = testList "OpenGraphImage" [ Expect.equal props[2] ("og:image:type", "image/bmp") "The MIME type for BMP was not derived correctly" } test "succeeds when deriving GIF" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "its.a.soft.g.gif" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "its.a.soft.g.gif" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/gif") "The MIME type for GIF was not derived correctly" } test "succeeds when deriving ICO" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "favicon.ico" }.ToProperties transform) + let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "favicon.ico" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/vnd.microsoft.icon") "The MIME type for ICO was not derived correctly" } test "succeeds when deriving JPEG" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "big/name/photo.jpeg" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "big/name/photo.jpeg" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/jpeg") "The MIME type for JPEG was not derived correctly" } test "succeeds when deriving PNG" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "some/nice/graphic.png" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "some/nice/graphic.png" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/png") "The MIME type for PNG was not derived correctly" } test "succeeds when deriving SVG" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "fancy-new-vector.svg" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "fancy-new-vector.svg" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/svg+xml") "The MIME type for SVG was not derived correctly" } test "succeeds when deriving TIF" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.tif" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.tif" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIF was not derived correctly" } test "succeeds when deriving TIFF" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.two.tiff" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.two.tiff" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIFF was not derived correctly" } test "succeeds when deriving WEBP" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "modern/photo.webp" }.ToProperties transform) + let props = + Array.ofSeq ({ OpenGraphImage.Empty with Url = "modern/photo.webp" }.ToProperties(transform, None)) Expect.hasLength props 3 "There should be three properties" Expect.equal props[2] ("og:image:type", "image/webp") "The MIME type for WEBP was not derived correctly" } test "succeeds when type cannot be derived" { - let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "intro.mp3" }.ToProperties transform) + let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "intro.mp3" }.ToProperties(transform, None)) Expect.hasLength props 2 "There should be two properties (only URLs; no type derived)" } ] @@ -630,7 +650,7 @@ let openGraphPropertiesTests = testList "OpenGraphProperties" [ let props = { OpenGraphProperties.Empty with Image = { OpenGraphImage.Empty with Url = "http://this.aint.nothing" } } - .ToProperties WebLog.Empty.UrlToAbsolute + .ToProperties WebLog.Empty.UrlToAbsolute None |> Array.ofSeq Expect.hasLength props 2 "There should have been two properties" Expect.equal props[0] ("og:type", "article") "Type not written correctly" @@ -647,7 +667,7 @@ let openGraphPropertiesTests = testList "OpenGraphProperties" [ LocaleAlternate = Some [ "en_UK"; "es_MX" ] Video = Some { OpenGraphVideo.Empty with Url = "http://this.video.file" } Other = Some [ { Name = "book.publisher"; Value = "Yep" } ] } - .ToProperties WebLog.Empty.UrlToAbsolute + .ToProperties WebLog.Empty.UrlToAbsolute None |> Array.ofSeq Expect.hasLength props 10 "There should have been ten properties" Expect.equal props[0] ("og:type", "book") "Type not written correctly" diff --git a/src/MyWebLog/Template.fs b/src/MyWebLog/Template.fs index a168e4d..33481b7 100644 --- a/src/MyWebLog/Template.fs +++ b/src/MyWebLog/Template.fs @@ -219,8 +219,11 @@ let parser = |> Permalink |> app.WebLog.AbsoluteUrl |> function url -> writeOgProp ("og:url", url) + let fallback = + if assetExists "favicon.ico" app.WebLog then Some $"theme/{app.WebLog.ThemeId}/favicon.ico" + else None match if app.IsPage then app.Page.OpenGraph else app.Posts.Posts[0].OpenGraph with - | Some props -> props.ToProperties app.WebLog.UrlToAbsolute |> Seq.iter writeOgProp + | Some props -> props.ToProperties app.WebLog.UrlToAbsolute fallback |> Seq.iter writeOgProp | None -> () writer.WriteLine $"""{s}"""