diff --git a/src/MyWebLog/Handlers/Feed.fs b/src/MyWebLog/Handlers/Feed.fs index 94dfd75..c1e8cdd 100644 --- a/src/MyWebLog/Handlers/Feed.fs +++ b/src/MyWebLog/Handlers/Feed.fs @@ -431,20 +431,23 @@ let editCustomFeed feedId : HttpHandler = requireAccess WebLogAdmin >=> fun next | _ -> ctx.WebLog.Rss.CustomFeeds |> List.tryFind (fun f -> f.Id = CustomFeedId feedId) match customFeed with | Some f -> - hashForPage $"""{if feedId = "new" then "Add" else "Edit"} Custom RSS Feed""" - |> withAntiCsrf ctx - |> addToHash ViewContext.Model (EditCustomFeedModel.FromFeed f) - |> addToHash "medium_values" [| - KeyValuePair.Create("", "– Unspecified –") - KeyValuePair.Create(string Podcast, "Podcast") - KeyValuePair.Create(string Music, "Music") - KeyValuePair.Create(string Video, "Video") - KeyValuePair.Create(string Film, "Film") - KeyValuePair.Create(string Audiobook, "Audiobook") - KeyValuePair.Create(string Newsletter, "Newsletter") - KeyValuePair.Create(string Blog, "Blog") - |] - |> adminView "custom-feed-edit" next ctx + let ratings = [ + { Name = string Yes; Value = "Yes" } + { Name = string No; Value = "No" } + { Name = string Clean; Value = "Clean" } + ] + let mediums = [ + { Name = ""; Value = "– Unspecified –" } + { Name = string Podcast; Value = "Podcast" } + { Name = string Music; Value = "Music" } + { Name = string Video; Value = "Video" } + { Name = string Film; Value = "Film" } + { Name = string Audiobook; Value = "Audiobook" } + { Name = string Newsletter; Value = "Newsletter" } + { Name = string Blog; Value = "Blog" } + ] + Views.Admin.feedEdit (EditCustomFeedModel.FromFeed f) ratings mediums + |> adminPage $"""{if feedId = "new" then "Add" else "Edit"} Custom RSS Feed""" true next ctx | None -> Error.notFound next ctx // POST /admin/settings/rss/save diff --git a/src/MyWebLog/Views/Admin.fs b/src/MyWebLog/Views/Admin.fs index 8568499..e1e171a 100644 --- a/src/MyWebLog/Views/Admin.fs +++ b/src/MyWebLog/Views/Admin.fs @@ -81,6 +81,216 @@ let dashboard (model: DashboardModel) app = [ ] +/// Custom RSS feed edit form +let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: MetaItem list) app = [ + h2 [ _class "my-3" ] [ raw app.PageTitle ] + article [] [ + form [ _action (relUrl app "admin/settings/rss/save"); _method "post"; _class "container" ] [ + antiCsrf app + input [ _type "hidden"; _name "Id"; _value model.Id ] + div [ _class "row pb-3" ] [ + div [ _class "col" ] [ + a [ _href (relUrl app "admin/settings#rss-settings") ] [ raw "« Back to Settings" ] + ] + ] + div [ _class "row pb-3" ] [ + div [ _class "col-12 col-lg-6" ] [ + fieldset [ _class "container pb-0" ] [ + legend [] [ raw "Identification" ] + div [ _class "row" ] [ + div [ _class "col" ] [ + textField [ _required ] (nameof model.Path) "Relative Feed Path" model.Path [ + span [ _class "form-text fst-italic" ] [ raw "Appended to "; txt app.WebLog.UrlBase ] + ] + ] + ] + div [ _class "row" ] [ + div [ _class "col py-3 d-flex align-self-center justify-content-center" ] [ + checkboxSwitch [ _onclick "Admin.checkPodcast()"; if model.IsPodcast then _checked ] + (nameof model.IsPodcast) "This Is a Podcast Feed" model.IsPodcast [] + ] + ] + ] + ] + div [ _class "col-12 col-lg-6" ] [ + fieldset [ _class "container pb-0" ] [ + legend [] [ raw "Feed Source" ] + div [ _class "row d-flex align-items-center" ] [ + div [ _class "col-1 d-flex justify-content-end pb-3" ] [ + div [ _class "form-check form-check-inline me-0" ] [ + input [ _type "radio"; _name (nameof model.SourceType); _id "SourceTypeCat" + _class "form-check-input"; _value "category" + if model.SourceType <> "tag" then _checked + _onclick "Admin.customFeedBy('category')" ] + label [ _for "SourceTypeCat"; _class "form-check-label d-none" ] [ raw "Category" ] + ] + ] + div [ _class "col-11 pb-3" ] [ + let cats = + app.Categories + |> Seq.ofArray + |> Seq.map (fun c -> + let parents = + if c.ParentNames.Length = 0 then "" + else + c.ParentNames + |> Array.map (fun it -> $"{it} ⟩ ") + |> String.concat "" + { Name = c.Id; Value = $"{parents} {c.Name}".Trim() }) + |> Seq.append [ { Name = ""; Value = "– Select Category –" } ] + |> List.ofSeq + selectField [ _id "SourceValueCat"; _required + if model.SourceType = "tag" then _disabled ] + (nameof model.SourceValue) "Category" model.SourceValue cats (_.Name) + (_.Value) [] + ] + div [ _class "col-1 d-flex justify-content-end pb-3" ] [ + div [ _class "form-check form-check-inline me-0" ] [ + input [ _type "radio"; _name (nameof model.SourceType); _id "SourceTypeTag" + _class "form-check-input"; _value "tag" + if model.SourceType= "tag" then _checked + _onclick "Admin.customFeedBy('tag')" ] + label [ _for "sourceTypeTag"; _class "form-check-label d-none" ] [ raw "Tag" ] + ] + ] + div [ _class "col-11 pb-3" ] [ + textField [ _id "SourceValueTag"; _required + if model.SourceType <> "tag" then _disabled ] + (nameof model.SourceValue) "Tag" + (if model.SourceType = "tag" then model.SourceValue else "") [] + ] + ] + ] + ] + ] + div [ _class "row pb-3" ] [ + div [ _class "col" ] [ + fieldset [ _class "container"; _id "podcastFields"; if not model.IsPodcast then _disabled ] [ + legend [] [ raw "Podcast Settings" ] + div [ _class "row" ] [ + div [ _class "col-12 col-md-5 col-lg-4 offset-lg-1 pb-3" ] [ + textField [ _required ] (nameof model.Title) "Title" model.Title [] + ] + div [ _class "col-12 col-md-4 col-lg-4 pb-3" ] [ + textField [] (nameof model.Subtitle) "Podcast Subtitle" model.Subtitle [] + ] + div [ _class "col-12 col-md-3 col-lg-2 pb-3" ] [ + numberField [ _required ] (nameof model.ItemsInFeed) "# Episodes" model.ItemsInFeed [] + ] + ] + div [ _class "row" ] [ + div [ _class "col-12 col-md-5 col-lg-4 offset-lg-1 pb-3" ] [ + textField [ _required ] (nameof model.AppleCategory) "iTunes Category" + model.AppleCategory [ + span [ _class "form-text fst-italic" ] [ + a [ _href "https://www.thepodcasthost.com/planning/itunes-podcast-categories/" + _target "_blank"; _rel "noopener" ] [ + raw "iTunes Category / Subcategory List" + ] + ] + ] + ] + div [ _class "col-12 col-md-4 pb-3" ] [ + textField [] (nameof model.AppleSubcategory) "iTunes Subcategory" model.AppleSubcategory + [] + ] + div [ _class "col-12 col-md-3 col-lg-2 pb-3" ] [ + selectField [ _required ] (nameof model.Explicit) "Explicit Rating" model.Explicit + ratings (_.Name) (_.Value) [] + ] + ] + div [ _class "row" ] [ + div [ _class "col-12 col-md-6 col-lg-4 offset-xxl-1 pb-3" ] [ + textField [ _required ] (nameof model.DisplayedAuthor) "Displayed Author" + model.DisplayedAuthor [] + ] + div [ _class "col-12 col-md-6 col-lg-4 pb-3" ] [ + emailField [ _required ] (nameof model.Email) "Author E-mail" model.Email [ + span [ _class "form-text fst-italic" ] [ + raw "For iTunes, must match registered e-mail" + ] + ] + ] + div [ _class "col-12 col-sm-5 col-md-4 col-lg-4 col-xl-3 offset-xl-1 col-xxl-2 offset-xxl-0 pb-3" ] [ + textField [] (nameof model.DefaultMediaType) "Default Media Type" + model.DefaultMediaType [ + span [ _class "form-text fst-italic" ] [ raw "Optional; blank for no default" ] + ] + ] + div [ _class "col-12 col-sm-7 col-md-8 col-lg-10 offset-lg-1 pb-3" ] [ + textField [ _required ] (nameof model.ImageUrl) "Image URL" model.ImageUrl [ + span [ _class "form-text fst-italic"] [ + raw "Relative URL will be appended to "; txt app.WebLog.UrlBase; raw "/" + ] + ] + ] + ] + div [ _class "row pb-3" ] [ + div [ _class "col-12 col-lg-10 offset-lg-1" ] [ + textField [ _required ] (nameof model.Summary) "Summary" model.Summary [ + span [ _class "form-text fst-italic" ] [ raw "Displayed in podcast directories" ] + ] + ] + ] + div [ _class "row pb-3" ] [ + div [ _class "col-12 col-lg-10 offset-lg-1" ] [ + textField [] (nameof model.MediaBaseUrl) "Media Base URL" model.MediaBaseUrl [ + span [ _class "form-text fst-italic" ] [ + raw "Optional; prepended to episode media file if present" + ] + ] + ] + ] + div [ _class "row" ] [ + div [ _class "col-12 col-lg-5 offset-lg-1 pb-3" ] [ + textField [] (nameof model.FundingUrl) "Funding URL" model.FundingUrl [ + span [ _class "form-text fst-italic" ] [ + raw "Optional; URL describing donation options for this podcast, " + raw "relative URL supported" + ] + ] + ] + div [ _class "col-12 col-lg-5 pb-3" ] [ + textField [ _maxlength "128" ] (nameof model.FundingText) "Funding Text" + model.FundingText [ + span [ _class "form-text fst-italic" ] [ raw "Optional; text for the funding link" ] + ] + ] + ] + div [ _class "row pb-3" ] [ + div [ _class "col-8 col-lg-5 offset-lg-1 pb-3" ] [ + textField [] (nameof model.PodcastGuid) "Podcast GUID" model.PodcastGuid [ + span [ _class "form-text fst-italic" ] [ + raw "Optional; v5 UUID uniquely identifying this podcast; " + raw "once entered, do not change this value (" + a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid" + _target "_blank"; _rel "noopener" ] [ + raw "documentation" + ]; raw ")" + ] + ] + ] + div [ _class "col-4 col-lg-3 offset-lg-2 pb-3" ] [ + selectField [] (nameof model.Medium) "Medium" model.Medium mediums (_.Name) (_.Value) [ + span [ _class "form-text fst-italic" ] [ + raw "Optional; medium of the podcast content (" + a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium" + _target "_blank"; _rel "noopener" ] [ + raw "documentation" + ]; raw ")" + ] + ] + ] + ] + ] + ] + ] + div [ _class "row pb-3" ] [ div [ _class "col text-center" ] [ saveButton ] ] + ] + ] +] + + /// Redirect Rule edit form let redirectEdit (model: EditRedirectRuleModel) app = [ let url = relUrl app $"admin/settings/redirect-rules/{model.RuleId}" diff --git a/src/MyWebLog/Views/Helpers.fs b/src/MyWebLog/Views/Helpers.fs index 5d4dfee..5372127 100644 --- a/src/MyWebLog/Views/Helpers.fs +++ b/src/MyWebLog/Views/Helpers.fs @@ -104,11 +104,17 @@ let yesOrNo value = /// Create a text input field let inputField fieldType attrs name labelText value extra = + let fieldId, newAttrs = + let passedId = attrs |> List.tryFind (fun x -> match x with KeyValue ("id", _) -> true | _ -> false) + match passedId with + | Some (KeyValue (_, idValue)) -> + idValue, attrs |> List.filter (fun x -> match x with KeyValue ("id", _) -> false | _ -> true) + | Some _ | None -> name, attrs div [ _class "form-floating" ] [ - [ _type fieldType; _name name; _id name; _class "form-control"; _placeholder labelText; _value value ] - |> List.append attrs + [ _type fieldType; _name name; _id fieldId; _class "form-control"; _placeholder labelText; _value value ] + |> List.append newAttrs |> input - label [ _for name ] [ raw labelText ] + label [ _for fieldId ] [ raw labelText ] yield! extra ] @@ -135,7 +141,7 @@ let selectField<'T, 'a> select ([ _name name; _id name; _class "form-control" ] |> List.append attrs) [ for item in values do let itemId = string (idFunc item) - option [ _value itemId; if value = itemId then _selected ] [ txt (displayFunc item) ] + option [ _value itemId; if value = itemId then _selected ] [ raw (displayFunc item) ] ] label [ _for name ] [ raw labelText ] yield! extra diff --git a/src/admin-theme/custom-feed-edit.liquid b/src/admin-theme/custom-feed-edit.liquid deleted file mode 100644 index 20d4297..0000000 --- a/src/admin-theme/custom-feed-edit.liquid +++ /dev/null @@ -1,259 +0,0 @@ -

