diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 3a5bfec..27a6df9 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -262,6 +262,18 @@ type EditCustomFeedModel = /// The base URL for relative URL media files for this podcast (optional; defaults to web log base) mediaBaseUrl : string + + /// The URL for funding information for the podcast + fundingUrl : string + + /// The text for the funding link + fundingText : string + + /// A unique identifier to follow this podcast + guid : string + + /// The medium for the content of this podcast + medium : string } /// An empty custom feed model @@ -283,6 +295,10 @@ type EditCustomFeedModel = explicit = "no" defaultMediaType = "audio/mpeg" mediaBaseUrl = "" + fundingUrl = "" + fundingText = "" + guid = "" + medium = "" } /// Create a model from a custom feed @@ -310,6 +326,12 @@ type EditCustomFeedModel = explicit = ExplicitRating.toString p.explicit defaultMediaType = defaultArg p.defaultMediaType "" mediaBaseUrl = defaultArg p.mediaBaseUrl "" + fundingUrl = defaultArg p.fundingUrl "" + fundingText = defaultArg p.fundingText "" + guid = p.guid + |> Option.map (fun it -> it.ToString().ToLowerInvariant ()) + |> Option.defaultValue "" + medium = p.medium |> Option.map PodcastMedium.toString |> Option.defaultValue "" } | None -> rss @@ -333,11 +355,10 @@ type EditCustomFeedModel = explicit = ExplicitRating.parse this.explicit defaultMediaType = noneIfBlank this.defaultMediaType mediaBaseUrl = noneIfBlank this.mediaBaseUrl - // TODO: implement UI to update these - guid = None - fundingUrl = None - fundingText = None - medium = None + guid = noneIfBlank this.guid |> Option.map Guid.Parse + fundingUrl = noneIfBlank this.fundingUrl + fundingText = noneIfBlank this.fundingText + medium = noneIfBlank this.medium |> Option.map PodcastMedium.parse } else None diff --git a/src/MyWebLog/Handlers/Feed.fs b/src/MyWebLog/Handlers/Feed.fs index d5c7964..4f99184 100644 --- a/src/MyWebLog/Handlers/Feed.fs +++ b/src/MyWebLog/Handlers/Feed.fs @@ -2,6 +2,7 @@ module MyWebLog.Handlers.Feed open System +open System.Collections.Generic open System.IO open System.Net open System.ServiceModel.Syndication @@ -129,17 +130,19 @@ let private toFeedItem webLog (authors : MetaItem list) (cats : DisplayCategory[ |> List.iter item.Categories.Add item +/// Convert non-absolute URLs to an absolute URL for this web log +let toAbsolute webLog (link : string) = + if link.StartsWith "http" then link else WebLog.absoluteUrl webLog (Permalink link) + /// Add episode information to a podcast feed item let private addEpisode webLog (podcast : PodcastOptions) (episode : Episode) (post : Post) (item : SyndicationItem) = - // Convert non-absolute URLs to an absolute URL for this web log - let toAbsolute (link : string) = if link.StartsWith "http" then link else WebLog.absoluteUrl webLog (Permalink link) let epMediaUrl = match episode.media with | link when link.StartsWith "http" -> link | link when Option.isSome podcast.mediaBaseUrl -> $"{podcast.mediaBaseUrl.Value}{link}" | link -> WebLog.absoluteUrl webLog (Permalink link) let epMediaType = [ episode.mediaType; podcast.defaultMediaType ] |> List.tryFind Option.isSome |> Option.flatten - let epImageUrl = defaultArg episode.imageUrl (Permalink.toString podcast.imageUrl) |> toAbsolute + let epImageUrl = defaultArg episode.imageUrl (Permalink.toString podcast.imageUrl) |> toAbsolute webLog let epExplicit = defaultArg episode.explicit podcast.explicit |> ExplicitRating.toString let xmlDoc = XmlDocument () @@ -165,7 +168,7 @@ let private addEpisode webLog (podcast : PodcastOptions) (episode : Episode) (po match episode.chapterFile with | Some chapters -> - let url = toAbsolute chapters + let url = toAbsolute webLog chapters let typ = match episode.chapterType with | Some mime -> Some mime @@ -179,7 +182,7 @@ let private addEpisode webLog (podcast : PodcastOptions) (episode : Episode) (po match episode.transcriptUrl with | Some transcript -> - let url = toAbsolute transcript + let url = toAbsolute webLog transcript let elt = xmlDoc.CreateElement ("podcast", "transcript", Namespace.podcast) elt.SetAttribute ("url", url) elt.SetAttribute ("type", Option.get episode.transcriptType) @@ -301,6 +304,17 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) = rssFeed.ElementExtensions.Add ("author", Namespace.iTunes, podcast.displayedAuthor) rssFeed.ElementExtensions.Add ("explicit", Namespace.iTunes, ExplicitRating.toString podcast.explicit) podcast.subtitle |> Option.iter (fun sub -> rssFeed.ElementExtensions.Add ("subtitle", Namespace.iTunes, sub)) + podcast.fundingUrl + |> Option.iter (fun url -> + let funding = xmlDoc.CreateElement ("podcast", "funding", Namespace.podcast) + funding.SetAttribute ("url", toAbsolute webLog url) + funding.InnerText <- defaultArg podcast.fundingText "Support This Podcast" + rssFeed.ElementExtensions.Add funding) + podcast.guid + |> Option.iter (fun guid -> + rssFeed.ElementExtensions.Add ("guid", Namespace.podcast, guid.ToString().ToLowerInvariant ())) + podcast.medium + |> Option.iter (fun med -> rssFeed.ElementExtensions.Add ("medium", Namespace.podcast, PodcastMedium.toString med)) /// Get the feed's self reference and non-feed link let private selfAndLink webLog feedType ctx = @@ -402,7 +416,7 @@ let generate (feedType : FeedType) postCount : HttpHandler = fun next ctx -> bac open DotLiquid -// GET: /admin/rss/settings +// GET: /admin/settings/rss let editSettings : HttpHandler = fun next ctx -> task { let webLog = ctx.WebLog let feeds = @@ -418,7 +432,7 @@ let editSettings : HttpHandler = fun next ctx -> task { |> viewForTheme "admin" "rss-settings" next ctx } -// POST: /admin/rss/settings +// POST: /admin/settings/rss let saveSettings : HttpHandler = fun next ctx -> task { let data = ctx.Data let! model = ctx.BindFormAsync () @@ -432,7 +446,7 @@ let saveSettings : HttpHandler = fun next ctx -> task { | None -> return! Error.notFound next ctx } -// GET: /admin/rss/{id}/edit +// GET: /admin/settings/rss/{id}/edit let editCustomFeed feedId : HttpHandler = fun next ctx -> task { let customFeed = match feedId with @@ -441,16 +455,26 @@ let editCustomFeed feedId : HttpHandler = fun next ctx -> task { match customFeed with | Some f -> return! Hash.FromAnonymousObject - {| csrf = csrfToken ctx - page_title = $"""{if feedId = "new" then "Add" else "Edit"} Custom RSS Feed""" - model = EditCustomFeedModel.fromFeed f - categories = CategoryCache.get ctx + {| csrf = csrfToken ctx + page_title = $"""{if feedId = "new" then "Add" else "Edit"} Custom RSS Feed""" + model = EditCustomFeedModel.fromFeed f + categories = CategoryCache.get ctx + medium_values = [| + KeyValuePair.Create ("", "– Unspecified –") + KeyValuePair.Create (PodcastMedium.toString Podcast, "Podcast") + KeyValuePair.Create (PodcastMedium.toString Music, "Music") + KeyValuePair.Create (PodcastMedium.toString Video, "Video") + KeyValuePair.Create (PodcastMedium.toString Film, "Film") + KeyValuePair.Create (PodcastMedium.toString Audiobook, "Audiobook") + KeyValuePair.Create (PodcastMedium.toString Newsletter, "Newsletter") + KeyValuePair.Create (PodcastMedium.toString Blog, "Blog") + |] |} |> viewForTheme "admin" "custom-feed-edit" next ctx | None -> return! Error.notFound next ctx } -// POST: /admin/rss/save +// POST: /admin/settings/rss/save let saveCustomFeed : HttpHandler = fun next ctx -> task { let data = ctx.Data match! data.WebLog.findById ctx.WebLog.id with @@ -476,7 +500,7 @@ let saveCustomFeed : HttpHandler = fun next ctx -> task { | None -> return! Error.notFound next ctx } -// POST /admin/rss/{id}/delete +// POST /admin/settings/rss/{id}/delete let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task { let data = ctx.Data match! data.WebLog.findById ctx.WebLog.id with diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index e43a349..9c3a26a 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -41,13 +41,17 @@ module DataImplementation = let get (sp : IServiceProvider) : IData = let config = sp.GetRequiredService () if (config.GetConnectionString >> isNull >> not) "SQLite" then + let log = sp.GetRequiredService> () let conn = new SqliteConnection (config.GetConnectionString "SQLite") + log.LogInformation $"Using SQL database {conn.DataSource}" SQLiteData.setUpConnection conn |> Async.AwaitTask |> Async.RunSynchronously upcast SQLiteData (conn, sp.GetRequiredService> ()) elif (config.GetSection "RethinkDB").Exists () then + let log = sp.GetRequiredService> () Json.all () |> Seq.iter Converter.Serializer.Converters.Add let rethinkCfg = DataConfig.FromConfiguration (config.GetSection "RethinkDB") let conn = rethinkCfg.CreateConnectionAsync () |> Async.AwaitTask |> Async.RunSynchronously + log.LogInformation $"Using RethinkDB database {rethinkCfg.Database}" upcast RethinkDbData (conn, rethinkCfg, sp.GetRequiredService> ()) else let log = sp.GetRequiredService> () diff --git a/src/admin-theme/custom-feed-edit.liquid b/src/admin-theme/custom-feed-edit.liquid index c20c29f..0a2f3fc 100644 --- a/src/admin-theme/custom-feed-edit.liquid +++ b/src/admin-theme/custom-feed-edit.liquid @@ -185,7 +185,7 @@ -
+
+
+
+
+ + + + Optional; URL describing donation options for this podcast, relative URL supported + +
+
+
+
+ + + Optional; text for the funding link +
+
+
+
+
+
+ + + + Optional; v5 UUID uniquely identifying this podcast; once entered, do not change this value + (documentation) + +
+
+
+
+ + + + Optional; medium of the podcast content + (documentation) + +
+
+