WIP on OpenGraph post/page model (#52)
- Removed SecureUrl prop; will generate if URL starts with https:
This commit is contained in:
parent
3ad6b5a521
commit
e33966b3df
@ -399,18 +399,14 @@ type OpenGraphAudio = {
|
||||
/// <summary>The URL for this audio file</summary>
|
||||
Url: string
|
||||
|
||||
/// <summary>The URL for this audio file for sites that require https</summary>
|
||||
SecureUrl: string option
|
||||
|
||||
/// <summary>The MIME type of the audio file</summary>
|
||||
Type: string option
|
||||
} with
|
||||
|
||||
/// <summary>An empty audio file</summary>
|
||||
static member Empty =
|
||||
{ Url = ""
|
||||
SecureUrl = None
|
||||
Type = None }
|
||||
{ Url = ""
|
||||
Type = None }
|
||||
|
||||
/// <summary>MIME types we can derive from the file extension</summary>
|
||||
static member private DeriveTypes =
|
||||
@ -423,13 +419,10 @@ type OpenGraphAudio = {
|
||||
|
||||
/// <summary>The <c>meta</c> properties for this image</summary>
|
||||
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 = {
|
||||
/// <summary>The URL for this image</summary>
|
||||
Url: string
|
||||
|
||||
/// <summary>The URL for this image for sites that require https</summary>
|
||||
SecureUrl: string option
|
||||
|
||||
/// <summary>The MIME type of the image</summary>
|
||||
Type: string option
|
||||
|
||||
@ -461,12 +451,11 @@ type OpenGraphImage = {
|
||||
|
||||
/// <summary>An empty image file</summary>
|
||||
static member Empty =
|
||||
{ Url = ""
|
||||
SecureUrl = None
|
||||
Type = None
|
||||
Width = None
|
||||
Height = None
|
||||
Alt = None }
|
||||
{ Url = ""
|
||||
Type = None
|
||||
Width = None
|
||||
Height = None
|
||||
Alt = None }
|
||||
|
||||
/// <summary>MIME types we can derive from the file extension</summary>
|
||||
static member private DeriveTypes =
|
||||
@ -485,10 +474,7 @@ type OpenGraphImage = {
|
||||
/// <summary>The <c>meta</c> properties for this image</summary>
|
||||
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 = {
|
||||
/// <summary>The URL for this video</summary>
|
||||
Url: string
|
||||
|
||||
/// <summary>The URL for this video for sites that require https</summary>
|
||||
SecureUrl: string option
|
||||
|
||||
/// <summary>The MIME type of the video</summary>
|
||||
Type: string option
|
||||
|
||||
@ -522,11 +505,10 @@ type OpenGraphVideo = {
|
||||
|
||||
/// <summary>An empty video file</summary>
|
||||
static member Empty =
|
||||
{ Url = ""
|
||||
SecureUrl = None
|
||||
Type = None
|
||||
Width = None
|
||||
Height = None }
|
||||
{ Url = ""
|
||||
Type = None
|
||||
Width = None
|
||||
Height = None }
|
||||
|
||||
/// <summary>MIME types we can derive from the file extension</summary>
|
||||
static member private DeriveTypes =
|
||||
@ -540,10 +522,7 @@ type OpenGraphVideo = {
|
||||
/// <summary>The <c>meta</c> properties for this video</summary>
|
||||
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 ->
|
||||
|
@ -372,6 +372,63 @@ type EditCommonModel() =
|
||||
/// <summary>The text of the page or post</summary>
|
||||
member val Text = "" with get, set
|
||||
|
||||
/// <summary>Whether to assign OpenGraph properties to this page or post</summary>
|
||||
member val AssignOpenGraph = false with get, set
|
||||
|
||||
/// <summary>The type of object represented by this page or post</summary>
|
||||
member val OpenGraphType = "" with get, set
|
||||
|
||||
/// <summary>The URL for the image associated with this page or post</summary>
|
||||
member val OpenGraphImageUrl = "" with get, set
|
||||
|
||||
/// <summary>The MIME type of the image associated with this page or post</summary>
|
||||
member val OpenGraphImageType = "" with get, set
|
||||
|
||||
/// <summary>The width of the image associated with this page or post</summary>
|
||||
member val OpenGraphImageWidth = "" with get, set
|
||||
|
||||
/// <summary>The height of the image associated with this page or post</summary>
|
||||
member val OpenGraphImageHeight = "" with get, set
|
||||
|
||||
/// <summary>The alternate text for the image associated with this page or post</summary>
|
||||
member val OpenGraphImageAlt = "" with get, set
|
||||
|
||||
/// <summary>The URL of an audio file associated with this page or post</summary>
|
||||
member val OpenGraphAudioUrl = "" with get, set
|
||||
|
||||
/// <summary>The MIME type of the audio file associated with this page or post</summary>
|
||||
member val OpenGraphAudioType = "" with get, set
|
||||
|
||||
/// <summary>A short description of this page or post</summary>
|
||||
member val OpenGraphDescription = "" with get, set
|
||||
|
||||
/// <summary>A word excluded from the title when alphabetizing</summary>
|
||||
member val OpenGraphDeterminer = "" with get, set
|
||||
|
||||
/// <summary>The primary locale for this page or post</summary>
|
||||
member val OpenGraphLocale = "" with get, set
|
||||
|
||||
/// <summary>Alternate locales in which this page or post is available</summary>
|
||||
member val OpenGraphAlternateLocales = "" with get, set
|
||||
|
||||
/// <summary>The URL of a video file associated with this page or post</summary>
|
||||
member val OpenGraphVideoUrl = "" with get, set
|
||||
|
||||
/// <summary>The MIME type of a video file associated with this page or post</summary>
|
||||
member val OpenGraphVideoType = "" with get, set
|
||||
|
||||
/// <summary>The width of the video file associated with this page or post</summary>
|
||||
member val OpenGraphVideoWidth = "" with get, set
|
||||
|
||||
/// <summary>The height of the video file associated with this page or post</summary>
|
||||
member val OpenGraphVideoHeight = "" with get, set
|
||||
|
||||
/// <summary>The names of extra OpenGraph properties for this page or post</summary>
|
||||
member val OpenGraphExtraNames: string array = [||] with get, set
|
||||
|
||||
/// <summary>The values of extra OpenGraph properties for this page or post</summary>
|
||||
member val OpenGraphExtraValues: string array = [||] with get, set
|
||||
|
||||
/// <summary>Names of metadata items</summary>
|
||||
member val MetaNames: string array = [||] with get, set
|
||||
|
||||
@ -381,9 +438,40 @@ type EditCommonModel() =
|
||||
/// <summary>Whether this is a new page or post</summary>
|
||||
member this.IsNew with get () = this.Id = "new"
|
||||
|
||||
/// <summary>Populate the OpenGraph properties</summary>
|
||||
/// <param name="og">The existing OpenGraph property set</param>
|
||||
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 -> ()
|
||||
|
||||
/// <summary>Fill the properties of this object from a page</summary>
|
||||
/// <param name="page">The page from which this model should be populated</param>
|
||||
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
|
||||
|
||||
/// <summary>Fill the properties of this object from a post</summary>
|
||||
/// <param name="post">The post from which this model should be populated</param>
|
||||
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
|
||||
|
||||
/// <summary>Convert the properties of the model into a set of OpenGraph properties</summary>
|
||||
/// <param name="webLog">The current web log</param>
|
||||
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
|
||||
|
||||
/// <summary>View model to edit a custom RSS feed</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user