Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ _site
.sass-cache
.jekyll-metadata
public/
tests/public_test/
node_modules/
_gen/
.hugo_build.lock
Expand Down
3 changes: 0 additions & 3 deletions config/_default/params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,6 @@ ui:
# Sidebar generation is slow otherwise
sidebar_cache_limit: 100

# We have almost 200 attributes; don't truncate the sidebar to max 50 contents.
sidebar_menu_truncate: 0

# Set to true to disable breadcrumb navigation.
breadcrumb_disable: false

Expand Down
50 changes: 50 additions & 0 deletions config/testing/hugo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Hugo testing environment configuration
#
# Used exclusively when running the bibliography JSON-LD test suite:
#
# hugo --environment testing --destination tests/public_test
#
# This environment mounts the test fixtures from tests/fixtures/bibliography/
# alongside the real bibliography content, so the JSON-LD template is exercised
# against controlled data without those pages ever appearing in a production build.
#
# The module.mounts block must re-declare all standard project-level mounts
# because adding any mount replaces Hugo's defaults for the project.
# Theme (Docsy) mounts defined within that module are unaffected.

baseURL: "http://localhost/"

publishDir: tests/public_test

# Broken refs in unrelated content files must not block the test build.
refLinksErrorLevel: WARNING

# Explicit mounts (see module section below) require this to be re-stated
# so that English content URLs do not gain an /en/ prefix.
defaultContentLanguageInSubdir: false

module:
mounts:
# ── Standard project mounts (preserve defaults) ──────────────────────────
# Mount content/en/ directly as the English language content root so that
# URLs are generated without an /en/ prefix (matching production behavior).
- source: content/en
target: content
lang: en
- source: static
target: static
- source: layouts
target: layouts
- source: data
target: data
- source: assets
target: assets
- source: i18n
target: i18n
- source: archetypes
target: archetypes
# ── Test fixtures ─────────────────────────────────────────────────────────
# Mount alongside the real bibliography content for the English language.
- source: tests/fixtures/bibliography
target: content/history/bibliography
lang: en
1 change: 1 addition & 0 deletions content/en/history/bibliography/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ heading: Interlisp Bibliography
type: bibliography
cascade:
type: bibliography
toc_hide: true
weight: 5
aliases:
- /bibliography/
Expand Down
27 changes: 27 additions & 0 deletions layouts/_partials/bibliography-date.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{- /*
bibliography-date.html
Resolves a bibliography page's publication date.

The front-matter `date` param is a plain YYYY-MM-DD string produced by
bibSplit.pl. Hugo's built-in .Date can be a full datetime parsed from git
history; this partial prefers the explicit param when it is well-formed.

Accepts: page context (.)
Returns: dict
dateISO – YYYY-MM-DD string; "0001-01-01" when no date is known
hasDate – true when dateISO is not the zero value
dateLabel – .Params.readabledate when present, otherwise dateISO
*/ -}}
{{- $dateTime := .Date -}}
{{- with .Params.date -}}
{{- $iso := trim . " " -}}
{{- if and (ne $iso "") (findRE `^\d{4}-\d{2}-\d{2}$` $iso) -}}
{{- $dateTime = time $iso -}}
{{- end -}}
{{- end -}}
{{- $dateISO := $dateTime.Format "2006-01-02" -}}
{{- return dict
"dateISO" $dateISO
"hasDate" (ne $dateISO "0001-01-01")
"dateLabel" (or .Params.readabledate $dateISO)
-}}
75 changes: 49 additions & 26 deletions layouts/_partials/bibliography-json-ld.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,13 @@
*/ -}}
{{- if eq .Type "bibliography" -}}

{{- /* ── Date (resolved once, used by the IsPage branch below) ───────────── */ -}}
{{- $date := partial "bibliography-date.html" . -}}

{{- if .IsPage -}}

