From 7757bd8394f8454e12959caa5670e635371a9f6c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sun, 29 May 2022 18:18:46 -0400 Subject: [PATCH] WIP on custom feed edit page --- src/MyWebLog.Domain/SupportTypes.fs | 16 +- src/MyWebLog.Domain/ViewModels.fs | 104 ++++++++++ src/MyWebLog/Handlers/Feed.fs | 47 +++-- src/MyWebLog/Handlers/Routes.fs | 6 +- src/MyWebLog/Program.fs | 9 +- .../themes/admin/custom-feed-edit.liquid | 186 ++++++++++++++++++ src/MyWebLog/themes/admin/rss-settings.liquid | 4 +- src/MyWebLog/wwwroot/themes/admin/admin.js | 25 +++ 8 files changed, 379 insertions(+), 18 deletions(-) create mode 100644 src/MyWebLog/themes/admin/custom-feed-edit.liquid diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs index 6cdbd9f..9de2868 100644 --- a/src/MyWebLog.Domain/SupportTypes.fs +++ b/src/MyWebLog.Domain/SupportTypes.fs @@ -278,13 +278,16 @@ type PodcastOptions = iTunesCategory : string /// A further refinement of the categorization of this podcast (iTunes field / values) - iTunesSubcategory : string + iTunesSubcategory : string option /// The explictness rating (iTunes field) explicit : ExplicitRating /// The default media type for files in this podcast defaultMediaType : string option + + /// The base URL for relative URL media files for this podcast (optional; defaults to web log base) + mediaBaseUrl : string option } @@ -303,6 +306,17 @@ type CustomFeed = podcast : PodcastOptions option } +/// Functions to support custom feeds +module CustomFeed = + + /// An empty custom feed + let empty = + { id = CustomFeedId "" + source = Category (CategoryId "") + path = Permalink "" + podcast = None + } + /// Really Simple Syndication (RSS) options for this web log type RssOptions = diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 3bf3e73..0cde291 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -164,6 +164,110 @@ type EditCategoryModel = } +/// View model to edit a custom RSS feed +[] +type EditCustomFeedModel = + { /// The ID of the feed being editing + id : string + + /// The type of source for this feed ("category" or "tag") + sourceType : string + + /// The category ID or tag on which this feed is based + sourceValue : string + + /// The relative path at which this feed is served + path : string + + /// Whether this feed defines a podcast + isPodcast : bool + + /// The title of the podcast + title : string + + /// A subtitle for the podcast + subtitle : string + + /// The number of items in the podcast feed + itemsInFeed : int + + /// A summary of the podcast (iTunes field) + summary : string + + /// The display name of the podcast author (iTunes field) + displayedAuthor : string + + /// The e-mail address of the user who registered the podcast at iTunes + email : string + + /// The link to the image for the podcast + imageUrl : string + + /// The category from iTunes under which this podcast is categorized + itunesCategory : string + + /// A further refinement of the categorization of this podcast (iTunes field / values) + itunesSubcategory : string + + /// The explictness rating (iTunes field) + explicit : string + + /// The default media type for files in this podcast + defaultMediaType : string + + /// The base URL for relative URL media files for this podcast (optional; defaults to web log base) + mediaBaseUrl : string + } + + /// An empty custom feed model + static member empty = + { id = "" + sourceType = "category" + sourceValue = "" + path = "" + isPodcast = false + title = "" + subtitle = "" + itemsInFeed = 25 + summary = "" + displayedAuthor = "" + email = "" + imageUrl = "" + itunesCategory = "" + itunesSubcategory = "" + explicit = "no" + defaultMediaType = "audio/mpeg" + mediaBaseUrl = "" + } + + /// Create a model from a custom feed + static member fromFeed (feed : CustomFeed) = + let rss = + { EditCustomFeedModel.empty with + id = CustomFeedId.toString feed.id + sourceType = match feed.source with Category _ -> "category" | Tag _ -> "tag" + sourceValue = match feed.source with Category (CategoryId catId) -> catId | Tag tag -> tag + path = Permalink.toString feed.path + } + match feed.podcast with + | Some p -> + { rss with + isPodcast = true + title = p.title + subtitle = defaultArg p.subtitle "" + itemsInFeed = p.itemsInFeed + summary = p.summary + displayedAuthor = p.displayedAuthor + email = p.email + itunesCategory = p.iTunesCategory + itunesSubcategory = defaultArg p.iTunesSubcategory "" + explicit = ExplicitRating.toString p.explicit + defaultMediaType = defaultArg p.defaultMediaType "" + mediaBaseUrl = defaultArg p.mediaBaseUrl "" + } + | None -> rss + + /// View model to edit a page [] type EditPageModel = diff --git a/src/MyWebLog/Handlers/Feed.fs b/src/MyWebLog/Handlers/Feed.fs index 6334428..03a87e6 100644 --- a/src/MyWebLog/Handlers/Feed.fs +++ b/src/MyWebLog/Handlers/Feed.fs @@ -180,9 +180,11 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) = let categoryXml = XmlDocument () let catElt = categoryXml.CreateElement ("itunes", "category", "") catElt.SetAttribute ("text", podcast.iTunesCategory) - let subCat = categoryXml.CreateElement ("itunes", "category", "") - subCat.SetAttribute ("text", podcast.iTunesSubcategory) - catElt.AppendChild subCat |> ignore + podcast.iTunesSubcategory + |> Option.iter (fun subCat -> + let subCatElt = categoryXml.CreateElement ("itunes", "category", "") + subCatElt.SetAttribute ("text", subCat) + catElt.AppendChild subCatElt |> ignore) categoryXml.AppendChild catElt |> ignore [ "dc", "http://purl.org/dc/elements/1.1/" @@ -287,13 +289,12 @@ let editSettings : HttpHandler = fun next ctx -> task { webLog.rss.customFeeds |> List.map (DisplayCustomFeed.fromFeed (CategoryCache.get ctx)) |> Array.ofList - return! - Hash.FromAnonymousObject - {| csrf = csrfToken ctx - page_title = "RSS Settings" - model = EditRssModel.fromRssOptions webLog.rss - custom_feeds = feeds - |} + return! Hash.FromAnonymousObject + {| csrf = csrfToken ctx + page_title = "RSS Settings" + model = EditRssModel.fromRssOptions webLog.rss + custom_feeds = feeds + |} |> viewForTheme "admin" "rss-settings" next ctx } @@ -311,6 +312,30 @@ let saveSettings : HttpHandler = fun next ctx -> task { | None -> return! Error.notFound next ctx } +// GET: /admin/rss/{id}/edit +let editCustomFeed feedId : HttpHandler = fun next ctx -> task { + let customFeed = + match feedId with + | "new" -> Some CustomFeed.empty + | _ -> ctx.WebLog.rss.customFeeds |> List.tryFind (fun f -> f.id = CustomFeedId feedId) + 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 + |} + |> viewForTheme "admin" "custom-feed-edit" next ctx + | None -> return! Error.notFound next ctx +} + +// POST: /admin/rss/save +let saveCustomFeed : HttpHandler = fun next ctx -> task { + // TODO: stub + return! Error.notFound next ctx +} + // POST /admin/rss/{id}/delete let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task { let conn = ctx.Conn @@ -329,7 +354,7 @@ let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task { WebLogCache.set webLog do! addMessage ctx { UserMessage.success with message = "Custom feed deleted successfully" } else - do! addMessage ctx { UserMessage.warning with message = "Post not found; nothing deleted" } + do! addMessage ctx { UserMessage.warning with message = "Custom feed not found; no action taken" } return! redirectToGet (WebLog.relativeUrl webLog (Permalink "admin/settings/rss")) next ctx | None -> return! Error.notFound next ctx } diff --git a/src/MyWebLog/Handlers/Routes.fs b/src/MyWebLog/Handlers/Routes.fs index 17f6d01..95c10fb 100644 --- a/src/MyWebLog/Handlers/Routes.fs +++ b/src/MyWebLog/Handlers/Routes.fs @@ -95,7 +95,10 @@ let router : HttpHandler = choose [ ]) subRoute "/settings" (choose [ route "" >=> Admin.settings - route "/rss" >=> Feed.editSettings + subRoute "/rss" (choose [ + route "" >=> Feed.editSettings + routef "/%s/edit" Feed.editCustomFeed + ]) subRoute "/tag-mapping" (choose [ route "s" >=> Admin.tagMappings routef "/%s/edit" Admin.editMapping @@ -122,6 +125,7 @@ let router : HttpHandler = choose [ route "" >=> Admin.saveSettings subRoute "/rss" (choose [ route "" >=> Feed.saveSettings + route "/save" >=> Feed.saveCustomFeed routef "/%s/delete" Feed.deleteCustomFeed ]) subRoute "/tag-mapping" (choose [ diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index 2e7da5e..189931e 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -220,10 +220,11 @@ let main args = [ // Domain types typeof; typeof; typeof; typeof; typeof; typeof // View models - typeof; typeof; typeof; typeof - typeof; typeof; typeof; typeof - typeof; typeof; typeof; typeof - typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof // Framework types typeof; typeof; typeof; typeof typeof; typeof; typeof diff --git a/src/MyWebLog/themes/admin/custom-feed-edit.liquid b/src/MyWebLog/themes/admin/custom-feed-edit.liquid new file mode 100644 index 0000000..e74794a --- /dev/null +++ b/src/MyWebLog/themes/admin/custom-feed-edit.liquid @@ -0,0 +1,186 @@ +

