diff --git a/README.md b/README.md index 1aec6878..a0885234 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ disable_title = false [paige.page] disable_alert = false disable_authors = false +disable_auto_schema = false # Disable the automatic SEO JSON-LD schema generation disable_date = false disable_description = false disable_edit = false @@ -326,7 +327,44 @@ disable_series = false disable_title = false disable_toc = false +# paige.page.base_schema specifies the JSON-LD schema that all page schemas override. +# +# Example: +# +# [paige.page.base_schema] +# isAccessibleForFree = true +# isFamilyFriendly = true +# [paige.page.base_schema.publisher] +# "@type" = "Organization" +# name = "John Doe" +# url = "https://example.com" +base_schema = {} + +# paige.page.schemas is the page JSON-LD schemas. +# +# Examples: +# +# [paige.page.schemas] +# "@context" = "https://schema.org" +# "@type" = "Book" +# name = "My Book" +# url = "https://example.com" +schemas = [] + [paige.site] +# paige.site.base_schema specifies the JSON-LD schema that all site schemas override. +# +# Example: +# +# [paige.site.base_schema] +# isAccessibleForFree = true +# isFamilyFriendly = true +# [paige.site.base_schema.publisher] +# "@type" = "Organization" +# name = "John Doe" +# url = "https://example.com" +base_schema = {} + disable_breadcrumbs = false disable_copyright = false disable_credit = false @@ -335,6 +373,17 @@ disable_license = false disable_menu = false disable_title = false +# paige.site.schemas is the site JSON-LD schemas. +# +# Examples: +# +# [paige.site.schemas] +# "@context" = "https://schema.org" +# "@type" = "Organization" +# name = "Acme" +# url = "https://example.com" +schemas = [] + [paige.search] disable = false # Exclude the page from search ``` @@ -392,6 +441,18 @@ render = "never" The `email` and `url` parameters in the front matter of an author term page are used in feeds if present. +### SEO + +The "author", "description", and "keywords" meta tags are generated from the page parameters. +The keywords are the page parameters "keywords", "categories", and "tags". + +A JSON-LD script is generated from the page parameters, +which can be disabled with `paige.page.disable_auto_schema`. +Arbitrary schemas can be specified for the site with `paige.site.schemas` +or for pages with `paige.pages.schemas`, at the site or page level. +A base schema can be specified for site schemas with `paige.site.base_schema`, +and for page schemas with `paige.page.base_schema`. + ## Layouts ### Cloud diff --git a/layouts/partials/paige/schemas.html b/layouts/partials/paige/schemas.html new file mode 100644 index 00000000..8b7f2bb1 --- /dev/null +++ b/layouts/partials/paige/schemas.html @@ -0,0 +1,154 @@ +{{ $page := . }} + +{{ $auto := $page.Param "paige.page.disable_auto_schema" | not }} +{{ $pagebase := $page.Param "paige.page.base_schema" }} +{{ $sitebase := $page.Param "paige.site.base_schema" }} + +{{ range $page.Param "paige.site.schemas" }} + +{{ end }} + +{{ range $page.Param "paige.page.schemas" }} + +{{ end }} + +{{ if $auto }} + {{ $audios := $page.Params.audio | default slice}} + {{ $authors := $page.GetTerms "authors" }} + {{ $images := $page.Params.images | default slice }} + {{ $keywords := sort (union (union $page.Keywords $page.Params.tags) $page.Params.categories) }} + {{ $license := $page.Param "paige.license" | markdownify | plainify }} + {{ $schema := newScratch }} + {{ $videos := $page.Params.videos | default slice }} + + {{ $schema.Set "@context" "https://schema.org" }} + {{ $schema.Set "@type" "Article" }} + {{ $schema.Set "url" $page.Permalink }} + + {{ with $page.Summary }} + {{ $schema.Set "abstract" (. | plainify | chomp) }} + {{ end }} + + {{ with $page.Description }} + {{ $schema.Set "alternativeHeadline" (plainify .) }} + {{ end }} + + {{ with $page.CurrentSection.Title }} + {{ $schema.Set "articleSection" . }} + {{ end }} + + {{ with $audios }} + {{ $audioObjects := slice }} + + {{ range $audios }} + {{ $audioObjects = $audioObjects | append (dict "@type" "AudioObject" "url" .) }} + {{ end }} + + {{ $schema.Set "audio" $audioObjects }} + {{ end }} + + {{ with $authors }} + {{ $authorObjects := slice }} + + {{ range $authors }} + {{ $authoremail := cond (. | not | not) .Params.email "" }} + {{ $authorname := cond (. | not | not) .Params.name "" }} + {{ $authorurl := cond (. | not | not) .Params.url "" }} + + {{ if or $authoremail $authorname $authorurl }} + {{ $authorSchema := newScratch }} + + {{ with $authoremail }} + {{ $authorSchema.Set "email" . }} + {{ end }} + + {{ with $authorname }} + {{ $authorSchema.Set "name" . }} + {{ end }} + + {{ with $authorurl }} + {{ $authorSchema.Set "url" . }} + {{ end }} + + {{ $authorSchema.Set "@type" "Person" }} + {{ $authorObjects = $authorObjects | append $authorSchema.Values }} + {{ end }} + {{ end }} + + {{ $schema.Set "author" $authorObjects }} + {{ end }} + + {{ with site.Copyright }} + {{ $schema.Set "copyrightNotice" (plainify .) }} + {{ end }} + + {{ if and (not $page.Date.IsZero) (ne $page.Date $page.PublishDate) }} + {{ $schema.Set "dateCreated" $page.Date }} + {{ end }} + + {{ if not $page.Lastmod.IsZero }} + {{ $schema.Set "dateModified" $page.Lastmod }} + {{ end }} + + {{ with and (not $page.PublishDate.IsZero) (ne $page.Date $page.PublishDate) }} + {{ $schema.Set "datePublished" $page.PublishDate }} + {{ end }} + + {{ with $page.Description }} + {{ $schema.Set "description" (plainify .) }} + {{ end }} + + {{ if not $page.ExpiryDate.IsZero }} + {{ $schema.Set "expiresAt" $page.ExpiryDate }} + {{ end }} + + {{ with $page.Title }} + {{ $schema.Set "headline" (plainify .) }} + {{ end }} + + {{ with $images }} + {{ $imageObjects := slice }} + + {{ range $images }} + {{ $imageObjects = $imageObjects | append (dict "@type" "ImageObject" "url" .) }} + {{ end }} + + {{ $schema.Set "image" $imageObjects }} + {{ end }} + + {{ with site.Language }} + {{ $schema.Set "inLanguage" .Lang }} + {{ end }} + + {{ with $keywords }} + {{ $schema.Set "keywords" . }} + {{ end }} + + {{ with $license }} + {{ $schema.Set "license" (plainify .) }} + {{ end }} + + {{ with $page.Title }} + {{ $schema.Set "name" (plainify .) }} + {{ end }} + + {{ with $page.ReadingTime }} + {{ $schema.Set "timeRequired" (printf "PT%dM" .) }} + {{ end }} + + {{ with $videos }} + {{ $videoObjects := slice }} + + {{ range $videos }} + {{ $videoObjects = $videoObjects | append (dict "@type" "VideoObject" "url" .) }} + {{ end }} + + {{ $schema.Set "video" $videoObjects }} + {{ end }} + + {{ with $page.WordCount }} + {{ $schema.Set "wordCount" . }} + {{ end }} + + +{{ end }} diff --git a/layouts/partials/paige/scripts.html b/layouts/partials/paige/scripts.html index 0387444f..a4be8213 100644 --- a/layouts/partials/paige/scripts.html +++ b/layouts/partials/paige/scripts.html @@ -8,5 +8,6 @@ {{ end }} {{ partial "paige/analytics.html" $page }} +{{ partial "paige/schemas.html" $page }}