WIP on RSS settings page

- Move tag mappings to /settings URLs
- Remove container wrap for table pages
- Add notes for empty tables
This commit is contained in:
Daniel J. Summers 2022-05-29 12:12:57 -04:00
parent 92cc50254c
commit 50179ffab9
12 changed files with 199 additions and 147 deletions

View File

@ -302,6 +302,9 @@ type RssOptions =
/// Whether feeds are enabled for all tags
tagEnabled : bool
/// A copyright string to be placed in all feeds
copyright : string option
/// Custom feeds for this web log
customFeeds: CustomFeed list
}
@ -316,6 +319,7 @@ module RssOptions =
itemsInFeed = None
categoryEnabled = true
tagEnabled = true
copyright = None
customFeeds = []
}

View File

@ -199,7 +199,6 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) =
"link", feedUrl
]
|> List.fold (fun doc (name, value) -> addChild doc name "" value) (XmlDocument ()))
// TODO: is copyright required?
rssFeed.ElementExtensions.Add ("summary", "itunes", podcast.summary)
rssFeed.ElementExtensions.Add ("author", "itunes", podcast.displayedAuthor)
podcast.subtitle |> Option.iter (fun sub -> rssFeed.ElementExtensions.Add ("subtitle", "itunes", sub))
@ -238,10 +237,11 @@ let createFeed (feedType : FeedType) posts : HttpHandler = fun next ctx -> backg
feed.Items <- posts |> Seq.ofList |> Seq.map toItem
feed.Language <- "en"
feed.Id <- webLog.urlBase
webLog.rss.copyright |> Option.iter (fun copy -> feed.Copyright <- TextSyndicationContent copy)
// TODO: adjust this link for non-root feeds
feed.Links.Add (SyndicationLink (Uri $"{webLog.urlBase}/feed.xml", "self", "", "application/rss+xml", 0L))
feed.AttributeExtensions.Add
(XmlQualifiedName ("content", "http://www.w3.org/2000/xmlns/"), "http://purl.org/rss/1.0/modules/content/")
addNamespace feed "content" "http://purl.org/rss/1.0/modules/content/"
feed.ElementExtensions.Add ("link", "", webLog.urlBase)
podcast |> Option.iter (addPodcast webLog feed)
@ -273,7 +273,9 @@ let editSettings : HttpHandler = fun next ctx -> task {
// TODO: stopped here
return!
Hash.FromAnonymousObject
{| csrf = csrfToken ctx
{| csrf = csrfToken ctx
model = ctx.WebLog.rss
page_title = "RSS Settings"
|}
|> viewForTheme "admin" "rss-settings" next ctx
}

View File

@ -93,13 +93,13 @@ let router : HttpHandler = choose [
routef "/%s/edit" Post.edit
routef "/%s/permalinks" Post.editPermalinks
])
subRoute "/rss" (choose [
route "/settings" >=> Feed.editSettings
])
route "/settings" >=> Admin.settings
subRoute "/tag-mapping" (choose [
route "s" >=> Admin.tagMappings
routef "/%s/edit" Admin.editMapping
subRoute "/settings" (choose [
route "" >=> Admin.settings
route "/rss" >=> Feed.editSettings
subRoute "/tag-mapping" (choose [
route "s" >=> Admin.tagMappings
routef "/%s/edit" Admin.editMapping
])
])
route "/user/edit" >=> User.edit
]
@ -118,10 +118,12 @@ let router : HttpHandler = choose [
route "/permalinks" >=> Post.savePermalinks
routef "/%s/delete" Post.delete
])
route "/settings" >=> Admin.saveSettings
subRoute "/tag-mapping" (choose [
route "/save" >=> Admin.saveMapping
routef "/%s/delete" Admin.deleteMapping
subRoute "/settings" (choose [
route "" >=> Admin.saveSettings
subRoute "/tag-mapping" (choose [
route "/save" >=> Admin.saveMapping
routef "/%s/delete" Admin.deleteMapping
])
])
route "/user/save" >=> User.save
]

View File

@ -202,7 +202,7 @@ let main args =
opts.TableName <- "Session"
opts.Connection <- conn)
let _ = builder.Services.AddSession(fun opts ->
opts.IdleTimeout <- TimeSpan.FromMinutes 30
opts.IdleTimeout <- TimeSpan.FromMinutes 60
opts.Cookie.HttpOnly <- true
opts.Cookie.IsEssential <- true)
@ -218,15 +218,15 @@ let main args =
Template.RegisterTag<UserLinksTag> "user_links"
[ // Domain types
typeof<MetaItem>; typeof<Page>; typeof<TagMap>; typeof<WebLog>
typeof<CustomFeed>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>; typeof<TagMap>; typeof<WebLog>
// View models
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayPage>; typeof<EditCategoryModel>
typeof<EditPageModel>; typeof<EditPostModel>; typeof<EditTagMapModel>; typeof<EditUserModel>
typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<PostDisplay>; typeof<PostListItem>
typeof<SettingsModel>; typeof<UserMessage>
// Framework types
typeof<AntiforgeryTokenSet>; typeof<KeyValuePair>; typeof<MetaItem list>; typeof<string list>
typeof<string option>; typeof<TagMap list>
typeof<AntiforgeryTokenSet>; typeof<int option>; typeof<KeyValuePair>; typeof<MetaItem list>
typeof<string list>; typeof<string option>; typeof<TagMap list>
]
|> List.iter (fun it -> Template.RegisterSafeType (it, [| "*" |]))

