V2 #1

Merged
danieljsummers merged 102 commits from v2 into main 2022-06-23 00:35:12 +00:00
7 changed files with 137 additions and 35 deletions
Showing only changes of commit 7a1d438d68 - Show all commits

View File

@ -610,7 +610,7 @@ type PostListItem =
tags : string list
/// Metadata for the post
meta : MetaItem list
metadata : MetaItem list
}
/// Create a post list item from a post
@ -627,7 +627,7 @@ type PostListItem =
text = if extra = "" then post.text else post.text.Replace ("href=\"/", $"href=\"{extra}/")
categoryIds = post.categoryIds |> List.map CategoryId.toString
tags = post.tags
meta = post.metadata
metadata = post.metadata
}

View File

@ -122,21 +122,21 @@ let private addEpisode webLog (feed : CustomFeed) (post : Post) (item : Syndicat
let meta name = post.metadata |> List.tryFind (fun it -> it.name = name)
let value (item : MetaItem) = item.value
let epMediaUrl =
match (meta >> Option.get >> value) "media" with
match (meta >> Option.get >> value) "episode_media_file" with
| link when link.StartsWith "http" -> link
| link -> WebLog.absoluteUrl webLog (Permalink link)
let epMediaType =
match meta "mediaType", podcast.defaultMediaType with
match meta "episode_media_type", podcast.defaultMediaType with
| Some epType, _ -> Some epType.value
| None, Some defType -> Some defType
| _ -> None
let epImageUrl =
match defaultArg ((meta >> Option.map value) "image") (Permalink.toString podcast.imageUrl) with
match defaultArg ((meta >> Option.map value) "episode_image") (Permalink.toString podcast.imageUrl) with
| link when link.StartsWith "http" -> link
| link -> WebLog.absoluteUrl webLog (Permalink link)
let epExplicit =
try
(meta >> Option.map (value >> ExplicitRating.parse)) "explicit"
(meta >> Option.map (value >> ExplicitRating.parse)) "episode_explicit"
|> Option.defaultValue podcast.explicit
|> ExplicitRating.toString
with :? ArgumentException -> ExplicitRating.toString podcast.explicit
@ -144,8 +144,8 @@ let private addEpisode webLog (feed : CustomFeed) (post : Post) (item : Syndicat
let xmlDoc = XmlDocument ()
let enclosure = xmlDoc.CreateElement "enclosure"
enclosure.SetAttribute ("url", epMediaUrl)
meta "length" |> Option.iter (fun it -> enclosure.SetAttribute ("length", it.value))
epMediaType |> Option.iter (fun typ -> enclosure.SetAttribute ("type", typ))
meta "episode_media_length" |> Option.iter (fun it -> enclosure.SetAttribute ("length", it.value))
epMediaType |> Option.iter (fun typ -> enclosure.SetAttribute ("type", typ))
item.ElementExtensions.Add enclosure
item.ElementExtensions.Add ("creator", Namespace.dc, podcast.displayedAuthor)
@ -153,8 +153,10 @@ let private addEpisode webLog (feed : CustomFeed) (post : Post) (item : Syndicat
item.ElementExtensions.Add ("summary", Namespace.iTunes, stripHtml post.text)
item.ElementExtensions.Add ("image", Namespace.iTunes, epImageUrl)
item.ElementExtensions.Add ("explicit", Namespace.iTunes, epExplicit)
meta "subtitle" |> Option.iter (fun it -> item.ElementExtensions.Add ("subtitle", Namespace.iTunes, it.value))
meta "duration" |> Option.iter (fun it -> item.ElementExtensions.Add ("duration", Namespace.iTunes, it.value))
meta "episode_subtitle"
|> Option.iter (fun it -> item.ElementExtensions.Add ("subtitle", Namespace.iTunes, it.value))
meta "episode_duration"
|> Option.iter (fun it -> item.ElementExtensions.Add ("duration", Namespace.iTunes, it.value))
if post.metadata |> List.exists (fun it -> it.name = "chapter") then
try

View File

@ -231,11 +231,14 @@ let edit postId : HttpHandler = fun next ctx -> task {
}
match result with
| Some (title, post) ->
let! cats = Data.Category.findAllForView webLog.id conn
let! cats = Data.Category.findAllForView webLog.id conn
let model = EditPostModel.fromPost webLog post
return!
Hash.FromAnonymousObject {|
csrf = csrfToken ctx
model = EditPostModel.fromPost webLog post
model = model
metadata = Array.zip model.metaNames model.metaValues
|> Array.mapi (fun idx (name, value) -> [| string idx; name; value |])
page_title = title
categories = cats
|}

View File

@ -55,8 +55,14 @@
</button>
</legend>
<div id="metaItemContainer" class="collapse">
{%- assign has_media = model.metaNames | where: "episode_media_file" | size -%}
{%- unless has_media == 0 %}
<button class="btn btn-sm btn-secondary" onclick="Admin.addPodcastFields()" id="addPodcastFieldButton">
Add Podcast Episode Fields
</button>
{%- endunless %}
<div id="metaItems" class="container">
{%- for meta in model.metadata %}
{%- 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] }})">
@ -82,7 +88,7 @@
</div>
<button type="button" class="btn btn-sm btn-secondary" onclick="Admin.addMetaItem()">Add an Item</button>
<script>
document.addEventListener("DOMContentLoaded", () => Admin.setNextMetaIndex({{ model.metadata | size }}))
document.addEventListener("DOMContentLoaded", () => Admin.setNextMetaIndex({{ metadata | size }}))
</script>
</div>
</fieldset>

