Remove CSRF from page, post, category delete routes (#56)

- Chapter delete swap target needs work
This commit is contained in:
Daniel J. Summers 2025-07-04 22:24:32 -04:00
parent 87fbb1a8c7
commit d1840f63e5
5 changed files with 50 additions and 58 deletions

View File

@ -218,7 +218,7 @@ let router : HttpHandler = choose [
])
route "/upload/save" >=> Upload.save
]
DELETE >=> validateCsrf >=> choose [
DELETE >=> choose [
routef "/category/%s" Admin.Category.delete
subRoute "/page" (choose [
routef "/%s" Page.delete

View File

@ -527,7 +527,7 @@ let manageRevisions (model: ManageRevisionsModel) app = [
span [ _class "text-muted" ] [ raw " • " ]
a [ _href revRestore; _hxPost revRestore ] [ raw "Restore as Current" ]
span [ _class "text-muted" ] [ raw " • " ]
a [ _href revUrlPrefix; _hxDelete revUrlPrefix; _hxTarget $"#{asOfId}"
a [ _href revUrlPrefix; _hxDelete revUrlPrefix; _hxTarget $"#{asOfId}"; _hxPushUrl "false"
_hxSwap HxSwap.OuterHtml; _class "text-danger" ] [
raw "Delete"
]

View File

@ -29,7 +29,7 @@ let pageEdit (model: EditPageModel) templates app = [
/// Display a list of pages for this web log
let pageList (pages: DisplayPage list) pageNbr hasNext app = [
h2 [ _class "my-3" ] [ raw app.PageTitle ]
article [] [
article [ _class "container mb-3" ] [
a [ _href (relUrl app "admin/page/new/edit"); _class "btn btn-primary btn-sm mb-3" ] [ raw "Create a New Page" ]
if pages.Length = 0 then
p [ _class "text-muted fst-italic text-center" ] [ raw "This web log has no pages" ]
@ -37,50 +37,47 @@ let pageList (pages: DisplayPage list) pageNbr hasNext app = [
let titleCol = "col-12 col-md-5"
let linkCol = "col-12 col-md-5"
let upd8Col = "col-12 col-md-2"
form [ _method "post"; _class "container mb-3"; _hxTarget "body" ] [
antiCsrf app
div [ _class "row mwl-table-heading" ] [
div [ _class titleCol ] [
span [ _class "d-none d-md-inline" ] [ raw "Title" ]; span [ _class "d-md-none" ] [ raw "Page" ]
]
div [ _class $"{linkCol} d-none d-md-inline-block" ] [ raw "Permalink" ]
div [ _class $"{upd8Col} d-none d-md-inline-block" ] [ raw "Updated" ]
div [ _class "row mwl-table-heading" ] [
div [ _class titleCol ] [
span [ _class "d-none d-md-inline" ] [ raw "Title" ]; span [ _class "d-md-none" ] [ raw "Page" ]
]
for pg in pages do
let pageLink = if pg.IsDefault then "" else pg.Permalink
div [ _class "row mwl-table-detail" ] [
div [ _class titleCol ] [
txt pg.Title
if pg.IsDefault then
raw "   "; span [ _class "badge bg-success" ] [ raw "HOME PAGE" ]
if pg.IsInPageList then
raw "   "; span [ _class "badge bg-primary" ] [ raw "IN PAGE LIST" ]
br [] ; small [] [
let adminUrl = relUrl app $"admin/page/{pg.Id}"
a [ _href (relUrl app pageLink); _target "_blank" ] [ raw "View Page" ]
if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId pg.AuthorId) then
span [ _class "text-muted" ] [ raw " • " ]
a [ _href $"{adminUrl}/edit" ] [ raw "Edit" ]
if app.IsWebLogAdmin then
span [ _class "text-muted" ] [ raw " • " ]
a [ _href adminUrl; _hxDelete adminUrl; _class "text-danger"
_hxConfirm $"Are you sure you want to delete the page “{pg.Title}”? This action cannot be undone." ] [
raw "Delete"
]
]
]
div [ _class linkCol ] [
small [ _class "d-md-none" ] [ txt pageLink ]
span [ _class "d-none d-md-inline" ] [ txt pageLink ]
]
div [ _class upd8Col ] [
small [ _class "d-md-none text-muted" ] [
raw "Updated "; txt (pg.UpdatedOn.ToString "MMMM d, yyyy")
]
span [ _class "d-none d-md-inline" ] [ txt (pg.UpdatedOn.ToString "MMMM d, yyyy") ]
div [ _class $"{linkCol} d-none d-md-inline-block" ] [ raw "Permalink" ]
div [ _class $"{upd8Col} d-none d-md-inline-block" ] [ raw "Updated" ]
]
for pg in pages do
let pageLink = if pg.IsDefault then "" else pg.Permalink
div [ _class "row mwl-table-detail" ] [
div [ _class titleCol ] [
txt pg.Title
if pg.IsDefault then
raw "   "; span [ _class "badge bg-success" ] [ raw "HOME PAGE" ]
if pg.IsInPageList then
raw "   "; span [ _class "badge bg-primary" ] [ raw "IN PAGE LIST" ]
br [] ; small [] [
let adminUrl = relUrl app $"admin/page/{pg.Id}"
a [ _href (relUrl app pageLink); _target "_blank" ] [ raw "View Page" ]
if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId pg.AuthorId) then
span [ _class "text-muted" ] [ raw " • " ]
a [ _href $"{adminUrl}/edit" ] [ raw "Edit" ]
if app.IsWebLogAdmin then
span [ _class "text-muted" ] [ raw " • " ]
a [ _href adminUrl; _hxDelete adminUrl; _hxTarget "body"; _class "text-danger"
_hxConfirm $"Are you sure you want to delete the page “{pg.Title}”? This action cannot be undone." ] [
raw "Delete"
]
]
]
]
div [ _class linkCol ] [
small [ _class "d-md-none" ] [ txt pageLink ]
span [ _class "d-none d-md-inline" ] [ txt pageLink ]
]
div [ _class upd8Col ] [
small [ _class "d-md-none text-muted" ] [
raw "Updated "; txt (pg.UpdatedOn.ToString "MMMM d, yyyy")
]
span [ _class "d-none d-md-inline" ] [ txt (pg.UpdatedOn.ToString "MMMM d, yyyy") ]
]
]
if pageNbr > 1 || hasNext then
div [ _class "d-flex justify-content-evenly mb-3" ] [
div [] [

View File

@ -77,7 +77,7 @@ let chapterEdit (model: EditChapterModel) app = [
em [ _class "form-text" ] [
raw "Optional; "
a [ _href "https://www.openstreetmap.org/"; _target "_blank"; _relNoOpener ] [ raw "get ID" ]
raw ", "
raw ", "
a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/location/location.md#osm-recommended"
_target "_blank"; _relNoOpener ] [
raw "see spec"
@ -102,9 +102,7 @@ let chapterEdit (model: EditChapterModel) app = [
/// Display a list of chapters
let chapterList withNew (model: ManageChaptersModel) app =
form [ _method "post"; _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [
antiCsrf app
input [ _type "hidden"; _name "Id"; _value model.Id ]
div [ _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [
div [ _class "row mwl-table-heading" ] [
div [ _class "col-3 col-md-2" ] [ raw "Start" ]
div [ _class "col-3 col-md-6 col-lg-8" ] [ raw "Title" ]
@ -355,7 +353,7 @@ let postEdit (model: EditPostModel) templates (ratings: MetaItem list) app = [
]
div [ _class "col-12 col-md-4 pb-3" ] [
selectField [] (nameof model.Explicit) "Explicit Rating" model.Explicit ratings
(_.Name) (_.Value) [
_.Name _.Value [
div [ _class "form-text" ] [ raw "Optional; overrides podcast default" ]
]
]

View File

@ -33,7 +33,7 @@ let categoryEdit (model: EditCategoryModel) app =
|> String.concat ""
{ Name = c.Id; Value = $"{parents}{c.Name}" })
|> Seq.append [ { Name = ""; Value = "– None –" } ]
selectField [] (nameof model.ParentId) "Parent Category" model.ParentId cats (_.Name) (_.Value) []
selectField [] (nameof model.ParentId) "Parent Category" model.ParentId cats _.Name _.Value []
]
div [ _class "col-12 col-xl-10 offset-xl-1 mb-3" ] [
textField [] (nameof model.Description) "Description" model.Description []
@ -107,9 +107,6 @@ let categoryList includeNew app = [
div [ _class catCol ] [ raw "Category"; span [ _class "d-md-none" ] [ raw "; Description" ] ]
div [ _class $"{descCol} d-none d-md-inline-block" ] [ raw "Description" ]
]
]
form [ _method "post"; _class "container" ] [
antiCsrf app
div [ _class "row mwl-table-detail"; _id "cat_new" ] [ if includeNew then loadNew ]
yield! app.Categories |> Seq.ofArray |> Seq.map categoryDetail |> List.ofSeq
]
@ -249,8 +246,8 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
|> Seq.append [ { Name = ""; Value = "– Select Category –" } ]
selectField [ _id "SourceValueCat"; _required
if model.SourceType = "tag" then _disabled ]
(nameof model.SourceValue) "Category" model.SourceValue cats (_.Name)
(_.Value) []
(nameof model.SourceValue) "Category" model.SourceValue cats _.Name _.Value
[]
]
div [ _class "col-1 d-flex justify-content-end pb-3" ] [
div [ _class "form-check form-check-inline me-0" ] [
@ -305,7 +302,7 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
]
div [ _class "col-12 col-md-3 col-lg-2 pb-3" ] [
selectField [ _required ] (nameof model.Explicit) "Explicit Rating" model.Explicit
ratings (_.Name) (_.Value) []
ratings _.Name _.Value []
]
]
div [ _class "row" ] [
@ -380,7 +377,7 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
]
]
div [ _class "col-4 col-lg-3 offset-lg-2 pb-3" ] [
selectField [] (nameof model.Medium) "Medium" model.Medium mediums (_.Name) (_.Value) [
selectField [] (nameof model.Medium) "Medium" model.Medium mediums _.Name _.Value [
span [ _class "form-text fst-italic" ] [
raw "Optional; medium of the podcast content ("
a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium"
@ -779,7 +776,7 @@ let webLogSettings
]
div [ _class "col-12 col-md-6 offset-md-1 col-xl-4 offset-xl-0 pb-3" ] [
selectField [ _required ] (nameof model.DefaultPage) "Default Page" model.DefaultPage pages
(fun p -> string p.Id) (_.Title) []
(fun p -> string p.Id) _.Title []
]
div [ _class "col-12 col-md-4 col-xl-2 pb-3" ] [
numberField [ _required; _min "0"; _max "50" ] (nameof model.PostsPerPage) "Posts per Page"