mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-20 21:31:32 +02:00
Merge commit '5be51ac3db225d5df501ed1fa1499c41d97dbf65'
This commit is contained in:
213
docs/content/en/functions/transform/PortableText.md
Normal file
213
docs/content/en/functions/transform/PortableText.md
Normal file
@@ -0,0 +1,213 @@
|
||||
---
|
||||
title: transform.PortableText
|
||||
description: Converts Portable Text to Markdown.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
returnType: string
|
||||
signatures: [transform.PortableText MAP]
|
||||
---
|
||||
|
||||
{{< new-in "0.145.0" />}}
|
||||
|
||||
[Portable Text](https://www.portabletext.org/) is a JSON structure that represent rich text content in the [Sanity](https://www.sanity.io/) CMS. In Hugo, this function is typically used in a [Content Adapter](https://gohugo.io/content-management/content-adapters/) that creates pages from Sanity data.
|
||||
|
||||
## Types supported
|
||||
|
||||
- `block` and `span`
|
||||
- `image`. Note that the image handling is currently very simple; we link to the `asset.url` using `asset.altText` as the image alt text and `asset.title` as the title. For more fine grained control you may want to process the images in a [image render hook](/render-hooks/images/).
|
||||
- `code` (see the [code-input](https://www.sanity.io/plugins/code-input) plugin). Code will be rendered as [fenced code blocks](/contribute/documentation/#fenced-code-blocks) with any file name provided passed on as a markdown attribute.
|
||||
|
||||
> [!note]
|
||||
> Since the Portable Text gets converted to Markdown before it gets passed to Hugo, rendering of links, headings, images and code blocks can be controlled with [Render Hooks](https://gohugo.io/render-hooks/).
|
||||
|
||||
## Example
|
||||
|
||||
### Content Adapter
|
||||
|
||||
```go-html-template {file="content/_content.gotmpl" copy=true}
|
||||
{{ $projectID := "mysanityprojectid" }}
|
||||
{{ $useCached := true }}
|
||||
{{ $api := "api" }}
|
||||
{{ if $useCached }}
|
||||
{{/* See https://www.sanity.io/docs/api-cdn */}}
|
||||
{{ $api = "apicdn" }}
|
||||
{{ end }}
|
||||
{{ $url := printf "https://%s.%s.sanity.io/v2021-06-07/data/query/production" $projectID $api }}
|
||||
|
||||
{{/* prettier-ignore-start */ -}}
|
||||
{{ $q := `*[_type == 'post']{
|
||||
title, publishedAt, summary, slug, body[]{
|
||||
...,
|
||||
_type == "image" => {
|
||||
...,
|
||||
asset->{
|
||||
_id,
|
||||
path,
|
||||
url,
|
||||
altText,
|
||||
title,
|
||||
description,
|
||||
metadata {
|
||||
dimensions {
|
||||
aspectRatio,
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}`
|
||||
}}
|
||||
{{/* prettier-ignore-end */ -}}
|
||||
{{ $body := dict "query" $q | jsonify }}
|
||||
{{ $opts := dict "method" "post" "body" $body }}
|
||||
{{ $r := resources.GetRemote $url $opts }}
|
||||
{{ $m := $r | transform.Unmarshal }}
|
||||
{{ $result := $m.result }}
|
||||
{{ range $result }}
|
||||
{{ if not .slug }}
|
||||
{{ continue }}
|
||||
{{ end }}
|
||||
{{ $markdown := transform.PortableText .body }}
|
||||
{{ $content := dict
|
||||
"mediaType" "text/markdown"
|
||||
"value" $markdown
|
||||
}}
|
||||
{{ $params := dict
|
||||
"portabletext" (.body | jsonify (dict "indent" " "))
|
||||
}}
|
||||
{{ $page := dict
|
||||
"content" $content
|
||||
"kind" "page"
|
||||
"path" .slug.current
|
||||
"title" .title
|
||||
"date" (.publishedAt | time )
|
||||
"summary" .summary
|
||||
"params" $params
|
||||
}}
|
||||
{{ $.AddPage $page }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### Sanity setup
|
||||
|
||||
Below outlines a suitable Sanity studio setup for the above example.
|
||||
|
||||
```ts {file="sanity.config.ts" copy=true}
|
||||
import {defineConfig} from 'sanity'
|
||||
import {structureTool} from 'sanity/structure'
|
||||
import {visionTool} from '@sanity/vision'
|
||||
import {schemaTypes} from './schemaTypes'
|
||||
import {media} from 'sanity-plugin-media'
|
||||
import {codeInput} from '@sanity/code-input'
|
||||
|
||||
export default defineConfig({
|
||||
name: 'default',
|
||||
title: 'my-sanity-project',
|
||||
|
||||
projectId: 'mysanityprojectid',
|
||||
dataset: 'production',
|
||||
|
||||
plugins: [structureTool(), visionTool(), media(),codeInput()],
|
||||
|
||||
schema: {
|
||||
types: schemaTypes,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Type/schema definition:
|
||||
|
||||
```ts {file="schemaTypes/postType.ts" copy=true}
|
||||
import {defineField, defineType} from 'sanity'
|
||||
|
||||
export const postType = defineType({
|
||||
name: 'post',
|
||||
title: 'Post',
|
||||
type: 'document',
|
||||
fields: [
|
||||
defineField({
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
validation: (rule) => rule.required(),
|
||||
}),
|
||||
defineField({
|
||||
name: 'summary',
|
||||
type: 'string',
|
||||
validation: (rule) => rule.required(),
|
||||
}),
|
||||
defineField({
|
||||
name: 'slug',
|
||||
type: 'slug',
|
||||
options: {source: 'title'},
|
||||
validation: (rule) => rule.required(),
|
||||
}),
|
||||
defineField({
|
||||
name: 'publishedAt',
|
||||
type: 'datetime',
|
||||
initialValue: () => new Date().toISOString(),
|
||||
validation: (rule) => rule.required(),
|
||||
}),
|
||||
defineField({
|
||||
name: 'body',
|
||||
type: 'array',
|
||||
of: [
|
||||
{
|
||||
type: 'block',
|
||||
},
|
||||
{
|
||||
type: 'image'
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
options: {
|
||||
language: 'css',
|
||||
languageAlternatives: [
|
||||
{title: 'HTML', value: 'html'},
|
||||
{title: 'CSS', value: 'css'},
|
||||
],
|
||||
withFilename: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
Note that the above requires some additional plugins to be installed:
|
||||
|
||||
```bash
|
||||
npm i sanity-plugin-media @sanity/code-input
|
||||
```
|
||||
|
||||
```ts {file="schemaTypes/index.ts" copy=true}
|
||||
import {postType} from './postType'
|
||||
|
||||
export const schemaTypes = [postType]
|
||||
```
|
||||
|
||||
## Server setup
|
||||
|
||||
Unfortunately, Sanity's API does not support [RFC 7234](https://tools.ietf.org/html/rfc7234) and their output changes even if the data has not. A recommended setup is therefore to use their cached `apicdn` endpoint (see above) and then set up a reasonable polling and file cache strategy in your Hugo configuration, e.g:
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
[HTTPCache]
|
||||
[[HTTPCache.polls]]
|
||||
disable = false
|
||||
low = '30s'
|
||||
high = '3m'
|
||||
[HTTPCache.polls.for]
|
||||
includes = ['https://*.*.sanity.io/**']
|
||||
|
||||
[caches.getresource]
|
||||
dir = ':cacheDir/:project'
|
||||
maxAge = "5m"
|
||||
{{< /code-toggle >}}
|
||||
|
||||
The polling above will be used when running the server/watch mode and rebuild when you push new content in Sanity.
|
||||
|
||||
See [Caching in resources.GetRemote](/functions/resources/getremote/#caching) for more fine grained control.
|
Reference in New Issue
Block a user