V2 #1
|
@ -278,13 +278,16 @@ type PodcastOptions =
|
||||||
iTunesCategory : string
|
iTunesCategory : string
|
||||||
|
|
||||||
/// A further refinement of the categorization of this podcast (iTunes field / values)
|
/// A further refinement of the categorization of this podcast (iTunes field / values)
|
||||||
iTunesSubcategory : string
|
iTunesSubcategory : string option
|
||||||
|
|
||||||
/// The explictness rating (iTunes field)
|
/// The explictness rating (iTunes field)
|
||||||
explicit : ExplicitRating
|
explicit : ExplicitRating
|
||||||
|
|
||||||
/// The default media type for files in this podcast
|
/// The default media type for files in this podcast
|
||||||
defaultMediaType : string option
|
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
|
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
|
/// Really Simple Syndication (RSS) options for this web log
|
||||||
type RssOptions =
|
type RssOptions =
|
||||||
|
|
|
@ -164,6 +164,110 @@ type EditCategoryModel =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// View model to edit a custom RSS feed
|
||||||
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
|
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
|
/// View model to edit a page
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type EditPageModel =
|
type EditPageModel =
|
||||||
|
|
|
@ -180,9 +180,11 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) =
|
||||||
let categoryXml = XmlDocument ()
|
let categoryXml = XmlDocument ()
|
||||||
let catElt = categoryXml.CreateElement ("itunes", "category", "")
|
let catElt = categoryXml.CreateElement ("itunes", "category", "")
|
||||||
catElt.SetAttribute ("text", podcast.iTunesCategory)
|
catElt.SetAttribute ("text", podcast.iTunesCategory)
|
||||||
let subCat = categoryXml.CreateElement ("itunes", "category", "")
|
podcast.iTunesSubcategory
|
||||||
subCat.SetAttribute ("text", podcast.iTunesSubcategory)
|
|> Option.iter (fun subCat ->
|
||||||
catElt.AppendChild subCat |> ignore
|
let subCatElt = categoryXml.CreateElement ("itunes", "category", "")
|
||||||
|
subCatElt.SetAttribute ("text", subCat)
|
||||||
|
catElt.AppendChild subCatElt |> ignore)
|
||||||
categoryXml.AppendChild catElt |> ignore
|
categoryXml.AppendChild catElt |> ignore
|
||||||
|
|
||||||
[ "dc", "http://purl.org/dc/elements/1.1/"
|
[ "dc", "http://purl.org/dc/elements/1.1/"
|
||||||
|
@ -287,13 +289,12 @@ let editSettings : HttpHandler = fun next ctx -> task {
|
||||||
webLog.rss.customFeeds
|
webLog.rss.customFeeds
|
||||||
|> List.map (DisplayCustomFeed.fromFeed (CategoryCache.get ctx))
|
|> List.map (DisplayCustomFeed.fromFeed (CategoryCache.get ctx))
|
||||||
|> Array.ofList
|
|> Array.ofList
|
||||||
return!
|
return! Hash.FromAnonymousObject
|
||||||
Hash.FromAnonymousObject
|
{| csrf = csrfToken ctx
|
||||||
{| csrf = csrfToken ctx
|
page_title = "RSS Settings"
|
||||||
page_title = "RSS Settings"
|
model = EditRssModel.fromRssOptions webLog.rss
|
||||||
model = EditRssModel.fromRssOptions webLog.rss
|
custom_feeds = feeds
|
||||||
custom_feeds = feeds
|
|}
|
||||||
|}
|
|
||||||
|> viewForTheme "admin" "rss-settings" next ctx
|
|> viewForTheme "admin" "rss-settings" next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +312,30 @@ let saveSettings : HttpHandler = fun next ctx -> task {
|
||||||
| None -> return! Error.notFound next ctx
|
| 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
|
// POST /admin/rss/{id}/delete
|
||||||
let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task {
|
let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task {
|
||||||
let conn = ctx.Conn
|
let conn = ctx.Conn
|
||||||
|
@ -329,7 +354,7 @@ let deleteCustomFeed feedId : HttpHandler = fun next ctx -> task {
|
||||||
WebLogCache.set webLog
|
WebLogCache.set webLog
|
||||||
do! addMessage ctx { UserMessage.success with message = "Custom feed deleted successfully" }
|
do! addMessage ctx { UserMessage.success with message = "Custom feed deleted successfully" }
|
||||||
else
|
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
|
return! redirectToGet (WebLog.relativeUrl webLog (Permalink "admin/settings/rss")) next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,10 @@ let router : HttpHandler = choose [
|
||||||
])
|
])
|
||||||
subRoute "/settings" (choose [
|
subRoute "/settings" (choose [
|
||||||
route "" >=> Admin.settings
|
route "" >=> Admin.settings
|
||||||
route "/rss" >=> Feed.editSettings
|
subRoute "/rss" (choose [
|
||||||
|
route "" >=> Feed.editSettings
|
||||||
|
routef "/%s/edit" Feed.editCustomFeed
|
||||||
|
])
|
||||||
subRoute "/tag-mapping" (choose [
|
subRoute "/tag-mapping" (choose [
|
||||||
route "s" >=> Admin.tagMappings
|
route "s" >=> Admin.tagMappings
|
||||||
routef "/%s/edit" Admin.editMapping
|
routef "/%s/edit" Admin.editMapping
|
||||||
|
@ -122,6 +125,7 @@ let router : HttpHandler = choose [
|
||||||
route "" >=> Admin.saveSettings
|
route "" >=> Admin.saveSettings
|
||||||
subRoute "/rss" (choose [
|
subRoute "/rss" (choose [
|
||||||
route "" >=> Feed.saveSettings
|
route "" >=> Feed.saveSettings
|
||||||
|
route "/save" >=> Feed.saveCustomFeed
|
||||||
routef "/%s/delete" Feed.deleteCustomFeed
|
routef "/%s/delete" Feed.deleteCustomFeed
|
||||||
])
|
])
|
||||||
subRoute "/tag-mapping" (choose [
|
subRoute "/tag-mapping" (choose [
|
||||||
|
|
|
@ -220,10 +220,11 @@ let main args =
|
||||||
[ // Domain types
|
[ // Domain types
|
||||||
typeof<CustomFeed>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>; typeof<TagMap>; typeof<WebLog>
|
typeof<CustomFeed>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>; typeof<TagMap>; typeof<WebLog>
|
||||||
// View models
|
// View models
|
||||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
||||||
typeof<EditCategoryModel>; typeof<EditPageModel>; typeof<EditPostModel>; typeof<EditRssModel>
|
typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditPageModel>; typeof<EditPostModel>
|
||||||
typeof<EditTagMapModel>; typeof<EditUserModel>; typeof<LogOnModel>; typeof<ManagePermalinksModel>
|
typeof<EditRssModel>; typeof<EditTagMapModel>; typeof<EditUserModel>; typeof<LogOnModel>
|
||||||
typeof<PostDisplay>; typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
typeof<ManagePermalinksModel>; typeof<PostDisplay>; typeof<PostListItem>; typeof<SettingsModel>
|
||||||
|
typeof<UserMessage>
|
||||||
// Framework types
|
// Framework types
|
||||||
typeof<AntiforgeryTokenSet>; typeof<int option>; typeof<KeyValuePair>; typeof<MetaItem list>
|
typeof<AntiforgeryTokenSet>; typeof<int option>; typeof<KeyValuePair>; typeof<MetaItem list>
|
||||||
typeof<string list>; typeof<string option>; typeof<TagMap list>
|
typeof<string list>; typeof<string option>; typeof<TagMap list>
|
||||||
|
|
186
src/MyWebLog/themes/admin/custom-feed-edit.liquid
Normal file
186
src/MyWebLog/themes/admin/custom-feed-edit.liquid
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
<h2 class="my-3">{{ page_title }}</h2>
|
||||||
|
<article>
|
||||||
|
<form action="{{ "admin/settings/rss/save" | relative_link }}" method="post">
|
||||||
|
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
||||||
|
{%- assign is_cat = model.source_type == "category" -%}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col-12 col-xl-10 offset-xl-1">
|
||||||
|
<fieldset class="container pb-0">
|
||||||
|
<legend>Feed Source</legend>
|
||||||
|
<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="sourceType" id="sourceTypeCat" class="form-check-input" value="category"
|
||||||
|
{% if is_cat %}checked="checked"{% endif %} onclick="Admin.customFeedBy('category')">
|
||||||
|
<label for="sourceTypeCat" class="form-check-label d-none">Category</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-11 col-lg-5 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<select name="sourceValue" id="sourceValueCat" class="form-control" required
|
||||||
|
{% unless is_cat %}disabled="disabled"{% endunless %}>
|
||||||
|
<option value="">– Select Category –</option>
|
||||||
|
{% for cat in categories -%}
|
||||||
|
<option value="{{ cat.id }}"
|
||||||
|
{%- if is_cat and model.source_value == cat.id %} selected="selected"{% endif -%}>
|
||||||
|
{% for it in cat.parent_names %}{{ it }} ⟩ {% endfor %}{{ cat.name }}
|
||||||
|
</option>
|
||||||
|
{%- endfor %}
|
||||||
|
</select>
|
||||||
|
<label for="sourceValueCat">Category</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 d-flex justify-content-end pb-3">
|
||||||
|
<div class="form-check form-check-inline me-0">
|
||||||
|
<input type="radio" name="sourceType" id="sourceTypeTag" class="form-check-input" value="tag"
|
||||||
|
{%- unless is_cat %} checked="checked"{% endunless %} onclick="Admin.customFeedBy('tag')">
|
||||||
|
<label for="sourceTypeTag" class="form-check-label d-none">Tag</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-11 col-lg-5 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="sourceValue" id="sourceValueTag" class="form-control" placeholder="Tag"
|
||||||
|
{%- if is_cat %} disabled="disabled"{% endif %} required
|
||||||
|
{%- unless is_cat %} value="{{ model.source_value }}"{% endunless %}>
|
||||||
|
<label for="sourceValueTag">Tag</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6 col-lg-5 offset-lg-1 col-xl-4 offset-xl-2 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="path" id="path" class="form-control" placeholder="Relative Feed Path"
|
||||||
|
value="{{ model.path }}">
|
||||||
|
<label for="path">Relative Feed Path</label>
|
||||||
|
<span class="form-text fst-italic">Appended to {{ web_log.url_base }}/</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-lg-5 col-xl-4 pb-3 d-flex align-self-center justify-content-center">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input type="checkbox" name="isPodcast" id="isPodcast" class="form-check-input" value="true"
|
||||||
|
{%- if model.is_podcast %} checked="checked"{% endif %} onclick="Admin.checkPodcast()">
|
||||||
|
<label for="isPodcast" class="form-check-label">Is Podcast Feed</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col">
|
||||||
|
<fieldset class="container" id="podcastFields"{% unless model.is_podcast %} disabled="disabled"{%endunless%}>
|
||||||
|
<legend>Podcast Settings</legend>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-6 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="title" id="title" class="form-control" placeholder="Title" required
|
||||||
|
value="{{ model.title }}">
|
||||||
|
<label for="title">Title</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="subtitle" id="subtitle" class="form-control" placeholder="Subtitle"
|
||||||
|
value="{{ model.subtitle }}">
|
||||||
|
<label for="subtitle">Podcast Subtitle</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="summary" id="summary" class="form-control" placeholder="Summary" required
|
||||||
|
value="{{ model.summary }}">
|
||||||
|
<label for="summary">Summary</label>
|
||||||
|
<span class="form-text fst-italic">Displayed in podcast directories</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-5 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="displayedAuthor" id="displayedAuthor" class="form-control"
|
||||||
|
placeholder="Author" required value="{{ model.displayed_author }}">
|
||||||
|
<label for="displayedAuthor">Displayed Author</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-5 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="email" id="email" class="form-control" placeholder="Email" required
|
||||||
|
value="{{ model.email }}">
|
||||||
|
<label for="email">Author E-mail</label>
|
||||||
|
<span class="form-text fst-italic">For iTunes, must match registered e-mail</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-2 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="number" name="itemsInFeed" id="itemsInFeed" class="form-control" placeholder="Items"
|
||||||
|
required value="{{ model.items_in_feed }}">
|
||||||
|
<label for="itemsInFeed"># Episodes</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-6 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="itunesCategory" id="itunesCategory" class="form-control"
|
||||||
|
placeholder="iTunes Category" required value="{{ model.itunes_category }}">
|
||||||
|
<label for="itunesCategory">iTunes Category</label>
|
||||||
|
<span class="form-text fst-italic">
|
||||||
|
<a href="https://www.thepodcasthost.com/planning/itunes-podcast-categories/" target="_blank"
|
||||||
|
rel="noopener">
|
||||||
|
iTunes Category / Subcategory List
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="itunesSubcategory" id="itunesSubcategory" class="form-control"
|
||||||
|
placeholder="iTunes Subcategory" value="{{ model.itunes_subcategory }}">
|
||||||
|
<label for="itunesSubcategory">iTunes Subcategory</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-sm-6 col-lg-4">
|
||||||
|
<div class="form-floating">
|
||||||
|
<select name="explicit" id="explicit" class="form-control" required>
|
||||||
|
<option value="yes"{% if model.explicit == "yes" %} selected="selected"{% endif %}>Yes</option>
|
||||||
|
<option value="no"{% if model.explicit == "no" %} selected="selected"{% endif %}>No</option>
|
||||||
|
<option value="clean"{% if model.explicit == "clean" %} selected="selected"{% endif %}>
|
||||||
|
Clean
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<label for="explicit">Explicit Rating</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 col-lg-4">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="defaultMediaType" id="defaultMediaType" class="form-control"
|
||||||
|
placeholder="Media Type" value="{{ model.default_media_type }}">
|
||||||
|
<label for="defaultMediaType">Default Media Type</label>
|
||||||
|
<span class="form-text fst-italic">Optional; blank for no default</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 col-lg-4">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="mediaBaseUrl" id="mediaBaseUrl" class="form-control"
|
||||||
|
placeholder="Media Base URL" value="{{ model.media_base_url }}">
|
||||||
|
<label for="mediaBaseUrl">Media Base URL</label>
|
||||||
|
<span class="form-text fst-italic">Optional; prepended to episode media file if present</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col text-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</article>
|
|
@ -64,7 +64,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<h3>Custom Feeds</h3>
|
<h3>Custom Feeds</h3>
|
||||||
<a class="btn btn-sm btn-secondary" href="{{ 'admin/rss/new/edit' | relative_link }}">Add a New Custom Feed</a>
|
<a class="btn btn-sm btn-secondary" href="{{ 'admin/settings/rss/new/edit' | relative_link }}">
|
||||||
|
Add a New Custom Feed
|
||||||
|
</a>
|
||||||
<table class="table table-sm table-hover">
|
<table class="table table-sm table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -139,6 +139,31 @@
|
||||||
this.nextPermalink++
|
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
|
* Remove a metadata item
|
||||||
* @param idx The index of the metadata item to remove
|
* @param idx The index of the metadata item to remove
|
||||||
|
|
Loading…
Reference in New Issue
Block a user