V2 #1
@ -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
|
||||
|
@ -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 =
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 "{{ post.title | escape }}"">
|
||||
@ -8,11 +8,11 @@
|
||||
</h1>
|
||||
<h4 class="text-center">
|
||||
<i class="fa fa-calendar" title="Date"></i> {{ post.published_on | date: "MMMM d, yyyy" }}
|
||||
<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>
|
@ -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>
|
||||
|
@ -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" }}
|
||||
<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
|
||||
{% 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 “{{ cat_name | escape }}”">
|
||||
{{ cat_name }}
|
||||
<a href="/category/{{ cat.slug }}" title="Categorized under “{{ cat.name | escape }}”">
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</span>
|
||||
{%- endfor %}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user