Add / update / delete custom feed complete
This commit is contained in:
parent
7757bd8394
commit
f99623d1cb
@ -3,6 +3,15 @@
|
||||
open System
|
||||
open MyWebLog
|
||||
|
||||
/// Helper functions for view models
|
||||
[<AutoOpen>]
|
||||
module private Helpers =
|
||||
|
||||
/// Create a string option if a string is blank
|
||||
let noneIfBlank (it : string) =
|
||||
match it.Trim () with "" -> None | trimmed -> Some trimmed
|
||||
|
||||
|
||||
/// Details about a category, used to display category lists
|
||||
[<NoComparison; NoEquality>]
|
||||
type DisplayCategory =
|
||||
@ -259,6 +268,7 @@ type EditCustomFeedModel =
|
||||
summary = p.summary
|
||||
displayedAuthor = p.displayedAuthor
|
||||
email = p.email
|
||||
imageUrl = Permalink.toString p.imageUrl
|
||||
itunesCategory = p.iTunesCategory
|
||||
itunesSubcategory = defaultArg p.iTunesSubcategory ""
|
||||
explicit = ExplicitRating.toString p.explicit
|
||||
@ -266,7 +276,31 @@ type EditCustomFeedModel =
|
||||
mediaBaseUrl = defaultArg p.mediaBaseUrl ""
|
||||
}
|
||||
| None -> rss
|
||||
|
||||
|
||||
/// Update a feed with values from this model
|
||||
member this.updateFeed (feed : CustomFeed) =
|
||||
{ feed with
|
||||
source = if this.sourceType = "tag" then Tag this.sourceValue else Category (CategoryId this.sourceValue)
|
||||
path = Permalink this.path
|
||||
podcast =
|
||||
if this.isPodcast then
|
||||
Some {
|
||||
title = this.title
|
||||
subtitle = noneIfBlank this.subtitle
|
||||
itemsInFeed = this.itemsInFeed
|
||||
summary = this.summary
|
||||
displayedAuthor = this.displayedAuthor
|
||||
email = this.email
|
||||
imageUrl = Permalink this.imageUrl
|
||||
iTunesCategory = this.itunesCategory
|
||||
iTunesSubcategory = noneIfBlank this.itunesSubcategory
|
||||
explicit = ExplicitRating.parse this.explicit
|
||||
defaultMediaType = noneIfBlank this.defaultMediaType
|
||||
mediaBaseUrl = noneIfBlank this.mediaBaseUrl
|
||||
}
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
/// View model to edit a page
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
@ -427,7 +461,7 @@ type EditRssModel =
|
||||
itemsInFeed = if this.itemsInFeed = 0 then None else Some this.itemsInFeed
|
||||
categoryEnabled = this.categoryEnabled
|
||||
tagEnabled = this.tagEnabled
|
||||
copyright = if this.copyright.Trim () = "" then None else Some (this.copyright.Trim ())
|
||||
copyright = noneIfBlank this.copyright
|
||||
}
|
||||
|
||||
|
||||
|
@ -316,7 +316,7 @@ let saveSettings : HttpHandler = fun next ctx -> task {
|
||||
let editCustomFeed feedId : HttpHandler = fun next ctx -> task {
|
||||
let customFeed =
|
||||
match feedId with
|
||||
| "new" -> Some CustomFeed.empty
|
||||
| "new" -> Some { CustomFeed.empty with id = CustomFeedId "new" }
|
||||
| _ -> ctx.WebLog.rss.customFeeds |> List.tryFind (fun f -> f.id = CustomFeedId feedId)
|
||||
match customFeed with
|
||||
| Some f ->
|
||||
@ -332,8 +332,28 @@ let editCustomFeed feedId : HttpHandler = fun next ctx -> task {
|
||||
|
||||
// POST: /admin/rss/save
|
||||
let saveCustomFeed : HttpHandler = fun next ctx -> task {
|
||||
// TODO: stub
|
||||
return! Error.notFound next ctx
|
||||
let conn = ctx.Conn
|
||||
match! Data.WebLog.findById ctx.WebLog.id conn with
|
||||
| Some webLog ->
|
||||
let! model = ctx.BindFormAsync<EditCustomFeedModel> ()
|
||||
let theFeed =
|
||||
match model.id with
|
||||
| "new" -> Some { CustomFeed.empty with id = CustomFeedId.create () }
|
||||
| _ -> webLog.rss.customFeeds |> List.tryFind (fun it -> CustomFeedId.toString it.id = model.id)
|
||||
match theFeed with
|
||||
| Some feed ->
|
||||
let feeds = model.updateFeed feed :: (webLog.rss.customFeeds |> List.filter (fun it -> it.id <> feed.id))
|
||||
let webLog = { webLog with rss = { webLog.rss with customFeeds = feeds } }
|
||||
do! Data.WebLog.updateRssOptions webLog conn
|
||||
WebLogCache.set webLog
|
||||
do! addMessage ctx {
|
||||
UserMessage.success with
|
||||
message = $"""Successfully {if model.id = "new" then "add" else "sav"}ed custom feed"""
|
||||
}
|
||||
let nextUrl = $"admin/settings/rss/{CustomFeedId.toString feed.id}/edit"
|
||||
return! redirectToGet (WebLog.relativeUrl webLog (Permalink nextUrl)) next ctx
|
||||
| None -> return! Error.notFound next ctx
|
||||
| None -> return! Error.notFound next ctx
|
||||
}
|
||||
|
||||
// POST /admin/rss/{id}/delete
|
||||
|
@ -2,28 +2,59 @@
|
||||
<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" -%}
|
||||
<input type="hidden" name="id" value="{{ model.id }}">
|
||||
{%- assign typ = model.source_type -%}
|
||||
<div class="container">
|
||||
<div class="row pb-3">
|
||||
<div class="col-12 col-xl-10 offset-xl-1">
|
||||
<div class="col">
|
||||
<a href="{{ "admin/settings/rss" | relative_link }}">« Back to RSS Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pb-3">
|
||||
<div class="col-12 col-lg-6">
|
||||
<fieldset class="container pb-0">
|
||||
<legend>Identification</legend>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<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>
|
||||
<div class="row">
|
||||
<div class="col py-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">This Is a Podcast Feed</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<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')">
|
||||
{%- unless typ == "tag" %} checked="checked" {% endunless -%}
|
||||
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="col-11 pb-3">
|
||||
<div class="form-floating">
|
||||
<select name="sourceValue" id="sourceValueCat" class="form-control" required
|
||||
{% unless is_cat %}disabled="disabled"{% endunless %}>
|
||||
{%- if typ == "tag" %} disabled="disabled"{% endif %}>
|
||||
<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 -%}>
|
||||
{%- if typ != "tag" and model.source_value == cat.id %} selected="selected"{% endif -%}>
|
||||
{% for it in cat.parent_names %}{{ it }} ⟩ {% endfor %}{{ cat.name }}
|
||||
</option>
|
||||
{%- endfor %}
|
||||
@ -34,15 +65,15 @@
|
||||
<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')">
|
||||
{%- if typ == "tag" %} checked="checked"{% endif %} 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="col-11 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 %}>
|
||||
{%- unless typ == "tag" %} disabled="disabled"{% endunless %} required
|
||||
{%- if typ == "tag" %} value="{{ model.source_value }}"{% endif %}>
|
||||
<label for="sourceValueTag">Tag</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,70 +81,26 @@
|
||||
</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="col-12 col-md-5 col-lg-4 offset-lg-1 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="col-12 col-md-4 col-lg-4 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="col-12 col-md-3 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 }}">
|
||||
@ -122,7 +109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 pb-3">
|
||||
<div class="col-12 col-md-5 col-lg-4 offset-lg-1 pb-3">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="itunesCategory" id="itunesCategory" class="form-control"
|
||||
placeholder="iTunes Category" required value="{{ model.itunes_category }}">
|
||||
@ -135,16 +122,14 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 pb-3">
|
||||
<div class="col-12 col-md-4 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="col-12 col-md-3 col-lg-2 pb-3">
|
||||
<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>
|
||||
@ -156,7 +141,24 @@
|
||||
<label for="explicit">Explicit Rating</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-4">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-lg-4 offset-xxl-1 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-md-6 col-lg-4 pb-3">
|
||||
<div class="form-floating">
|
||||
<input type="email" 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-sm-5 col-md-4 col-lg-4 col-xl-3 offset-xl-1 col-xxl-2 offset-xxl-0">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="defaultMediaType" id="defaultMediaType" class="form-control"
|
||||
placeholder="Media Type" value="{{ model.default_media_type }}">
|
||||
@ -164,7 +166,27 @@
|
||||
<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="col-12 col-sm-7 col-md-8 col-lg-10 offset-lg-1">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="imageUrl" id="imageUrl" class="form-control" placeholder="Image URL" required
|
||||
value="{{ model.image_url }}">
|
||||
<label for="imageUrl">Image URL</label>
|
||||
<span class="form-text fst-italic">Relative URL will be appended to {{ web_log.url_base }}/</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pb-3">
|
||||
<div class="col-12 col-lg-10 offset-lg-1">
|
||||
<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-10 offset-lg-1">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="mediaBaseUrl" id="mediaBaseUrl" class="form-control"
|
||||
placeholder="Media Base URL" value="{{ model.media_base_url }}">
|
||||
|
@ -85,10 +85,10 @@
|
||||
<small>
|
||||
<a href="{{ feed.path | relative_link }}" target="_blank">View Feed</a>
|
||||
<span class="text-muted"> • </span>
|
||||
{%- capture feed_edit %}admin/rss/{{ feed.id }}/edit{% endcapture -%}
|
||||
{%- capture feed_edit %}admin/settings/rss/{{ feed.id }}/edit{% endcapture -%}
|
||||
<a href="{{ feed_edit | relative_link }}">Edit</a>
|
||||
<span class="text-muted"> • </span>
|
||||
{%- capture feed_del %}admin/rss/{{ feed.id }}/delete{% endcapture -%}
|
||||
{%- capture feed_del %}admin/settings/rss/{{ feed.id }}/delete{% endcapture -%}
|
||||
{%- capture feed_del_link %}{{ feed_del | relative_link }}{% endcapture -%}
|
||||
<a href="{{ feed_del_link }}" class="text-danger"
|
||||
onclick="return Admin.deleteCustomFeed('{{ feed.source }}', '{{ feed_del_link }}')">
|
||||
|
Loading…
Reference in New Issue
Block a user