{{ page_title }}

-
-
- - - {%- assign typ = model.source_type -%} -
- -
-
-
- Identification -
-
-
- - - Appended to {{ web_log.url_base }}/ -
-
-
-
-
-
- - -
-
-
-
-
-
-
- Feed Source -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
-
-
-
-
-
- Podcast Settings -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- - - - - iTunes Category / Subcategory List - - -
-
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- - -
-
-
-
- - - For iTunes, must match registered e-mail -
-
-
-
- - - Optional; blank for no default -
-
-
-
- - - Relative URL will be appended to {{ web_log.url_base }}/ -
-
-
-
-
-
- - - Displayed in podcast directories -
-
-
-
-
-
- - - Optional; prepended to episode media file if present -
-
-
-
-
-
- - - - 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) - -
-
-
-
-
-
-
-
- -
-
-
-
-
diff --git a/src/admin-theme/wwwroot/admin.js b/src/admin-theme/wwwroot/admin.js index 4727ca9..d69e134 100644 --- a/src/admin-theme/wwwroot/admin.js +++ b/src/admin-theme/wwwroot/admin.js @@ -249,7 +249,7 @@ this.Admin = { * Check to enable or disable podcast fields */ checkPodcast() { - document.getElementById("podcastFields").disabled = !document.getElementById("isPodcast").checked + document.getElementById("podcastFields").disabled = !document.getElementById("IsPodcast").checked }, /** @@ -269,8 +269,8 @@ this.Admin = { * @param {string} source The source that was selected */ customFeedBy(source) { - const categoryInput = document.getElementById("sourceValueCat") - const tagInput = document.getElementById("sourceValueTag") + const categoryInput = document.getElementById("SourceValueCat") + const tagInput = document.getElementById("SourceValueTag") if (source === "category") { tagInput.value = "" tagInput.disabled = true