{{- /* ── Date: mirror the logic in single.html ──────────────────────────── */ -}}
{{- $dateTime := .Date -}}
{{- with .Params.date -}}
{{- $iso := trim . " " -}}
{{- if and (ne $iso "") (findRE `^\d{4}-\d{2}-\d{2}$` $iso) -}}
{{- $dateTime = time $iso -}}
{{- end -}}
{{- end -}}
{{- $dateISO := $dateTime.Format "2006-01-02" -}}
{{- $hasDate := ne $dateISO "0001-01-01" -}}
{{- $dateISO := $date.dateISO -}}
{{- $hasDate := $date.hasDate -}}

{{- /* ── Map item_type → Schema.org @type ──────────────────────────────── */ -}}
{{- $itemType := .Params.item_type | default "document" -}}
Expand Down Expand Up @@ -91,11 +86,36 @@
{{- with .Params.url_source -}}{{- $sameAs = append (trim . " ") $sameAs -}}{{- end -}}
{{- with .Params.zotero_url -}}{{- $sameAs = append (trim . " ") $sameAs -}}{{- end -}}

{{- /* ── CS Concepts: about[] + keywords[] ────────────────────────────
Reads the `concepts:` list from front matter (list of concept IDs).
Looks each ID up in data/bibliography/concepts.json to get the name.
Emits schema:about (array of DefinedTerm) and schema:keywords (string).
*/ -}}
{{- $conceptData := site.Data.bibliography.concepts -}}
{{- $conceptIds := .Params.concepts -}}
{{- $aboutObjs := slice -}}
{{- $keywordNames := slice -}}
{{- if reflect.IsSlice $conceptIds -}}
{{- range $conceptIds -}}
{{- $id := trim . " " -}}
{{- $concept := index $conceptData $id -}}
{{- if $concept -}}
{{- $aboutObjs = append (dict
"@type" "DefinedTerm"
"@id" (printf "https://interlisp.org/concepts/#%s" $id)
"name" $concept.name
"inDefinedTermSet" "https://interlisp.org/data/cs-concepts.jsonld"
) $aboutObjs -}}
{{- $keywordNames = append $concept.name $keywordNames -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{- /* ── Base JSON-LD object (properties common to all types) ─────────── */ -}}
{{- $ld := dict
"@context" "https://schema.org"
"@type" $schemaType
"name" .Title
"name" (.Title | strings.TrimRight "\n ")
"url" .Permalink
"inLanguage" "en-US"
-}}
Expand Down Expand Up @@ -124,6 +144,14 @@
{{- $ld = merge $ld (dict "sameAs" $sameAs) -}}
{{- end -}}

{{- if gt (len $aboutObjs) 0 -}}
{{- $ld = merge $ld (dict "about" $aboutObjs) -}}
{{- end -}}

{{- if gt (len $keywordNames) 0 -}}
{{- $ld = merge $ld (dict "keywords" (delimit $keywordNames ", ")) -}}
{{- end -}}

{{- /* ── Patent: additionalType + structured identifiers ──────────────── */ -}}
{{- if eq $itemType "patent" -}}
{{- $ld = merge $ld (dict "additionalType" "Patent") -}}
Expand All @@ -140,10 +168,10 @@
{{- end -}}

{{- with .Params.assignee -}}
{{- $ld = merge $ld (dict "funder" (dict "@type" "Organization" "name" .)) -}}
{{- $ld = merge $ld (dict "copyrightHolder" (dict "@type" "Organization" "name" .)) -}}
{{- end -}}
{{- with .Params.issuing_authority -}}
{{- $ld = merge $ld (dict "countryOfOrigin" (dict "@type" "Country" "name" .)) -}}
{{- $ld = merge $ld (dict "validIn" (dict "@type" "DefinedRegion" "name" .)) -}}
{{- end -}}
{{- end -}}

Expand All @@ -160,21 +188,21 @@
{{- /* ── Conference paper ─────────────────────────────────────────────── */ -}}
{{- if eq $itemType "paper-conference" -}}
{{- with .Params.proceedings_title -}}
{{- $ld = merge $ld (dict "isPartOf" (dict "@type" "Periodical" "name" .)) -}}
{{- $ld = merge $ld (dict "isPartOf" (dict "@type" "Book" "name" .)) -}}
{{- end -}}
{{- with .Params.publisher -}}
{{- $ld = merge $ld (dict "publisher" (dict "@type" "Organization" "name" .)) -}}
{{- end -}}
{{- with .Params.place -}}
{{- $ld = merge $ld (dict "locationCreated" (dict "@type" "Place" "name" .)) -}}
{{- end -}}
{{- end -}}

{{- /* ── Book ─────────────────────────────────────────────────────────── */ -}}
{{- if eq $itemType "book" -}}
{{- with .Params.publisher -}}
{{- $ld = merge $ld (dict "publisher" (dict "@type" "Organization" "name" .)) -}}
{{- end -}}
{{- with .Params.place -}}
{{- $ld = merge $ld (dict "locationCreated" .) -}}
{{- end -}}
{{- end -}}

{{- /* ── Chapter ──────────────────────────────────────────────────────── */ -}}
Expand All @@ -194,10 +222,10 @@
{{- with .Params.publisher -}}
{{- $ld = merge $ld (dict "publisher" (dict "@type" "Organization" "name" .)) -}}
{{- end -}}
{{- with .Params.reportType -}}
{{- with .Params.report_type -}}
{{- $ld = merge $ld (dict "additionalType" .) -}}
{{- end -}}
{{- with .Params.reportNumber -}}
{{- with .Params.report_number -}}
{{- $ld = merge $ld (dict "identifier" (dict "@type" "PropertyValue" "propertyID" "report-number" "value" .)) -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -243,15 +271,12 @@
{{- end -}}
{{- end -}}

<script type="application/ld+json">
{{- $ld | jsonify (dict "indent" " ") | safeJS -}}
</script>
{{- printf "<script type=\"application/ld+json\">\n%s\n</script>" ($ld | jsonify (dict "indent" " ")) | safeHTML -}}

{{- else if .IsSection -}}

{{- /* ── COLLECTION PAGE (bibliography index) ──────────────────────────── */ -}}
{{- $pages := .RegularPages -}}
{{- $pages = sort $pages "Date" "desc" -}}
{{- $pages = sort $pages "Title" "asc" -}}

{{- $itemList := slice -}}
Expand Down Expand Up @@ -281,9 +306,7 @@
)
-}}

