Version 2.1 #41

Merged
danieljsummers merged 123 commits from version-2.1 into main 2024-03-27 00:13:28 +00:00
13 changed files with 25 additions and 610 deletions
Showing only changes of commit 64599316d5 - Show all commits

View File

@ -384,7 +384,7 @@ module Theme =
if ctx.Request.HasFormContentType && ctx.Request.Form.Files.Count > 0 then
let themeFile = Seq.head ctx.Request.Form.Files
match deriveIdFromFileName themeFile.FileName with
| Ok themeId when themeId <> adminTheme ->
| Ok themeId when themeId <> ThemeId "admin" ->
let data = ctx.Data
let! exists = data.Theme.Exists themeId
let isNew = not exists

View File

@ -355,13 +355,6 @@ let themedView template next ctx hash = task {
return! viewForTheme (hash[ViewContext.WebLog] :?> WebLog).ThemeId template next ctx hash
}
/// The ID for the admin theme
let adminTheme = ThemeId "admin"
/// Display a bare view for the admin theme
let adminBareView template =
bareForTheme adminTheme template
/// Display a page for an admin endpoint
let adminPage pageTitle includeCsrf next ctx (content: AppViewContext -> XmlNode list) = task {
let! messages = getCurrentMessages ctx
@ -416,7 +409,7 @@ let someTask<'T> (it: 'T) = Task.FromResult(Some it)
/// Create an absolute URL from a string that may already be an absolute URL
let absoluteUrl (url: string) (ctx: HttpContext) =
if url.StartsWith "http" then url else ctx.WebLog.AbsoluteUrl (Permalink url)
if url.StartsWith "http" then url else ctx.WebLog.AbsoluteUrl(Permalink url)
open MyWebLog.Data

View File

