Version 2.1 #41
@ -73,82 +73,6 @@ type DisplayCategory = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A display version of an episode chapter
|
|
||||||
type DisplayChapter = {
|
|
||||||
/// The start time of the chapter (H:mm:ss.FF format)
|
|
||||||
StartTime: string
|
|
||||||
|
|
||||||
/// The title of the chapter
|
|
||||||
Title: string
|
|
||||||
|
|
||||||
/// An image to display for this chapter
|
|
||||||
ImageUrl: string
|
|
||||||
|
|
||||||
/// A URL with information about this chapter
|
|
||||||
Url: string
|
|
||||||
|
|
||||||
/// Whether this chapter should be displayed in podcast players
|
|
||||||
IsHidden: bool
|
|
||||||
|
|
||||||
/// The end time of the chapter (H:mm:ss.FF format)
|
|
||||||
EndTime: string
|
|
||||||
|
|
||||||
/// The name of a location
|
|
||||||
LocationName: string
|
|
||||||
|
|
||||||
/// The geographic coordinates of the location
|
|
||||||
LocationGeo: string
|
|
||||||
|
|
||||||
/// An OpenStreetMap query for this location
|
|
||||||
LocationOsm: string
|
|
||||||
} with
|
|
||||||
|
|
||||||
/// Create a display chapter from a chapter
|
|
||||||
static member FromChapter (chapter: Chapter) =
|
|
||||||
let pattern = DurationPattern.CreateWithInvariantCulture "H:mm:ss.FF"
|
|
||||||
{ StartTime = pattern.Format chapter.StartTime
|
|
||||||
Title = defaultArg chapter.Title ""
|
|
||||||
ImageUrl = defaultArg chapter.ImageUrl ""
|
|
||||||
Url = defaultArg chapter.Url ""
|
|
||||||
IsHidden = defaultArg chapter.IsHidden false
|
|
||||||
EndTime = chapter.EndTime |> Option.map pattern.Format |> Option.defaultValue ""
|
|
||||||
LocationName = chapter.Location |> Option.map _.Name |> Option.defaultValue ""
|
|
||||||
LocationGeo = chapter.Location |> Option.map _.Geo |> Option.defaultValue ""
|
|
||||||
LocationOsm = chapter.Location |> Option.map _.Osm |> Option.flatten |> Option.defaultValue "" }
|
|
||||||
|
|
||||||
|
|
||||||
/// A display version of a custom feed definition
|
|
||||||
type DisplayCustomFeed = {
|
|
||||||
/// The ID of the custom feed
|
|
||||||
Id: string
|
|
||||||
|
|
||||||
/// The source of the custom feed
|
|
||||||
Source: string
|
|
||||||
|
|
||||||
/// The relative path at which the custom feed is served
|
|
||||||
Path: string
|
|
||||||
|
|
||||||
/// Whether this custom feed is for a podcast
|
|
||||||
IsPodcast: bool
|
|
||||||
} with
|
|
||||||
|
|
||||||
/// Create a display version from a custom feed
|
|
||||||
static member FromFeed (cats: DisplayCategory array) (feed: CustomFeed) =
|
|
||||||
let source =
|
|
||||||
match feed.Source with
|
|
||||||
| Category (CategoryId catId) ->
|
|
||||||
cats
|
|
||||||
|> Array.tryFind (fun cat -> cat.Id = catId)
|
|
||||||
|> Option.map _.Name
|
|
||||||
|> Option.defaultValue "--INVALID; DELETE THIS FEED--"
|
|
||||||
|> sprintf "Category: %s"
|
|
||||||
| Tag tag -> $"Tag: {tag}"
|
|
||||||
{ Id = string feed.Id
|
|
||||||
Source = source
|
|
||||||
Path = string feed.Path
|
|
||||||
IsPodcast = Option.isSome feed.Podcast }
|
|
||||||
|
|
||||||
|
|
||||||
/// Details about a page used to display page lists
|
/// Details about a page used to display page lists
|
||||||
[<NoComparison; NoEquality>]
|
[<NoComparison; NoEquality>]
|
||||||
type DisplayPage = {
|
type DisplayPage = {
|
||||||
@ -269,50 +193,6 @@ type DisplayUpload = {
|
|||||||
Source = string source }
|
Source = string source }
|
||||||
|
|
||||||
|
|
||||||
/// View model to display a user's information
|
|
||||||
[<NoComparison; NoEquality>]
|
|
||||||
type DisplayUser = {
|
|
||||||
/// The ID of the user
|
|
||||||
Id: string
|
|
||||||
|
|
||||||
/// The user name (e-mail address)
|
|
||||||
Email: string
|
|
||||||
|
|
||||||
/// The user's first name
|
|
||||||
FirstName: string
|
|
||||||
|
|
||||||
/// The user's last name
|
|
||||||
LastName: string
|
|
||||||
|
|
||||||
/// The user's preferred name
|
|
||||||
PreferredName: string
|
|
||||||
|
|
||||||
/// The URL of the user's personal site
|
|
||||||
Url: string
|
|
||||||
|
|
||||||
/// The user's access level
|
|
||||||
AccessLevel: string
|
|
||||||
|
|
||||||
/// When the user was created
|
|
||||||
CreatedOn: DateTime
|
|
||||||
|
|
||||||
/// When the user last logged on
|
|
||||||
LastSeenOn: Nullable<DateTime>
|
|
||||||
} with
|
|
||||||
|
|
||||||
/// Construct a displayed user from a web log user
|
|
||||||
static member FromUser (webLog: WebLog) (user: WebLogUser) =
|
|
||||||
{ Id = string user.Id
|
|
||||||
Email = user.Email
|
|
||||||
FirstName = user.FirstName
|
|
||||||
LastName = user.LastName
|
|
||||||
PreferredName = user.PreferredName
|
|
||||||
Url = defaultArg user.Url ""
|
|
||||||
AccessLevel = string user.AccessLevel
|
|
||||||
CreatedOn = webLog.LocalTime user.CreatedOn
|
|
||||||
LastSeenOn = user.LastSeenOn |> Option.map webLog.LocalTime |> Option.toNullable }
|
|
||||||
|
|
||||||
|
|
||||||
/// View model for editing categories
|
/// View model for editing categories
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type EditCategoryModel = {
|
type EditCategoryModel = {
|
||||||
@ -386,19 +266,19 @@ type EditChapterModel = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// Create a display chapter from a chapter
|
/// Create a display chapter from a chapter
|
||||||
static member FromChapter (postId: PostId) idx chapter =
|
static member FromChapter (postId: PostId) idx (chapter: Chapter) =
|
||||||
let it = DisplayChapter.FromChapter chapter
|
let pattern = DurationPattern.CreateWithInvariantCulture "H:mm:ss.FF"
|
||||||
{ PostId = string postId
|
{ PostId = string postId
|
||||||
Index = idx
|
Index = idx
|
||||||
StartTime = it.StartTime
|
StartTime = pattern.Format chapter.StartTime
|
||||||
Title = it.Title
|
Title = defaultArg chapter.Title ""
|
||||||
ImageUrl = it.ImageUrl
|
ImageUrl = defaultArg chapter.ImageUrl ""
|
||||||
Url = it.Url
|
Url = defaultArg chapter.Url ""
|
||||||
IsHidden = it.IsHidden
|
IsHidden = defaultArg chapter.IsHidden false
|
||||||
EndTime = it.EndTime
|
EndTime = chapter.EndTime |> Option.map pattern.Format |> Option.defaultValue ""
|
||||||
LocationName = it.LocationName
|
LocationName = chapter.Location |> Option.map _.Name |> Option.defaultValue ""
|
||||||
LocationGeo = it.LocationGeo
|
LocationGeo = chapter.Location |> Option.map _.Geo |> Option.defaultValue ""
|
||||||
LocationOsm = it.LocationOsm
|
LocationOsm = chapter.Location |> Option.map _.Osm |> Option.flatten |> Option.defaultValue ""
|
||||||
AddAnother = false }
|
AddAnother = false }
|
||||||
|
|
||||||
/// Create a chapter from the values in this model
|
/// Create a chapter from the values in this model
|
||||||
@ -427,6 +307,76 @@ type EditChapterModel = {
|
|||||||
Location = location }
|
Location = location }
|
||||||
|
|
||||||
|
|
||||||
|
/// View model common to page and post edits
|
||||||
|
type EditCommonModel() =
|
||||||
|
|
||||||
|
/// Find the latest revision within a list of revisions
|
||||||
|
let findLatestRevision (revs: Revision list) =
|
||||||
|
match revs |> List.sortByDescending _.AsOf |> List.tryHead with Some rev -> rev | None -> Revision.Empty
|
||||||
|
|
||||||
|
/// The ID of the page or post
|
||||||
|
member val Id = "" with get, set
|
||||||
|
|
||||||
|
/// The title of the page or post
|
||||||
|
member val Title = "" with get, set
|
||||||
|
|
||||||
|
/// The permalink for the page or post
|
||||||
|
member val Permalink = "" with get, set
|
||||||
|
|
||||||
|
/// The entity to which this model applies ("page" or "post")
|
||||||
|
member val Entity = "" with get, set
|
||||||
|
|
||||||
|
/// Whether to provide a link to manage chapters
|
||||||
|
member val IncludeChapterLink = false with get, set
|
||||||
|
|
||||||
|
/// The template to use to display the page
|
||||||
|
member val Template = "" with get, set
|
||||||
|
|
||||||
|
/// The source type ("HTML" or "Markdown")
|
||||||
|
member val Source = "" with get, set
|
||||||
|
|
||||||
|
/// The text of the page or post
|
||||||
|
member val Text = "" with get, set
|
||||||
|
|
||||||
|
/// Names of metadata items
|
||||||
|
member val MetaNames: string array = [||] with get, set
|
||||||
|
|
||||||
|
/// Values of metadata items
|
||||||
|
member val MetaValues: string array = [||] with get, set
|
||||||
|
|
||||||
|
/// Whether this is a new page or post
|
||||||
|
member this.IsNew with get () = this.Id = "new"
|
||||||
|
|
||||||
|
/// Fill the properties of this object from a 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
|
||||||
|
this.Title <- page.Title
|
||||||
|
this.Permalink <- string page.Permalink
|
||||||
|
this.Entity <- "page"
|
||||||
|
this.Template <- defaultArg page.Template ""
|
||||||
|
this.Source <- latest.Text.SourceType
|
||||||
|
this.Text <- latest.Text.Text
|
||||||
|
this.MetaNames <- metaItems |> List.map _.Name |> Array.ofList
|
||||||
|
this.MetaValues <- metaItems |> List.map _.Value |> Array.ofList
|
||||||
|
|
||||||
|
/// Fill the properties of this object from a 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
|
||||||
|
this.Title <- post.Title
|
||||||
|
this.Permalink <- string post.Permalink
|
||||||
|
this.Entity <- "post"
|
||||||
|
this.IncludeChapterLink <- Option.isSome post.Episode && Option.isSome post.Episode.Value.Chapters
|
||||||
|
this.Template <- defaultArg post.Template ""
|
||||||
|
this.Source <- latest.Text.SourceType
|
||||||
|
this.Text <- latest.Text.Text
|
||||||
|
this.MetaNames <- metaItems |> List.map _.Name |> Array.ofList
|
||||||
|
this.MetaValues <- metaItems |> List.map _.Value |> Array.ofList
|
||||||
|
|
||||||
|
|
||||||
/// View model to edit a custom RSS feed
|
/// View model to edit a custom RSS feed
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type EditCustomFeedModel = {
|
type EditCustomFeedModel = {
|
||||||
@ -604,74 +554,6 @@ type EditMyInfoModel = {
|
|||||||
NewPasswordConfirm = "" }
|
NewPasswordConfirm = "" }
|
||||||
|
|
||||||
|
|
||||||
/// View model common to page and post edits
|
|
||||||
type EditCommonModel() =
|
|
||||||
|
|
||||||
/// Find the latest revision within a list of revisions
|
|
||||||
let findLatestRevision (revs: Revision list) =
|
|
||||||
match revs |> List.sortByDescending _.AsOf |> List.tryHead with Some rev -> rev | None -> Revision.Empty
|
|
||||||
|
|
||||||
/// The ID of the page or post
|
|
||||||
member val Id = "" with get, set
|
|
||||||
|
|
||||||
/// The title of the page or post
|
|
||||||
member val Title = "" with get, set
|
|
||||||
|
|
||||||
/// The permalink for the page or post
|
|
||||||
member val Permalink = "" with get, set
|
|
||||||
|
|
||||||
/// The entity to which this model applies ("page" or "post")
|
|
||||||
member val Entity = "" with get, set
|
|
||||||
|
|
||||||
/// Whether to provide a link to manage chapters
|
|
||||||
member val IncludeChapterLink = false with get, set
|
|
||||||
|
|
||||||
/// The template to use to display the page
|
|
||||||
member val Template = "" with get, set
|
|
||||||
|
|
||||||
/// The source type ("HTML" or "Markdown")
|
|
||||||
member val Source = "" with get, set
|
|
||||||
|
|
||||||
/// The text of the page or post
|
|
||||||
member val Text = "" with get, set
|
|
||||||
|
|
||||||
/// Names of metadata items
|
|
||||||
member val MetaNames: string array = [||] with get, set
|
|
||||||
|
|
||||||
/// Values of metadata items
|
|
||||||
member val MetaValues: string array = [||] with get, set
|
|
||||||
|
|
||||||
/// Whether this is a new page or post
|
|
||||||
member this.IsNew with get () = this.Id = "new"
|
|
||||||
|
|
||||||
/// Fill the properties of this object from a page
|
|
||||||
member this.PopulateFromPage (page: Page) =
|
|
||||||
let latest = findLatestRevision page.Revisions
|
|
||||||
this.Id <- string page.Id
|
|
||||||
this.Title <- page.Title
|
|
||||||
this.Permalink <- string page.Permalink
|
|
||||||
this.Entity <- "page"
|
|
||||||
this.Template <- defaultArg page.Template ""
|
|
||||||
this.Source <- latest.Text.SourceType
|
|
||||||
this.Text <- latest.Text.Text
|
|
||||||
this.MetaNames <- page.Metadata |> List.map _.Name |> Array.ofList
|
|
||||||
this.MetaValues <- page.Metadata |> List.map _.Value |> Array.ofList
|
|
||||||
|
|
||||||
/// Fill the properties of this object from a post
|
|
||||||
member this.PopulateFromPost (post: Post) =
|
|
||||||
let latest = findLatestRevision post.Revisions
|
|
||||||
this.Id <- string post.Id
|
|
||||||
this.Title <- post.Title
|
|
||||||
this.Permalink <- string post.Permalink
|
|
||||||
this.Entity <- "post"
|
|
||||||
this.IncludeChapterLink <- Option.isSome post.Episode && Option.isSome post.Episode.Value.Chapters
|
|
||||||
this.Template <- defaultArg post.Template ""
|
|
||||||
this.Source <- latest.Text.SourceType
|
|
||||||
this.Text <- latest.Text.Text
|
|
||||||
this.MetaNames <- post.Metadata |> List.map _.Name |> Array.ofList
|
|
||||||
this.MetaValues <- post.Metadata |> List.map _.Value |> Array.ofList
|
|
||||||
|
|
||||||
|
|
||||||
/// View model to edit a page
|
/// View model to edit a page
|
||||||
type EditPageModel() =
|
type EditPageModel() =
|
||||||
inherit EditCommonModel()
|
inherit EditCommonModel()
|
||||||
@ -801,7 +683,6 @@ type EditPostModel() =
|
|||||||
/// Create an edit model from an existing past
|
/// Create an edit model from an existing past
|
||||||
static member FromPost (webLog: WebLog) (post: Post) =
|
static member FromPost (webLog: WebLog) (post: Post) =
|
||||||
let model = EditPostModel()
|
let model = EditPostModel()
|
||||||
let post = if post.Metadata |> List.isEmpty then { post with Metadata = [ MetaItem.Empty ] } else post
|
|
||||||
model.PopulateFromPost post
|
model.PopulateFromPost post
|
||||||
let episode = defaultArg post.Episode Episode.Empty
|
let episode = defaultArg post.Episode Episode.Empty
|
||||||
model.Tags <- post.Tags |> String.concat ", "
|
model.Tags <- post.Tags |> String.concat ", "
|
||||||
|
@ -33,88 +33,6 @@ let addBaseToRelativeUrlsTests = testList "PublicHelpers.addBaseToRelativeUrls"
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
/// Unit tests for the DisplayChapter type
|
|
||||||
let displayChapterTests = testList "DisplayChapter.FromChapter" [
|
|
||||||
test "succeeds for a minimally-filled chapter" {
|
|
||||||
let chapter = DisplayChapter.FromChapter { Chapter.Empty with StartTime = Duration.FromSeconds 322L }
|
|
||||||
Expect.equal chapter.StartTime "0:05:22" "Start time not filled/formatted properly"
|
|
||||||
Expect.equal chapter.Title "" "Title not filled properly"
|
|
||||||
Expect.equal chapter.ImageUrl "" "Image URL not filled properly"
|
|
||||||
Expect.isFalse chapter.IsHidden "Is hidden flag not filled properly"
|
|
||||||
Expect.equal chapter.EndTime "" "End time not filled properly"
|
|
||||||
Expect.equal chapter.LocationName "" "Location name not filled properly"
|
|
||||||
Expect.equal chapter.LocationGeo "" "Location geo URL not filled properly"
|
|
||||||
Expect.equal chapter.LocationOsm "" "Location OSM query not filled properly"
|
|
||||||
}
|
|
||||||
test "succeeds for a fully-filled chapter" {
|
|
||||||
let chapter =
|
|
||||||
DisplayChapter.FromChapter
|
|
||||||
{ StartTime = Duration.FromSeconds 7201.43242
|
|
||||||
Title = Some "My Test Chapter"
|
|
||||||
ImageUrl = Some "two-hours-in.jpg"
|
|
||||||
Url = Some "https://example.com/about"
|
|
||||||
IsHidden = Some true
|
|
||||||
EndTime = Some (Duration.FromSeconds 7313.788)
|
|
||||||
Location = Some { Name = "Over Here"; Geo = "geo:23432"; Osm = Some "SF98fFSu-8" } }
|
|
||||||
Expect.equal chapter.StartTime "2:00:01.43" "Start time not filled/formatted properly"
|
|
||||||
Expect.equal chapter.Title "My Test Chapter" "Title not filled properly"
|
|
||||||
Expect.equal chapter.ImageUrl "two-hours-in.jpg" "Image URL not filled properly"
|
|
||||||
Expect.equal chapter.Url "https://example.com/about" "URL not filled properly"
|
|
||||||
Expect.isTrue chapter.IsHidden "Is hidden flag not filled properly"
|
|
||||||
Expect.equal chapter.EndTime "2:01:53.78" "End time not filled/formatted properly"
|
|
||||||
Expect.equal chapter.LocationName "Over Here" "Location name not filled properly"
|
|
||||||
Expect.equal chapter.LocationGeo "geo:23432" "Location geo URL not filled properly"
|
|
||||||
Expect.equal chapter.LocationOsm "SF98fFSu-8" "Location OSM query not filled properly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/// Unit tests for the DisplayCustomFeed type
|
|
||||||
let displayCustomFeedTests = testList "DisplayCustomFeed.FromFeed" [
|
|
||||||
test "succeeds for a feed for an existing category" {
|
|
||||||
let cats =
|
|
||||||
[| { DisplayCategory.Id = "abc"
|
|
||||||
Slug = "a-b-c"
|
|
||||||
Name = "My Lovely Category"
|
|
||||||
Description = None
|
|
||||||
ParentNames = [||]
|
|
||||||
PostCount = 3 } |]
|
|
||||||
let feed =
|
|
||||||
{ CustomFeed.Empty with
|
|
||||||
Id = CustomFeedId "test-feed"
|
|
||||||
Source = Category (CategoryId "abc")
|
|
||||||
Path = Permalink "test-feed.xml" }
|
|
||||||
let model = DisplayCustomFeed.FromFeed cats feed
|
|
||||||
Expect.equal model.Id "test-feed" "Id not filled properly"
|
|
||||||
Expect.equal model.Source "Category: My Lovely Category" "Source not filled properly"
|
|
||||||
Expect.equal model.Path "test-feed.xml" "Path not filled properly"
|
|
||||||
Expect.isFalse model.IsPodcast "IsPodcast not filled properly"
|
|
||||||
}
|
|
||||||
test "succeeds for a feed for a non-existing category" {
|
|
||||||
let feed =
|
|
||||||
{ CustomFeed.Empty with
|
|
||||||
Id = CustomFeedId "bad-feed"
|
|
||||||
Source = Category (CategoryId "xyz")
|
|
||||||
Path = Permalink "trouble.xml" }
|
|
||||||
let model = DisplayCustomFeed.FromFeed [||] feed
|
|
||||||
Expect.equal model.Id "bad-feed" "Id not filled properly"
|
|
||||||
Expect.equal model.Source "Category: --INVALID; DELETE THIS FEED--" "Source not filled properly"
|
|
||||||
Expect.equal model.Path "trouble.xml" "Path not filled properly"
|
|
||||||
Expect.isFalse model.IsPodcast "IsPodcast not filled properly"
|
|
||||||
}
|
|
||||||
test "succeeds for a feed for a tag" {
|
|
||||||
let feed =
|
|
||||||
{ Id = CustomFeedId "tag-feed"
|
|
||||||
Source = Tag "testing"
|
|
||||||
Path = Permalink "testing-posts.xml"
|
|
||||||
Podcast = Some PodcastOptions.Empty }
|
|
||||||
let model = DisplayCustomFeed.FromFeed [||] feed
|
|
||||||
Expect.equal model.Id "tag-feed" "Id not filled properly"
|
|
||||||
Expect.equal model.Source "Tag: testing" "Source not filled properly"
|
|
||||||
Expect.equal model.Path "testing-posts.xml" "Path not filled properly"
|
|
||||||
Expect.isTrue model.IsPodcast "IsPodcast not filled properly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/// Unit tests for the DisplayPage type
|
/// Unit tests for the DisplayPage type
|
||||||
let displayPageTests = testList "DisplayPage" [
|
let displayPageTests = testList "DisplayPage" [
|
||||||
let page =
|
let page =
|
||||||
@ -242,47 +160,6 @@ let displayUploadTests = test "DisplayUpload.FromUpload succeeds" {
|
|||||||
model.UpdatedOn.Value ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "UpdatedOn not filled properly"
|
model.UpdatedOn.Value ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "UpdatedOn not filled properly"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unit tests for the DisplayUser type
|
|
||||||
let displayUserTests = testList "DisplayUser.FromUser" [
|
|
||||||
let minimalUser =
|
|
||||||
{ WebLogUser.Empty with
|
|
||||||
Id = WebLogUserId "test-user"
|
|
||||||
Email = "jim.james@example.com"
|
|
||||||
FirstName = "Jim"
|
|
||||||
LastName = "James"
|
|
||||||
PreferredName = "John"
|
|
||||||
AccessLevel = Editor
|
|
||||||
CreatedOn = Noda.epoch }
|
|
||||||
test "succeeds when the user has minimal information" {
|
|
||||||
let model = DisplayUser.FromUser WebLog.Empty minimalUser
|
|
||||||
Expect.equal model.Id "test-user" "Id not filled properly"
|
|
||||||
Expect.equal model.Email "jim.james@example.com" "Email not filled properly"
|
|
||||||
Expect.equal model.FirstName "Jim" "FirstName not filled properly"
|
|
||||||
Expect.equal model.LastName "James" "LastName not filled properly"
|
|
||||||
Expect.equal model.PreferredName "John" "PreferredName not filled properly"
|
|
||||||
Expect.equal model.Url "" "Url not filled properly"
|
|
||||||
Expect.equal model.AccessLevel "Editor" "AccessLevel not filled properly"
|
|
||||||
Expect.equal model.CreatedOn (Noda.epoch.ToDateTimeUtc()) "CreatedOn not filled properly"
|
|
||||||
Expect.isFalse model.LastSeenOn.HasValue "LastSeenOn should have been null"
|
|
||||||
}
|
|
||||||
test "succeeds when the user has all information" {
|
|
||||||
let model =
|
|
||||||
DisplayUser.FromUser
|
|
||||||
{ WebLog.Empty with TimeZone = "Etc/GMT-1" }
|
|
||||||
{ minimalUser with
|
|
||||||
Url = Some "https://my.site"
|
|
||||||
LastSeenOn = Some (Noda.epoch + Duration.FromDays 4) }
|
|
||||||
Expect.equal model.Url "https://my.site" "Url not filled properly"
|
|
||||||
Expect.equal
|
|
||||||
model.CreatedOn ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "CreatedOn not filled properly"
|
|
||||||
Expect.isTrue model.LastSeenOn.HasValue "LastSeenOn should not have been null"
|
|
||||||
Expect.equal
|
|
||||||
model.LastSeenOn.Value
|
|
||||||
((Noda.epoch + Duration.FromDays 4 + Duration.FromHours 1).ToDateTimeUtc())
|
|
||||||
"LastSeenOn not filled properly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/// Unit tests for the EditCategoryModel type
|
/// Unit tests for the EditCategoryModel type
|
||||||
let editCategoryModelTests = testList "EditCategoryModel" [
|
let editCategoryModelTests = testList "EditCategoryModel" [
|
||||||
testList "FromCategory" [
|
testList "FromCategory" [
|
||||||
@ -315,6 +192,131 @@ let editCategoryModelTests = testList "EditCategoryModel" [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// A full page used to test various models
|
||||||
|
let private testFullPage =
|
||||||
|
{ Page.Empty with
|
||||||
|
Id = PageId "the-page"
|
||||||
|
Title = "Test Page"
|
||||||
|
Permalink = Permalink "blog/page.html"
|
||||||
|
Template = Some "bork"
|
||||||
|
IsInPageList = true
|
||||||
|
Revisions =
|
||||||
|
[ { AsOf = Noda.epoch + Duration.FromHours 1; Text = Markdown "# Howdy!" }
|
||||||
|
{ AsOf = Noda.epoch; Text = Html "<h1>howdy</h1>" } ]
|
||||||
|
Metadata = [ { Name = "Test"; Value = "me" }; { Name = "Two"; Value = "2" } ] }
|
||||||
|
|
||||||
|
/// A full post used to test various models
|
||||||
|
let testFullPost =
|
||||||
|
{ Post.Empty with
|
||||||
|
Id = PostId "a-post"
|
||||||
|
Status = Published
|
||||||
|
Title = "A Post"
|
||||||
|
Permalink = Permalink "1970/01/a-post.html"
|
||||||
|
PublishedOn = Some (Noda.epoch + Duration.FromDays 7)
|
||||||
|
UpdatedOn = Noda.epoch + Duration.FromDays 365
|
||||||
|
Template = Some "demo"
|
||||||
|
Text = "<p>A post!</p>"
|
||||||
|
CategoryIds = [ CategoryId "cat-a"; CategoryId "cat-b"; CategoryId "cat-n" ]
|
||||||
|
Tags = [ "demo"; "post" ]
|
||||||
|
Metadata = [ { Name = "A Meta"; Value = "A Value" } ]
|
||||||
|
Revisions =
|
||||||
|
[ { AsOf = Noda.epoch + Duration.FromDays 365; Text = Html "<p>A post!</p>" }
|
||||||
|
{ AsOf = Noda.epoch + Duration.FromDays 7; Text = Markdown "A post!" } ]
|
||||||
|
Episode =
|
||||||
|
Some { Media = "a-post-ep.mp3"
|
||||||
|
Length = 15555L
|
||||||
|
Duration = Some (Duration.FromMinutes 15L + Duration.FromSeconds 22L)
|
||||||
|
MediaType = Some "audio/mpeg3"
|
||||||
|
ImageUrl = Some "uploads/podcast-cover.jpg"
|
||||||
|
Subtitle = Some "Narration"
|
||||||
|
Explicit = Some Clean
|
||||||
|
Chapters = None
|
||||||
|
ChapterFile = Some "uploads/1970/01/chapters.txt"
|
||||||
|
ChapterType = Some "chapters"
|
||||||
|
ChapterWaypoints = Some true
|
||||||
|
TranscriptUrl = Some "uploads/1970/01/transcript.txt"
|
||||||
|
TranscriptType = Some "transcript"
|
||||||
|
TranscriptLang = Some "EN-us"
|
||||||
|
TranscriptCaptions = Some true
|
||||||
|
SeasonNumber = Some 3
|
||||||
|
SeasonDescription = Some "Season Three"
|
||||||
|
EpisodeNumber = Some 322.
|
||||||
|
EpisodeDescription = Some "Episode 322" } }
|
||||||
|
|
||||||
|
/// Unit tests for the EditCommonModel type
|
||||||
|
let editCommonModelTests = testList "EditCommonModel" [
|
||||||
|
testList "IsNew" [
|
||||||
|
test "succeeds for a new page or post" {
|
||||||
|
Expect.isTrue (EditCommonModel(Id = "new")).IsNew "IsNew should have been set"
|
||||||
|
}
|
||||||
|
test "succeeds for an existing page or post" {
|
||||||
|
Expect.isFalse (EditCommonModel(Id = string (PageId.Create ()))).IsNew "IsNew should not have been set"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "PopulateFromPage" [
|
||||||
|
test "succeeds for empty page" {
|
||||||
|
let model = EditCommonModel()
|
||||||
|
model.PopulateFromPage { Page.Empty with Id = PageId "abc" }
|
||||||
|
Expect.equal model.Id "abc" "PageId not filled properly"
|
||||||
|
Expect.equal model.Title "" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "" "Permalink not filled properly"
|
||||||
|
Expect.equal model.Template "" "Template not filled properly"
|
||||||
|
Expect.equal model.Source "HTML" "Source not filled properly"
|
||||||
|
Expect.equal model.Text "" "Text not set properly"
|
||||||
|
Expect.equal model.MetaNames.Length 1 "MetaNames should have one entry"
|
||||||
|
Expect.equal model.MetaNames[0] "" "Meta name not set properly"
|
||||||
|
Expect.equal model.MetaValues.Length 1 "MetaValues should have one entry"
|
||||||
|
Expect.equal model.MetaValues[0] "" "Meta value not set properly"
|
||||||
|
}
|
||||||
|
test "succeeds for filled page" {
|
||||||
|
let model = EditCommonModel()
|
||||||
|
model.PopulateFromPage testFullPage
|
||||||
|
Expect.equal model.Id "the-page" "PageId not filled properly"
|
||||||
|
Expect.equal model.Title "Test Page" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "blog/page.html" "Permalink not filled properly"
|
||||||
|
Expect.equal model.Template "bork" "Template not filled properly"
|
||||||
|
Expect.equal model.Source "Markdown" "Source not filled properly"
|
||||||
|
Expect.equal model.Text "# Howdy!" "Text not filled properly"
|
||||||
|
Expect.equal model.MetaNames.Length 2 "MetaNames should have two entries"
|
||||||
|
Expect.equal model.MetaNames[0] "Test" "Meta name 0 not set properly"
|
||||||
|
Expect.equal model.MetaNames[1] "Two" "Meta name 1 not set properly"
|
||||||
|
Expect.equal model.MetaValues.Length 2 "MetaValues should have two entries"
|
||||||
|
Expect.equal model.MetaValues[0] "me" "Meta value 0 not set properly"
|
||||||
|
Expect.equal model.MetaValues[1] "2" "Meta value 1 not set properly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "PopulateFromPost" [
|
||||||
|
test "succeeds for empty post" {
|
||||||
|
let model = EditCommonModel()
|
||||||
|
model.PopulateFromPost { Post.Empty with Id = PostId "la-la-la" }
|
||||||
|
Expect.equal model.Id "la-la-la" "PostId not filled properly"
|
||||||
|
Expect.equal model.Title "" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "" "Permalink not filled properly"
|
||||||
|
Expect.equal model.Source "HTML" "Source not filled properly"
|
||||||
|
Expect.equal model.Text "" "Text not filled properly"
|
||||||
|
Expect.equal model.Template "" "Template not filled properly"
|
||||||
|
Expect.equal model.MetaNames.Length 1 "MetaNames not filled properly"
|
||||||
|
Expect.equal model.MetaNames[0] "" "Meta name 0 not filled properly"
|
||||||
|
Expect.equal model.MetaValues.Length 1 "MetaValues not filled properly"
|
||||||
|
Expect.equal model.MetaValues[0] "" "Meta value 0 not filled properly"
|
||||||
|
}
|
||||||
|
test "succeeds for full post with external chapters" {
|
||||||
|
let model = EditCommonModel()
|
||||||
|
model.PopulateFromPost testFullPost
|
||||||
|
Expect.equal model.Id "a-post" "PostId not filled properly"
|
||||||
|
Expect.equal model.Title "A Post" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "1970/01/a-post.html" "Permalink not filled properly"
|
||||||
|
Expect.equal model.Source "HTML" "Source not filled properly"
|
||||||
|
Expect.equal model.Text "<p>A post!</p>" "Text not filled properly"
|
||||||
|
Expect.equal model.Template "demo" "Template not filled properly"
|
||||||
|
Expect.equal model.MetaNames.Length 1 "MetaNames not filled properly"
|
||||||
|
Expect.equal model.MetaNames[0] "A Meta" "Meta name 0 not filled properly"
|
||||||
|
Expect.equal model.MetaValues.Length 1 "MetaValues not filled properly"
|
||||||
|
Expect.equal model.MetaValues[0] "A Value" "Meta value 0 not filled properly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
/// Unit tests for the EditCustomFeedModel type
|
/// Unit tests for the EditCustomFeedModel type
|
||||||
let editCustomFeedModelTests = testList "EditCustomFeedModel" [
|
let editCustomFeedModelTests = testList "EditCustomFeedModel" [
|
||||||
let minimalPodcast =
|
let minimalPodcast =
|
||||||
@ -502,63 +504,26 @@ let editMyInfoModelTests = test "EditMyInfoModel.FromUser succeeds" {
|
|||||||
Expect.equal model.NewPasswordConfirm "" "NewPasswordConfirm not filled properly"
|
Expect.equal model.NewPasswordConfirm "" "NewPasswordConfirm not filled properly"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unit tests for the EditPageModel type
|
||||||
let editPageModelTests = testList "EditPageModel" [
|
let editPageModelTests = testList "EditPageModel" [
|
||||||
let fullPage =
|
|
||||||
{ Page.Empty with
|
|
||||||
Id = PageId "the-page"
|
|
||||||
Title = "Test Page"
|
|
||||||
Permalink = Permalink "blog/page.html"
|
|
||||||
Template = Some "bork"
|
|
||||||
IsInPageList = true
|
|
||||||
Revisions =
|
|
||||||
[ { AsOf = Noda.epoch + Duration.FromHours 1; Text = Markdown "# Howdy!" }
|
|
||||||
{ AsOf = Noda.epoch; Text = Html "<h1>howdy</h1>" } ]
|
|
||||||
Metadata = [ { Name = "Test"; Value = "me" }; { Name = "Two"; Value = "2" } ] }
|
|
||||||
testList "FromPage" [
|
testList "FromPage" [
|
||||||
test "succeeds for empty page" {
|
test "succeeds for empty page" {
|
||||||
let model = EditPageModel.FromPage { Page.Empty with Id = PageId "abc" }
|
let model = EditPageModel.FromPage { Page.Empty with Id = PageId "abc" }
|
||||||
Expect.equal model.PageId "abc" "PageId not filled properly"
|
Expect.equal model.Id "abc" "Parent fields not filled properly"
|
||||||
Expect.equal model.Title "" "Title not filled properly"
|
|
||||||
Expect.equal model.Permalink "" "Permalink not filled properly"
|
|
||||||
Expect.equal model.Template "" "Template not filled properly"
|
|
||||||
Expect.isFalse model.IsShownInPageList "IsShownInPageList should not have been set"
|
Expect.isFalse model.IsShownInPageList "IsShownInPageList should not have been set"
|
||||||
Expect.equal model.Source "HTML" "Source not filled properly"
|
|
||||||
Expect.equal model.Text "" "Text not set properly"
|
|
||||||
Expect.equal model.MetaNames.Length 1 "MetaNames should have one entry"
|
|
||||||
Expect.equal model.MetaNames[0] "" "Meta name not set properly"
|
|
||||||
Expect.equal model.MetaValues.Length 1 "MetaValues should have one entry"
|
|
||||||
Expect.equal model.MetaValues[0] "" "Meta value not set properly"
|
|
||||||
}
|
}
|
||||||
test "succeeds for filled page" {
|
test "succeeds for filled page" {
|
||||||
let model = EditPageModel.FromPage fullPage
|
let model = EditPageModel.FromPage testFullPage
|
||||||
Expect.equal model.PageId "the-page" "PageId not filled properly"
|
Expect.equal model.Id "the-page" "Parent fields not filled properly"
|
||||||
Expect.equal model.Title "Test Page" "Title not filled properly"
|
|
||||||
Expect.equal model.Permalink "blog/page.html" "Permalink not filled properly"
|
|
||||||
Expect.equal model.Template "bork" "Template not filled properly"
|
|
||||||
Expect.isTrue model.IsShownInPageList "IsShownInPageList should have been set"
|
Expect.isTrue model.IsShownInPageList "IsShownInPageList should have been set"
|
||||||
Expect.equal model.Source "Markdown" "Source not filled properly"
|
|
||||||
Expect.equal model.Text "# Howdy!" "Text not filled properly"
|
|
||||||
Expect.equal model.MetaNames.Length 2 "MetaNames should have two entries"
|
|
||||||
Expect.equal model.MetaNames[0] "Test" "Meta name 0 not set properly"
|
|
||||||
Expect.equal model.MetaNames[1] "Two" "Meta name 1 not set properly"
|
|
||||||
Expect.equal model.MetaValues.Length 2 "MetaValues should have two entries"
|
|
||||||
Expect.equal model.MetaValues[0] "me" "Meta value 0 not set properly"
|
|
||||||
Expect.equal model.MetaValues[1] "2" "Meta value 1 not set properly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "IsNew" [
|
|
||||||
test "succeeds for a new page" {
|
|
||||||
Expect.isTrue
|
|
||||||
(EditPageModel.FromPage { Page.Empty with Id = PageId "new" }).IsNew "IsNew should have been set"
|
|
||||||
}
|
|
||||||
test "succeeds for an existing page" {
|
|
||||||
Expect.isFalse (EditPageModel.FromPage Page.Empty).IsNew "IsNew should not have been set"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "UpdatePage" [
|
testList "UpdatePage" [
|
||||||
test "succeeds with minimal changes" {
|
test "succeeds with minimal changes" {
|
||||||
let model = { EditPageModel.FromPage fullPage with Title = "Updated Page"; IsShownInPageList = false }
|
let model = EditPageModel.FromPage testFullPage
|
||||||
let page = model.UpdatePage fullPage (Noda.epoch + Duration.FromHours 4)
|
model.Title <- "Updated Page"
|
||||||
|
model.IsShownInPageList <- false
|
||||||
|
let page = model.UpdatePage testFullPage (Noda.epoch + Duration.FromHours 4)
|
||||||
Expect.equal page.Title "Updated Page" "Title not filled properly"
|
Expect.equal page.Title "Updated Page" "Title not filled properly"
|
||||||
Expect.equal page.Permalink (Permalink "blog/page.html") "Permalink not filled properly"
|
Expect.equal page.Permalink (Permalink "blog/page.html") "Permalink not filled properly"
|
||||||
Expect.isEmpty page.PriorPermalinks "PriorPermalinks should be empty"
|
Expect.isEmpty page.PriorPermalinks "PriorPermalinks should be empty"
|
||||||
@ -582,18 +547,18 @@ let editPageModelTests = testList "EditPageModel" [
|
|||||||
Expect.equal rev2.Text (Html "<h1>howdy</h1>") "Revision 1 text not filled properly"
|
Expect.equal rev2.Text (Html "<h1>howdy</h1>") "Revision 1 text not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds with all changes" {
|
test "succeeds with all changes" {
|
||||||
let model =
|
let model = EditPageModel()
|
||||||
{ PageId = "this-page"
|
model.Id <- "this-page"
|
||||||
Title = "My Updated Page"
|
model.Title <- "My Updated Page"
|
||||||
Permalink = "blog/updated.html"
|
model.Permalink <- "blog/updated.html"
|
||||||
Template = ""
|
model.Template <- ""
|
||||||
IsShownInPageList = false
|
model.IsShownInPageList <- false
|
||||||
Source = "HTML"
|
model.Source <- "HTML"
|
||||||
Text = "<h1>Howdy, partners!</h1>"
|
model.Text <- "<h1>Howdy, partners!</h1>"
|
||||||
MetaNames = [| "banana"; "apple"; "grape" |]
|
model.MetaNames <- [| "banana"; "apple"; "grape" |]
|
||||||
MetaValues = [| "monkey"; "zebra"; "ape" |] }
|
model.MetaValues <- [| "monkey"; "zebra"; "ape" |]
|
||||||
let now = Noda.epoch + Duration.FromDays 7
|
let now = Noda.epoch + Duration.FromDays 7
|
||||||
let page = model.UpdatePage fullPage now
|
let page = model.UpdatePage testFullPage now
|
||||||
Expect.equal page.Title "My Updated Page" "Title not filled properly"
|
Expect.equal page.Title "My Updated Page" "Title not filled properly"
|
||||||
Expect.equal page.Permalink (Permalink "blog/updated.html") "Permalink not filled properly"
|
Expect.equal page.Permalink (Permalink "blog/updated.html") "Permalink not filled properly"
|
||||||
Expect.equal page.PriorPermalinks [ Permalink "blog/page.html" ] "PriorPermalinks not filled properly"
|
Expect.equal page.PriorPermalinks [ Permalink "blog/page.html" ] "PriorPermalinks not filled properly"
|
||||||
@ -621,59 +586,14 @@ let editPageModelTests = testList "EditPageModel" [
|
|||||||
|
|
||||||
/// Unit tests for the EditPostModel type
|
/// Unit tests for the EditPostModel type
|
||||||
let editPostModelTests = testList "EditPostModel" [
|
let editPostModelTests = testList "EditPostModel" [
|
||||||
let fullPost =
|
|
||||||
{ Post.Empty with
|
|
||||||
Id = PostId "a-post"
|
|
||||||
Status = Published
|
|
||||||
Title = "A Post"
|
|
||||||
Permalink = Permalink "1970/01/a-post.html"
|
|
||||||
PublishedOn = Some (Noda.epoch + Duration.FromDays 7)
|
|
||||||
UpdatedOn = Noda.epoch + Duration.FromDays 365
|
|
||||||
Template = Some "demo"
|
|
||||||
Text = "<p>A post!</p>"
|
|
||||||
CategoryIds = [ CategoryId "cat-a"; CategoryId "cat-b"; CategoryId "cat-n" ]
|
|
||||||
Tags = [ "demo"; "post" ]
|
|
||||||
Metadata = [ { Name = "A Meta"; Value = "A Value" } ]
|
|
||||||
Revisions =
|
|
||||||
[ { AsOf = Noda.epoch + Duration.FromDays 365; Text = Html "<p>A post!</p>" }
|
|
||||||
{ AsOf = Noda.epoch + Duration.FromDays 7; Text = Markdown "A post!" } ]
|
|
||||||
Episode =
|
|
||||||
Some { Media = "a-post-ep.mp3"
|
|
||||||
Length = 15555L
|
|
||||||
Duration = Some (Duration.FromMinutes 15L + Duration.FromSeconds 22L)
|
|
||||||
MediaType = Some "audio/mpeg3"
|
|
||||||
ImageUrl = Some "uploads/podcast-cover.jpg"
|
|
||||||
Subtitle = Some "Narration"
|
|
||||||
Explicit = Some Clean
|
|
||||||
Chapters = None
|
|
||||||
ChapterFile = Some "uploads/1970/01/chapters.txt"
|
|
||||||
ChapterType = Some "chapters"
|
|
||||||
ChapterWaypoints = Some true
|
|
||||||
TranscriptUrl = Some "uploads/1970/01/transcript.txt"
|
|
||||||
TranscriptType = Some "transcript"
|
|
||||||
TranscriptLang = Some "EN-us"
|
|
||||||
TranscriptCaptions = Some true
|
|
||||||
SeasonNumber = Some 3
|
|
||||||
SeasonDescription = Some "Season Three"
|
|
||||||
EpisodeNumber = Some 322.
|
|
||||||
EpisodeDescription = Some "Episode 322" } }
|
|
||||||
testList "FromPost" [
|
testList "FromPost" [
|
||||||
test "succeeds for empty post" {
|
test "succeeds for empty post" {
|
||||||
let model = EditPostModel.FromPost WebLog.Empty { Post.Empty with Id = PostId "la-la-la" }
|
let model = EditPostModel.FromPost WebLog.Empty { Post.Empty with Id = PostId "la-la-la" }
|
||||||
Expect.equal model.PostId "la-la-la" "PostId not filled properly"
|
Expect.equal model.Id "la-la-la" "Parent fields not filled properly"
|
||||||
Expect.equal model.Title "" "Title not filled properly"
|
|
||||||
Expect.equal model.Permalink "" "Permalink not filled properly"
|
|
||||||
Expect.equal model.Source "HTML" "Source not filled properly"
|
|
||||||
Expect.equal model.Text "" "Text not filled properly"
|
|
||||||
Expect.equal model.Tags "" "Tags not filled properly"
|
Expect.equal model.Tags "" "Tags not filled properly"
|
||||||
Expect.equal model.Template "" "Template not filled properly"
|
|
||||||
Expect.isEmpty model.CategoryIds "CategoryIds not filled properly"
|
Expect.isEmpty model.CategoryIds "CategoryIds not filled properly"
|
||||||
Expect.equal model.Status (string Draft) "Status not filled properly"
|
Expect.equal model.Status (string Draft) "Status not filled properly"
|
||||||
Expect.isFalse model.DoPublish "DoPublish should not have been set"
|
Expect.isFalse model.DoPublish "DoPublish should not have been set"
|
||||||
Expect.equal model.MetaNames.Length 1 "MetaNames not filled properly"
|
|
||||||
Expect.equal model.MetaNames[0] "" "Meta name 0 not filled properly"
|
|
||||||
Expect.equal model.MetaValues.Length 1 "MetaValues not filled properly"
|
|
||||||
Expect.equal model.MetaValues[0] "" "Meta value 0 not filled properly"
|
|
||||||
Expect.isFalse model.SetPublished "SetPublished should not have been set"
|
Expect.isFalse model.SetPublished "SetPublished should not have been set"
|
||||||
Expect.isFalse model.PubOverride.HasValue "PubOverride not filled properly"
|
Expect.isFalse model.PubOverride.HasValue "PubOverride not filled properly"
|
||||||
Expect.isFalse model.SetUpdated "SetUpdated should not have been set"
|
Expect.isFalse model.SetUpdated "SetUpdated should not have been set"
|
||||||
@ -699,21 +619,12 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal model.EpisodeDescription "" "EpisodeDescription not filled properly"
|
Expect.equal model.EpisodeDescription "" "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for full post with external chapters" {
|
test "succeeds for full post with external chapters" {
|
||||||
let model = EditPostModel.FromPost { WebLog.Empty with TimeZone = "Etc/GMT+1" } fullPost
|
let model = EditPostModel.FromPost { WebLog.Empty with TimeZone = "Etc/GMT+1" } testFullPost
|
||||||
Expect.equal model.PostId "a-post" "PostId not filled properly"
|
Expect.equal model.Id "a-post" "Parent fields not filled properly"
|
||||||
Expect.equal model.Title "A Post" "Title not filled properly"
|
|
||||||
Expect.equal model.Permalink "1970/01/a-post.html" "Permalink not filled properly"
|
|
||||||
Expect.equal model.Source "HTML" "Source not filled properly"
|
|
||||||
Expect.equal model.Text "<p>A post!</p>" "Text not filled properly"
|
|
||||||
Expect.equal model.Tags "demo, post" "Tags not filled properly"
|
Expect.equal model.Tags "demo, post" "Tags not filled properly"
|
||||||
Expect.equal model.Template "demo" "Template not filled properly"
|
|
||||||
Expect.equal model.CategoryIds [| "cat-a"; "cat-b"; "cat-n" |] "CategoryIds not filled properly"
|
Expect.equal model.CategoryIds [| "cat-a"; "cat-b"; "cat-n" |] "CategoryIds not filled properly"
|
||||||
Expect.equal model.Status (string Published) "Status not filled properly"
|
Expect.equal model.Status (string Published) "Status not filled properly"
|
||||||
Expect.isFalse model.DoPublish "DoPublish should not have been set"
|
Expect.isFalse model.DoPublish "DoPublish should not have been set"
|
||||||
Expect.equal model.MetaNames.Length 1 "MetaNames not filled properly"
|
|
||||||
Expect.equal model.MetaNames[0] "A Meta" "Meta name 0 not filled properly"
|
|
||||||
Expect.equal model.MetaValues.Length 1 "MetaValues not filled properly"
|
|
||||||
Expect.equal model.MetaValues[0] "A Value" "Meta value 0 not filled properly"
|
|
||||||
Expect.isFalse model.SetPublished "SetPublished should not have been set"
|
Expect.isFalse model.SetPublished "SetPublished should not have been set"
|
||||||
Expect.isTrue model.PubOverride.HasValue "PubOverride should not have been null"
|
Expect.isTrue model.PubOverride.HasValue "PubOverride should not have been null"
|
||||||
Expect.equal
|
Expect.equal
|
||||||
@ -746,63 +657,52 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
let model =
|
let model =
|
||||||
EditPostModel.FromPost
|
EditPostModel.FromPost
|
||||||
{ WebLog.Empty with TimeZone = "Etc/GMT+1" }
|
{ WebLog.Empty with TimeZone = "Etc/GMT+1" }
|
||||||
{ fullPost with
|
{ testFullPost with
|
||||||
Episode =
|
Episode =
|
||||||
Some
|
Some
|
||||||
{ fullPost.Episode.Value with
|
{ testFullPost.Episode.Value with
|
||||||
Chapters = Some []
|
Chapters = Some []
|
||||||
ChapterFile = None
|
ChapterFile = None
|
||||||
ChapterType = None } }
|
ChapterType = None } }
|
||||||
Expect.equal model.ChapterSource "internal" "ChapterSource not filled properly"
|
Expect.equal model.ChapterSource "internal" "ChapterSource not filled properly"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "IsNew" [
|
let updatedModel () =
|
||||||
test "succeeds for a new post" {
|
let model = EditPostModel.FromPost WebLog.Empty testFullPost
|
||||||
Expect.isTrue
|
model.Title <- "An Updated Post"
|
||||||
(EditPostModel.FromPost WebLog.Empty { Post.Empty with Id = PostId "new" }).IsNew
|
model.Permalink <- "1970/01/updated-post.html"
|
||||||
"IsNew should be set for new post"
|
model.Source <- "HTML"
|
||||||
}
|
model.Text <- "<p>An updated post!</p>"
|
||||||
test "succeeds for a not-new post" {
|
model.Tags <- "Zebras, Aardvarks, , Turkeys"
|
||||||
Expect.isFalse
|
model.Template <- "updated"
|
||||||
(EditPostModel.FromPost WebLog.Empty { Post.Empty with Id = PostId "nu" }).IsNew
|
model.CategoryIds <- [| "cat-x"; "cat-y" |]
|
||||||
"IsNew should not be set for not-new post"
|
model.MetaNames <- [| "Zed Meta"; "A Meta" |]
|
||||||
}
|
model.MetaValues <- [| "A Value"; "Zed Value" |]
|
||||||
]
|
model.Media <- "an-updated-ep.mp3"
|
||||||
let updatedModel =
|
model.Length <- 14444L
|
||||||
{ EditPostModel.FromPost WebLog.Empty fullPost with
|
model.Duration <- "0:14:42"
|
||||||
Title = "An Updated Post"
|
model.MediaType <- "audio/mp3"
|
||||||
Permalink = "1970/01/updated-post.html"
|
model.ImageUrl <- "updated-cover.png"
|
||||||
Source = "HTML"
|
model.Subtitle <- "Talking"
|
||||||
Text = "<p>An updated post!</p>"
|
model.Explicit <- "no"
|
||||||
Tags = "Zebras, Aardvarks, , Turkeys"
|
model.ChapterSource <- "external"
|
||||||
Template = "updated"
|
model.ChapterFile <- "updated-chapters.txt"
|
||||||
CategoryIds = [| "cat-x"; "cat-y" |]
|
model.ChapterType <- "indexes"
|
||||||
MetaNames = [| "Zed Meta"; "A Meta" |]
|
model.TranscriptUrl <- "updated-transcript.txt"
|
||||||
MetaValues = [| "A Value"; "Zed Value" |]
|
model.TranscriptType <- "subtitles"
|
||||||
Media = "an-updated-ep.mp3"
|
model.TranscriptLang <- "ES-mx"
|
||||||
Length = 14444L
|
model.SeasonNumber <- 4
|
||||||
Duration = "0:14:42"
|
model.SeasonDescription <- "Season Fo"
|
||||||
MediaType = "audio/mp3"
|
model.EpisodeNumber <- "432.1"
|
||||||
ImageUrl = "updated-cover.png"
|
model.EpisodeDescription <- "Four Three Two pt One"
|
||||||
Subtitle = "Talking"
|
model
|
||||||
Explicit = "no"
|
|
||||||
ChapterSource = "external"
|
|
||||||
ChapterFile = "updated-chapters.txt"
|
|
||||||
ChapterType = "indexes"
|
|
||||||
TranscriptUrl = "updated-transcript.txt"
|
|
||||||
TranscriptType = "subtitles"
|
|
||||||
TranscriptLang = "ES-mx"
|
|
||||||
SeasonNumber = 4
|
|
||||||
SeasonDescription = "Season Fo"
|
|
||||||
EpisodeNumber = "432.1"
|
|
||||||
EpisodeDescription = "Four Three Two pt One" }
|
|
||||||
testList "UpdatePost" [
|
testList "UpdatePost" [
|
||||||
test "succeeds for a full podcast episode" {
|
test "succeeds for a full podcast episode" {
|
||||||
let post = updatedModel.UpdatePost fullPost (Noda.epoch + Duration.FromDays 400)
|
let post = (updatedModel ()).UpdatePost testFullPost (Noda.epoch + Duration.FromDays 400)
|
||||||
Expect.equal post.Title "An Updated Post" "Title not filled properly"
|
Expect.equal post.Title "An Updated Post" "Title not filled properly"
|
||||||
Expect.equal post.Permalink (Permalink "1970/01/updated-post.html") "Permalink not filled properly"
|
Expect.equal post.Permalink (Permalink "1970/01/updated-post.html") "Permalink not filled properly"
|
||||||
Expect.equal post.PriorPermalinks [ Permalink "1970/01/a-post.html" ] "PriorPermalinks not filled properly"
|
Expect.equal post.PriorPermalinks [ Permalink "1970/01/a-post.html" ] "PriorPermalinks not filled properly"
|
||||||
Expect.equal post.PublishedOn fullPost.PublishedOn "PublishedOn should not have changed"
|
Expect.equal post.PublishedOn testFullPost.PublishedOn "PublishedOn should not have changed"
|
||||||
Expect.equal post.UpdatedOn (Noda.epoch + Duration.FromDays 400) "UpdatedOn not filled properly"
|
Expect.equal post.UpdatedOn (Noda.epoch + Duration.FromDays 400) "UpdatedOn not filled properly"
|
||||||
Expect.equal post.Text "<p>An updated post!</p>" "Text not filled properly"
|
Expect.equal post.Text "<p>An updated post!</p>" "Text not filled properly"
|
||||||
Expect.equal post.Tags [ "aardvarks"; "turkeys"; "zebras" ] "Tags not filled properly"
|
Expect.equal post.Tags [ "aardvarks"; "turkeys"; "zebras" ] "Tags not filled properly"
|
||||||
@ -841,25 +741,24 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal ep.EpisodeDescription (Some "Four Three Two pt One") "EpisodeDescription not filled properly"
|
Expect.equal ep.EpisodeDescription (Some "Four Three Two pt One") "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for a minimal podcast episode" {
|
test "succeeds for a minimal podcast episode" {
|
||||||
let minModel =
|
let minModel = updatedModel ()
|
||||||
{ updatedModel with
|
minModel.Duration <- ""
|
||||||
Duration = ""
|
minModel.MediaType <- ""
|
||||||
MediaType = ""
|
minModel.ImageUrl <- ""
|
||||||
ImageUrl = ""
|
minModel.Subtitle <- ""
|
||||||
Subtitle = ""
|
minModel.Explicit <- ""
|
||||||
Explicit = ""
|
minModel.ChapterFile <- ""
|
||||||
ChapterFile = ""
|
minModel.ChapterType <- ""
|
||||||
ChapterType = ""
|
minModel.ContainsWaypoints <- false
|
||||||
ContainsWaypoints = false
|
minModel.TranscriptUrl <- ""
|
||||||
TranscriptUrl = ""
|
minModel.TranscriptType <- ""
|
||||||
TranscriptType = ""
|
minModel.TranscriptLang <- ""
|
||||||
TranscriptLang = ""
|
minModel.TranscriptCaptions <- false
|
||||||
TranscriptCaptions = false
|
minModel.SeasonNumber <- 0
|
||||||
SeasonNumber = 0
|
minModel.SeasonDescription <- ""
|
||||||
SeasonDescription = ""
|
minModel.EpisodeNumber <- ""
|
||||||
EpisodeNumber = ""
|
minModel.EpisodeDescription <- ""
|
||||||
EpisodeDescription = "" }
|
let post = minModel.UpdatePost testFullPost (Noda.epoch + Duration.FromDays 500)
|
||||||
let post = minModel.UpdatePost fullPost (Noda.epoch + Duration.FromDays 500)
|
|
||||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||||
let ep = post.Episode.Value
|
let ep = post.Episode.Value
|
||||||
Expect.equal ep.Media "an-updated-ep.mp3" "Media not filled properly"
|
Expect.equal ep.Media "an-updated-ep.mp3" "Media not filled properly"
|
||||||
@ -882,12 +781,11 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.isNone ep.EpisodeDescription "EpisodeDescription not filled properly"
|
Expect.isNone ep.EpisodeDescription "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for a podcast episode with internal chapters" {
|
test "succeeds for a podcast episode with internal chapters" {
|
||||||
let minModel =
|
let minModel = updatedModel ()
|
||||||
{ updatedModel with
|
minModel.ChapterSource <- "internal"
|
||||||
ChapterSource = "internal"
|
minModel.ChapterFile <- ""
|
||||||
ChapterFile = ""
|
minModel.ChapterType <- ""
|
||||||
ChapterType = "" }
|
let post = minModel.UpdatePost testFullPost (Noda.epoch + Duration.FromDays 500)
|
||||||
let post = minModel.UpdatePost fullPost (Noda.epoch + Duration.FromDays 500)
|
|
||||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||||
let ep = post.Episode.Value
|
let ep = post.Episode.Value
|
||||||
Expect.equal ep.Chapters (Some []) "Chapters not filled properly"
|
Expect.equal ep.Chapters (Some []) "Chapters not filled properly"
|
||||||
@ -895,10 +793,11 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.isNone ep.ChapterType "ChapterType not filled properly"
|
Expect.isNone ep.ChapterType "ChapterType not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for a podcast episode with no chapters" {
|
test "succeeds for a podcast episode with no chapters" {
|
||||||
let minModel = { updatedModel with ChapterSource = "none" }
|
let minModel = updatedModel ()
|
||||||
|
minModel.ChapterSource <- "none"
|
||||||
let post =
|
let post =
|
||||||
minModel.UpdatePost
|
minModel.UpdatePost
|
||||||
{ fullPost with Episode = Some { fullPost.Episode.Value with Chapters = Some [] } }
|
{ testFullPost with Episode = Some { testFullPost.Episode.Value with Chapters = Some [] } }
|
||||||
(Noda.epoch + Duration.FromDays 500)
|
(Noda.epoch + Duration.FromDays 500)
|
||||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||||
let ep = post.Episode.Value
|
let ep = post.Episode.Value
|
||||||
@ -908,14 +807,17 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.isNone ep.ChapterWaypoints "ChapterWaypoints not filled properly"
|
Expect.isNone ep.ChapterWaypoints "ChapterWaypoints not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for no podcast episode and no template" {
|
test "succeeds for no podcast episode and no template" {
|
||||||
let post = { updatedModel with IsEpisode = false; Template = "" }.UpdatePost fullPost Noda.epoch
|
let model = updatedModel ()
|
||||||
|
model.IsEpisode <- false
|
||||||
|
model.Template <- ""
|
||||||
|
let post = model.UpdatePost testFullPost Noda.epoch
|
||||||
Expect.isNone post.Template "Template not filled properly"
|
Expect.isNone post.Template "Template not filled properly"
|
||||||
Expect.isNone post.Episode "Episode not filled properly"
|
Expect.isNone post.Episode "Episode not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds when publishing a draft" {
|
test "succeeds when publishing a draft" {
|
||||||
let post =
|
let model = updatedModel ()
|
||||||
{ updatedModel with DoPublish = true }.UpdatePost
|
model.DoPublish <- true
|
||||||
{ fullPost with Status = Draft } (Noda.epoch + Duration.FromDays 375)
|
let post = model.UpdatePost { testFullPost with Status = Draft } (Noda.epoch + Duration.FromDays 375)
|
||||||
Expect.equal post.Status Published "Status not set properly"
|
Expect.equal post.Status Published "Status not set properly"
|
||||||
Expect.equal post.PublishedOn (Some (Noda.epoch + Duration.FromDays 375)) "PublishedOn not set properly"
|
Expect.equal post.PublishedOn (Some (Noda.epoch + Duration.FromDays 375)) "PublishedOn not set properly"
|
||||||
}
|
}
|
||||||
@ -1322,13 +1224,11 @@ let userMessageTests = testList "UserMessage" [
|
|||||||
/// All tests in the Domain.ViewModels file
|
/// All tests in the Domain.ViewModels file
|
||||||
let all = testList "ViewModels" [
|
let all = testList "ViewModels" [
|
||||||
addBaseToRelativeUrlsTests
|
addBaseToRelativeUrlsTests
|
||||||
displayChapterTests
|
|
||||||
displayCustomFeedTests
|
|
||||||
displayPageTests
|
displayPageTests
|
||||||
displayThemeTests
|
displayThemeTests
|
||||||
displayUploadTests
|
displayUploadTests
|
||||||
displayUserTests
|
|
||||||
editCategoryModelTests
|
editCategoryModelTests
|
||||||
|
editCommonModelTests
|
||||||
editCustomFeedModelTests
|
editCustomFeedModelTests
|
||||||
editMyInfoModelTests
|
editMyInfoModelTests
|
||||||
editPageModelTests
|
editPageModelTests
|
||||||
|
@ -225,12 +225,11 @@ let register () =
|
|||||||
Template.RegisterTag<UserLinksTag> "user_links"
|
Template.RegisterTag<UserLinksTag> "user_links"
|
||||||
|
|
||||||
[ // Domain types
|
[ // Domain types
|
||||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>
|
||||||
typeof<RedirectRule>; typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
typeof<TagMap>; typeof<WebLog>
|
||||||
// View models
|
// View models
|
||||||
typeof<AppViewContext>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
typeof<AppViewContext>; typeof<DisplayCategory>; typeof<DisplayPage>; typeof<EditPageModel>; typeof<PostDisplay>
|
||||||
typeof<DisplayTheme>; typeof<DisplayUpload>; typeof<DisplayUser>; typeof<EditPageModel>
|
typeof<PostListItem>; typeof<UserMessage>
|
||||||
typeof<EditPostModel>; typeof<PostDisplay>; typeof<PostListItem>; typeof<UserMessage>
|
|
||||||
// Framework types
|
// Framework types
|
||||||
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
||||||
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list>
|
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list>
|
||||||
|
@ -455,11 +455,9 @@ module WebLog =
|
|||||||
|> List.append [ { Page.Empty with Id = PageId "posts"; Title = "- First Page of Posts -" } ]
|
|> List.append [ { Page.Empty with Id = PageId "posts"; Title = "- First Page of Posts -" } ]
|
||||||
let! themes = data.Theme.All()
|
let! themes = data.Theme.All()
|
||||||
let uploads = [ Database; Disk ]
|
let uploads = [ Database; Disk ]
|
||||||
let feeds = ctx.WebLog.Rss.CustomFeeds |> List.map (DisplayCustomFeed.FromFeed (CategoryCache.get ctx))
|
|
||||||
return!
|
return!
|
||||||
Views.WebLog.webLogSettings
|
Views.WebLog.webLogSettings
|
||||||
(SettingsModel.FromWebLog ctx.WebLog) themes pages uploads (EditRssModel.FromRssOptions ctx.WebLog.Rss)
|
(SettingsModel.FromWebLog ctx.WebLog) themes pages uploads (EditRssModel.FromRssOptions ctx.WebLog.Rss)
|
||||||
feeds
|
|
||||||
|> adminPage "Web Log Settings" true next ctx
|
|> adminPage "Web Log Settings" true next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,7 +699,40 @@ let uploadNew app = [
|
|||||||
/// Web log settings page
|
/// Web log settings page
|
||||||
let webLogSettings
|
let webLogSettings
|
||||||
(model: SettingsModel) (themes: Theme list) (pages: Page list) (uploads: UploadDestination list)
|
(model: SettingsModel) (themes: Theme list) (pages: Page list) (uploads: UploadDestination list)
|
||||||
(rss: EditRssModel) (feeds: DisplayCustomFeed list) app = [
|
(rss: EditRssModel) (app: AppViewContext) = [
|
||||||
|
let feedDetail (feed: CustomFeed) =
|
||||||
|
let source =
|
||||||
|
match feed.Source with
|
||||||
|
| Category (CategoryId catId) ->
|
||||||
|
app.Categories
|
||||||
|
|> Array.tryFind (fun cat -> cat.Id = catId)
|
||||||
|
|> Option.map _.Name
|
||||||
|
|> Option.defaultValue "--INVALID; DELETE THIS FEED--"
|
||||||
|
|> sprintf "Category: %s"
|
||||||
|
| Tag tag -> $"Tag: {tag}"
|
||||||
|
div [ _class "row mwl-table-detail" ] [
|
||||||
|
div [ _class "col-12 col-md-6" ] [
|
||||||
|
txt source
|
||||||
|
if Option.isSome feed.Podcast then
|
||||||
|
raw " "; span [ _class "badge bg-primary" ] [ raw "PODCAST" ]
|
||||||
|
br []
|
||||||
|
small [] [
|
||||||
|
let feedUrl = relUrl app $"admin/settings/rss/{feed.Id}"
|
||||||
|
a [ _href (relUrl app (string feed.Path)); _target "_blank" ] [ raw "View Feed" ]
|
||||||
|
actionSpacer
|
||||||
|
a [ _href $"{feedUrl}/edit" ] [ raw "Edit" ]; actionSpacer
|
||||||
|
a [ _href feedUrl; _hxDelete feedUrl; _class "text-danger"
|
||||||
|
_hxConfirm $"Are you sure you want to delete the custom RSS feed based on {feed.Source}? This action cannot be undone." ] [
|
||||||
|
raw "Delete"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
div [ _class "col-12 col-md-6" ] [
|
||||||
|
small [ _class "d-md-none" ] [ raw "Served at "; txt (string feed.Path) ]
|
||||||
|
span [ _class "d-none d-md-inline" ] [ txt (string feed.Path) ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
h2 [ _class "my-3" ] [ txt app.WebLog.Name; raw " Settings" ]
|
h2 [ _class "my-3" ] [ txt app.WebLog.Name; raw " Settings" ]
|
||||||
article [] [
|
article [] [
|
||||||
p [ _class "text-muted" ] [
|
p [ _class "text-muted" ] [
|
||||||
@ -824,7 +857,7 @@ let webLogSettings
|
|||||||
a [ _class "btn btn-sm btn-secondary"; _href (relUrl app "admin/settings/rss/new/edit") ] [
|
a [ _class "btn btn-sm btn-secondary"; _href (relUrl app "admin/settings/rss/new/edit") ] [
|
||||||
raw "Add a New Custom Feed"
|
raw "Add a New Custom Feed"
|
||||||
]
|
]
|
||||||
if feeds.Length = 0 then
|
if app.WebLog.Rss.CustomFeeds.Length = 0 then
|
||||||
p [ _class "text-muted fst-italic text-center" ] [ raw "No custom feeds defined" ]
|
p [ _class "text-muted fst-italic text-center" ] [ raw "No custom feeds defined" ]
|
||||||
else
|
else
|
||||||
form [ _method "post"; _class "container g-0"; _hxTarget "body" ] [
|
form [ _method "post"; _class "container g-0"; _hxTarget "body" ] [
|
||||||
@ -834,31 +867,9 @@ let webLogSettings
|
|||||||
span [ _class "d-md-none" ] [ raw "Feed" ]
|
span [ _class "d-md-none" ] [ raw "Feed" ]
|
||||||
span [ _class "d-none d-md-inline" ] [ raw "Source" ]
|
span [ _class "d-none d-md-inline" ] [ raw "Source" ]
|
||||||
]
|
]
|
||||||
div [ _class $"col-12 col-md-6 d-none d-md-inline-block" ] [ raw "Relative Path" ]
|
div [ _class "col-12 col-md-6 d-none d-md-inline-block" ] [ raw "Relative Path" ]
|
||||||
]
|
|
||||||
for feed in feeds do
|
|
||||||
div [ _class "row mwl-table-detail" ] [
|
|
||||||
div [ _class "col-12 col-md-6" ] [
|
|
||||||
txt feed.Source
|
|
||||||
if feed.IsPodcast then
|
|
||||||
raw " "; span [ _class "badge bg-primary" ] [ raw "PODCAST" ]
|
|
||||||
br []
|
|
||||||
small [] [
|
|
||||||
let feedUrl = relUrl app $"admin/settings/rss/{feed.Id}"
|
|
||||||
a [ _href (relUrl app feed.Path); _target "_blank" ] [ raw "View Feed" ]
|
|
||||||
actionSpacer
|
|
||||||
a [ _href $"{feedUrl}/edit" ] [ raw "Edit" ]; actionSpacer
|
|
||||||
a [ _href feedUrl; _hxDelete feedUrl; _class "text-danger"
|
|
||||||
_hxConfirm $"Are you sure you want to delete the custom RSS feed based on {feed.Source}? This action cannot be undone." ] [
|
|
||||||
raw "Delete"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
div [ _class "col-12 col-md-6" ] [
|
|
||||||
small [ _class "d-md-none" ] [ raw "Served at "; txt feed.Path ]
|
|
||||||
span [ _class "d-none d-md-inline" ] [ txt feed.Path ]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
yield! app.WebLog.Rss.CustomFeeds |> List.map feedDetail
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user