WIP on revision mgt template (#13)
This commit is contained in:
parent
2906c20efa
commit
d667d09372
@ -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
|
||||
|
||||
/// 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
|
||||
[<NoComparison; NoEquality>]
|
||||
type PostListItem =
|
||||
|
@ -226,11 +226,11 @@ let register () =
|
||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
||||
typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
||||
// View models
|
||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
||||
typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditPageModel>
|
||||
typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>; typeof<EditUserModel>
|
||||
typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<PostDisplay>; typeof<PostListItem>
|
||||
typeof<SettingsModel>; typeof<UserMessage>
|
||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
||||
typeof<DisplayRevision>; typeof<DisplayUpload>; typeof<EditCategoryModel>; typeof<EditCustomFeedModel>
|
||||
typeof<EditPageModel>; typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>
|
||||
typeof<EditUserModel>; typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>
|
||||
typeof<PostDisplay>; typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
||||
// Framework types
|
||||
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
||||
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list>
|
||||
|
@ -190,6 +190,21 @@ let savePagePermalinks : HttpHandler = fun next ctx -> task {
|
||||
| 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
|
||||
let deletePage pgId : HttpHandler = fun next ctx -> task {
|
||||
let webLog = ctx.WebLog
|
||||
|
58
src/admin-theme/revisions.liquid
Normal file
58
src/admin-theme/revisions.liquid
Normal 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 }}">« 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"> • </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"> • </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>
|
Loading…
Reference in New Issue
Block a user