@ -115,18 +115,9 @@ let private findPageRevision pgId revDate (ctx: HttpContext) = task {
let previewRevision (pgId, revDate) : HttpHandler = requireAccess Author >=> fun next ctx -> task {
match! findPageRevision pgId revDate ctx with
| Some pg, Some rev when canEdit pg.AuthorId ctx ->
return! {|
content =
[ """<div class="mwl-revision-preview mb-3">"""
rev.Text.AsHtml() |> addBaseToRelativeUrls ctx.WebLog.ExtraPath
"</div>"
]
|> String.concat ""
|}
|> makeHash |> adminBareView "" next ctx
return! adminBarePage "" false next ctx (Views.Helpers.commonPreview rev)
| Some _, Some _ -> return! Error.notAuthorized next ctx
| None, _
| _, None -> return! Error.notFound next ctx
| None, _ | _, None -> return! Error.notFound next ctx
}
// POST /admin/page/{id}/revision/{revision-date}/restore
@ -150,7 +141,7 @@ let deleteRevision (pgId, revDate) : HttpHandler = requireAccess Author >=> fun
| Some pg, Some rev when canEdit pg.AuthorId ctx ->
do! ctx.Data.Page.Update { pg with Revisions = pg.Revisions |> List.filter (fun r -> r.AsOf <> rev.AsOf) }
do! addMessage ctx { UserMessage.Success with Message = "Revision deleted successfully" }
return! adminBareView "" next ctx (makeHash {| content = "" |})
return! adminBarePage "" false next ctx (fun _ -> [])
| Some _, Some _ -> return! Error.notAuthorized next ctx
| None, _
| _, None -> return! Error.notFound next ctx

View File

@ -358,18 +358,9 @@ let private findPostRevision postId revDate (ctx: HttpContext) = task {
let previewRevision (postId, revDate) : HttpHandler = requireAccess Author >=> fun next ctx -> task {
match! findPostRevision postId revDate ctx with
| Some post, Some rev when canEdit post.AuthorId ctx ->
return! {|
content =
[ """<div class="mwl-revision-preview mb-3">"""
rev.Text.AsHtml() |> addBaseToRelativeUrls ctx.WebLog.ExtraPath
"</div>"
]
|> String.concat ""
|}
|> makeHash |> adminBareView "" next ctx
return! adminBarePage "" false next ctx (Views.Helpers.commonPreview rev)
| Some _, Some _ -> return! Error.notAuthorized next ctx
| None, _
| _, None -> return! Error.notFound next ctx
| None, _ | _, None -> return! Error.notFound next ctx
}
// POST /admin/post/{id}/revision/{revision-date}/restore
@ -393,7 +384,7 @@ let deleteRevision (postId, revDate) : HttpHandler = requireAccess Author >=> fu
| Some post, Some rev when canEdit post.AuthorId ctx ->
do! ctx.Data.Post.Update { post with Revisions = post.Revisions |> List.filter (fun r -> r.AsOf <> rev.AsOf) }
do! addMessage ctx { UserMessage.Success with Message = "Revision deleted successfully" }
return! adminBareView "" next ctx (makeHash {| content = "" |})
return! adminBarePage "" false next ctx (fun _ -> [])
| Some _, Some _ -> return! Error.notAuthorized next ctx
| None, _
| _, None -> return! Error.notFound next ctx

View File

@ -393,6 +393,14 @@ let commonMetaItems (model: EditCommonModel) =
]
/// Revision preview template
let commonPreview (rev: Revision) app =
div [ _class "mwl-revision-preview mb-3" ] [
rev.Text.AsHtml() |> addBaseToRelativeUrls app.WebLog.ExtraPath |> raw
]
|> List.singleton
/// Form to manage permalinks for pages or posts
let managePermalinks (model: ManagePermalinksModel) app = [
let baseUrl = relUrl app $"admin/{model.Entity}/"

View File

@ -1,38 +0,0 @@
<div class="form-floating pb-3">
<input type=text name=Title id=title class=form-control placeholder=Title autofocus required
value="{{ model.title }}">
<label for=title>Title</label>
</div>
<div class="form-floating pb-3">
<input type=text name=Permalink id=permalink class=form-control placeholder=Permalink required
value="{{ model.permalink }}">
<label for=permalink>Permalink</label>
{%- unless model.is_new %}
{%- assign entity_url_base = "admin/" | append: entity | append: "/" | append: entity_id -%}
<span class=form-text>
<a href="{{ entity_url_base | append: "/permalinks" | relative_link }}">Manage Permalinks</a>
<span class=text-muted> &bull; </span>
<a href="{{ entity_url_base | append: "/revisions" | relative_link }}">Manage Revisions</a>
{% if model.chapter_source == "internal" %}
<span id="chapterEditLink">
<span class=text-muted> &bull; </span>
<a href="{{ entity_url_base | append: "/chapters" | relative_link }}">Manage Chapters</a>
</span>
{% endif %}
</span>
{%- endunless -%}
</div>
<div class=mb-2>
<label for=text>Text</label> &nbsp; &nbsp;
<div class="btn-group btn-group-sm" role=group aria-label="Text format button group">
<input type=radio name=Source id=source_html class=btn-check value=HTML
{%- if model.source == "HTML" %} checked{% endif %}>
<label class="btn btn-sm btn-outline-secondary" for=source_html>HTML</label>
<input type=radio name=Source id=source_md class=btn-check value=Markdown
{%- if model.source == "Markdown" %} checked{% endif %}>
<label class="btn btn-sm btn-outline-secondary" for=source_md>Markdown</label>
</div>
</div>
<div class=pb-3>
<textarea name=Text id=text class=form-control rows=20>{{ model.text }}</textarea>
</div>

View File

@ -1,80 +0,0 @@
<header>
<nav class="navbar navbar-dark bg-dark navbar-expand-md justify-content-start px-2 position-fixed top-0 w-100">
<div class=container-fluid>
<a class=navbar-brand href="{{ "" | relative_link }}" hx-boost=false>{{ web_log.name }}</a>
<button class=navbar-toggler type=button data-bs-toggle=collapse data-bs-target=#navbarText
aria-controls=navbarText aria-expanded=false aria-label="Toggle navigation">
<span class=navbar-toggler-icon></span>
</button>
<div class="collapse navbar-collapse" id=navbarText>
{%- if is_logged_on %}
<ul class=navbar-nav>
{{ "admin/dashboard" | nav_link: "Dashboard" }}
{%- if is_author %}
{{ "admin/pages" | nav_link: "Pages" }}
{{ "admin/posts" | nav_link: "Posts" }}
{{ "admin/uploads" | nav_link: "Uploads" }}
{%- endif %}
{%- if is_web_log_admin %}
{{ "admin/categories" | nav_link: "Categories" }}
{{ "admin/settings" | nav_link: "Settings" }}
{%- endif %}
{%- if is_administrator %}
{{ "admin/administration" | nav_link: "Admin" }}
{%- endif %}
</ul>
{%- endif %}
<ul class="navbar-nav flex-grow-1 justify-content-end">
{%- if is_logged_on %}
{{ "admin/my-info" | nav_link: "My Info" }}
<li class=nav-item>
<a class=nav-link href=https://bitbadger.solutions/open-source/myweblog/#how-to-use-myweblog target=_blank>
Docs
</a>
<li class=nav-item>
<a class=nav-link href="{{ "user/log-off" | relative_link }}" hx-boost=false>Log Off</a>
{%- else -%}
<li class=nav-item>
<a class=nav-link href=https://bitbadger.solutions/open-source/myweblog/#how-to-use-myweblog target=_blank>
Docs
</a>
{{ "user/log-on" | nav_link: "Log On" }}
{%- endif %}
</ul>
</div>
</div>
</nav>
</header>
<div id=toastHost class="position-fixed top-0 w-100" aria-live=polite aria-atomic=true>
<div id=toasts class="toast-container position-absolute p-3 mt-5 top-0 end-0">
{% for msg in messages %}
<div class=toast role=alert aria-live=assertive aria-atomic=true
{%- unless msg.level == "success" %} data-bs-autohide="false"{% endunless %}>
<div class="toast-header bg-{{ msg.level }}{% unless msg.level == "warning" %} text-white{% endunless %}">
<strong class="me-auto text-uppercase">
{% if msg.level == "danger" %}error{% else %}{{ msg.level}}{% endif %}
</strong>
<button type=button class=btn-close data-bs-dismiss=toast aria-label=Close></button>
</div>
<div class="toast-body bg-{{ msg.level }} bg-opacity-25">
{{ msg.message }}
{%- if msg.detail %}
<hr>
{{ msg.detail.value }}
{%- endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<main class="mx-3 mt-3">
<div class="load-overlay p-5" id=loadOverlay><h1 class=p-3>Loading&hellip;</h1></div>
{{ content }}
</main>
<footer class="position-fixed bottom-0 w-100">
<div class="text-end text-white me-2">
{%- assign version = generator | split: " " -%}
<small class="me-1 align-baseline">v{{ version[1] }}</small>
<img src="{{ "themes/admin/logo-light.png" | relative_link }}" alt=myWebLog width=120 height=34>
</div>
</footer>

View File

@ -1,5 +0,0 @@
<!DOCTYPE html>
<html lang=en>
<title></title>
{{ content }}
</html>

View File

@ -1,5 +0,0 @@
<!DOCTYPE html>
<html lang=en>
<title>{{ page_title | strip_html }} &laquo; Admin &laquo; {{ web_log.name | strip_html }}</title>
{% include_template "_layout" %}
</html>

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang=en>
<meta name=viewport content="width=device-width, initial-scale=1">
<meta name=generator content="{{ generator }}">
<title>{{ page_title | strip_html }} &laquo; Admin &laquo; {{ web_log.name | strip_html }}</title>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css
integrity=sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3 crossorigin=anonymous>
<link rel=stylesheet href="{{ "themes/admin/admin.css" | relative_link }}">
<body hx-boost=true hx-indicator=#loadOverlay>
{% include_template "_layout" %}
<script src=https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js
integrity=sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p
crossorigin=anonymous></script>
{{ htmx_script }}
<script src="{{ "themes/admin/admin.js" | relative_link }}"></script>
</body>
</html>

View File

@ -1,82 +0,0 @@
<h2 class=my-3>{{ page_title }}</h2>
<article>
<form action="{{ "admin/page/save" | relative_link }}" method="post" hx-push-url="true">
<input type=hidden name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
<input type=hidden name=PageId value="{{ model.page_id }}">
<div class=container>
<div class="row mb-3">
<div class=col-9>
{%- assign entity = "page" -%}
{%- assign entity_id = model.page_id -%}
{% include_template "_edit-common" %}
</div>
<div class=col-3>
<div class="form-floating pb-3">
<select name=Template id=template class=form-control>
{% for tmpl in templates -%}
<option value="{{ tmpl[0] }}"{% if model.template == tmpl[0] %} selected{% endif %}>
{{ tmpl[1] }}
</option>
{%- endfor %}
</select>
<label for=template>Page Template</label>
</div>
<div class="form-check form-switch">
<input type=checkbox name=IsShownInPageList id=showList class=form-check-input value=true
{%- if model.is_shown_in_page_list %} checked{% endif %}>
<label for=showList class=form-check-label>Show in Page List</label>
</div>
</div>
</div>
<div class="row mb-3">
<div class=col>
<button type=submit class="btn btn-primary">Save Changes</button>
</div>
</div>
<div class="row mb-3">
<div class=col>
<fieldset>
<legend>
Metadata
<button type=button class="btn btn-sm btn-secondary" data-bs-toggle=collapse
data-bs-target=#metaItemContainer>
show
</button>
</legend>
<div id=metaItemContainer class=collapse>
<div id=metaItems class=container>
{%- for meta in metadata %}
<div id="meta_{{ meta[0] }}" class="row mb-3">
<div class="col-1 text-center align-self-center">
<button type=button class="btn btn-sm btn-danger" onclick="Admin.removeMetaItem({{ meta[0] }})">
&minus;
</button>
</div>
<div class=col-3>
<div class=form-floating>
<input type=text name=MetaNames id="metaNames_{{ meta[0] }}" class=form-control
placeholder=Name value="{{ meta[1] }}">
<label for="metaNames_{{ meta[0] }}">Name</label>
</div>
</div>
<div class=col-8>
<div class=form-floating>
<input type=text name=MetaValues id="metaValues_{{ meta[0] }}" class=form-control
placeholder=Value value="{{ meta[2] }}">
<label for="metaValues_{{ meta[0] }}">Value</label>
</div>
</div>
</div>
{% endfor -%}
</div>
<button type=button class="btn btn-sm btn-secondary" onclick="Admin.addMetaItem()">Add an Item</button>
<script>
document.addEventListener("DOMContentLoaded", () => Admin.setNextMetaIndex({{ metadata | size }}))
</script>
</div>
</fieldset>
</div>
</div>
</div>
</form>
</article>

View File

@ -1,341 +0,0 @@
<h2 class=my-3>{{ page_title }}</h2>
<article>
<form action="{{ "admin/post/save" | relative_link }}" method=post hx-push-url=true>
<input type=hidden name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
<input type=hidden name=PostId value="{{ model.post_id }}">
<div class=container>
<div class="row mb-3">
<div class="col-12 col-lg-9">
{%- assign entity = "post" -%}
{%- assign entity_id = model.post_id -%}
{% include_template "_edit-common" %}
<div class="form-floating pb-3">
<input type=text name=Tags id=tags class=form-control placeholder=Tags value="{{ model.tags }}">
<label for=tags>Tags</label>
<div class=form-text>comma-delimited</div>
</div>
{% if model.status == "Draft" %}
<div class="form-check form-switch pb-2">
<input type=checkbox name=DoPublish id=doPublish class=form-check-input value=true>
<label for=doPublish class=form-check-label>Publish This Post</label>
</div>
{% endif %}
<button type=submit class="btn btn-primary pb-2">Save Changes</button>
<hr class=mb-3>
<fieldset class=mb-3>
<legend>
<span class="form-check form-switch">
<small>
<input type=checkbox name=IsEpisode id=isEpisode class=form-check-input value=true
data-bs-toggle=collapse data-bs-target=#episodeItems onclick="Admin.toggleEpisodeFields()"
{%- if model.is_episode %} checked{% endif %}>
</small>
<label for=isEpisode>Podcast Episode</label>
</span>
</legend>
<div id=episodeItems class="container p-0 collapse{% if model.is_episode %} show{% endif %}">
<div class=row>
<div class="col-12 col-md-8 pb-3">
<div class=form-floating>
<input type=text name=Media id=media class=form-control placeholder=Media required
value="{{ model.media }}">
<label for=media>Media File</label>
<div class=form-text>
Relative URL will be appended to base media path (if set) or served from this web log
</div>
</div>
</div>
<div class="col-12 col-md-4 pb-3">
<div class=form-floating>
<input type=text name=MediaType id=mediaType class=form-control placeholder="Media Type"
value="{{ model.media_type }}">
<label for=mediaType>Media MIME Type</label>
<div class=form-text>Optional; overrides podcast default</div>
</div>
</div>
</div>
<div class="row pb-3">
<div class=col>
<div class=form-floating>
<input type=number name=Length id=length class=form-control placeholder=Length required
value="{{ model.length }}">
<label for=length>Media Length (bytes)</label>
<div class=form-text>TODO: derive from above file name</div>
</div>
</div>
<div class=col>
<div class=form-floating>
<input type=text name=Duration id=duration class=form-control placeholder=Duration
value="{{ model.duration }}">
<label for=duration>Duration</label>
<div class=form-text>Recommended; enter in <code>HH:MM:SS</code> format</div>
</div>
</div>
</div>
<div class="row pb-3">
<div class=col>
<div class=form-floating>
<input type=text name=Subtitle id=subtitle class=form-control placeholder=Subtitle
value="{{ model.subtitle }}">
<label for=subtitle>Subtitle</label>
<div class=form-text>Optional; a subtitle for this episode</div>
</div>
</div>
</div>
<div class=row>
<div class="col-12 col-md-8 pb-3">
<div class=form-floating>
<input type=text name=ImageUrl id=imageUrl class=form-control placeholder="Image URL"
value="{{ model.image_url }}">
<label for=imageUrl>Image URL</label>
<div class=form-text>
Optional; overrides podcast default; relative URL served from this web log
</div>
</div>
</div>
<div class="col-12 col-md-4 pb-3">
<div class=form-floating>
<select name=Explicit id=explicit class=form-control>
{% for exp_value in explicit_values %}
<option value="{{ exp_value[0] }}"{% if model.explicit == exp_value[0] %} selected{% endif -%}>
{{ exp_value[1] }}
</option>
{% endfor %}
</select>
<label for=explicit>Explicit Rating</label>
<div class=form-text>Optional; overrides podcast default</div>
</div>
</div>
</div>
<div class=row>
<div class="col-12 col-md-8 pb-3">
<div class="form-text">Chapters</div>
<div class="form-check form-check-inline">
<input type=radio name=ChapterSource id=chapterSourceNone value=none
class=form-check-input{% if model.chapter_source == "none" %} checked{% endif %}
onclick="Admin.setChapterSource('none')">
<label for=chapterSourceNone>None</label>
</div>
<div class="form-check form-check-inline">
<input type=radio name=ChapterSource id=chapterSourceInternal value=internal
class=form-check-input{% if model.chapter_source == "internal" %} checked{% endif %}
onclick="Admin.setChapterSource('internal')">
<label for=chapterSourceInternal>Defined Here</label>
</div>
<div class="form-check form-check-inline">
<input type=radio name=ChapterSource id=chapterSourceExternal value=none
class=form-check-input{% if model.chapter_source == "external" %} checked{% endif %}
onclick="Admin.setChapterSource('external')">
<label for=chapterSourceExternal>Separate File</label>
</div>
</div>
<div class="col-md-4 d-flex justify-content-center">
<div class="form-check form-switch align-self-center pb-3">
<input type=checkbox name=ContainsWaypoints id=containsWaypoints class=form-check-input
value=true{% if model.contains_waypoints %} checked{% endif %}>
<label for=containsWaypoints>Chapters contain waypoints</label>
</div>
</div>
</div>
<div class=row>
<div class="col-12 col-md-8 pb-3">
<div class=form-floating>
<input type=text name=ChapterFile id=chapterFile class=form-control placeholder="Chapter File"
value="{{ model.chapter_file }}">
<label for=chapterFile>Chapter File</label>
<div class=form-text>Relative URL served from this web log</div>
</div>
</div>
<div class="col-12 col-md-4 pb-3">
<div class=form-floating>
<input type=text name=ChapterType id=chapterType class=form-control placeholder="Chapter Type"
value="{{ model.chapter_type }}">
<label for=chapterType>Chapter MIME Type</label>
<div class=form-text>
Optional; <code>application/json+chapters</code> assumed if chapter file ends with
<code>.json</code>
</div>
</div>
</div>
</div>
<div class=row>
<div class="col-12 col-md-8 pb-3">
<div class=form-floating>
<input type=text name=TranscriptUrl id=transcriptUrl class=form-control placeholder="Transcript URL"
value="{{ model.transcript_url }}" onkeyup="Admin.requireTranscriptType()">
<label for=transcriptUrl>Transcript URL</label>
<div class=form-text>Optional; relative URL served from this web log</div>
</div>
</div>
<div class="col-12 col-md-4 pb-3">
<div class=form-floating>
<input type=text name=TranscriptType id=transcriptType class=form-control
placeholder="Transcript Type" value="{{ model.transcript_type }}"
{%- if model.transcript_url != "" %} required{% endif %}>
<label for=transcriptType>Transcript MIME Type</label>
<div class=form-text>Required if transcript URL provided</div>
</div>
</div>
</div>
<div class="row pb-3">
<div class=col>
<div class=form-floating>
<input type=text name=TranscriptLang id=transcriptLang class=form-control
placeholder="Transcript Language" value="{{ model.transcript_lang }}">
<label for=transcriptLang>Transcript Language</label>
<div class=form-text>Optional; overrides podcast default</div>
</div>
</div>
<div class="col d-flex justify-content-center">
<div class="form-check form-switch align-self-center pb-3">
<input type=checkbox name=TranscriptCaptions id=transcriptCaptions class=form-check-input
value=true{% if model.transcript_captions %} checked{% endif %}>
<label for=transcriptCaptions>This is a captions file</label>
</div>
</div>
</div>
<div class="row pb-3">
<div class="col col-md-4">
<div class=form-floating>
<input type=number name=SeasonNumber id=seasonNumber class=form-control placeholder="Season Number"
value="{{ model.season_number }}">
<label for=seasonNumber>Season Number</label>
<div class=form-text>Optional</div>
</div>
</div>
<div class="col col-md-8">
<div class=form-floating>
<input type=text name=SeasonDescription id=seasonDescription class=form-control
placeholder="Season Description" maxlength=128 value="{{ model.season_description }}">
<label for=seasonDescription>Season Description</label>
<div class=form-text>Optional</div>
</div>
</div>
</div>
<div class="row pb-3">
<div class="col col-md-4">
<div class=form-floating>
<input type=number name=EpisodeNumber id=episodeNumber class=form-control step=0.01
placeholder="Episode Number" value="{{ model.episode_number }}">
<label for=episodeNumber>Episode Number</label>
<div class=form-text>Optional; up to 2 decimal points</div>
</div>
</div>
<div class="col col-md-8">
<div class=form-floating>
<input type=text name=EpisodeDescription id=episodeDescription class=form-control
placeholder="Episode Description" maxlength=128 value="{{ model.episode_description }}">
<label for=episodeDescription>Episode Description</label>
<div class=form-text>Optional</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => Admin.toggleEpisodeFields())
</script>
</fieldset>
<fieldset class=pb-3>
<legend>
Metadata
<button type=button class="btn btn-sm btn-secondary" data-bs-toggle=collapse
data-bs-target="#metaItemContainer">
show
</button>
</legend>
<div id=metaItemContainer class=collapse>
<div id=metaItems class=container>
{%- for meta in metadata %}
<div id="meta_{{ meta[0] }}" class="row mb-3">
<div class="col-1 text-center align-self-center">
<button type=button class="btn btn-sm btn-danger" onclick="Admin.removeMetaItem({{ meta[0] }})">
&minus;
</button>
</div>
<div class=col-3>
<div class=form-floating>
<input type=text name=MetaNames id="metaNames_{{ meta[0] }}" class=form-control
placeholder=Name value="{{ meta[1] }}">
<label for="metaNames_{{ meta[0] }}">Name</label>
</div>
</div>
<div class=col-8>
<div class=form-floating>
<input type=text name=MetaValues id="metaValues_{{ meta[0] }}" class=form-control
placeholder=Value value="{{ meta[2] }}">
<label for="metaValues_{{ meta[0] }}">Value</label>
</div>
</div>
</div>
{% endfor -%}
</div>
<button type=button class="btn btn-sm btn-secondary" onclick="Admin.addMetaItem()">Add an Item</button>
<script>
document.addEventListener("DOMContentLoaded", () => Admin.setNextMetaIndex({{ metadata | size }}))
</script>
</div>
</fieldset>
{% if model.status == "Published" %}
<fieldset class=pb-3>
<legend>Maintenance</legend>
<div class=container>
<div class=row>
<div class="col align-self-center">
<div class="form-check form-switch pb-2">
<input type=checkbox name=SetPublished id=setPublished class=form-check-input value=true>
<label for=setPublished class=form-check-label>Set Published Date</label>
</div>
</div>
<div class=col-4>
<div class=form-floating>
<input type=datetime-local name=PubOverride id=pubOverride class=form-control
placeholder="Override Date"
{%- if model.pub_override -%}
value="{{ model.pub_override | date: "yyyy-MM-dd\THH:mm" }}"
{%- endif %}>
<label for=pubOverride class=form-label>Published On</label>
</div>
</div>
<div class="col-5 align-self-center">
<div class="form-check form-switch pb-2">
<input type=checkbox name=SetUpdated id=setUpdated class=form-check-input value=true>
<label for=setUpdated class=form-check-label>
Purge revisions and<br>set as updated date as well
</label>
</div>
</div>
</div>
</div>
</fieldset>
{% endif %}
</div>
<div class="col-12 col-lg-3">
<div class="form-floating pb-3">
<select name=Template id=template class=form-control>
{% for tmpl in templates -%}
<option value="{{ tmpl[0] }}"{% if model.template == tmpl[0] %} selected{% endif %}>
{{ tmpl[1] }}
</option>
{%- endfor %}
</select>
<label for=template>Post Template</label>
</div>
<fieldset>
<legend>Categories</legend>
{% for cat in categories %}
<div class=form-check>
<input type=checkbox name=CategoryIds id="categoryId_{{ cat.id }}" class=form-check-input
value="{{ cat.id }}"{% if model.category_ids contains cat.id %} checked{% endif %}>
<label for="categoryId_{{ cat.id }}" class=form-check-label
{%- if cat.description %} title="{{ cat.description.value | strip_html | escape }}"{% endif %}>
{%- for it in cat.parent_names %}&nbsp; &rang; &nbsp;{% endfor %}{{ cat.name }}
</label>
</div>
{% endfor %}
</fieldset>
</div>
</div>
</div>
</form>
</article>
<script>window.setTimeout(() => Admin.toggleEpisodeFields(), 500)</script>

View File

@ -217,13 +217,13 @@ this.Admin = {
* @param {"none"|"internal"|"external"} src The source for chapters for this episode
*/
setChapterSource(src) {
document.getElementById("containsWaypoints").disabled = src === "none"
document.getElementById("ContainsWaypoints").disabled = src === "none"
const isDisabled = src === "none" || src === "internal"
const chapterFile = document.getElementById("chapterFile")
const chapterFile = document.getElementById("ChapterFile")
chapterFile.disabled = isDisabled
chapterFile.required = !isDisabled
document.getElementById("chapterType").disabled = isDisabled
const link = document.getElementById("chapterEditLink")
document.getElementById("ChapterType").disabled = isDisabled
const link = document.getElementById("ChapterEditLink")
if (link) link.style.display = src === "none" || src === "external" ? "none" : ""
},
@ -231,13 +231,13 @@ this.Admin = {
* Enable or disable podcast fields
*/
toggleEpisodeFields() {
const disabled = !document.getElementById("isEpisode").checked
const disabled = !document.getElementById("IsEpisode").checked
let fields = [
"media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "transcriptUrl", "transcriptType",
"transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription", "episodeNumber", "episodeDescription"
"Media", "MediaType", "Length", "Duration", "Subtitle", "ImageUrl", "Explicit", "TranscriptUrl", "TranscriptType",
"TranscriptLang", "TranscriptCaptions", "SeasonNumber", "SeasonDescription", "EpisodeNumber", "EpisodeDescription"
]
if (disabled) {
fields.push("chapterFile", "chapterType", "containsWaypoints")
fields.push("ChapterFile", "ChapterType", "ContainsWaypoints")
} else {
const src = [...document.getElementsByName("ChapterSource")].filter(it => it.checked)[0].value
this.setChapterSource(src)
@ -302,7 +302,7 @@ this.Admin = {
* Require transcript type if transcript URL is present
*/
requireTranscriptType() {
document.getElementById("transcriptType").required = document.getElementById("transcriptUrl").value.trim() !== ""
document.getElementById("TranscriptType").required = document.getElementById("TranscriptUrl").value.trim() !== ""
},
/**