View File

@ -1,5 +1,5 @@
<h2 class="my-3">{{ page_title }}</h2>
<article class="container">
<article>
<a href="{{ "admin/category/new/edit" | relative_link }}" class="btn btn-primary btn-sm mb-3">Add a New Category</a>
<table class="table table-sm table-hover">
<thead>
@ -9,36 +9,43 @@
</tr>
</thead>
<tbody>
{% for cat in categories -%}
<tr>
<td class="no-wrap">
{%- if cat.parent_names %}
<small class="text-muted">{% for name in cat.parent_names %}{{ name }} &rang; {% endfor %}</small>
{%- endif %}
{{ cat.name }}<br>
<small>
{%- if cat.post_count > 0 %}
<a href="{{ cat | category_link }}" target="_blank">
View {{ cat.post_count }} Post{% unless cat.post_count == 1 %}s{% endunless -%}
</a>
<span class="text-muted"> &bull; </span>
{%- assign cat_count = categories | size -%}
{% if cat_count > 0 %}
{% for cat in categories -%}
<tr>
<td class="no-wrap">
{%- if cat.parent_names %}
<small class="text-muted">{% for name in cat.parent_names %}{{ name }} &rang; {% endfor %}</small>
{%- endif %}
{%- capture cat_edit %}admin/category/{{ cat.id }}/edit{% endcapture -%}
<a href="{{ cat_edit | relative_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture cat_del %}admin/category/{{ cat.id }}/delete{% endcapture -%}
{%- capture cat_del_link %}{{ cat_del | relative_link }}{% endcapture -%}
<a href="{{ cat_del_link }}" class="text-danger"
onclick="return Admin.deleteCategory('{{ cat.name }}', '{{ cat_del_link }}')">
Delete
</a>
</small>
</td>
<td>
{%- if cat.description %}{{ cat.description.value }}{% else %}<em class="text-muted">none</em>{% endif %}
</td>
{{ cat.name }}<br>
<small>
{%- if cat.post_count > 0 %}
<a href="{{ cat | category_link }}" target="_blank">
View {{ cat.post_count }} Post{% unless cat.post_count == 1 %}s{% endunless -%}
</a>
<span class="text-muted"> &bull; </span>
{%- endif %}
{%- capture cat_edit %}admin/category/{{ cat.id }}/edit{% endcapture -%}
<a href="{{ cat_edit | relative_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture cat_del %}admin/category/{{ cat.id }}/delete{% endcapture -%}
{%- capture cat_del_link %}{{ cat_del | relative_link }}{% endcapture -%}
<a href="{{ cat_del_link }}" class="text-danger"
onclick="return Admin.deleteCategory('{{ cat.name }}', '{{ cat_del_link }}')">
Delete
</a>
</small>
</td>
<td>
{%- if cat.description %}{{ cat.description.value }}{% else %}<em class="text-muted">none</em>{% endif %}
</td>
</tr>
{%- endfor %}
{%- else -%}
<tr>
<td colspan="2" class="text-muted fst-italic text-center">This web log has no categores defined</td>
</tr>
{%- endfor %}
{%- endif %}
</tbody>
</table>
<form method="post" id="deleteForm">

