Add all categories to post render context

- Convert post date/time to web log timezone
This commit is contained in:
Daniel J. Summers 2022-04-26 22:24:17 -04:00
parent ff87a71c9c
commit 6e7f4cc8ce
8 changed files with 66 additions and 31 deletions

View File

@ -308,14 +308,16 @@ type PostListItem =
}
/// Create a post list item from a post
static member fromPost (post : Post) =
static member fromPost (webLog : WebLog) (post : Post) =
let tz = TimeZoneInfo.FindSystemTimeZoneById webLog.timeZone
let inTZ (it : DateTime) = TimeZoneInfo.ConvertTimeFromUtc (DateTime (it.Ticks, DateTimeKind.Utc), tz)
{ id = PostId.toString post.id
authorId = WebLogUserId.toString post.authorId
status = PostStatus.toString post.status
title = post.title
permalink = Permalink.toString post.permalink
publishedOn = Option.toNullable post.publishedOn
updatedOn = post.updatedOn
publishedOn = post.publishedOn |> Option.map inTZ |> Option.toNullable
updatedOn = inTZ post.updatedOn
text = post.text
categoryIds = post.categoryIds |> List.map CategoryId.toString
tags = post.tags

View File

@ -52,6 +52,22 @@ module PageListCache =
_cache[Cache.makeKey ctx] <- pages |> List.map (DisplayPage.fromPage webLog) |> Array.ofList
}
/// Cache of all categories, indexed by web log
module CategoryCache =
open MyWebLog.ViewModels
/// The cache itself
let private _cache = ConcurrentDictionary<string, DisplayCategory[]> ()
/// Get the categories for the web log for this request
let get ctx = _cache[Cache.makeKey ctx]
/// Set the categories for the current web log
let set ctx cats = _cache[Cache.makeKey ctx] <- cats
/// Cache for parsed templates
module TemplateCache =

View File

@ -285,6 +285,12 @@ module Admin =
/// Handlers to manipulate categories
module Category =
/// Update the category cache with flattened category hierarchy
let private updateCategoryCache webLogId ctx conn = task {
let! cats = Data.Category.findAllForView webLogId conn
CategoryCache.set ctx cats
}
// GET /categories
let all : HttpHandler = requireUser >=> fun next ctx -> task {
let! cats = Data.Category.findAllForView (webLogId ctx) (conn ctx)
@ -305,7 +311,6 @@ module Category =
| Some cat -> return Some ("Edit Category", cat)
| None -> return None
}
let! allCats = Data.Category.findAllForView webLogId conn
match result with
| Some (title, cat) ->
return!
@ -313,7 +318,7 @@ module Category =
csrf = csrfToken ctx
model = EditCategoryModel.fromCategory cat
page_title = title
categories = allCats
categories = CategoryCache.get ctx
|}
|> viewForTheme "admin" "category-edit" next ctx
| None -> return! Error.notFound next ctx
@ -339,6 +344,7 @@ module Category =
parentId = if model.parentId = "" then None else Some (CategoryId model.parentId)
}
do! (match model.categoryId with "new" -> Data.Category.add | _ -> Data.Category.update) cat conn
do! updateCategoryCache webLogId ctx conn
do! addMessage ctx { UserMessage.success with message = "Category saved successfully" }
return! redirectToGet $"/category/{CategoryId.toString cat.id}/edit" next ctx
| None -> return! Error.notFound next ctx
@ -346,8 +352,12 @@ module Category =
// POST /category/{id}/delete
let delete catId : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task {
match! Data.Category.delete (CategoryId catId) (webLogId ctx) (conn ctx) with
| true -> do! addMessage ctx { UserMessage.success with message = "Category deleted successfully" }
let webLogId = webLogId ctx
let conn = conn ctx
match! Data.Category.delete (CategoryId catId) webLogId conn with
| true ->
do! updateCategoryCache webLogId ctx conn
do! addMessage ctx { UserMessage.success with message = "Category deleted successfully" }
| false -> do! addMessage ctx { UserMessage.error with message = "Category not found; cannot delete" }
return! redirectToGet "/categories" next ctx
}
@ -452,7 +462,7 @@ module Page =
module Post =
/// Convert a list of posts into items ready to be displayed
let private preparePostList (webLog : WebLog) (posts : Post list) pageNbr perPage conn = task {
let private preparePostList (webLog : WebLog) (posts : Post list) pageNbr perPage ctx conn = task {
let! authors =
posts
|> List.map (fun p -> p.authorId)
@ -468,7 +478,7 @@ module Post =
posts
|> Seq.ofList
|> Seq.truncate perPage
|> Seq.map PostListItem.fromPost
|> Seq.map (PostListItem.fromPost webLog)
|> Array.ofSeq
let model =
{ posts = postItems
@ -476,9 +486,9 @@ module Post =
categories = cats
subtitle = None
hasNewer = pageNbr <> 1
hasOlder = posts |> List.length > perPage
hasOlder = List.length posts > perPage
}
return Hash.FromAnonymousObject {| model = model |}
return Hash.FromAnonymousObject {| model = model; categories = CategoryCache.get ctx |}
}
// GET /page/{pageNbr}
@ -486,7 +496,7 @@ module Post =
let webLog = WebLogCache.get ctx
let conn = conn ctx
let! posts = Data.Post.findPageOfPublishedPosts webLog.id pageNbr webLog.postsPerPage conn
let! hash = preparePostList webLog posts pageNbr webLog.postsPerPage conn
let! hash = preparePostList webLog posts pageNbr webLog.postsPerPage ctx conn
let title =
match pageNbr, webLog.defaultPage with
| 1, "posts" -> None
@ -518,7 +528,7 @@ module Post =
// Current post
match! Data.Post.findByPermalink permalink webLog.id conn with
| Some post ->
let! model = preparePostList webLog [ post ] 1 1 conn
let! model = preparePostList webLog [ post ] 1 1 ctx conn
model.Add ("page_title", post.title)
return! themedView "single-post" next ctx model
| None ->
@ -548,7 +558,7 @@ module Post =
let webLog = WebLogCache.get ctx
let conn = conn ctx
let! posts = Data.Post.findPageOfPosts webLog.id pageNbr 25 conn
let! hash = preparePostList webLog posts pageNbr 25 conn
let! hash = preparePostList webLog posts pageNbr 25 ctx conn
hash.Add ("page_title", "Posts")
return! viewForTheme "admin" "post-list" next ctx hash
}