<script type="application/ld+json">
{{- $ld | jsonify (dict "indent" " ") | safeJS -}}
</script>
{{- printf "<script type=\"application/ld+json\">\n%s\n</script>" ($ld | jsonify (dict "indent" " ")) | safeHTML -}}

{{- end -}}

Expand Down
14 changes: 3 additions & 11 deletions layouts/bibliography/single.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,10 @@ <h1>{{ .Title }}</h1>
</p>
{{- end -}}
<p>
{{- $dateTime := .Date -}}
{{- with .Params.date -}}
{{- $isoDate := trim . " " -}}
{{- if and (ne $isoDate "") (findRE `^\d{4}-\d{2}-\d{2}` $isoDate) -}}
{{- $dateTime = time $isoDate -}}
{{- end -}}
{{- end -}}
{{- $dateISO := $dateTime.Format "2006-01-02" -}}
{{- $dateLabel := or .Params.readabledate $dateISO -}}
{{- $date := partial "bibliography-date.html" . -}}
{{- /* Don't display bogus date */ -}}
{{- if ne $dateISO "0001-01-01" -}}
<time datetime="{{ $dateISO }}">{{ $dateLabel }}</time>
{{- if $date.hasDate -}}
<time datetime="{{ $date.dateISO }}">{{ $date.dateLabel }}</time>
{{- end -}}
</p>
<p>
Expand Down
23 changes: 13 additions & 10 deletions scripts/bibSplit.pl
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ sub sanitize_text {

my $type = $obj->{type} // '';

# Titles can have colons and other special characters. Place YAML keyword on one line
# and follow it with the title indented on subsequent line
# Titles can have colons and other special characters. Use a YAML double-quoted scalar
# so the decoded value has no trailing newline (the literal-block `|` style would add one).
my $itemTitle = sanitize_text($obj->{title} // '');
my $title = $itemTitle eq '' ? "title: ''" : "title: |\n $itemTitle\n";
(my $escapedTitle = $itemTitle) =~ s/\\/\\\\/g;
$escapedTitle =~ s/"/\\"/g;
my $title = $itemTitle eq '' ? "title: ''" : "title: \"$escapedTitle\"";

# Abstracts can be multi-line and contain multiple paragraphs. Place YAML keyword on
# one line and follow it with the abstract indented on subsequent lines.
Expand All @@ -78,25 +80,25 @@ sub sanitize_text {
my $itemAuthors = '';
if (ref($obj->{authorsFormatted}) eq 'ARRAY' && @{$obj->{authorsFormatted}}) {
$itemAuthors = "\n";
for my $a (@{$obj->{authorsFormatted}}) {
for my $author (@{$obj->{authorsFormatted}}) {
# The encode_json is where extended unicode chars get corrupted, e.g., "Emanuelson, Pär"
# There may be other things that now don't work!!
# my $quoted = encode_json($a // '');
# sanitize_text seems to handle them correctly
my $san = sanitize_text($a // '');
$itemAuthors .= " - \"$san\"\n";
my $san_author = sanitize_text($author // '');
$itemAuthors .= " - \"$san_author\"\n";
}
$itemAuthors =~ s/\n$//u; # strip trailing newline
}

my $itemEditors = '';
if (ref($obj->{editorsFormatted}) eq 'ARRAY' && @{$obj->{editorsFormatted}}) {
$itemEditors = "\n";
for my $a (@{$obj->{editorsFormatted}}) {
for my $editor (@{$obj->{editorsFormatted}}) {
# as above...
# my $quoted = encode_json($a // '');
my $san = sanitize_text($a // '');
$itemEditors .= " - \"$san\"\n";
# my $quoted = encode_json($editor // '');
my $san_editor = sanitize_text($editor // '');
$itemEditors .= " - \"$san_editor\"\n";
}
$itemEditors =~ s/\n$//u; # strip trailing newline
}
Expand Down Expand Up @@ -185,6 +187,7 @@ sub sanitize_text {

$extraFields
tags:
concepts:
url_source: $urlSource
zotero_url: "https://www.zotero.org/groups/2914042/items/$key"

Expand Down
20 changes: 20 additions & 0 deletions tests/fixtures/bibliography/ZTEST-BLOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: "Test Blog Post"
date: 2021-08-20
readabledate: 2021-08-20
type: bibliography
item_type: post-weblog
authors:
- "Blogger, Test"
editors:
abstract: |
A test abstract for a blog post used by automated test fixtures.

blog_title: "Test Tech Blog"

tags:
url_source: "https://example.com/blog/post"
zotero_url: "https://www.zotero.org/groups/test/items/ZTESTBLG"

lastmod: 2023-08-01T00:00:00Z
---
20 changes: 20 additions & 0 deletions tests/fixtures/bibliography/ZTEST-BOOK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: "Test Book Title"
date: 1995-01-01
readabledate: 1995
type: bibliography
item_type: book
authors:
- "Author, Test"
editors:
abstract: |
A test abstract for a book used by automated test fixtures.

publisher: "Test Publisher"

tags:
url_source: "https://example.com/book"
zotero_url: "https://www.zotero.org/groups/test/items/ZTESTBOOK"

lastmod: 2022-01-01T00:00:00Z
---
Loading