View File

@ -24,7 +24,7 @@
{{ "admin/pages" | nav_link: "Pages" }}
{{ "admin/posts" | nav_link: "Posts" }}
{{ "admin/categories" | nav_link: "Categories" }}
{{ "admin/tag-mappings" | nav_link: "Tag Mappings" }}
{{ "admin/settings" | nav_link: "Settings" }}
</ul>
{%- endif %}
<ul class="navbar-nav flex-grow-1 justify-content-end">

View File

@ -1,5 +1,5 @@
<h2 class="my-3">{{ page_title }}</h2>
<article class="container">
<article>
<a href="{{ "admin/page/new/edit" | relative_link }}" class="btn btn-primary btn-sm mb-3">Create a New Page</a>
<table class="table table-sm table-hover">
<thead>
@ -10,30 +10,37 @@
</tr>
</thead>
<tbody>
{% for pg in pages -%}
{%- assign page_count = pages | size -%}
{% if page_count > 0 %}
{% for pg in pages -%}
<tr>
<td>
{{ pg.title }}
{%- if pg.is_default %} &nbsp; <span class="badge bg-success">HOME PAGE</span>{% endif -%}
{%- if pg.show_in_page_list %} &nbsp; <span class="badge bg-primary">IN PAGE LIST</span> {% endif -%}<br>
<small>
{%- capture pg_link %}{% unless pg.is_default %}{{ pg.permalink }}{% endunless %}{% endcapture -%}
<a href="{{ pg_link | relative_link }}" target="_blank">View Page</a>
<span class="text-muted"> &bull; </span>
<a href="{{ pg | edit_page_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture pg_del %}admin/page/{{ pg.id }}/delete{% endcapture -%}
{%- capture pg_del_link %}{{ pg_del | relative_link }}{% endcapture -%}
<a href="{{ pg_del_link }}" class="text-danger"
onclick="return Admin.deletePage('{{ pg.title }}', '{{ pg_del_link }}')">
Delete
</a>
</small>
</td>
<td>/{% unless pg.is_default %}{{ pg.permalink }}{% endunless %}</td>
<td>{{ pg.updated_on | date: "MMMM d, yyyy" }}</td>
</tr>
{%- endfor %}
{% else %}
<tr>
<td>
{{ pg.title }}
{%- if pg.is_default %} &nbsp; <span class="badge bg-success">HOME PAGE</span>{% endif -%}
{%- if pg.show_in_page_list %} &nbsp; <span class="badge bg-primary">IN PAGE LIST</span> {% endif -%}<br>
<small>
{%- capture pg_link %}{% unless pg.is_default %}{{ pg.permalink }}{% endunless %}{% endcapture -%}
<a href="{{ pg_link | relative_link }}" target="_blank">View Page</a>
<span class="text-muted"> &bull; </span>
<a href="{{ pg | edit_page_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture pg_del %}admin/page/{{ pg.id }}/delete{% endcapture -%}
{%- capture pg_del_link %}{{ pg_del | relative_link }}{% endcapture -%}
<a href="{{ pg_del_link }}" class="text-danger"
onclick="return Admin.deletePage('{{ pg.title }}', '{{ pg_del_link }}')">
Delete
</a>
</small>
</td>
<td>/{% unless pg.is_default %}{{ pg.permalink }}{% endunless %}</td>
<td>{{ pg.updated_on | date: "MMMM d, yyyy" }}</td>
<td colspan="3" class="text-muted fst-italic text-center">This web log has no pages</td>
</tr>
{%- endfor %}
{% endif %}
</tbody>
</table>
<form method="post" id="deleteForm">

