Use favicon if OG image URL is not specified (#52)

This commit is contained in:
2026-07-17 22:23:39 -04:00
parent abfae6786d
commit 03dc289991
3 changed files with 45 additions and 20 deletions
+6 -4
View File
@@ -508,9 +508,10 @@ type OpenGraphImage = {
/// <summary>The <c>meta</c> properties for this image</summary> /// <summary>The <c>meta</c> properties for this image</summary>
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param> /// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
/// <param name="fallbackImage">A relative URL to use for the image if the image property is blank</param>
/// <returns>A sequence of key/value pairs for this OpenGraph image file</returns> /// <returns>A sequence of key/value pairs for this OpenGraph image file</returns>
member this.ToProperties(urlTransform: string -> string) = seq { member this.ToProperties(urlTransform: string -> string, fallbackImage: string option) = seq {
let url = urlTransform this.Url let url = urlTransform (if this.Url = "" then defaultArg fallbackImage "" else this.Url)
yield "og:image", url yield "og:image", url
if url.StartsWith "https:" then yield "og:image:secure_url", url if url.StartsWith "https:" then yield "og:image:secure_url", url
match this.Type with match this.Type with
@@ -691,10 +692,11 @@ type OpenGraphProperties = {
/// <summary>The <c>meta</c> properties for this page or post</summary> /// <summary>The <c>meta</c> properties for this page or post</summary>
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param> /// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
/// <param name="fallbackImage">A relative URL to use for the image if the image property is blank</param>
/// <returns>A sequence of key/value pairs for this set of OpenGraph properties</returns> /// <returns>A sequence of key/value pairs for this set of OpenGraph properties</returns>
member this.ToProperties urlTransform = seq { member this.ToProperties urlTransform fallbackImage = seq {
yield "og:type", string this.Type 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.Description with Some desc -> yield "og:description", desc | None -> ()
match this.Determiner with Some det -> yield "og:determiner", det | None -> () match this.Determiner with Some det -> yield "og:determiner", det | None -> ()
match this.Locale with Some loc -> yield "og:locale", loc | None -> () match this.Locale with Some loc -> yield "og:locale", loc | None -> ()
+35 -15
View File
@@ -356,12 +356,24 @@ let openGraphImageTests = testList "OpenGraphImage" [
let transform = webLog.UrlToAbsolute let transform = webLog.UrlToAbsolute
testList "ToProperties" [ testList "ToProperties" [
test "succeeds with minimum required" { 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.hasLength props 1 "There should be one property"
Expect.equal props[0] ("og:image", "http://test.url") "The URL was not written correctly" 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" { 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.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 props[0] ("og:image", "https://secure.url") "The URL was not written correctly"
Expect.equal Expect.equal
@@ -373,7 +385,7 @@ let openGraphImageTests = testList "OpenGraphImage" [
Type = Some "image/jpeg" Type = Some "image/jpeg"
Width = Some 400 Width = Some 400
Height = Some 600 Height = Some 600
Alt = Some "This ought to be good" }.ToProperties transform Alt = Some "This ought to be good" }.ToProperties(transform, None)
|> Array.ofSeq |> Array.ofSeq
Expect.hasLength props 5 "There should be five properties" Expect.hasLength props 5 "There should be five properties"
Expect.equal props[0] ("og:image", "http://test.this") "The URL was not written correctly" 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" 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" { 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.hasLength props 3 "There should be three properties"
Expect.equal Expect.equal
props[0] ("og:image", "https://unit.test/taco/old/windows.bmp") "The URL was not transformed correctly" 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" Expect.equal props[2] ("og:image:type", "image/bmp") "The MIME type for BMP was not derived correctly"
} }
test "succeeds when deriving GIF" { 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.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" Expect.equal props[2] ("og:image:type", "image/gif") "The MIME type for GIF was not derived correctly"
} }
test "succeeds when deriving ICO" { 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.hasLength props 3 "There should be three properties"
Expect.equal Expect.equal
props[2] ("og:image:type", "image/vnd.microsoft.icon") "The MIME type for ICO was not derived correctly" props[2] ("og:image:type", "image/vnd.microsoft.icon") "The MIME type for ICO was not derived correctly"
} }
test "succeeds when deriving JPEG" { 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.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" Expect.equal props[2] ("og:image:type", "image/jpeg") "The MIME type for JPEG was not derived correctly"
} }
test "succeeds when deriving PNG" { 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.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" Expect.equal props[2] ("og:image:type", "image/png") "The MIME type for PNG was not derived correctly"
} }
test "succeeds when deriving SVG" { 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.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" Expect.equal props[2] ("og:image:type", "image/svg+xml") "The MIME type for SVG was not derived correctly"
} }
test "succeeds when deriving TIF" { 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.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" Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIF was not derived correctly"
} }
test "succeeds when deriving TIFF" { 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.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" Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIFF was not derived correctly"
} }
test "succeeds when deriving WEBP" { 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.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" 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" { 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)" Expect.hasLength props 2 "There should be two properties (only URLs; no type derived)"
} }
] ]
@@ -630,7 +650,7 @@ let openGraphPropertiesTests = testList "OpenGraphProperties" [
let props = let props =
{ OpenGraphProperties.Empty with { OpenGraphProperties.Empty with
Image = { OpenGraphImage.Empty with Url = "http://this.aint.nothing" } } Image = { OpenGraphImage.Empty with Url = "http://this.aint.nothing" } }
.ToProperties WebLog.Empty.UrlToAbsolute .ToProperties WebLog.Empty.UrlToAbsolute None
|> Array.ofSeq |> Array.ofSeq
Expect.hasLength props 2 "There should have been two properties" Expect.hasLength props 2 "There should have been two properties"
Expect.equal props[0] ("og:type", "article") "Type not written correctly" 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" ] LocaleAlternate = Some [ "en_UK"; "es_MX" ]
Video = Some { OpenGraphVideo.Empty with Url = "http://this.video.file" } Video = Some { OpenGraphVideo.Empty with Url = "http://this.video.file" }
Other = Some [ { Name = "book.publisher"; Value = "Yep" } ] } Other = Some [ { Name = "book.publisher"; Value = "Yep" } ] }
.ToProperties WebLog.Empty.UrlToAbsolute .ToProperties WebLog.Empty.UrlToAbsolute None
|> Array.ofSeq |> Array.ofSeq
Expect.hasLength props 10 "There should have been ten properties" Expect.hasLength props 10 "There should have been ten properties"
Expect.equal props[0] ("og:type", "book") "Type not written correctly" Expect.equal props[0] ("og:type", "book") "Type not written correctly"
+4 -1
View File
@@ -219,8 +219,11 @@ let parser =
|> Permalink |> Permalink
|> app.WebLog.AbsoluteUrl |> app.WebLog.AbsoluteUrl
|> function url -> writeOgProp ("og:url", url) |> 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 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 -> () | None -> ()
writer.WriteLine $"""{s}<meta name=generator content="{app.Generator}">""" writer.WriteLine $"""{s}<meta name=generator content="{app.Generator}">"""