{{ page_title }}

+
+
+ + {%- assign is_cat = model.source_type == "category" -%} +
+
+
+
+ Feed Source +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + + Appended to {{ web_log.url_base }}/ +
+
+
+
+ + +
+
+
+
+
+
+ Podcast Settings +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + + Displayed in podcast directories +
+
+
+
+
+
+ + +
+
+
+
+ + + For iTunes, must match registered e-mail +
+
+
+
+ + +
+
+
+
+
+
+ + + + + iTunes Category / Subcategory List + + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + Optional; blank for no default +
+
+
+
+ + + Optional; prepended to episode media file if present +
+
+
+
+
+
+
+
+ +
+
+
+
+
diff --git a/src/MyWebLog/themes/admin/rss-settings.liquid b/src/MyWebLog/themes/admin/rss-settings.liquid index 9ba2fad..41b2ec4 100644 --- a/src/MyWebLog/themes/admin/rss-settings.liquid +++ b/src/MyWebLog/themes/admin/rss-settings.liquid @@ -64,7 +64,9 @@

Custom Feeds

- Add a New Custom Feed + + Add a New Custom Feed + diff --git a/src/MyWebLog/wwwroot/themes/admin/admin.js b/src/MyWebLog/wwwroot/themes/admin/admin.js index b6bd865..20c5bef 100644 --- a/src/MyWebLog/wwwroot/themes/admin/admin.js +++ b/src/MyWebLog/wwwroot/themes/admin/admin.js @@ -139,6 +139,31 @@ this.nextPermalink++ }, + /** + * Check to enable or disable podcast fields + */ + checkPodcast() { + document.getElementById("podcastFields").disabled = !document.getElementById("isPodcast").checked + }, + + /** + * Toggle the source of a custom RSS feed + * @param source The source that was selected + */ + customFeedBy(source) { + const categoryInput = document.getElementById("sourceValueCat") + const tagInput = document.getElementById("sourceValueTag") + if (source === "category") { + tagInput.value = "" + tagInput.disabled = true + categoryInput.disabled = false + } else { + categoryInput.selectedIndex = -1 + categoryInput.disabled = true + tagInput.disabled = false + } + }, + /** * Remove a metadata item * @param idx The index of the metadata item to remove