View File

@ -17,6 +17,8 @@ type WebLogMiddleware (next : RequestDelegate) =
| Some webLog ->
WebLogCache.set ctx webLog
do! PageListCache.update ctx
let! cats = Data.Category.findAllForView webLog.id conn
CategoryCache.set ctx cats
return! next.Invoke ctx
| None -> ctx.Response.StatusCode <- 404
}

View File

@ -1,5 +1,5 @@
<div class="content">
{% for post in model.posts %}
{%- for post in model.posts %}
<article class="item">
<h1 class="item-heading">
<a href="/{{ post.permalink }}" title="Permanent Link to &quot;{{ post.title | escape }}&quot;">
@ -8,11 +8,11 @@
</h1>
<h4 class="text-center">
<i class="fa fa-calendar" title="Date"></i> {{ post.published_on | date: "MMMM d, yyyy" }} &nbsp;
<i class="fa fa-clock-o" title="Time"></i> {{ post.published_on | date: "h:mmtt" | downcase }}
<i class="fa fa-clock-o" title="Time"></i> {{ post.published_on | date: "h:mm tt" | downcase }}
</h4>
{{ post.text }}
</article>
{% endfor %}
{%- endfor %}
<nav aria-label="pagination">
<ul class="pager">
{% if model.newer_link -%}
@ -52,8 +52,13 @@
</div>
<div class="item">
<h4 class="item-heading">Categories</h4>
<div>
TODO: list_categories({ class: 'cat' })
</div>
<ul class="cat-list">
{% for cat in categories -%}
{%- assign indent = cat.parent_names | size -%}
<li class="cat-list-item"{% if indent > 0 %} style="padding-left:{{ indent }}rem;"{% endif %}>
<a href="/category/{{ cat.slug }}" class="cat-list-link">{{ cat.name }}</a>
</li>
{%- endfor %}
</ul>
</div>
</div>

View File

@ -11,7 +11,7 @@
<link rel="preload" href="/themes/{{ web_log.theme_path }}/style.css" as="style">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/themes/{{ web_log.theme_path }}/style.css">
{% if is_home -%}
{%- if is_home %}
<link rel="alternate" type="application/rss+xml" title="{{ web_log.name | escape }}" href="/feed.xml">
{%- endif %}
<script src="/themes/{{ web_log.theme_path }}/djs.js"></script>
@ -21,9 +21,9 @@
<p><a class="nav-home" href="/">{{ web_log.name }}</a></p>
{%- if web_log.subtitle %}<p>{{ web_log.subtitle.value }}</p>{% endif -%}
<p class="nav-spacer"></p>
{% for page in page_list %}
{%- for page in page_list %}
<p class="desktop"><a href="/{{ page.permalink }}">{{ page.title }}</a></p>
{% endfor %}
{%- endfor %}
<p class="desktop">
<a href="https://devotions.summershome.org" target="_blank" rel="noopener">A Word from the Word</a>
</p>
@ -31,9 +31,9 @@
</nav>
{{ content }}
<footer class="part-1" id="links">
{% for page in page_list %}
{%- for page in page_list %}
<p class="mobile"><a href="{{ page.permalink }}">{{ page.title }}</a></p>
{% endfor %}
{%- endfor %}
<p class="mobile">
<a href="https://devotions.summershome.org" target="_blank" rel="noopener">A Word from the Word</a>
</p>

View File

@ -5,7 +5,7 @@
<h4 class="text-center">
{% if post.published_on -%}
<i class="fa fa-calendar" title="Date"></i> {{ post.published_on | date: "MMMM d, yyyy" }} &nbsp;
<i class="fa fa-clock-o" title="Time"></i> {{ post.published_on | date: "h:mmtt" | downcase }}
<i class="fa fa-clock-o" title="Time"></i> {{ post.published_on | date: "h:mm tt" | downcase }}
{%- else -%}
**DRAFT**
{% endif %}
@ -15,12 +15,12 @@
{% if cat_count > 0 -%}
<h4>
Categorized &nbsp;
{% for cat in post.category_ids -%}
{% assign cat_name = model.categories | value: cat %}
{% for cat_id in post.category_ids -%}
{% assign cat = categories | where: "id", cat_id | first %}
<span class="no-wrap">
<i class="fa fa-folder-open-o" title="Category"></i>
<a href="/category/{{ cat }}" title="Categorized under &ldquo;{{ cat_name | escape }}&rdquo;">
{{ cat_name }}
<a href="/category/{{ cat.slug }}" title="Categorized under &ldquo;{{ cat.name | escape }}&rdquo;">
{{ cat.name }}
</a> &nbsp; &nbsp;
</span>
{%- endfor %}

View File

@ -154,7 +154,7 @@ img {
.cat-list ul {
padding-left: 1rem;
}
.cat-list ul li {
.cat-list li {
list-style-type: none;
padding-bottom: .2rem;
}