View File

@ -1,5 +1,5 @@
<h2 class="my-3">{{ page_title }}</h2>
<article class="container">
<article>
<a href="{{ "admin/post/new/edit" | relative_link }}" class="btn btn-primary btn-sm mb-3">Write a New Post</a>
<table class="table table-sm table-hover">
<thead>
@ -12,34 +12,41 @@
</tr>
</thead>
<tbody>
{% for post in model.posts -%}
{%- assign post_count = model.posts | size -%}
{%- if post_count > 0 %}
{% for post in model.posts -%}
<tr>
<td class="no-wrap">
{% if post.published_on %}{{ post.published_on | date: "MMMM d, yyyy" }}{% else %}Not Published{% endif %}
{%- if post.published_on != post.updated_on %}<br>
<small class="text-muted"><em>{{ post.updated_on | date: "MMMM d, yyyy" }}</em></small>
{%- endif %}
</td>
<td>
{{ post.title }}<br>
<small>
<a href="{{ post | relative_link }}" target="_blank">View Post</a>
<span class="text-muted"> &bull; </span>
<a href="{{ post | edit_post_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture post_del %}admin/post/{{ post.id }}/delete{% endcapture -%}
{%- capture post_del_link %}{{ post_del | relative_link }}{% endcapture -%}
<a href="{{ post_del_link }}" class="text-danger"
onclick="return Admin.deletePost('{{ post.title }}', '{{ post_del_link }}')">
Delete
</a>
</small>
</td>
<td class="no-wrap">{{ model.authors | value: post.author_id }}</td>
<td>{{ post.status }}</td>
<td><span class="no-wrap">{{ post.tags | join: "</span>, <span class='no-wrap'>" }}</span></td>
</tr>
{%- endfor %}
{% else %}
<tr>
<td class="no-wrap">
{% if post.published_on %}{{ post.published_on | date: "MMMM d, yyyy" }}{% else %}Not Published{% endif %}
{%- if post.published_on != post.updated_on %}<br>
<small class="text-muted"><em>{{ post.updated_on | date: "MMMM d, yyyy" }}</em></small>
{%- endif %}
</td>
<td>
{{ post.title }}<br>
<small>
<a href="{{ post | relative_link }}" target="_blank">View Post</a>
<span class="text-muted"> &bull; </span>
<a href="{{ post | edit_post_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture post_del %}admin/post/{{ post.id }}/delete{% endcapture -%}
{%- capture post_del_link %}{{ post_del | relative_link }}{% endcapture -%}
<a href="{{ post_del_link }}" class="text-danger"
onclick="return Admin.deletePost('{{ post.title }}', '{{ post_del_link }}')">
Delete
</a>
</small>
</td>
<td class="no-wrap">{{ model.authors | value: post.author_id }}</td>
<td>{{ post.status }}</td>
<td><span class="no-wrap">{{ post.tags | join: "</span>, <span class='no-wrap'>" }}</span></td>
<td colspan="5" class="text-muted fst-italic text-center">This web log has no posts</td>
</tr>
{%- endfor %}
{% endif %}
</tbody>
</table>
{% if model.newer_link or model.older_link %}

View File

