diff --git a/src/MyWebLog.Data/Data.fs b/src/MyWebLog.Data/Data.fs index 058f108..7282b81 100644 --- a/src/MyWebLog.Data/Data.fs +++ b/src/MyWebLog.Data/Data.fs @@ -394,6 +394,21 @@ module Page = ] write; withRetryDefault; ignoreResult } + + /// Update prior permalinks for a page + let updatePriorPermalinks pageId webLogId (permalinks : Permalink list) conn = task { + match! findById pageId webLogId conn with + | Some _ -> + do! rethink { + withTable Table.Page + get pageId + update [ "priorPermalinks", permalinks :> obj ] + write; withRetryDefault; ignoreResult conn + } + return true + | None -> return false + } + /// Functions to manipulate posts module Post = @@ -542,6 +557,27 @@ module Post = write; withRetryDefault; ignoreResult } + /// Update prior permalinks for a post + let updatePriorPermalinks (postId : PostId) webLogId (permalinks : Permalink list) conn = task { + match! ( + rethink { + withTable Table.Post + get postId + without [ "revisions"; "priorPermalinks" ] + resultOption; withRetryOptionDefault + } + |> verifyWebLog webLogId (fun p -> p.webLogId)) conn with + | Some _ -> + do! rethink { + withTable Table.Post + get postId + update [ "priorPermalinks", permalinks :> obj ] + write; withRetryDefault; ignoreResult conn + } + return true + | None -> return false + } + /// Functions to manipulate web logs module WebLog = diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 18b7cfe..ccbc837 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -299,6 +299,44 @@ type LogOnModel = { emailAddress = ""; password = ""; returnTo = None } +/// View model to manage permalinks +[] +type ManagePermalinksModel = + { /// The ID for the entity being edited + id : string + + /// The type of entity being edited ("page" or "post") + entity : string + + /// The current title of the page or post + currentTitle : string + + /// The current permalink of the page or post + currentPermalink : string + + /// The prior permalinks for the page or post + prior : string[] + } + + /// Create a permalink model from a page + static member fromPage (pg : Page) = + { id = PageId.toString pg.id + entity = "page" + currentTitle = pg.title + currentPermalink = Permalink.toString pg.permalink + prior = pg.priorPermalinks |> List.map Permalink.toString |> Array.ofList + } + + /// Create a permalink model from a post + static member fromPost (post : Post) = + { id = PostId.toString post.id + entity = "post" + currentTitle = post.title + currentPermalink = Permalink.toString post.permalink + prior = post.priorPermalinks |> List.map Permalink.toString |> Array.ofList + } + + /// View model for posts in a list [] type PostListItem = diff --git a/src/MyWebLog/Handlers/Page.fs b/src/MyWebLog/Handlers/Page.fs index 0c34fef..1667768 100644 --- a/src/MyWebLog/Handlers/Page.fs +++ b/src/MyWebLog/Handlers/Page.fs @@ -45,6 +45,31 @@ let edit pgId : HttpHandler = requireUser >=> fun next ctx -> task { | None -> return! Error.notFound next ctx } +// GET /page/{id}/permalinks +let editPermalinks pgId : HttpHandler = requireUser >=> fun next ctx -> task { + match! Data.Page.findByFullId (PageId pgId) (webLogId ctx) (conn ctx) with + | Some pg -> + return! + Hash.FromAnonymousObject {| + csrf = csrfToken ctx + model = ManagePermalinksModel.fromPage pg + page_title = $"Manage Prior Permalinks" + |} + |> viewForTheme "admin" "permalinks" next ctx + | None -> return! Error.notFound next ctx +} + +// POST /page/permalinks +let savePermalinks : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task { + let! model = ctx.BindFormAsync () + let links = model.prior |> Array.map Permalink |> List.ofArray + match! Data.Page.updatePriorPermalinks (PageId model.id) (webLogId ctx) links (conn ctx) with + | true -> + do! addMessage ctx { UserMessage.success with message = "Page permalinks saved successfully" } + return! redirectToGet $"/page/{model.id}/permalinks" next ctx + | false -> return! Error.notFound next ctx +} + open System #nowarn "3511" diff --git a/src/MyWebLog/Handlers/Post.fs b/src/MyWebLog/Handlers/Post.fs index 4606098..c6912bd 100644 --- a/src/MyWebLog/Handlers/Post.fs +++ b/src/MyWebLog/Handlers/Post.fs @@ -328,6 +328,31 @@ let edit postId : HttpHandler = requireUser >=> fun next ctx -> task { | None -> return! Error.notFound next ctx } +// GET /post/{id}/permalinks +let editPermalinks postId : HttpHandler = requireUser >=> fun next ctx -> task { + match! Data.Post.findByFullId (PostId postId) (webLogId ctx) (conn ctx) with + | Some post -> + return! + Hash.FromAnonymousObject {| + csrf = csrfToken ctx + model = ManagePermalinksModel.fromPost post + page_title = $"Manage Prior Permalinks" + |} + |> viewForTheme "admin" "permalinks" next ctx + | None -> return! Error.notFound next ctx +} + +// POST /post/permalinks +let savePermalinks : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task { + let! model = ctx.BindFormAsync () + let links = model.prior |> Array.map Permalink |> List.ofArray + match! Data.Post.updatePriorPermalinks (PostId model.id) (webLogId ctx) links (conn ctx) with + | true -> + do! addMessage ctx { UserMessage.success with message = "Post permalinks saved successfully" } + return! redirectToGet $"/post/{model.id}/permalinks" next ctx + | false -> return! Error.notFound next ctx +} + #nowarn "3511" // POST /post/save diff --git a/src/MyWebLog/Handlers/Routes.fs b/src/MyWebLog/Handlers/Routes.fs index 64f07c3..9c3a21f 100644 --- a/src/MyWebLog/Handlers/Routes.fs +++ b/src/MyWebLog/Handlers/Routes.fs @@ -30,24 +30,27 @@ let endpoints = [ ] subRoute "/page" [ GET [ - routef "/%d" Post.pageOfPosts - //routef "/%d/" (fun pg -> redirectTo true $"/page/{pg}") - routef "/%s/edit" Page.edit - route "s" (Page.all 1) - routef "s/page/%d" Page.all + routef "/%d" Post.pageOfPosts + routef "/%s/edit" Page.edit + routef "/%s/permalinks" Page.editPermalinks + route "s" (Page.all 1) + routef "s/page/%d" Page.all ] POST [ - route "/save" Page.save + route "/permalinks" Page.savePermalinks + route "/save" Page.save ] ] subRoute "/post" [ GET [ - routef "/%s/edit" Post.edit - route "s" (Post.all 1) - routef "s/page/%d" Post.all + routef "/%s/edit" Post.edit + routef "/%s/permalinks" Post.editPermalinks + route "s" (Post.all 1) + routef "s/page/%d" Post.all ] POST [ - route "/save" Post.save + route "/permalinks" Post.savePermalinks + route "/save" Post.save ] ] subRoute "/tag" [ diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index 656dbd7..1a87eff 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -253,9 +253,10 @@ let main args = [ // Domain types typeof; typeof; typeof // View models - typeof; typeof; typeof; typeof - typeof; typeof; typeof; typeof - typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof; typeof; typeof; typeof + typeof // Framework types typeof; typeof; typeof; typeof typeof diff --git a/src/MyWebLog/appsettings.json b/src/MyWebLog/appsettings.json index 7003291..df13854 100644 --- a/src/MyWebLog/appsettings.json +++ b/src/MyWebLog/appsettings.json @@ -3,5 +3,5 @@ "hostname": "data02.bitbadger.solutions", "database": "myWebLog_dev" }, - "Generator": "myWebLog 2.0-alpha05" + "Generator": "myWebLog 2.0-alpha06" } diff --git a/src/MyWebLog/themes/admin/page-edit.liquid b/src/MyWebLog/themes/admin/page-edit.liquid index 3cc3cef..5a286ee 100644 --- a/src/MyWebLog/themes/admin/page-edit.liquid +++ b/src/MyWebLog/themes/admin/page-edit.liquid @@ -15,6 +15,9 @@ + {%- if model.page_id != "new" %} + Manage Permalinks + {% endif -%}
diff --git a/src/MyWebLog/themes/admin/permalinks.liquid b/src/MyWebLog/themes/admin/permalinks.liquid new file mode 100644 index 0000000..25bcc0a --- /dev/null +++ b/src/MyWebLog/themes/admin/permalinks.liquid @@ -0,0 +1,57 @@ +

