WIP on revision mgt template (#13)

This commit is contained in:
Daniel J. Summers 2022-07-14 23:25:29 -04:00
parent 2906c20efa
commit d667d09372
4 changed files with 133 additions and 5 deletions

View File

@ -147,6 +147,28 @@ type DisplayPage =
} }
/// Information about a revision used for display
[<CLIMutable; NoComparison; NoEquality>]
type DisplayRevision =
{ /// The as-of date/time for the revision
asOf : DateTime
/// The as-of date/time for the revision in the web log's local time zone
asOfLocal : DateTime
/// The format of the text of the revision
format : string
}
with
/// Create a display revision from an actual revision
static member fromRevision webLog (rev : Revision) =
{ asOf = rev.asOf
asOfLocal = WebLog.localTime webLog rev.asOf
format = MarkupText.sourceType rev.text
}
open System.IO open System.IO
/// Information about an uploaded file used for display /// Information about an uploaded file used for display
@ -766,6 +788,39 @@ type ManagePermalinksModel =
} }
/// View model to manage revisions
[<CLIMutable; NoComparison; NoEquality>]
type ManageRevisionsModel =
{ /// 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 revisions for the page or post
revisions : DisplayRevision[]
}
/// Create a revision model from a page
static member fromPage webLog (pg : Page) =
{ id = PageId.toString pg.id
entity = "page"
currentTitle = pg.title
revisions = pg.revisions |> List.map (DisplayRevision.fromRevision webLog) |> Array.ofList
}
/// Create a revision model from a post
static member fromPost webLog (post : Post) =
{ id = PostId.toString post.id
entity = "post"
currentTitle = post.title
revisions = post.revisions |> List.map (DisplayRevision.fromRevision webLog) |> Array.ofList
}
/// View model for posts in a list /// View model for posts in a list
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type PostListItem = type PostListItem =

View File

@ -226,11 +226,11 @@ let register () =
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page> typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog> typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
// View models // View models
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage> typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditPageModel> typeof<DisplayRevision>; typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>
typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>; typeof<EditUserModel> typeof<EditPageModel>; typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>
typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<PostDisplay>; typeof<PostListItem> typeof<EditUserModel>; typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>
typeof<SettingsModel>; typeof<UserMessage> typeof<PostDisplay>; typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
// Framework types // Framework types
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair> typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list> typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list>

View File

@ -190,6 +190,21 @@ let savePagePermalinks : HttpHandler = fun next ctx -> task {
| false -> return! Error.notFound next ctx | false -> return! Error.notFound next ctx
} }
// GET /admin/page/{id}/revisions
let editPageRevisions pgId : HttpHandler = fun next ctx -> task {
let webLog = ctx.WebLog
match! ctx.Data.Page.findFullById (PageId pgId) webLog.id with
| Some pg ->
return!
Hash.FromAnonymousObject {|
csrf = csrfToken ctx
model = ManageRevisionsModel.fromPage webLog pg
page_title = $"Manage Page Permalinks"
|}
|> viewForTheme "admin" "revisions" next ctx
| None -> return! Error.notFound next ctx
}
// POST /admin/page/{id}/delete // POST /admin/page/{id}/delete
let deletePage pgId : HttpHandler = fun next ctx -> task { let deletePage pgId : HttpHandler = fun next ctx -> task {
let webLog = ctx.WebLog let webLog = ctx.WebLog

View File

@ -0,0 +1,58 @@
<h2 class="my-3">{{ page_title }}</h2>
<article>
<form method="post">
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
<input type="hidden" name="id" value="{{ model.id }}">
<div class="container">
<div class="row">
<div class="col">
<p style="line-height:1.2rem;">
<strong>{{ model.current_title }}</strong><br>
<small class="text-muted">
{%- capture back_link %}admin/{{ model.entity }}/{{ model.id }}/edit{% endcapture -%}
<a href="{{ back_link | relative_link }}">&laquo; Back to Edit {{ model.entity | capitalize }}</a>
</small>
</p>
</div>
</div>
{%- assign revision_count = model.revisions | size -%}
{%- capture rev_url_base %}admin/{{ model.entity }}/{{ model.id }}/revision{% endcapture -%}
{%- if revision_count > 1 %}
{% capture delete_all %}{{ rev_url_base }}s/purge{% endcapture %}
<div class="row mb-3">
<div class="col">
<button type="button" class="btn btn-sm btn-danger" hx-post="{{ delete_all | relative_link }}"
hx-confirm="This will remove all revisions but the current one; are you sure this is what you wish to do?">
Delete All Prior Revisions
</button>
</div>
</div>
{%- endif %}
{% for rev in model.revisions %}
<div class="row mb-3">
<div class="col">
{{ rev.as_of_local | date: "MMMM d, yyyy" }} at {{ rev.as_of_local | date: "h:mmaa" | downcase }}
<span class="badge bg-secondary text-uppercase">{{ ref.format }}</span>
{%- if forloop.first %}
<span class="badge bg-primary text-uppercase ms-2">Current Revision</span>
{%- endif %}<br>
{% unless forloop.first %}
{%- capture rev_url_prefix %}{{ rev_url_base }}/{{ rev.as_of | date: "o" }}{% endcapture -%}
<small>
<a href="TODO">Preview</a>
<span class="text-muted"> &bull; </span>
{%- capture rev_restore %}{{ rev_url_prefix }}/restore{% endcapture -%}
{%- capture rev_restore_link %}{{ rev_restore | relative_link }}{% endcapture -%}
<a href="{{ rev_restore_link }}" hx-post="{{ rev_restore_link }}">Restore as Current</a>
<span class="text-muted"> &bull; </span>
{%- capture rev_del %}{{ rev_url_prefix }}/delete{% endcapture -%}
{%- capture rev_del_link %}{{ rev_del | relative_link }}{% endcapture -%}
<a href="{{ rev_del_link }}" hx-post="{{ rev_del_link }}" class="text-danger">Delete</a>
</small>
{% endunless %}
</div>
</div>
{% endfor %}
</div>
</form>
</article>