@ -1,41 +1,58 @@
<h2 class="my-3">{{ web_log.name }} RSS Options</h2>
<h2 class="my-3">{{ page_title }}</h2>
<article>
<form action="{{ "admin/rss/settings" | relative_link }}" method="post">
<form action="{{ "admin/settings/rss" | relative_link }}" method="post">
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
<div class="container">
<div class="row">
<div class="col-12 col-md-6 col-xl-4 offset-xl-1 pb-3">
<div class="form-check form-switch pb-2">
<input type="checkbox" name="feedEnabled" id="feedEnabled" class="form-check-input" value="true"
{% if model.feed_enabled %}checked="checked"{% endif %}>
<label for="feedEnabled" class="form-check-label">Main Feed Enabled</label>
</div>
<div class="row pb-3">
<div class="col col-xl-8 offset-xl-2">
<fieldset class="d-flex justify-content-evenly flex-row">
<legend>Feeds Enabled</legend>
<div class="form-check form-switch pb-2">
<input type="checkbox" name="feedEnabled" id="feedEnabled" class="form-check-input" value="true"
{% if model.feed_enabled %}checked="checked"{% endif %}>
<label for="feedEnabled" class="form-check-label">All Posts</label>
</div>
<div class="form-check form-switch pb-2">
<input type="checkbox" name="categoryEnabled" id="categoryEnabled" class="form-check-input" value="true"
{% if model.category_enabled %}checked="checked"{% endif %}>
<label for="categoryEnabled" class="form-check-label">Posts by Category</label>
</div>
<div class="form-check form-switch pb-2">
<input type="checkbox" name="tagEnabled" id="tagEnabled" class="form-check-input" value="true"
{% if model.tag_enabled %}checked="checked"{% endif %}>
<label for="tagEnabled" class="form-check-label">Posts by Tag</label>
</div>
</fieldset>
</div>
<div class="col-12 col-md-6 col-xl-4 pb-3">
</div>
<div class="row">
<div class="col-12 col-sm-6 col-md-3 col-xl-2 offset-xl-2 pb-3">
<div class="form-floating">
<input type="text" name="feedName" id="feedName" class="form-control" value="{{ model.feed_name }}">
<input type="text" name="feedName" id="feedName" class="form-control" placeholder="Feed File Name"
value="{{ model.feed_name }}">
<label for="feedName">Feed File Name</label>
<span class="form-text">Default is <code>feed.xml</code></span>
</div>
</div>
<div class="col-12 col-md-4 col-xl-2 pb-3">
<div class="col-12 col-sm-6 col-md-4 col-xl-2 pb-3">
<div class="form-floating">
<input type="number" name="itemsInFeed" id="itemsInFeed" class="form-control" min="0" required
value="{{ model.items_in_feed }}">
<input type="number" name="itemsInFeed" id="itemsInFeed" class="form-control" min="0"
placeholder="Items in Feed" required value="{{ model.items_in_feed }}">
<label for="itemsInFeed">Items in Feed</label>
<span class="form-text">Set to &ldquo;0&rdquo; to use &ldquo;Posts per Page&rdquo; setting</span>
<span class="form-text">Set to &ldquo;0&rdquo; to use &ldquo;Posts per Page&rdquo; setting ({{ web_log.posts_per_page }})</span>
</div>
</div>
<div class="col-12 col-md-4 col-xl-3 offset-xl-1 pb-3">
<div class="form-check form-switch pb-2">
<input type="checkbox" name="categoryEnabled" id="categoryEnabled" class="form-check-input" value="true"
{% if model.category_enabled %}checked="checked"{% endif %}>
<label for="categoryEnabled" class="form-check-label">Enable Category Feed</label>
</div>
<div class="form-check form-switch pb-2">
<input type="checkbox" name="tagEnabled" id="tagEnabled" class="form-check-input" value="true"
{% if model.tag_enabled %}checked="checked"{% endif %}>
<label for="tagEnabled" class="form-check-label">Enable Tag Feed</label>
<div class="col-12 col-md-5 col-xl-4 pb-3">
<div class="form-floating">
<input type="text" name="copyright" id="copyright" class="form-control" placeholder="Copyright String"
{% if model.copyright %}value="{{ model.copyright.value }}"{% endif %}>
<label for="copyright">Copyright String</label>
<span class="form-text">
Can be a
<a href="https://creativecommons.org/share-your-work/" target="_blank" rel="noopener">
Creative Commons license string
</a>
</span>
</div>
</div>
</div>
@ -47,31 +64,33 @@
</div>
</form>
<h3>Custom Feeds</h3>
<a class="btn btn-secondary" href="{{ 'admin/rss/new/edit' | relative_link }}">Add a New Custom Feed</a>
{%- assign feed_count = model.custom_feeds | size -%}
{% if feed_count > 0 %}
<table class="table table-sm table-striped table-hover">
<thead>
<tr>
<th scope="col">Source</th>
<th scope="col">Path</th>
<th scope="col">Podcast?</th>
</tr>
</thead>
<tbody>
<a class="btn btn-sm btn-secondary" href="{{ 'admin/rss/new/edit' | relative_link }}">Add a New Custom Feed</a>
<table class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">Source</th>
<th scope="col">Path</th>
<th scope="col">Podcast?</th>
</tr>
</thead>
<tbody>
{%- assign feed_count = model.custom_feeds | size -%}
{% if feed_count > 0 %}
{% for feed in model.custom_feeds %}
<tr>
<td>
{{ feed.source }}
<!-- TODO: edit / delete -->
<!-- TODO: view / edit / delete -->
</td>
<td>{{ feed.path }}</td>
<td>{% if feed.is_podcast %}Yes{% else %}No{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted fst-italic">No custom feeds defined</p>
{% endif %}
{% else %}
<tr>
<td colspan="3" class="text-muted fst-italic text-center">No custom feeds defined</td>
</tr>
{% endif %}
</tbody>
</table>
</article>

View File

@ -1,4 +1,8 @@
<h2 class="my-3">{{ web_log.name }} Settings</h2>
<p class="text-muted">
Other Settings: <a href="{{ "admin/settings/tag-mappings" | relative_link }}">Tag Mappings</a> &bull;
<a href="{{ "admin/settings/rss" | relative_link }}">RSS Settings</a>
</p>
<article>
<form action="{{ "admin/settings" | relative_link }}" method="post">
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">

View File

@ -1,12 +1,12 @@
<h2 class="my-3">{{ page_title }}</h2>
<article>
<form action="{{ "admin/tag-mapping/save" | relative_link }}" method="post">
<form action="{{ "admin/settings/tag-mapping/save" | relative_link }}" method="post">
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
<input type="hidden" name="id" value="{{ model.id }}">
<div class="container">
<div class="row mb-3">
<div class="col">
<a href="{{ "admin/tag-mappings" | relative_link }}">&laquo; Back to Tag Mappings</a>
<a href="{{ "admin/settings/tag-mappings" | relative_link }}">&laquo; Back to Tag Mappings</a>
</div>
</div>
<div class="row mb-3">

View File

@ -1,6 +1,6 @@
<h2 class="my-3">{{ page_title }}</h2>
<article class="container">
<a href="{{ "admin/tag-mapping/new/edit" | relative_link }}" class="btn btn-primary btn-sm mb-3">
<a href="{{ "admin/settings/tag-mapping/new/edit" | relative_link }}" class="btn btn-primary btn-sm mb-3">
Add a New Tag Mapping
</a>
<table class="table table-sm table-hover">
@ -17,10 +17,10 @@
<td class="no-wrap">
{{ map.tag }}<br>
<small>
{%- capture map_edit %}admin/tag-mapping/{{ map_id }}/edit{% endcapture -%}
{%- capture map_edit %}admin/settings/tag-mapping/{{ map_id }}/edit{% endcapture -%}
<a href="{{ map_edit | relative_link }}">Edit</a>
<span class="text-muted"> &bull; </span>
{%- capture map_del %}admin/tag-mapping/{{ map_id }}/delete{% endcapture -%}
{%- capture map_del %}admin/settings/tag-mapping/{{ map_id }}/delete{% endcapture -%}
{%- capture map_del_link %}{{ map_del | relative_link }}{% endcapture -%}
<a href="{{ map_del_link }}" class="text-danger"
onclick="return Admin.deleteTagMapping('{{ map.tag }}', '{{ map_del_link }}')">