Version 2.1 #41
@ -419,7 +419,7 @@ type EditChapterModel = {
|
|||||||
let pattern =
|
let pattern =
|
||||||
match value |> Seq.fold (fun count chr -> if chr = ':' then count + 1 else count) 0 with
|
match value |> Seq.fold (fun count chr -> if chr = ':' then count + 1 else count) 0 with
|
||||||
| 0 -> "S"
|
| 0 -> "S"
|
||||||
| 1 -> "MM:ss"
|
| 1 -> "M:ss"
|
||||||
| 2 -> "H:mm:ss"
|
| 2 -> "H:mm:ss"
|
||||||
| _ -> invalidArg name "Max time format is H:mm:ss"
|
| _ -> invalidArg name "Max time format is H:mm:ss"
|
||||||
|> function
|
|> function
|
||||||
|
@ -6,8 +6,8 @@ open System.IO
|
|||||||
open System.Web
|
open System.Web
|
||||||
open DotLiquid
|
open DotLiquid
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open MyWebLog.AdminViews.Helpers
|
|
||||||
open MyWebLog.ViewModels
|
open MyWebLog.ViewModels
|
||||||
|
open MyWebLog.Views
|
||||||
|
|
||||||
/// Extensions on the DotLiquid Context object
|
/// Extensions on the DotLiquid Context object
|
||||||
type Context with
|
type Context with
|
||||||
|
@ -27,7 +27,7 @@ module Dashboard =
|
|||||||
ListedPages = listed
|
ListedPages = listed
|
||||||
Categories = cats
|
Categories = cats
|
||||||
TopLevelCategories = topCats }
|
TopLevelCategories = topCats }
|
||||||
return! adminPage "Dashboard" false (AdminViews.Admin.dashboard model) next ctx
|
return! adminPage "Dashboard" false (Views.Admin.dashboard model) next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /admin/administration
|
// GET /admin/administration
|
||||||
|
@ -3,8 +3,7 @@ module private MyWebLog.Handlers.Helpers
|
|||||||
|
|
||||||
open System.Text.Json
|
open System.Text.Json
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
open MyWebLog.AdminViews
|
open MyWebLog.Views
|
||||||
open MyWebLog.AdminViews.Helpers
|
|
||||||
|
|
||||||
/// Session extensions to get and set objects
|
/// Session extensions to get and set objects
|
||||||
type ISession with
|
type ISession with
|
||||||
|
@ -379,7 +379,7 @@ let chapters postId : HttpHandler = requireAccess Author >=> fun next ctx -> tas
|
|||||||
&& Option.isSome post.Episode.Value.Chapters
|
&& Option.isSome post.Episode.Value.Chapters
|
||||||
&& canEdit post.AuthorId ctx ->
|
&& canEdit post.AuthorId ctx ->
|
||||||
return!
|
return!
|
||||||
adminPage "Manage Chapters" true (AdminViews.Post.chapters false (ManageChaptersModel.Create post)) next ctx
|
adminPage "Manage Chapters" true (Views.Post.chapters false (ManageChaptersModel.Create post)) next ctx
|
||||||
| Some _ | None -> return! Error.notFound next ctx
|
| Some _ | None -> return! Error.notFound next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,9 +398,9 @@ let editChapter (postId, index) : HttpHandler = requireAccess Author >=> fun nex
|
|||||||
match chapter with
|
match chapter with
|
||||||
| Some chap ->
|
| Some chap ->
|
||||||
return!
|
return!
|
||||||
adminPage
|
adminBarePage
|
||||||
(if index = -1 then "Add a Chapter" else "Edit Chapter") true
|
(if index = -1 then "Add a Chapter" else "Edit Chapter") true
|
||||||
(AdminViews.Post.chapterEdit (EditChapterModel.FromChapter post.Id index chap)) next ctx
|
(Views.Post.chapterEdit (EditChapterModel.FromChapter post.Id index chap)) next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| Some _ | None -> return! Error.notFound next ctx
|
| Some _ | None -> return! Error.notFound next ctx
|
||||||
}
|
}
|
||||||
@ -418,7 +418,7 @@ let saveChapter (postId, index) : HttpHandler = requireAccess Author >=> fun nex
|
|||||||
if index >= -1 && index < List.length chapters then
|
if index >= -1 && index < List.length chapters then
|
||||||
try
|
try
|
||||||
let chapter = form.ToChapter()
|
let chapter = form.ToChapter()
|
||||||
let existing = if index = -1 then chapters else chapters |> List.removeAt index
|
let existing = if index = -1 then chapters else List.removeAt index chapters
|
||||||
let updatedPost =
|
let updatedPost =
|
||||||
{ post with
|
{ post with
|
||||||
Episode = Some
|
Episode = Some
|
||||||
@ -429,9 +429,32 @@ let saveChapter (postId, index) : HttpHandler = requireAccess Author >=> fun nex
|
|||||||
return!
|
return!
|
||||||
adminPage
|
adminPage
|
||||||
"Manage Chapters" true
|
"Manage Chapters" true
|
||||||
(AdminViews.Post.chapterList form.AddAnother (ManageChaptersModel.Create updatedPost)) next ctx
|
(Views.Post.chapterList form.AddAnother (ManageChaptersModel.Create updatedPost)) next ctx
|
||||||
with
|
with
|
||||||
| ex -> return! Error.notFound next ctx // TODO: return error
|
| ex -> return! Error.server ex.Message next ctx
|
||||||
|
else return! Error.notFound next ctx
|
||||||
|
| Some _ | None -> return! Error.notFound next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /admin/post/{id}/chapter/{idx}
|
||||||
|
let deleteChapter (postId, index) : HttpHandler = requireAccess Author >=> fun next ctx -> task {
|
||||||
|
let data = ctx.Data
|
||||||
|
match! data.Post.FindById (PostId postId) ctx.WebLog.Id with
|
||||||
|
| Some post
|
||||||
|
when Option.isSome post.Episode
|
||||||
|
&& Option.isSome post.Episode.Value.Chapters
|
||||||
|
&& canEdit post.AuthorId ctx ->
|
||||||
|
let chapters = post.Episode.Value.Chapters.Value
|
||||||
|
if index >= 0 && index < List.length chapters then
|
||||||
|
let updatedPost =
|
||||||
|
{ post with
|
||||||
|
Episode = Some { post.Episode.Value with Chapters = Some (List.removeAt index chapters) } }
|
||||||
|
do! data.Post.Update updatedPost
|
||||||
|
do! addMessage ctx { UserMessage.Success with Message = "Chapter deleted successfully" }
|
||||||
|
return!
|
||||||
|
adminPage
|
||||||
|
"Manage Chapters" true (Views.Post.chapterList false (ManageChaptersModel.Create updatedPost)) next
|
||||||
|
ctx
|
||||||
else return! Error.notFound next ctx
|
else return! Error.notFound next ctx
|
||||||
| Some _ | None -> return! Error.notFound next ctx
|
| Some _ | None -> return! Error.notFound next ctx
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,11 @@ let router : HttpHandler = choose [
|
|||||||
routef "/%s/delete" Upload.deleteFromDb
|
routef "/%s/delete" Upload.deleteFromDb
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
DELETE >=> validateCsrf >=> choose [
|
||||||
|
subRoute "/post" (choose [
|
||||||
|
routef "/%s/chapter/%i" Post.deleteChapter
|
||||||
|
])
|
||||||
|
]
|
||||||
])
|
])
|
||||||
GET_HEAD >=> routexp "/category/(.*)" Post.pageOfCategorizedPosts
|
GET_HEAD >=> routexp "/category/(.*)" Post.pageOfCategorizedPosts
|
||||||
GET_HEAD >=> routef "/page/%i" Post.pageOfPosts
|
GET_HEAD >=> routef "/page/%i" Post.pageOfPosts
|
||||||
|
@ -36,7 +36,7 @@ let logOn returnUrl : HttpHandler = fun next ctx ->
|
|||||||
match returnUrl with
|
match returnUrl with
|
||||||
| Some _ -> returnUrl
|
| Some _ -> returnUrl
|
||||||
| None -> if ctx.Request.Query.ContainsKey "returnUrl" then Some ctx.Request.Query["returnUrl"].[0] else None
|
| None -> if ctx.Request.Query.ContainsKey "returnUrl" then Some ctx.Request.Query["returnUrl"].[0] else None
|
||||||
adminPage "Log On" true (AdminViews.User.logOn { LogOnModel.Empty with ReturnTo = returnTo }) next ctx
|
adminPage "Log On" true (Views.User.logOn { LogOnModel.Empty with ReturnTo = returnTo }) next ctx
|
||||||
|
|
||||||
|
|
||||||
open System.Security.Claims
|
open System.Security.Claims
|
||||||
@ -93,7 +93,7 @@ let private goAway : HttpHandler = RequestErrors.BAD_REQUEST "really?"
|
|||||||
// GET /admin/settings/users
|
// GET /admin/settings/users
|
||||||
let all : HttpHandler = fun next ctx -> task {
|
let all : HttpHandler = fun next ctx -> task {
|
||||||
let! users = ctx.Data.WebLogUser.FindByWebLog ctx.WebLog.Id
|
let! users = ctx.Data.WebLogUser.FindByWebLog ctx.WebLog.Id
|
||||||
return! adminBarePage "User Administration" true (AdminViews.User.userList users) next ctx
|
return! adminBarePage "User Administration" true (Views.User.userList users) next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the edit user page
|
/// Show the edit user page
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="appsettings*.json" CopyToOutputDirectory="Always" />
|
<Content Include="appsettings*.json" CopyToOutputDirectory="Always" />
|
||||||
<Compile Include="Caches.fs" />
|
<Compile Include="Caches.fs" />
|
||||||
<Compile Include="AdminViews\Helpers.fs" />
|
<Compile Include="Views\Helpers.fs" />
|
||||||
<Compile Include="AdminViews\Admin.fs" />
|
<Compile Include="Views\Admin.fs" />
|
||||||
<Compile Include="AdminViews\Post.fs" />
|
<Compile Include="Views\Post.fs" />
|
||||||
<Compile Include="AdminViews\User.fs" />
|
<Compile Include="Views\User.fs" />
|
||||||
<Compile Include="Handlers\Helpers.fs" />
|
<Compile Include="Handlers\Helpers.fs" />
|
||||||
<Compile Include="Handlers\Admin.fs" />
|
<Compile Include="Handlers\Admin.fs" />
|
||||||
<Compile Include="Handlers\Feed.fs" />
|
<Compile Include="Handlers\Feed.fs" />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
module MyWebLog.AdminViews.Admin
|
module MyWebLog.Views.Admin
|
||||||
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open MyWebLog.ViewModels
|
open MyWebLog.ViewModels
|
@ -1,5 +1,5 @@
|
|||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module MyWebLog.AdminViews.Helpers
|
module MyWebLog.Views.Helpers
|
||||||
|
|
||||||
open Microsoft.AspNetCore.Antiforgery
|
open Microsoft.AspNetCore.Antiforgery
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
@ -1,4 +1,4 @@
|
|||||||
module MyWebLog.AdminViews.Post
|
module MyWebLog.Views.Post
|
||||||
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open Giraffe.ViewEngine.Htmx
|
open Giraffe.ViewEngine.Htmx
|
||||||
@ -9,6 +9,7 @@ open NodaTime.Text
|
|||||||
/// The pattern for chapter start times
|
/// The pattern for chapter start times
|
||||||
let startTimePattern = DurationPattern.CreateWithInvariantCulture "H:mm:ss.FF"
|
let startTimePattern = DurationPattern.CreateWithInvariantCulture "H:mm:ss.FF"
|
||||||
|
|
||||||
|
/// The form to add or edit a chapter
|
||||||
let chapterEdit (model: EditChapterModel) app = [
|
let chapterEdit (model: EditChapterModel) app = [
|
||||||
let postUrl = relUrl app $"admin/post/{model.PostId}/chapter/{model.Index}"
|
let postUrl = relUrl app $"admin/post/{model.PostId}/chapter/{model.Index}"
|
||||||
h3 [ _class "my-3" ] [ raw (if model.Index < 0 then "Add" else "Edit"); raw " Chapter" ]
|
h3 [ _class "my-3" ] [ raw (if model.Index < 0 then "Add" else "Edit"); raw " Chapter" ]
|
||||||
@ -128,6 +129,7 @@ let chapterEdit (model: EditChapterModel) app = [
|
|||||||
else
|
else
|
||||||
input [ _type "hidden"; _name "AddAnother"; _value "false" ]
|
input [ _type "hidden"; _name "AddAnother"; _value "false" ]
|
||||||
button [ _type "submit"; _class "btn btn-primary" ] [ raw "Save" ]
|
button [ _type "submit"; _class "btn btn-primary" ] [ raw "Save" ]
|
||||||
|
raw " "
|
||||||
a [ _href cancelLink; _hxGet cancelLink; _class "btn btn-secondary"; _hxTarget "body" ] [ raw "Cancel" ]
|
a [ _href cancelLink; _hxGet cancelLink; _class "btn btn-secondary"; _hxTarget "body" ] [ raw "Cancel" ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -146,20 +148,37 @@ let chapterList withNew (model: ManageChaptersModel) app =
|
|||||||
div [ _class "col" ] [ raw "Location?" ]
|
div [ _class "col" ] [ raw "Location?" ]
|
||||||
]
|
]
|
||||||
yield! model.Chapters |> List.mapi (fun idx chapter ->
|
yield! model.Chapters |> List.mapi (fun idx chapter ->
|
||||||
div [ _class "row pb-3 mwl-table-detail"; _id $"chapter{idx}" ] [
|
div [ _class "row mwl-table-detail"; _id $"chapter{idx}" ] [
|
||||||
div [ _class "col" ] [ txt (startTimePattern.Format chapter.StartTime) ]
|
div [ _class "col" ] [ txt (startTimePattern.Format chapter.StartTime) ]
|
||||||
div [ _class "col" ] [ txt (defaultArg chapter.Title "") ]
|
div [ _class "col" ] [
|
||||||
|
txt (defaultArg chapter.Title ""); br []
|
||||||
|
small [] [
|
||||||
|
if withNew then
|
||||||
|
raw " "
|
||||||
|
else
|
||||||
|
let chapterUrl = relUrl app $"admin/post/{model.Id}/chapter/{idx}"
|
||||||
|
a [ _href chapterUrl; _hxGet chapterUrl; _hxTarget $"#chapter{idx}"
|
||||||
|
_hxSwap $"innerHTML show:#chapter{idx}:top" ] [
|
||||||
|
raw "Edit"
|
||||||
|
]
|
||||||
|
span [ _class "text-muted" ] [ raw " • " ]
|
||||||
|
a [ _href chapterUrl; _hxDelete chapterUrl; _class "text-danger" ] [
|
||||||
|
raw "Delete"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
div [ _class "col" ] [ raw (if Option.isSome chapter.ImageUrl then "Y" else "N") ]
|
div [ _class "col" ] [ raw (if Option.isSome chapter.ImageUrl then "Y" else "N") ]
|
||||||
div [ _class "col" ] [ raw (if Option.isSome chapter.Location then "Y" else "N") ]
|
div [ _class "col" ] [ raw (if Option.isSome chapter.Location then "Y" else "N") ]
|
||||||
])
|
])
|
||||||
div [ _class "row pb-3"; _id "chapter-1" ] [
|
div [ _class "row pb-3"; _id "chapter-1" ] [
|
||||||
if withNew then
|
|
||||||
yield! chapterEdit (EditChapterModel.FromChapter (PostId model.Id) -1 Chapter.Empty) app
|
|
||||||
else
|
|
||||||
let newLink = relUrl app $"admin/post/{model.Id}/chapter/-1"
|
let newLink = relUrl app $"admin/post/{model.Id}/chapter/-1"
|
||||||
|
if withNew then
|
||||||
|
span [ _hxGet newLink; _hxTarget "#chapter-1"; _hxTrigger "load"; _hxSwap "show:#chapter-1:top" ] []
|
||||||
|
else
|
||||||
div [ _class "row pb-3 mwl-table-detail" ] [
|
div [ _class "row pb-3 mwl-table-detail" ] [
|
||||||
div [ _class "col-12" ] [
|
div [ _class "col-12" ] [
|
||||||
a [ _class "btn btn-primary"; _href newLink; _hxGet newLink; _hxTarget "#chapter-1" ] [
|
a [ _class "btn btn-primary"; _href newLink; _hxGet newLink; _hxTarget "#chapter-1"
|
||||||
|
_hxSwap "show:#chapter-1:top" ] [
|
||||||
raw "Add a New Chapter"
|
raw "Add a New Chapter"
|
||||||
]
|
]
|
||||||
]
|
]
|
@ -1,4 +1,4 @@
|
|||||||
module MyWebLog.AdminViews.User
|
module MyWebLog.Views.User
|
||||||
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open Giraffe.ViewEngine.Htmx
|
open Giraffe.ViewEngine.Htmx
|
Loading…
Reference in New Issue
Block a user