diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs
index 9fe14d2..f271343 100644
--- a/src/MyWebLog.Domain/SupportTypes.fs
+++ b/src/MyWebLog.Domain/SupportTypes.fs
@@ -399,18 +399,14 @@ type OpenGraphAudio = {
/// The URL for this audio file
Url: string
- /// The URL for this audio file for sites that require https
- SecureUrl: string option
-
/// The MIME type of the audio file
Type: string option
} with
/// An empty audio file
static member Empty =
- { Url = ""
- SecureUrl = None
- Type = None }
+ { Url = ""
+ Type = None }
/// MIME types we can derive from the file extension
static member private DeriveTypes =
@@ -423,13 +419,10 @@ type OpenGraphAudio = {
/// The meta properties for this image
member this.Properties = seq {
- yield ("og:audio", this.Url)
- match this.SecureUrl with
- | Some url -> yield ("og:audio:secure_url", url)
- | None when this.Url.StartsWith "https:" -> yield ("og:audio:secure_url", this.Url)
- | None -> ()
+ yield "og:audio", this.Url
+ if this.Url.StartsWith "https:" then yield "og:audio:secure_url", this.Url
match this.Type with
- | Some typ -> yield ("og:audio:type", typ)
+ | Some typ -> yield "og:audio:type", typ
| None ->
match deriveMimeType this.Url OpenGraphAudio.DeriveTypes with
| Some it -> yield "og:audio:type", it
@@ -443,9 +436,6 @@ type OpenGraphImage = {
/// The URL for this image
Url: string
- /// The URL for this image for sites that require https
- SecureUrl: string option
-
/// The MIME type of the image
Type: string option
@@ -461,12 +451,11 @@ type OpenGraphImage = {
/// An empty image file
static member Empty =
- { Url = ""
- SecureUrl = None
- Type = None
- Width = None
- Height = None
- Alt = None }
+ { Url = ""
+ Type = None
+ Width = None
+ Height = None
+ Alt = None }
/// MIME types we can derive from the file extension
static member private DeriveTypes =
@@ -485,10 +474,7 @@ type OpenGraphImage = {
/// The meta properties for this image
member this.Properties = seq {
yield "og:image", this.Url
- match this.SecureUrl with
- | Some url -> yield "og:image:secure_url", url
- | None when this.Url.StartsWith "https:" -> yield "og:image:secure_url", this.Url
- | None -> ()
+ if this.Url.StartsWith "https:" then yield "og:image:secure_url", this.Url
match this.Type with
| Some typ -> yield "og:image:type", typ
| None ->
@@ -507,9 +493,6 @@ type OpenGraphVideo = {
/// The URL for this video
Url: string
- /// The URL for this video for sites that require https
- SecureUrl: string option
-
/// The MIME type of the video
Type: string option
@@ -522,11 +505,10 @@ type OpenGraphVideo = {
/// An empty video file
static member Empty =
- { Url = ""
- SecureUrl = None
- Type = None
- Width = None
- Height = None }
+ { Url = ""
+ Type = None
+ Width = None
+ Height = None }
/// MIME types we can derive from the file extension
static member private DeriveTypes =
@@ -540,10 +522,7 @@ type OpenGraphVideo = {
/// The meta properties for this video
member this.Properties = seq {
yield "og:video", this.Url
- match this.SecureUrl with
- | Some url -> yield "og:video:secure_url", url
- | None when this.Url.StartsWith "https:" -> yield "og:video:secure_url", this.Url
- | None -> ()
+ if this.Url.StartsWith "https:" then yield "og:video:secure_url", this.Url
match this.Type with
| Some typ -> yield "og:video:type", typ
| None ->
diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs
index 776f801..20f631e 100644
--- a/src/MyWebLog.Domain/ViewModels.fs
+++ b/src/MyWebLog.Domain/ViewModels.fs
@@ -372,6 +372,63 @@ type EditCommonModel() =
/// The text of the page or post
member val Text = "" with get, set
+ /// Whether to assign OpenGraph properties to this page or post
+ member val AssignOpenGraph = false with get, set
+
+ /// The type of object represented by this page or post
+ member val OpenGraphType = "" with get, set
+
+ /// The URL for the image associated with this page or post
+ member val OpenGraphImageUrl = "" with get, set
+
+ /// The MIME type of the image associated with this page or post
+ member val OpenGraphImageType = "" with get, set
+
+ /// The width of the image associated with this page or post
+ member val OpenGraphImageWidth = "" with get, set
+
+ /// The height of the image associated with this page or post
+ member val OpenGraphImageHeight = "" with get, set
+
+ /// The alternate text for the image associated with this page or post
+ member val OpenGraphImageAlt = "" with get, set
+
+ /// The URL of an audio file associated with this page or post
+ member val OpenGraphAudioUrl = "" with get, set
+
+ /// The MIME type of the audio file associated with this page or post
+ member val OpenGraphAudioType = "" with get, set
+
+ /// A short description of this page or post
+ member val OpenGraphDescription = "" with get, set
+
+ /// A word excluded from the title when alphabetizing
+ member val OpenGraphDeterminer = "" with get, set
+
+ /// The primary locale for this page or post
+ member val OpenGraphLocale = "" with get, set
+
+ /// Alternate locales in which this page or post is available
+ member val OpenGraphAlternateLocales = "" with get, set
+
+ /// The URL of a video file associated with this page or post
+ member val OpenGraphVideoUrl = "" with get, set
+
+ /// The MIME type of a video file associated with this page or post
+ member val OpenGraphVideoType = "" with get, set
+
+ /// The width of the video file associated with this page or post
+ member val OpenGraphVideoWidth = "" with get, set
+
+ /// The height of the video file associated with this page or post
+ member val OpenGraphVideoHeight = "" with get, set
+
+ /// The names of extra OpenGraph properties for this page or post
+ member val OpenGraphExtraNames: string array = [||] with get, set
+
+ /// The values of extra OpenGraph properties for this page or post
+ member val OpenGraphExtraValues: string array = [||] with get, set
+
/// Names of metadata items
member val MetaNames: string array = [||] with get, set
@@ -381,9 +438,40 @@ type EditCommonModel() =
/// Whether this is a new page or post
member this.IsNew with get () = this.Id = "new"
+ /// Populate the OpenGraph properties
+ /// The existing OpenGraph property set
+ member private this.PopulateOpenGraph(og: OpenGraphProperties) =
+ this.AssignOpenGraph <- true
+ this.OpenGraphImageUrl <- og.Image.Url
+ this.OpenGraphImageType <- defaultArg og.Image.Type ""
+ this.OpenGraphImageWidth <- defaultArg (og.Image.Width |> Option.map string) ""
+ this.OpenGraphImageHeight <- defaultArg (og.Image.Height |> Option.map string) ""
+ this.OpenGraphImageAlt <- defaultArg og.Image.Alt ""
+ this.OpenGraphDescription <- defaultArg og.Description ""
+ this.OpenGraphDeterminer <- defaultArg og.Determiner ""
+ this.OpenGraphLocale <- defaultArg og.Locale ""
+ this.OpenGraphAlternateLocales <- defaultArg (og.LocaleAlternate |> Option.map (String.concat ", ")) ""
+ match og.Audio with
+ | Some audio ->
+ this.OpenGraphAudioUrl <- audio.Url
+ this.OpenGraphAudioType <- defaultArg audio.Type ""
+ | None -> ()
+ match og.Video with
+ | Some video ->
+ this.OpenGraphVideoUrl <- video.Url
+ this.OpenGraphVideoType <- defaultArg video.Type ""
+ this.OpenGraphVideoWidth <- defaultArg (video.Width |> Option.map string) ""
+ this.OpenGraphVideoHeight <- defaultArg (video.Height |> Option.map string) ""
+ | None -> ()
+ match og.Other with
+ | Some other ->
+ this.OpenGraphExtraNames <- other |> List.map _.Name |> Array.ofList
+ this.OpenGraphExtraValues <- other |> List.map _.Value |> Array.ofList
+ | None -> ()
+
/// Fill the properties of this object from a page
/// The page from which this model should be populated
- member this.PopulateFromPage (page: Page) =
+ member this.PopulateFromPage(page: Page) =
let latest = findLatestRevision page.Revisions
let metaItems = if page.Metadata.Length = 0 then [ MetaItem.Empty ] else page.Metadata
this.Id <- string page.Id
@@ -395,10 +483,11 @@ type EditCommonModel() =
this.Text <- latest.Text.Text
this.MetaNames <- metaItems |> List.map _.Name |> Array.ofList
this.MetaValues <- metaItems |> List.map _.Value |> Array.ofList
+ page.OpenGraph |> Option.iter this.PopulateOpenGraph
/// Fill the properties of this object from a post
/// The post from which this model should be populated
- member this.PopulateFromPost (post: Post) =
+ member this.PopulateFromPost(post: Post) =
let latest = findLatestRevision post.Revisions
let metaItems = if post.Metadata.Length = 0 then [ MetaItem.Empty ] else post.Metadata
this.Id <- string post.Id
@@ -411,7 +500,52 @@ type EditCommonModel() =
this.Text <- latest.Text.Text
this.MetaNames <- metaItems |> List.map _.Name |> Array.ofList
this.MetaValues <- metaItems |> List.map _.Value |> Array.ofList
+ post.OpenGraph |> Option.iter this.PopulateOpenGraph
+ /// Convert the properties of the model into a set of OpenGraph properties
+ /// The current web log
+ member this.ToOpenGraph(webLog: WebLog) =
+ if this.AssignOpenGraph then
+ let toAbsolute (url: string) =
+ if url.StartsWith "http" then url else webLog.AbsoluteUrl (Permalink url)
+ let audio =
+ match this.OpenGraphAudioUrl.Trim() with
+ | "" -> None
+ | url -> Some { OpenGraphAudio.Url = toAbsolute url; Type = noneIfBlank this.OpenGraphAudioType }
+ let video =
+ match this.OpenGraphVideoUrl.Trim() with
+ | "" -> None
+ | url ->
+ Some {
+ OpenGraphVideo.Url = toAbsolute url
+ Type = noneIfBlank this.OpenGraphVideoType
+ Width = noneIfBlank this.OpenGraphVideoWidth |> Option.map int
+ Height = noneIfBlank this.OpenGraphVideoHeight |> Option.map int
+ }
+ Some {
+ Type = if this.OpenGraphType = "" then Article else OpenGraphType.Parse this.OpenGraphType
+ Image = {
+ Url = toAbsolute this.OpenGraphImageUrl
+ Type = noneIfBlank this.OpenGraphImageType
+ Width = noneIfBlank this.OpenGraphImageWidth |> Option.map int
+ Height = noneIfBlank this.OpenGraphImageHeight |> Option.map int
+ Alt = noneIfBlank this.OpenGraphImageAlt
+ }
+ Description = noneIfBlank this.OpenGraphDescription
+ Determiner = noneIfBlank this.OpenGraphDeterminer
+ Locale = noneIfBlank this.OpenGraphLocale
+ LocaleAlternate = noneIfBlank this.OpenGraphAlternateLocales
+ |> Option.map (fun alts -> alts.Split ',' |> Array.map _.Trim() |> List.ofArray)
+ Audio = audio
+ Video = video
+ Other = Seq.zip this.OpenGraphExtraNames this.OpenGraphExtraValues
+ |> Seq.filter (fun it -> fst it > "")
+ |> Seq.map (fun it -> { Name = fst it; Value = snd it })
+ |> List.ofSeq
+ |> function it -> match it with [] -> None | _ -> Some it
+ }
+ else
+ None
/// View model to edit a custom RSS feed
[]
diff --git a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs
index e215441..884a471 100644
--- a/src/MyWebLog.Tests/Domain/SupportTypesTests.fs
+++ b/src/MyWebLog.Tests/Domain/SupportTypesTests.fs
@@ -273,16 +273,10 @@ let openGraphAudioTests = testList "OpenGraphAudio" [
props[1] ("og:audio:secure_url", "https://test.this") "The Secure URL was not written correctly"
}
test "succeeds with all properties filled" {
- let props =
- { Url = "http://test.this"
- SecureUrl = Some "https://test.other"
- Type = Some "audio/mpeg" }.Properties
- |> Array.ofSeq
- Expect.hasLength props 3 "There should be three properties"
+ let props = Array.ofSeq { Url = "http://test.this"; Type = Some "audio/mpeg" }.Properties
+ Expect.hasLength props 2 "There should be two properties"
Expect.equal props[0] ("og:audio", "http://test.this") "The URL was not written correctly"
- Expect.equal
- props[1] ("og:audio:secure_url", "https://test.other") "The Secure URL was not written correctly"
- Expect.equal props[2] ("og:audio:type", "audio/mpeg") "The MIME type was not written correctly"
+ Expect.equal props[1] ("og:audio:type", "audio/mpeg") "The MIME type was not written correctly"
}
test "succeeds when deriving AAC" {
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/this/cool.file.aac" }.Properties
@@ -333,21 +327,18 @@ let openGraphImageTests = testList "OpenGraphImage" [
}
test "succeeds with all properties filled" {
let props =
- { Url = "http://test.this"
- SecureUrl = Some "https://test.other"
- Type = Some "image/jpeg"
- Width = Some 400
- Height = Some 600
- Alt = Some "This ought to be good" }.Properties
+ { Url = "http://test.this"
+ Type = Some "image/jpeg"
+ Width = Some 400
+ Height = Some 600
+ Alt = Some "This ought to be good" }.Properties
|> Array.ofSeq
- Expect.hasLength props 6 "There should be six 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[1] ("og:image:secure_url", "https://test.other") "The Secure URL was not written correctly"
- Expect.equal props[2] ("og:image:type", "image/jpeg") "The MIME type was not written correctly"
- Expect.equal props[3] ("og:image:width", "400") "The width was not written correctly"
- Expect.equal props[4] ("og:image:height", "600") "The height was not written correctly"
- Expect.equal props[5] ("og:image:alt", "This ought to be good") "The alt text was not written correctly"
+ Expect.equal props[1] ("og:image:type", "image/jpeg") "The MIME type was not written correctly"
+ Expect.equal props[2] ("og:image:width", "400") "The width was not written correctly"
+ Expect.equal props[3] ("og:image:height", "600") "The height 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" {
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/old/windows.bmp" }.Properties
@@ -419,19 +410,16 @@ let openGraphVideoTests = testList "OpenGraphVideo" [
}
test "succeeds with all properties filled" {
let props =
- { Url = "http://test.this"
- SecureUrl = Some "https://test.other"
- Type = Some "video/mpeg"
- Width = Some 1200
- Height = Some 900 }.Properties
+ { Url = "http://test.this"
+ Type = Some "video/mpeg"
+ Width = Some 1200
+ Height = Some 900 }.Properties
|> Array.ofSeq
- Expect.hasLength props 5 "There should be five properties"
+ Expect.hasLength props 4 "There should be five properties"
Expect.equal props[0] ("og:video", "http://test.this") "The URL was not written correctly"
- Expect.equal
- props[1] ("og:video:secure_url", "https://test.other") "The Secure URL was not written correctly"
- Expect.equal props[2] ("og:video:type", "video/mpeg") "The MIME type was not written correctly"
- Expect.equal props[3] ("og:video:width", "1200") "The width was not written correctly"
- Expect.equal props[4] ("og:video:height", "900") "The height was not written correctly"
+ Expect.equal props[1] ("og:video:type", "video/mpeg") "The MIME type was not written correctly"
+ Expect.equal props[2] ("og:video:width", "1200") "The width was not written correctly"
+ Expect.equal props[3] ("og:video:height", "900") "The height was not written correctly"
}
test "succeeds when deriving AVI" {
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/my.video.avi" }.Properties