WIP on Fluid model/filters
This commit is contained in:
parent
d047035173
commit
95be82cc84
@ -23,7 +23,6 @@ type ISession with
|
|||||||
[<Literal>]
|
[<Literal>]
|
||||||
let MESSAGES = "messages"
|
let MESSAGES = "messages"
|
||||||
|
|
||||||
|
|
||||||
/// The HTTP item key for loading the session
|
/// The HTTP item key for loading the session
|
||||||
let private sessionLoadedKey = "session-loaded"
|
let private sessionLoadedKey = "session-loaded"
|
||||||
|
|
||||||
@ -132,6 +131,7 @@ let redirectToGet url : HttpHandler = fun _ ctx -> task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The MIME type for podcast episode JSON chapters
|
/// The MIME type for podcast episode JSON chapters
|
||||||
|
[<Literal>]
|
||||||
let JSON_CHAPTERS = "application/json+chapters"
|
let JSON_CHAPTERS = "application/json+chapters"
|
||||||
|
|
||||||
|
|
||||||
@ -185,10 +185,10 @@ let viewForTheme themeId template next ctx (viewCtx: AppViewContext) = task {
|
|||||||
// Render view content...
|
// Render view content...
|
||||||
match! Template.Cache.get themeId template ctx.Data with
|
match! Template.Cache.get themeId template ctx.Data with
|
||||||
| Ok contentTemplate ->
|
| Ok contentTemplate ->
|
||||||
let forLayout = { updated with Content = Template.render contentTemplate updated }
|
let forLayout = { updated with Content = Template.render contentTemplate updated ctx.Data }
|
||||||
// ...then render that content with its layout
|
// ...then render that content with its layout
|
||||||
match! Template.Cache.get themeId (if isHtmx ctx then "layout-partial" else "layout") ctx.Data with
|
match! Template.Cache.get themeId (if isHtmx ctx then "layout-partial" else "layout") ctx.Data with
|
||||||
| Ok layoutTemplate -> return! htmlString (Template.render layoutTemplate forLayout) next ctx
|
| Ok layoutTemplate -> return! htmlString (Template.render layoutTemplate forLayout ctx.Data) next ctx
|
||||||
| Error message -> return! Error.server message next ctx
|
| Error message -> return! Error.server message next ctx
|
||||||
| Error message -> return! Error.server message next ctx
|
| Error message -> return! Error.server message next ctx
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ let bareForTheme themeId template next ctx viewCtx = task {
|
|||||||
let withContent = task {
|
let withContent = task {
|
||||||
if updated.Content = "" then
|
if updated.Content = "" then
|
||||||
match! Template.Cache.get themeId template ctx.Data with
|
match! Template.Cache.get themeId template ctx.Data with
|
||||||
| Ok contentTemplate -> return Ok { updated with Content = Template.render contentTemplate updated }
|
| Ok contentTemplate -> return Ok { updated with Content = Template.render contentTemplate updated ctx.Data }
|
||||||
| Error message -> return Error message
|
| Error message -> return Error message
|
||||||
else
|
else
|
||||||
return Ok viewCtx
|
return Ok viewCtx
|
||||||
@ -210,7 +210,7 @@ let bareForTheme themeId template next ctx viewCtx = task {
|
|||||||
match! Template.Cache.get themeId "layout-bare" ctx.Data with
|
match! Template.Cache.get themeId "layout-bare" ctx.Data with
|
||||||
| Ok layoutTemplate ->
|
| Ok layoutTemplate ->
|
||||||
return!
|
return!
|
||||||
(messagesToHeaders completeCtx.Messages >=> htmlString (Template.render layoutTemplate completeCtx))
|
(messagesToHeaders completeCtx.Messages >=> htmlString (Template.render layoutTemplate completeCtx ctx.Data))
|
||||||
next ctx
|
next ctx
|
||||||
| Error message -> return! Error.server message next ctx
|
| Error message -> return! Error.server message next ctx
|
||||||
| Error message -> return! Error.server message next ctx
|
| Error message -> return! Error.server message next ctx
|
||||||
|
@ -27,7 +27,7 @@ type WebLogMiddleware(next: RequestDelegate, log: ILogger<WebLogMiddleware>) =
|
|||||||
|
|
||||||
|
|
||||||
/// Middleware to check redirects for the current web log
|
/// Middleware to check redirects for the current web log
|
||||||
type RedirectRuleMiddleware(next: RequestDelegate, log: ILogger<RedirectRuleMiddleware>) =
|
type RedirectRuleMiddleware(next: RequestDelegate, _log: ILogger<RedirectRuleMiddleware>) =
|
||||||
|
|
||||||
/// Shorthand for case-insensitive string equality
|
/// Shorthand for case-insensitive string equality
|
||||||
let ciEquals str1 str2 =
|
let ciEquals str1 str2 =
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
module MyWebLog.Template
|
module MyWebLog.Template
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.IO
|
||||||
|
open System.Text
|
||||||
open Fluid
|
open Fluid
|
||||||
open Fluid.Values
|
open Fluid.Values
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
|
open Microsoft.AspNetCore.Antiforgery
|
||||||
|
open Microsoft.Extensions.FileProviders
|
||||||
open MyWebLog
|
open MyWebLog
|
||||||
open MyWebLog.ViewModels
|
open MyWebLog.ViewModels
|
||||||
|
|
||||||
@ -48,10 +54,21 @@ module private Helpers =
|
|||||||
|
|
||||||
|
|
||||||
/// Fluid template options customized with myWebLog filters
|
/// Fluid template options customized with myWebLog filters
|
||||||
let options =
|
let options () =
|
||||||
let sValue = StringValue >> VTask<FluidValue>
|
let sValue = StringValue >> VTask<FluidValue>
|
||||||
|
|
||||||
let it = TemplateOptions.Default
|
let it = TemplateOptions.Default
|
||||||
it.MemberAccessStrategy.MemberNameStrategy <- MemberNameStrategies.SnakeCase
|
it.MemberAccessStrategy.MemberNameStrategy <- MemberNameStrategies.SnakeCase
|
||||||
|
[ // Domain types
|
||||||
|
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>; typeof<RssOptions>
|
||||||
|
typeof<TagMap>; typeof<WebLog>
|
||||||
|
// View models
|
||||||
|
typeof<AppViewContext>; typeof<DisplayCategory>; typeof<DisplayPage>; typeof<EditPageModel>; typeof<PostDisplay>
|
||||||
|
typeof<PostListItem>; typeof<UserMessage>
|
||||||
|
// Framework types
|
||||||
|
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
||||||
|
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list> ]
|
||||||
|
|> List.iter it.MemberAccessStrategy.Register
|
||||||
|
|
||||||
// A filter to generate an absolute link
|
// A filter to generate an absolute link
|
||||||
it.Filters.AddFilter("absolute_link", fun input _ ctx -> sValue (permalink input ctx.App.WebLog.AbsoluteUrl))
|
it.Filters.AddFilter("absolute_link", fun input _ ctx -> sValue (permalink input ctx.App.WebLog.AbsoluteUrl))
|
||||||
@ -132,12 +149,14 @@ let options =
|
|||||||
// A filter to retrieve the value of a meta item from a list
|
// A filter to retrieve the value of a meta item from a list
|
||||||
// (shorter than `{% assign item = list | where: "Name", [name] | first %}{{ item.value }}`)
|
// (shorter than `{% assign item = list | where: "Name", [name] | first %}{{ item.value }}`)
|
||||||
it.Filters.AddFilter("value",
|
it.Filters.AddFilter("value",
|
||||||
fun input args _ ->
|
fun input args ctx ->
|
||||||
let items = input.ToObjectValue() :?> MetaItem list
|
let name = args.At(0).ToStringValue()
|
||||||
let name = args.At(0).ToStringValue()
|
let picker (value: FluidValue) =
|
||||||
match items |> List.tryFind (fun it -> it.Name = name) with
|
let item = value.ToObjectValue() :?> MetaItem
|
||||||
| Some item -> item.Value
|
if item.Name = name then Some item.Value else None
|
||||||
| None -> $"-- {name} not found --"
|
(input :?> ArrayValue).Values
|
||||||
|
|> Seq.tryPick picker
|
||||||
|
|> Option.defaultValue $"-- {name} not found --"
|
||||||
|> sValue)
|
|> sValue)
|
||||||
|
|
||||||
it
|
it
|
||||||
@ -234,11 +253,13 @@ let parser =
|
|||||||
|
|
||||||
it
|
it
|
||||||
|
|
||||||
|
|
||||||
|
open MyWebLog.Data
|
||||||
|
|
||||||
/// Cache for parsed templates
|
/// Cache for parsed templates
|
||||||
module Cache =
|
module Cache =
|
||||||
|
|
||||||
open System.Collections.Concurrent
|
open System.Collections.Concurrent
|
||||||
open MyWebLog.Data
|
|
||||||
|
|
||||||
/// Cache of parsed templates
|
/// Cache of parsed templates
|
||||||
let private _cache = ConcurrentDictionary<string, IFluidTemplate> ()
|
let private _cache = ConcurrentDictionary<string, IFluidTemplate> ()
|
||||||
@ -277,6 +298,37 @@ module Cache =
|
|||||||
_cache.Clear()
|
_cache.Clear()
|
||||||
|
|
||||||
|
|
||||||
|
/// A file provider to retrieve files by theme
|
||||||
|
type ThemeFileProvider(themeId: ThemeId, data: IData) =
|
||||||
|
|
||||||
|
interface IFileProvider with
|
||||||
|
|
||||||
|
member _.GetDirectoryContents _ =
|
||||||
|
raise <| NotImplementedException "The theme file provider does not support directory listings"
|
||||||
|
|
||||||
|
member _.GetFileInfo path =
|
||||||
|
match data.Theme.FindById themeId |> Async.AwaitTask |> Async.RunSynchronously with
|
||||||
|
| Some theme ->
|
||||||
|
match theme.Templates |> List.tryFind (fun t -> t.Name = path) with
|
||||||
|
| Some template ->
|
||||||
|
{ new IFileInfo with
|
||||||
|
member _.Exists = true
|
||||||
|
member _.IsDirectory = false
|
||||||
|
member _.LastModified = DateTimeOffset.Now
|
||||||
|
member _.Length = int64 template.Text.Length
|
||||||
|
member _.Name = template.Name.Split '/' |> Array.last
|
||||||
|
member _.PhysicalPath = null
|
||||||
|
member _.CreateReadStream() =
|
||||||
|
new MemoryStream(Encoding.UTF8.GetBytes template.Text) }
|
||||||
|
| None -> NotFoundFileInfo path
|
||||||
|
| None -> NotFoundFileInfo path
|
||||||
|
|
||||||
|
member _.Watch _ =
|
||||||
|
raise <| NotImplementedException "The theme file provider does not support watching for changes"
|
||||||
|
|
||||||
|
|
||||||
/// Render a template to a string
|
/// Render a template to a string
|
||||||
let render (template: IFluidTemplate) (viewCtx: AppViewContext) =
|
let render (template: IFluidTemplate) (viewCtx: AppViewContext) data =
|
||||||
template.Render(TemplateContext(viewCtx, options, true))
|
let opts = options ()
|
||||||
|
opts.FileProvider <- ThemeFileProvider(viewCtx.WebLog.ThemeId, data)
|
||||||
|
template.Render(TemplateContext(viewCtx, opts, true))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Generator": "myWebLog 2.2",
|
"Generator": "myWebLog 3",
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"MyWebLog.Handlers": "Information"
|
"MyWebLog.Handlers": "Information"
|
||||||
|
Loading…
Reference in New Issue
Block a user