Add Fluid where filter, fix slug (#47)
- Fix issue with path base and logon redirects
This commit is contained in:
@@ -7,11 +7,11 @@ open MyWebLog.Views
|
|||||||
|
|
||||||
/// Session extensions to get and set objects
|
/// Session extensions to get and set objects
|
||||||
type ISession with
|
type ISession with
|
||||||
|
|
||||||
/// Set an item in the session
|
/// Set an item in the session
|
||||||
member this.Set<'T>(key, item: 'T) =
|
member this.Set<'T>(key, item: 'T) =
|
||||||
this.SetString(key, JsonSerializer.Serialize item)
|
this.SetString(key, JsonSerializer.Serialize item)
|
||||||
|
|
||||||
/// Get an item from the session
|
/// Get an item from the session
|
||||||
member this.TryGet<'T> key =
|
member this.TryGet<'T> key =
|
||||||
match this.GetString key with
|
match this.GetString key with
|
||||||
@@ -139,7 +139,13 @@ module Error =
|
|||||||
/// Handle unauthorized actions, redirecting to log on for GETs, otherwise returning a 401 Not Authorized response
|
/// Handle unauthorized actions, redirecting to log on for GETs, otherwise returning a 401 Not Authorized response
|
||||||
let notAuthorized : HttpHandler = fun next ctx ->
|
let notAuthorized : HttpHandler = fun next ctx ->
|
||||||
if ctx.Request.Method = "GET" then
|
if ctx.Request.Method = "GET" then
|
||||||
let redirectUrl = $"user/log-on?returnUrl={WebUtility.UrlEncode ctx.Request.Path}"
|
let fullPath =
|
||||||
|
String.concat ""
|
||||||
|
(seq {
|
||||||
|
if ctx.Request.PathBase.HasValue then yield ctx.Request.PathBase.Value
|
||||||
|
yield ctx.Request.Path.Value
|
||||||
|
})
|
||||||
|
let redirectUrl = $"user/log-on?returnUrl={WebUtility.UrlEncode fullPath}"
|
||||||
(next, ctx)
|
(next, ctx)
|
||||||
||> if isHtmx ctx then withHxRedirect redirectUrl >=> withHxRetarget "body" >=> redirectToGet redirectUrl
|
||> if isHtmx ctx then withHxRedirect redirectUrl >=> withHxRetarget "body" >=> redirectToGet redirectUrl
|
||||||
else redirectToGet redirectUrl
|
else redirectToGet redirectUrl
|
||||||
@@ -161,7 +167,7 @@ module Error =
|
|||||||
|]
|
|]
|
||||||
RequestErrors.notFound (messagesToHeaders messages) earlyReturn ctx
|
RequestErrors.notFound (messagesToHeaders messages) earlyReturn ctx
|
||||||
else RequestErrors.NOT_FOUND "Not found" earlyReturn ctx)
|
else RequestErrors.NOT_FOUND "Not found" earlyReturn ctx)
|
||||||
|
|
||||||
let server message : HttpHandler =
|
let server message : HttpHandler =
|
||||||
handleContext (fun ctx ->
|
handleContext (fun ctx ->
|
||||||
if isHtmx ctx then
|
if isHtmx ctx then
|
||||||
@@ -173,11 +179,11 @@ module Error =
|
|||||||
/// Render a view for the specified theme, using the specified template, layout, and context
|
/// Render a view for the specified theme, using the specified template, layout, and context
|
||||||
let viewForTheme themeId template next ctx (viewCtx: AppViewContext) = task {
|
let viewForTheme themeId template next ctx (viewCtx: AppViewContext) = task {
|
||||||
let! updated = updateViewContext ctx viewCtx
|
let! updated = updateViewContext ctx viewCtx
|
||||||
|
|
||||||
// NOTE: Although Fluid's view engine support implements layouts and sections, it also relies on the filesystem.
|
// NOTE: Although Fluid's view engine support implements layouts and sections, it also relies on the filesystem.
|
||||||
// As we are loading templates from memory or a database, we do a 2-pass render; the first for the content,
|
// As we are loading templates from memory or a database, we do a 2-pass render; the first for the content,
|
||||||
// the second for the overall page.
|
// the second for the overall page.
|
||||||
|
|
||||||
// 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 ->
|
||||||
@@ -193,12 +199,12 @@ let viewForTheme themeId template next ctx (viewCtx: AppViewContext) = task {
|
|||||||
let bareForTheme themeId template next ctx viewCtx = task {
|
let bareForTheme themeId template next ctx viewCtx = task {
|
||||||
let! updated = updateViewContext ctx viewCtx
|
let! updated = updateViewContext ctx viewCtx
|
||||||
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 ctx.Data }
|
| 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
|
||||||
}
|
}
|
||||||
match! withContent with
|
match! withContent with
|
||||||
| Ok completeCtx ->
|
| Ok completeCtx ->
|
||||||
@@ -260,7 +266,7 @@ let requireAccess level : HttpHandler = fun next ctx -> task {
|
|||||||
return! Error.notAuthorized next ctx
|
return! Error.notAuthorized next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if a user is authorized to edit a page or post, given the author
|
/// Determine if a user is authorized to edit a page or post, given the author
|
||||||
let canEdit authorId (ctx: HttpContext) =
|
let canEdit authorId (ctx: HttpContext) =
|
||||||
ctx.UserId = authorId || ctx.HasAccessLevel Editor
|
ctx.UserId = authorId || ctx.HasAccessLevel Editor
|
||||||
|
|
||||||
@@ -306,7 +312,7 @@ let getTagMappings (webLog: WebLog) (posts: Post list) (data: IData) =
|
|||||||
|> List.distinct
|
|> List.distinct
|
||||||
|> fun tags -> data.TagMap.FindMappingForTags tags webLog.Id
|
|> fun tags -> data.TagMap.FindMappingForTags tags webLog.Id
|
||||||
|
|
||||||
/// Get all category IDs for the given slug (includes owned subcategories)
|
/// Get all category IDs for the given slug (includes owned subcategories)
|
||||||
let getCategoryIds slug ctx =
|
let getCategoryIds slug ctx =
|
||||||
let allCats = CategoryCache.get ctx
|
let allCats = CategoryCache.get ctx
|
||||||
let cat = allCats |> Array.find (fun cat -> cat.Slug = slug)
|
let cat = allCats |> Array.find (fun cat -> cat.Slug = slug)
|
||||||
@@ -319,7 +325,7 @@ let getCategoryIds slug ctx =
|
|||||||
|
|
||||||
open NodaTime
|
open NodaTime
|
||||||
|
|
||||||
/// Parse a date/time to UTC
|
/// Parse a date/time to UTC
|
||||||
let parseToUtc (date: string) : Instant =
|
let parseToUtc (date: string) : Instant =
|
||||||
let result = roundTrip.Parse date
|
let result = roundTrip.Parse date
|
||||||
if result.Success then result.Value else raise result.Exception
|
if result.Success then result.Value else raise result.Exception
|
||||||
|
|||||||
@@ -172,6 +172,21 @@ let options () =
|
|||||||
|> Option.defaultValue $"-- {name} not found --"
|
|> Option.defaultValue $"-- {name} not found --"
|
||||||
|> sValue)
|
|> sValue)
|
||||||
|
|
||||||
|
// A filter to search through a collection of objects for a matching property
|
||||||
|
it.Filters.AddFilter("where",
|
||||||
|
fun input args ctx ->
|
||||||
|
let name = args.At(0).ToStringValue()
|
||||||
|
let value = args.At(1).ToStringValue()
|
||||||
|
input.Enumerate(ctx)
|
||||||
|
|> Seq.filter (fun it ->
|
||||||
|
let theItem = it.ToObjectValue()
|
||||||
|
match theItem.GetType().GetProperties() |> Array.tryFind (fun x -> x.Name = name) with
|
||||||
|
| Some pi -> pi.GetValue(theItem) = value
|
||||||
|
| None -> false)
|
||||||
|
|> Seq.toArray
|
||||||
|
|> ArrayValue :> FluidValue
|
||||||
|
|> VTask)
|
||||||
|
|
||||||
it
|
it
|
||||||
|
|
||||||
open Giraffe.Htmx.Common
|
open Giraffe.Htmx.Common
|
||||||
@@ -234,11 +249,11 @@ let parser =
|
|||||||
writer.WriteLine $"""{s}<link rel=canonical href="{app.WebLog.AbsoluteUrl Permalink.Empty}">"""
|
writer.WriteLine $"""{s}<link rel=canonical href="{app.WebLog.AbsoluteUrl Permalink.Empty}">"""
|
||||||
|
|
||||||
if app.WebLog.Rss.IsCategoryEnabled && app.IsCategoryHome then
|
if app.WebLog.Rss.IsCategoryEnabled && app.IsCategoryHome then
|
||||||
let slug = context.AmbientValues["slug"] :?> string
|
let slug = (context.Model.ToObjectValue() :?> AppViewContext).Slug
|
||||||
writer.WriteLine(feedLink app.WebLog.Name $"category/{slug}/{app.WebLog.Rss.FeedName}")
|
writer.WriteLine(feedLink app.WebLog.Name $"category/{slug}/{app.WebLog.Rss.FeedName}")
|
||||||
|
|
||||||
if app.WebLog.Rss.IsTagEnabled && app.IsTagHome then
|
if app.WebLog.Rss.IsTagEnabled && app.IsTagHome then
|
||||||
let slug = context.AmbientValues["slug"] :?> string
|
let slug = (context.Model.ToObjectValue() :?> AppViewContext).Slug
|
||||||
writer.WriteLine(feedLink app.WebLog.Name $"tag/{slug}/{app.WebLog.Rss.FeedName}")
|
writer.WriteLine(feedLink app.WebLog.Name $"tag/{slug}/{app.WebLog.Rss.FeedName}")
|
||||||
|
|
||||||
if app.IsPost then
|
if app.IsPost then
|
||||||
@@ -287,7 +302,7 @@ let parser =
|
|||||||
"</ul>"
|
"</ul>"
|
||||||
}
|
}
|
||||||
|> Seq.iter writer.WriteLine
|
|> Seq.iter writer.WriteLine
|
||||||
ok())
|
ok ())
|
||||||
|
|
||||||
it
|
it
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
Published on {{ post.published_on | date: "MMMM d, yyyy" }}
|
Published on {{ post.published_on | format_date: "MMMM d, yyyy" }}
|
||||||
at {{ post.published_on | date: "h:mmtt" | downcase }}
|
at {{ post.published_on | format_date: "h:mmtt" | downcase }}
|
||||||
by {{ model.authors | value: post.author_id }}
|
by {{ model.authors | value: post.author_id }}
|
||||||
{{ post.text }}
|
{{ post.text }}
|
||||||
{%- assign category_count = post.category_ids | size -%}
|
{%- assign category_count = post.category_ids | size -%}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<h1>{{ post.title }}</h1>
|
<h1>{{ post.title }}</h1>
|
||||||
<h4 class="item-meta text-muted">
|
<h4 class="item-meta text-muted">
|
||||||
{% if post.published_on -%}
|
{% if post.published_on -%}
|
||||||
Published {{ post.published_on | date: "dddd, MMMM d, yyyy" }}
|
Published {{ post.published_on | format_date: "dddd, MMMM d, yyyy" }}
|
||||||
at {{ post.published_on | date: "h:mm tt" | downcase }}
|
at {{ post.published_on | format_date: "h:mm tt" | downcase }}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
**DRAFT**
|
**DRAFT**
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
myWebLog Default Theme
|
myWebLog Default Theme
|
||||||
2.2
|
3
|
||||||
Reference in New Issue
Block a user