View File

@ -33,8 +33,8 @@
#[i.fa.fa-clock-o(title='Clock' aria-hidden='true')] #[= readingTime(post.content, 'minutes', 175)]
{% endcomment %}
</p>
{%- assign media = post.meta | value: "media" -%}
{%- unless media == "-- media not found --" %}
{%- assign media = post.metadata | value: "episode_media_file" -%}
{%- unless media == "-- episode_media_file not found --" %}
<aside class="podcast">
<p class="text-center"><strong>Listen While<br>You Read</strong></p>
<audio controls onplaying="awftw.countPlay('{{ media }}')">

View File

@ -2,8 +2,8 @@
<div class="content">
<article class="item">
<h1 class="item-heading">{{ post.title }}</h1>
{% assign media = post.meta | value: "media" %}
{%- unless media == "-- media not found --" %}
{% assign media = post.meta | value: "episode_media_file" %}
{%- unless media == "-- episode_media_file not found --" %}
<aside class="podcast">
<p class="text-center"><strong>Listen While<br>You Read</strong></p>
<audio controls onplaying="awftw.countPlay('{{ media }}')">

View File

@ -22,74 +22,138 @@
},
/**
* Add a new row for metadata entry
* Create a metadata remove button
* @returns {HTMLDivElement} The column with the remove button
*/
addMetaItem() {
// Remove button
createMetaRemoveColumn() {
const removeBtn = document.createElement("button")
removeBtn.type = "button"
removeBtn.className = "btn btn-sm btn-danger"
removeBtn.innerHTML = "&minus;"
removeBtn.setAttribute("onclick", `Admin.removeMetaItem(${this.nextMetaIndex})`)
const removeCol = document.createElement("div")
removeCol.className = "col-1 text-center align-self-center"
removeCol.appendChild(removeBtn)
// Name
return removeCol
},
/**
* Create a metadata name field
* @returns {HTMLInputElement} The name input element
*/
createMetaNameField() {
const nameField = document.createElement("input")
nameField.type = "text"
nameField.name = "metaNames"
nameField.id = `metaNames_${this.nextMetaIndex}`
nameField.className = "form-control"
nameField.placeholder = "Name"
return nameField
},
/**
* Create a metadata name column using the given name input field
* @param {HTMLInputElement} field The name field for the column
* @returns {HTMLDivElement} The name column
*/
createMetaNameColumn(field) {
const nameLabel = document.createElement("label")
nameLabel.htmlFor = nameField.id
nameLabel.innerText = nameField.placeholder
nameLabel.htmlFor = field.id
nameLabel.innerText = field.placeholder
const nameFloat = document.createElement("div")
nameFloat.className = "form-floating"
nameFloat.appendChild(nameField)
nameFloat.appendChild(field)
nameFloat.appendChild(nameLabel)
const nameCol = document.createElement("div")
nameCol.className = "col-3"
nameCol.appendChild(nameFloat)
return nameCol
},
// Value
/**
* Create a metadata value field
* @returns {HTMLInputElement} The metadata value field
*/
createMetaValueField() {
const valueField = document.createElement("input")
valueField.type = "text"
valueField.name = "metaValues"
valueField.id = `metaValues_${this.nextMetaIndex}`
valueField.className = "form-control"
valueField.placeholder = "Value"
return valueField
},
/**
* Create a metadata value column using the given input field
* @param {HTMLInputElement} field The metadata value input field
* @param {string|undefined} hintText Text to be added below the field
* @returns {HTMLDivElement} The value column
*/
createMetaValueColumn(field, hintText) {
const valueLabel = document.createElement("label")
valueLabel.htmlFor = valueField.id
valueLabel.innerText = valueField.placeholder
valueLabel.htmlFor = field.id
valueLabel.innerText = field.placeholder
const valueFloat = document.createElement("div")
valueFloat.className = "form-floating"
valueFloat.appendChild(valueField)
valueFloat.appendChild(field)
valueFloat.appendChild(valueLabel)
if (hintText) {
const valueHint = document.createElement("div")
valueHint.className = "form-text"
valueHint.innerText = hintText
valueFloat.appendChild(valueHint)
}
const valueCol = document.createElement("div")
valueCol.className = "col-8"
valueCol.appendChild(valueFloat)
// Put it all together
return valueCol
},
/**
* Construct and add a metadata item row
* @param {HTMLDivElement} removeCol The column with the remove button
* @param {HTMLDivElement} nameCol The column with the name field
* @param {HTMLDivElement} valueCol The column with the value field
*/
createMetaRow(removeCol, nameCol, valueCol) {
const newRow = document.createElement("div")
newRow.className = "row mb-3"
newRow.id = `meta_${this.nextMetaIndex}`
newRow.appendChild(removeCol)
newRow.appendChild(nameCol)
newRow.appendChild(valueCol)
document.getElementById("metaItems").appendChild(newRow)
document.getElementById(nameField.id).focus()
this.nextMetaIndex++
},
/**
* Add a new row for metadata entry
*/
addMetaItem() {
const nameField = this.createMetaNameField()
this.createMetaRow(
this.createMetaRemoveColumn(),
this.createMetaNameColumn(nameField),
this.createMetaValueColumn(this.createMetaValueField(), undefined))
document.getElementById(nameField.id).focus()
},
/**
* Add a new row for a permalink
@ -139,6 +203,33 @@
this.nextPermalink++
},
/**
* Add podcast episode metadata fields
*/
addPodcastFields() {
[ [ "episode_media_file", true, "required; relative URL will be appended to configured base media path" ],
[ "episode_media_length", true, "required; file size in bytes" ],
[ "episode_duration", false, "suggested; format is HH:MM:SS" ],
[ "episode_media_type", false, "optional; blank uses podcast default" ],
[ "episode_image", false, "optional; relative URLs are served from this web log" ],
[ "episode_subtitle", false, "optional" ],
[ "episode_explicit", false, "optional; blank uses podcast default"]
].forEach(([fieldName, isRequired, hintText]) => {
const nameField = this.createMetaNameField()
nameField.value = fieldName
nameField.readOnly = true
const valueField = this.createMetaValueField()
valueField.required = isRequired
this.createMetaRow(
this.createMetaRemoveColumn(),
this.createMetaNameColumn(nameField),
this.createMetaValueColumn(valueField, hintText))
})
document.getElementById("addPodcastFieldButton").remove()
},
/**
* Check to enable or disable podcast fields
*/