{{ page_title }}

+
+
+ + +
+
+
+

+ {{ model.current_title }}
+ + {{ model.current_permalink }}
+ « Back to Edit {{ model.entity | capitalize }} +
+

+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
diff --git a/src/MyWebLog/themes/admin/post-edit.liquid b/src/MyWebLog/themes/admin/post-edit.liquid index 7629d13..9a3a7e3 100644 --- a/src/MyWebLog/themes/admin/post-edit.liquid +++ b/src/MyWebLog/themes/admin/post-edit.liquid @@ -15,6 +15,9 @@ + {%- if model.page_id != "new" %} + Manage Permalinks + {% endif -%}
    diff --git a/src/MyWebLog/wwwroot/themes/admin/admin.css b/src/MyWebLog/wwwroot/themes/admin/admin.css index e0b2276..354bd87 100644 --- a/src/MyWebLog/wwwroot/themes/admin/admin.css +++ b/src/MyWebLog/wwwroot/themes/admin/admin.css @@ -9,6 +9,7 @@ body { scrollbar-color: var(--dark-gray) gray; padding-top: 3rem; padding-bottom: 2rem; + min-height: 100vh; } body::-webkit-scrollbar { width: 12px; diff --git a/src/MyWebLog/wwwroot/themes/admin/admin.js b/src/MyWebLog/wwwroot/themes/admin/admin.js index c8fac15..d992c7f 100644 --- a/src/MyWebLog/wwwroot/themes/admin/admin.js +++ b/src/MyWebLog/wwwroot/themes/admin/admin.js @@ -2,15 +2,25 @@ /** The next index for a metadata item */ nextMetaIndex : 0, + /** The next index for a permalink */ + nextPermalink : 0, + /** * Set the next meta item index * @param idx The index to set */ - // Calling a function with a Liquid variable does not look like an error in the IDE... setNextMetaIndex(idx) { this.nextMetaIndex = idx }, - + + /** + * Set the next permalink index + * @param idx The index to set + */ + setPermalinkIndex(idx) { + this.nextPermalink = idx + }, + /** * Add a new row for metadata entry */ @@ -80,7 +90,55 @@ document.getElementById(nameField.id).focus() this.nextMetaIndex++ }, - + + /** + * Add a new row for a permalink + */ + addPermalink() { + // Remove button + const removeBtn = document.createElement("button") + removeBtn.type = "button" + removeBtn.className = "btn btn-sm btn-danger" + removeBtn.innerHTML = "−" + removeBtn.setAttribute("onclick", `Admin.removePermalink(${this.nextPermalink})`) + + const removeCol = document.createElement("div") + removeCol.className = "col-1 text-center align-self-center" + removeCol.appendChild(removeBtn) + + // Link + const linkField = document.createElement("input") + linkField.type = "text" + linkField.name = "prior" + linkField.id = `prior_${this.nextPermalink}` + linkField.className = "form-control" + linkField.placeholder = "Link" + + const linkLabel = document.createElement("label") + linkLabel.htmlFor = linkField.id + linkLabel.innerText = linkField.placeholder + + const linkFloat = document.createElement("div") + linkFloat.className = "form-floating" + linkFloat.appendChild(linkField) + linkFloat.appendChild(linkLabel) + + const linkCol = document.createElement("div") + linkCol.className = "col-11" + linkCol.appendChild(linkFloat) + + // Put it all together + const newRow = document.createElement("div") + newRow.className = "row mb-3" + newRow.id = `meta_${this.nextPermalink}` + newRow.appendChild(removeCol) + newRow.appendChild(linkCol) + + document.getElementById("permalinks").appendChild(newRow) + document.getElementById(linkField.id).focus() + this.nextPermalink++ + }, + /** * Remove a metadata item * @param idx The index of the metadata item to remove @@ -88,7 +146,15 @@ removeMetaItem(idx) { document.getElementById(`meta_${idx}`).remove() }, - + + /** + * Remove a permalink + * @param idx The index of the permalink to remove + */ + removePermalink(idx) { + document.getElementById(`link_${idx}`).remove() + }, + /** * Confirm and delete a category * @param id The ID of the category to be deleted