diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 95ee8d1..43f3823 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -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 diff --git a/src/MyWebLog/Caches.fs b/src/MyWebLog/Caches.fs index 9380240..dd886a3 100644 --- a/src/MyWebLog/Caches.fs +++ b/src/MyWebLog/Caches.fs @@ -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 () + + /// 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 = diff --git a/src/MyWebLog/Handlers.fs b/src/MyWebLog/Handlers.fs index e34b3ac..4980b26 100644 --- a/src/MyWebLog/Handlers.fs +++ b/src/MyWebLog/Handlers.fs @@ -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 } diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index f931fa2..e04b3e6 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -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 } diff --git a/src/MyWebLog/themes/daniel-j-summers/index.liquid b/src/MyWebLog/themes/daniel-j-summers/index.liquid index b1971cc..e5f5afd 100644 --- a/src/MyWebLog/themes/daniel-j-summers/index.liquid +++ b/src/MyWebLog/themes/daniel-j-summers/index.liquid @@ -1,5 +1,5 @@
- {% for post in model.posts %} + {%- for post in model.posts %} - {% endfor %} + {%- endfor %}

Categories

-
- TODO: list_categories({ class: 'cat' }) -
+
\ No newline at end of file diff --git a/src/MyWebLog/themes/daniel-j-summers/layout.liquid b/src/MyWebLog/themes/daniel-j-summers/layout.liquid index a9ba6db..869f77c 100644 --- a/src/MyWebLog/themes/daniel-j-summers/layout.liquid +++ b/src/MyWebLog/themes/daniel-j-summers/layout.liquid @@ -11,7 +11,7 @@ - {% if is_home -%} + {%- if is_home %} {%- endif %} @@ -21,9 +21,9 @@

{{ web_log.name }}

{%- if web_log.subtitle %}

{{ web_log.subtitle.value }}

{% endif -%} - {% for page in page_list %} + {%- for page in page_list %}

{{ page.title }}

- {% endfor %} + {%- endfor %}

A Word from the Word

@@ -31,9 +31,9 @@ {{ content }}