diff --git a/layouts/partials/events/render-event.html b/layouts/partials/events/render-event.html
new file mode 100644
index 00000000..b4bb75b4
--- /dev/null
+++ b/layouts/partials/events/render-event.html
@@ -0,0 +1,93 @@
+{{/*
+
+ Renders a single event, given:
+
+ * `event_name`: the name we want to display for the event
+ * `event_data`: the event specification
+ * `desired_example_name` (optional): the exact name of the examples to render.
+ If `desired_example_name` is omitted we render all examples
+ whose names start with the `event_name`.
+
+*/}}
+
+{{ $event_name := .event_name }}
+{{ $desired_example_name := .desired_example_name }}
+{{ $event_data := .event_data }}
+
+
+
+
+
+
+
+ {{ $event_name }}
+
+
+
+
+{{ $event_data.description | markdownify }}
+
+
+
+{{ $state_key := index $event_data.properties "state_key" }}
+
+
+
+ Event type: |
+ {{ if $state_key }}State event{{ else }}Message event{{ end }} |
+
+{{ if $state_key }}
+
+ State key |
+ {{ $state_key.description | markdownify }} |
+
+{{ end }}
+
+
+Content
+
+{{ $additional_types := partial "json-schema/resolve-additional-types" $event_data.properties.content }}
+
+{{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+{{end}}
+
+Examples
+
+{{ $all_examples := index site.Data "event-schemas" "examples" }}
+
+{{ range $example_name, $example := $all_examples }}
+
+ {{/*
+ This is to allow the msgtype template to work.
+ It lets callers specify exactly the name of the example they want
+ */}}
+ {{ if $desired_example_name }}
+ {{ if eq $example_name $desired_example_name }}
+ {{ $example_content := partial "json-schema/resolve-refs" (dict "schema" $example "path" "event-schemas/examples") }}
+```json
+{{ jsonify (dict "indent" " ") $example_content }}
+```
+ {{ end }}
+ {{/*
+ If `$desired_example_name` is not given, we will include any
+ examples whose first part (before "$") matches the event name
+ */}}
+ {{ else }}
+
+ {{ $pieces := split $example_name "$" }}
+ {{ $example_base_name := index $pieces 0 }}
+ {{ if eq $event_name $example_base_name }}
+ {{ $example_content := partial "json-schema/resolve-refs" (dict "schema" $example "path" "event-schemas/examples") }}
+ {{ $example_json := jsonify (dict "indent" " ") $example_content }}
+ {{ $example_json = replace $example_json "\\u003c" "<" }}
+ {{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
+
+```json
+{{ $example_json }}
+```
+ {{ end }}
+ {{ end }}
+{{ end }}
+
+
diff --git a/layouts/partials/json-schema/resolve-additional-types.html b/layouts/partials/json-schema/resolve-additional-types.html
new file mode 100644
index 00000000..a4f95445
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-additional-types.html
@@ -0,0 +1,92 @@
+{{/*
+
+ Finds and returns all nested objects, given:
+
+ * `this_object`: a JSON schema object
+
+ Given a schema object, this template finds all nested objects under that
+ schema.
+
+ It "cleans" each object by copying only the parts of the objects that
+ the renderer needs, and adds the result to an array, `additional_objects`.
+
+ Finally it returns the array of all the objects it found.
+
+ Note that the returned array may contain duplicate objects.
+
+*/}}
+
+{{ $this_object := partial "json-schema/resolve-allof" . }}
+{{ $additional_objects := slice }}
+
+{{ if eq $this_object.type "object" }}
+
+ {{/*
+ Add the object we were passed into the $additional_objects array
+ */}}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" $this_object) }}
+
+ {{/*
+ Add any nested objects referenced in this object's `additionalProperties`
+ */}}
+ {{ if $this_object.additionalProperties }}
+ {{ if reflect.IsMap $this_object.additionalProperties }}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" $this_object.additionalProperties) }}
+
+ {{ range $key, $property := $this_object.additionalProperties.properties }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $property "additional_objects" $additional_objects) }}
+ {{ end }}
+
+ {{ end }}
+ {{ end }}
+
+ {{/*
+ Add any nested objects referenced in this object's `properties`
+ */}}
+ {{ range $key, $property := $this_object.properties}}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $property "additional_objects" $additional_objects) }}
+ {{ end }}
+
+{{ end }}
+
+{{ if eq $this_object.type "array" }}
+ {{/*
+ Add any nested objects referenced in this object's `items`
+ */}}
+ {{ if reflect.IsSlice $this_object.items}}
+ {{ range $this_object.items }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" . "additional_objects" $additional_objects) }}
+ {{ end }}
+ {{ else }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $this_object.items "additional_objects" $additional_objects) }}
+ {{ end }}
+{{ end }}
+
+{{ return $additional_objects }}
+
+
+{{/*
+ This actually makes the recursive call and adds the returned objects to the array
+*/}}
+{{ define "partials/get-additional-objects" }}
+ {{ $additional_objects := .additional_objects }}
+
+ {{ $this_object := partial "json-schema/resolve-allof" .this_object }}
+ {{ $more_objects := partial "json-schema/resolve-additional-types" $this_object }}
+ {{/*
+ As far as I know we don't have something like Array.concat(), so add them one at a time
+ */}}
+ {{ range $more_objects}}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" .) }}
+ {{ end }}
+ {{ return $additional_objects }}
+{{ end }}
+
+{{/*
+ Only copy the bits of the object that we actually care about.
+ This is needed for uniqify to work - otherwise objects that are the same
+ but with (for example) different examples will be considered differen.
+*/}}
+{{ define "partials/clean-object" }}
+ {{ return (dict "title" .title "properties" .properties "required" .required "enum" .enum) }}
+{{ end }}
diff --git a/layouts/partials/json-schema/resolve-allof.html b/layouts/partials/json-schema/resolve-allof.html
new file mode 100644
index 00000000..0150cfde
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-allof.html
@@ -0,0 +1,76 @@
+{{/*
+
+ Resolves the `allOf` keyword (https://swagger.io/specification/v2/#composition-and-inheritance-polymorphism),
+ given a JSON schema object.
+
+ `allOf` is used to support a kind of inheritance for JSON schema objects.
+
+ An object can reference a "parent" object using `allOf`, and it then inherits
+ its parent's properties. If the same property is present in the child, then
+ we use the child's version (the child overrides the parent).
+
+ Of course the parent can itself inherit from *its* parent, so we recurse to
+ handle that.
+
+*/}}
+
+{{ $ret := . }}
+{{ $original := . }}
+
+{{ $required := .required }}
+{{ if not $required }}
+ {{ $required := slice }}
+{{ end }}
+
+{{ with $ret.allOf }}
+
+ {{ $all_of_values := dict }}
+
+ {{/*
+ allOf is always an array
+ */}}
+ {{ range . }}
+
+ {{ with .required }}
+ {{ $required = union $required . }}
+ {{ end }}
+
+ {{/*
+ With merge, values from the second argument override those from the first argument.
+ So this order will accumulate values from allOf items, allowing later ones to override earlier
+ */}}
+ {{ $all_of_values = merge $all_of_values . }}
+
+ {{ end }}
+
+ {{/*
+ Then apply allOf values to the original, but allow the original to override allOf.
+ */}}
+ {{ $ret = merge $all_of_values $ret }}
+
+ {{/*
+ Except that if allOf *itself* contains allOf, we do want to override the original for that field only.
+ */}}
+ {{ with $all_of_values.allOf }}
+ {{ $ret = merge $ret (dict "allOf" . ) }}
+ {{ end }}
+
+ {{ with $ret.required }}
+ {{ $required = union $required $ret.required }}
+ {{ end }}
+
+ {{ $ret = merge $ret (dict "required" $required) }}
+
+{{ end }}
+
+{{/*
+ Recurse while we are finding new allOf entries to resolve
+*/}}
+{{ if ne $ret.allOf $original.allOf }}
+
+ {{ $resolved := partial "json-schema/resolve-allof" $ret }}
+ {{ $ret = merge $ret $resolved }}
+
+{{ end }}
+
+{{ return $ret }}
diff --git a/layouts/partials/json-schema/resolve-example.html b/layouts/partials/json-schema/resolve-example.html
new file mode 100644
index 00000000..51ce6d53
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-example.html
@@ -0,0 +1,29 @@
+{{/*
+
+ For complex objects, example content is sometimes attached to the
+ object's individual properties (and subproperties...), so to get
+ a complete example for the whole object we need to iterate through
+ its properties (and subproperties...) and assemble them.
+
+ That's what this template does.
+
+*/}}
+
+{{ $this_object := partial "json-schema/resolve-allof" . }}
+
+{{ if eq $this_object.type "object" }}
+
+ {{ if not $this_object.example }}
+ {{ $this_object := merge (dict "example" dict ) $this_object }}
+ {{ end }}
+
+ {{ range $key, $property := $this_object.properties}}
+ {{ $this_property_example := partial "json-schema/resolve-example" $property }}
+ {{ if $this_property_example }}
+ {{ $this_object = merge (dict "example" (dict $key $this_property_example)) $this_object }}
+ {{ end }}
+ {{ end }}
+
+{{ end }}
+
+{{ return $this_object.example }}
diff --git a/layouts/partials/json-schema/resolve-refs.html b/layouts/partials/json-schema/resolve-refs.html
new file mode 100644
index 00000000..1d99201d
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-refs.html
@@ -0,0 +1,65 @@
+{{/*
+
+ Resolves the `$ref` JSON schema keyword, by recursively replacing
+ it with the object it points to.
+
+ This template uses [`Scratch`](https://gohugo.io/functions/scratch/)
+ rather than a normal `dict` because with `dict` you can't replace key values:
+ https://discourse.gohugo.io/t/how-can-i-add-set-and-delete-keys-in-a-dictionary/29661
+
+*/}}
+
+{{ $schema := .schema }}
+{{ $path := .path}}
+
+{{ $ret := $schema }}
+
+{{ if reflect.IsMap $schema }}
+
+ {{ $scratch := newScratch }}
+ {{ $scratch.Set "result_map" dict }}
+
+ {{ $ref_value := index $schema "$ref"}}
+ {{ if $ref_value}}
+ {{ $full_path := path.Join $path $ref_value }}
+ {{/*
+ Apparently Hugo doesn't give us a nice way to split the extension off a filename.
+ */}}
+ {{ $without_ext := replaceRE "\\.[^\\.]*$" "" $full_path }}
+ {{ $pieces := split $without_ext "/" }}
+
+ {{ $ref := index site.Data $pieces }}
+
+ {{ $new_path := (path.Split $full_path).Dir}}
+ {{ $result_map := partial "json-schema/resolve-refs" (dict "schema" $ref "path" $new_path)}}
+ {{ if $result_map}}
+ {{ $scratch.Set "result_map" $result_map }}
+ {{end }}
+ {{ end }}
+
+
+ {{ range $key, $value := $schema }}
+ {{ if ne $key "$ref" }}
+ {{ $resolved := partial "json-schema/resolve-refs" (dict "schema" $value "path" $path) }}
+ {{ $scratch.SetInMap "result_map" $key $resolved }}
+ {{ end }}
+ {{ end }}
+
+ {{ $ret = $scratch.Get "result_map" }}
+
+{{ end }}
+
+{{ if reflect.IsSlice $schema }}
+
+ {{ $result_slice := slice }}
+
+ {{ range $schema }}
+ {{ $resolved := partial "json-schema/resolve-refs" (dict "schema" . "path" $path) }}
+ {{ $result_slice = $result_slice | append $resolved }}
+ {{ end }}
+
+ {{ $ret = $result_slice }}
+
+{{ end }}
+
+{{ return $ret }}
diff --git a/layouts/partials/openapi/render-api.html b/layouts/partials/openapi/render-api.html
new file mode 100644
index 00000000..db10b98c
--- /dev/null
+++ b/layouts/partials/openapi/render-api.html
@@ -0,0 +1,54 @@
+{{/*
+
+ Render an HTTP API, given:
+
+ * `api_data`: the OpenAPI/Swagger data
+ * `base_url`: the base URL: that is, the part we glue onto the front
+ of each value in `paths` to get a complete URL.
+ * `path`: the directory under /data where we found this API definition.
+ We use this to resolve "$ref" values, since they are relative to the schema's
+ location.
+
+*/}}
+
+{{ $api_data := index .api_data }}
+{{ $base_url := .base_url }}
+{{ $path := .path }}
+
+{{ range $path_name, $path_data := $api_data.paths }}
+
+ {{ $endpoint := delimit (slice $base_url $path_name ) "" }}
+
+ {{/* note that a `paths` entry can be a $ref */}}
+
+ {{ $params := dict "endpoint" $endpoint "path" $path }}
+
+ {{ with $path_data.get }}
+
+ {{ $operation_params := merge $params (dict "method" "GET" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.post }}
+
+ {{ $operation_params := merge $params (dict "method" "POST" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.put }}
+
+ {{ $operation_params := merge $params (dict "method" "PUT" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.delete }}
+
+ {{ $operation_params := merge $params (dict "method" "DELETE" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-object-table.html b/layouts/partials/openapi/render-object-table.html
new file mode 100644
index 00000000..0f4b3a77
--- /dev/null
+++ b/layouts/partials/openapi/render-object-table.html
@@ -0,0 +1,92 @@
+{{/*
+
+ Render a table listing the properties of an object, given:
+
+ * `caption`: optional caption for the table
+ * `properties`: dictionary of the properties to list, each given as:
+ `property_name` : `property_data`
+ * `required`: array containing the names of required properties.
+ In some cases (such as response body specifications) this isn't used, and
+ instead properties have a `required` boolean attribute. We support this too.
+
+*/}}
+
+{{ $caption := .caption }}
+{{ $properties := .properties}}
+{{ $required := .required}}
+
+{{ if $properties }}
+
+
+ {{ with $caption }}
+ {{ . }}
+ {{ end }}
+
+ Name |
+ Type |
+ Description |
+
+
+ {{ range $property_name, $property := $properties }}
+
+ {{ $property := partial "json-schema/resolve-allof" $property }}
+
+ {{/*
+ If the property has a `title`, use that rather than `type`.
+ This means we can write things like `EventFilter` rather than `object`.
+ */}}
+ {{ $type := $property.type}}
+ {{ if $property.title }}
+ {{ $type = $property.title }}
+ {{ end }}
+
+ {{/*
+ If the property is an array, indicate this with square brackets,
+ like`[type]`.
+ */}}
+ {{ if eq $type "array"}}
+ {{ $items := $property.items }}
+ {{ if $property.items }}
+ {{ $items = partial "json-schema/resolve-allof" $property.items }}
+ {{ end }}
+ {{ $inner_type := $items.type }}
+ {{ if $items.title }}
+ {{ $inner_type = $items.title }}
+ {{ end }}
+ {{ $type = delimit (slice "[" $inner_type "]") "" }}
+ {{ end }}
+
+ {{/*
+ If the property is an enum, indicate this.
+ */}}
+ {{ if (and (eq $type "string") ($property.enum)) }}
+ {{ $type = "enum" }}
+ {{ end }}
+
+ {{/*
+ If the property uses `additionalProperties` to describe its
+ internal structure, handle this.
+ */}}
+ {{ if reflect.IsMap $property.additionalProperties }}
+ {{ if $property.additionalProperties.title }}
+ {{ $type = delimit (slice "{ string: " $property.additionalProperties.title "}" ) "" }}
+ {{ end }}
+ {{ end }}
+
+ {{/*
+ Handle two ways of indicating "required", one for simple parameters,
+ the other for request and response body objects.
+ */}}
+ {{ $required := cond (or (in $required $property_name) ( eq $property.required true )) true false }}
+
+
+ {{ $property_name }} |
+ {{ $type }} |
+ {{ if $required }}Required: {{end}}{{ $property.description | markdownify }}{{ if eq $type "enum"}} One of: {{ $property.enum }} . {{ end }} |
+
+
+ {{ end }}
+
+
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-operation.html b/layouts/partials/openapi/render-operation.html
new file mode 100644
index 00000000..430c571a
--- /dev/null
+++ b/layouts/partials/openapi/render-operation.html
@@ -0,0 +1,65 @@
+{{/*
+
+ Render a single HTTP API operation: that is, a method+endpoint combination, given:
+
+ * `method`: the method, e.g. GET, PUT
+ * `endpoint`: the endpoint
+ * `operation_data`: the OpenAPI/Swagger data for the operation
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders the operation as a `` containing:
+
+ * an `` heading containing the method and endpoint
+ * a `` element containing the details, including:
+ * operation description
+ * basic info about the operation
+ * request details
+ * response details
+
+*/}}
+
+{{ $method := .method }}
+{{ $endpoint := .endpoint }}
+{{ $operation_data := .operation_data }}
+{{ $path := .path }}
+
+
+
+
+
+
+
+ {{ $method }}
+ {{ $endpoint }}
+
+
+
+
+{{ if $operation_data.deprecated }}
+ {{ partial "alert" (dict "type" "warning" "omit_title" "true" "content" "This API is deprecated and will be removed from a future release.") }}
+{{ end }}
+
+{{ $operation_data.description | markdownify }}
+
+
+
+
+
+ Rate-limited: |
+ {{ $rate_limited := index $operation_data.responses "429" }}
+ {{ if $rate_limited }}Yes{{ else }}No{{ end }} |
+
+
+ Requires authentication: |
+ {{ if $operation_data.security }}Yes{{ else }}No{{ end }} |
+
+
+
+
+{{ partial "openapi/render-request" (dict "parameters" $operation_data.parameters "path" $path) }}
+
+{{ partial "openapi/render-responses" (dict "responses" $operation_data.responses "path" $path) }}
+
+
+
+
diff --git a/layouts/partials/openapi/render-parameters.html b/layouts/partials/openapi/render-parameters.html
new file mode 100644
index 00000000..2b4fb09d
--- /dev/null
+++ b/layouts/partials/openapi/render-parameters.html
@@ -0,0 +1,30 @@
+{{/*
+
+ Render the parameters of a given type, given:
+
+ * `parameters`: OpenAPI/Swagger data specifying the parameters
+ * `type`: the type of parameters to render: "header, ""path", "query"
+ * `caption`: caption to use for the table
+
+ This template renders a single table containing parameters of the given type.
+
+*/}}
+
+{{ $parameters := .parameters }}
+{{ $type := .type }}
+{{ $caption := .caption }}
+
+{{ $parameters_of_type := where $parameters "in" $type }}
+
+{{ with $parameters_of_type }}
+
+ {{/* convert parameters into the format that render-object-table expects to see */}}
+ {{ $param_dict := dict }}
+ {{ range $parameter := . }}
+ {{ $param_dict = merge $param_dict (dict $parameter.name (dict "type" $parameter.type "description" $parameter.description "required" $parameter.required "enum" $parameter.enum) )}}
+ {{ end }}
+
+ {{/* and render the parameters */}}
+ {{ partial "openapi/render-object-table" (dict "caption" $caption "properties" $param_dict) }}
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-request.html b/layouts/partials/openapi/render-request.html
new file mode 100644
index 00000000..1695c2f8
--- /dev/null
+++ b/layouts/partials/openapi/render-request.html
@@ -0,0 +1,67 @@
+{{/*
+
+ Render the request part of a single HTTP API operation, given:
+
+ * `parameters`: OpenAPI/Swagger data specifying the parameters
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders:
+ * the "simple parameters" (header, path, query parameters)
+ * body parameters, which may be more complex, containing nested objects
+ * response body examples
+
+*/}}
+
+{{ $parameters := .parameters }}
+{{ $path := .path }}
+
+Request
+
+{{ if $parameters }}
+
+ {{ $simple_parameters := where $parameters "in" "!=" "body"}}
+ {{ if $simple_parameters }}
+Request parameters
+
+ {{ partial "openapi/render-parameters" (dict "parameters" $simple_parameters "type" "header" "caption" "header parameters") }}
+ {{ partial "openapi/render-parameters" (dict "parameters" $simple_parameters "type" "path" "caption" "path parameters") }}
+ {{ partial "openapi/render-parameters" (dict "parameters" $simple_parameters "type" "query" "caption" "query parameters") }}
+
+ {{ end }}
+
+ {{ $body_parameters := where $parameters "in" "body"}}
+ {{ if $body_parameters }}
+Request body
+
+ {{/* at most one body param is allowed by the spec */}}
+ {{ $body_parameter := index $body_parameters 0 }}
+ {{ $schema := partial "json-schema/resolve-refs" (dict "schema" $body_parameter.schema "path" $path) }}
+ {{ $schema := partial "json-schema/resolve-allof" $schema }}
+
+ {{ $additional_types := partial "json-schema/resolve-additional-types" $schema }}
+ {{ $additional_types = uniq $additional_types }}
+ {{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+ {{ end }}
+
+Request body example
+
+ {{ $example := partial "json-schema/resolve-example" $schema }}
+ {{ if $example }}
+ {{ $example_json := jsonify (dict "indent" " ") $example }}
+ {{ $example_json = replace $example_json "\\u003c" "<" }}
+ {{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
+
+```json
+{{ $example_json }}
+```
+
+ {{ else }}
+ {{ partial "alert" (dict "type" "warning" "omit_title" "true" "color" "warning" "content" "Specification error: Example invalid or not present") }}
+ {{ end }}
+
+ {{ end }}
+
+{{ else }}
+No request parameters or request body.
+{{ end }}
diff --git a/layouts/partials/openapi/render-responses.html b/layouts/partials/openapi/render-responses.html
new file mode 100644
index 00000000..c92aba8e
--- /dev/null
+++ b/layouts/partials/openapi/render-responses.html
@@ -0,0 +1,97 @@
+{{/*
+
+ Render the response part of a single HTTP API operation, given:
+
+ * `responses`: OpenAPI/Swagger data specifying the responses
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders:
+ * a summary of all the different responses
+ * details of the body for each response code
+ * body parameters, which may be more complex, containing nested objects
+ * response body examples
+
+*/}}
+
+{{ $responses := .responses }}
+{{ $path := .path }}
+
+Responses
+
+
+
+ Status |
+ Description |
+
+
+{{ range $code, $response := $responses }}
+
+
+ {{ $code }} |
+ {{ $response.description | markdownify }} |
+
+
+{{ end }}
+
+
+
+{{ range $code, $response := $responses }}
+
+ {{ if $response.schema }}
+
+ {{ $schema := partial "json-schema/resolve-refs" (dict "schema" $response.schema "path" $path) }}
+ {{ $schema := partial "json-schema/resolve-allof" $schema }}
+
+ {{ if or $schema.properties (eq $schema.type "array") }}
+{{ $code}} response
+
+ {{/*
+ All this is to work out how to express the content of the response
+ in the case where it is an array.
+ */}}
+ {{ if eq $schema.type "array" }}
+ {{ $type_of := "" }}
+ {{ if reflect.IsSlice $schema.items }}
+ {{ $types := slice }}
+ {{ range $schema.items }}
+ {{ if .title }}
+ {{ $types = $types | append .title}}
+ {{ else }}
+ {{ $types = $types | append .type }}
+ {{ end }}
+ {{ end }}
+ {{ $type_of = delimit $types ", "}}
+ {{ else }}
+ {{ $type_of = $schema.items.title }}
+ {{ end }}
+Array of {{ $type_of }}
.
+ {{ end }}
+
+ {{ $additional_types := partial "json-schema/resolve-additional-types" $schema }}
+ {{ $additional_types = uniq $additional_types }}
+ {{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+ {{ end }}
+
+ {{ $example := partial "json-schema/resolve-example" $schema }}
+ {{ if $response.examples }}
+ {{ $example = partial "json-schema/resolve-refs" (dict "schema" $response.examples "path" $path) }}
+ {{ $example = index $example "application/json" }}
+ {{ end }}
+
+ {{ if $example }}
+ {{ $example_json := jsonify (dict "indent" " ") $example }}
+ {{ $example_json = replace $example_json "\\u003c" "<" }}
+ {{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
+
+```json
+{{ $example_json }}
+```
+
+ {{ else }}
+ {{ partial "alert" (dict "type" "warning" "omit_title" "true" "color" "warning" "content" "Specification error: Example invalid or not present") }}
+ {{ end }}
+
+ {{ end }}
+ {{ end }}
+{{ end }}
diff --git a/layouts/shortcodes/definition.html b/layouts/shortcodes/definition.html
new file mode 100644
index 00000000..151e0606
--- /dev/null
+++ b/layouts/shortcodes/definition.html
@@ -0,0 +1,59 @@
+{{/*
+
+ This template is used to render EDUs and PDUs in the server-server and room versions specs.
+
+ It expects to be passed a `path` parameter, which is a path, relative to /data,
+ pointing to a schema file. The file extension is omitted. For example:
+
+ {{% definition path="api/server-server/definitions/edu" %}}
+
+ This template replaces the old {{definition_*}} template.
+
+*/}}
+
+{{ $path := .Params.path }}
+{{ $compact := .Params.compact }}
+{{ $pieces := split $path "/" }}
+
+{{/* The definition is referenced by the .path parameter */}}
+{{ $definition := index .Site.Data $pieces }}
+
+{{/* The base path, which we use to resolve $ref, omits the last component */}}
+{{ $pieces = first (sub (len $pieces) 1) $pieces}}
+{{ $path = delimit $pieces "/" }}
+
+{{/* Resolve $ref and allOf */}}
+{{ $definition = partial "json-schema/resolve-refs" (dict "schema" $definition "path" $path) }}
+{{ $definition = partial "json-schema/resolve-allof" $definition }}
+
+
+
+
+
+
+
+ {{ $definition.title }}
+
+
+
+
+{{ $definition.description | markdownify }}
+
+
+
+{{ $additional_types := partial "json-schema/resolve-additional-types" $definition }}
+{{ $additional_types = uniq $additional_types }}
+
+{{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+{{end}}
+
+Examples
+
+{{ $example := partial "json-schema/resolve-example" $definition }}
+
+```json
+{{ jsonify (dict "indent" " ") $example }}
+```
+
+
diff --git a/layouts/shortcodes/event-fields.html b/layouts/shortcodes/event-fields.html
new file mode 100644
index 00000000..83651c21
--- /dev/null
+++ b/layouts/shortcodes/event-fields.html
@@ -0,0 +1,45 @@
+{{/*
+
+ This template is used to render the fields that all basic events and room events
+ must or may contain.
+
+ It expects to be passed an `event_type` parameter, is the name of a file under
+ /data/event-schemas/core-event-schema. The file extension is omitted. For example:
+
+ {{% event-fields event_type="room_event" %}}
+
+ This template replaces the old {{common_event_fields}} and {{common_room_event_fields}} templates.
+
+*/}}
+
+{{ $event := index .Site.Data "event-schemas" "schema" "core-event-schema" .Params.event_type }}
+{{ $path := "event-schemas/schema/core-event-schema" }}
+
+{{ $event = partial "json-schema/resolve-refs" (dict "schema" $event "path" $path) }}
+{{ $event := partial "json-schema/resolve-allof" $event }}
+
+
+
+
+
+
+
+ {{ humanize $event.title }}
+
+
+
+
+{{ $event.description | markdownify }}
+
+
+
+{{ $event = merge $event (dict "title" "") }}
+
+{{ $additional_types := partial "json-schema/resolve-additional-types" $event }}
+{{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+{{end}}
+
+
+
+
diff --git a/layouts/shortcodes/event-group.html b/layouts/shortcodes/event-group.html
new file mode 100644
index 00000000..5e2900f0
--- /dev/null
+++ b/layouts/shortcodes/event-group.html
@@ -0,0 +1,32 @@
+{{/*
+
+ This template is used to render a group of events starting with a given prefix.
+
+ It expects to be passed a `group_name` parameter. For example:
+
+ {{% event-group group_name="m.call" %}}
+
+ The template will then render all events whose schema starts with the given name.
+
+ This template replaces the old {{*_events}} template.
+
+*/}}
+
+{{ $path := "event-schemas/schema" }}
+
+{{ $events := index .Site.Data "event-schemas" "schema" }}
+{{ $group_name := .Params.group_name }}
+
+{{ range $event_name, $event_data := $events }}
+
+ {{ $prefix := substr $event_name 0 (len $group_name) }}
+ {{ if eq $prefix $group_name }}
+
+ {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+ {{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+ {{ partial "events/render-event" (dict "event_name" $event_name "event_data" $event_data)}}
+
+ {{ end }}
+
+{{ end }}
diff --git a/layouts/shortcodes/event.html b/layouts/shortcodes/event.html
new file mode 100644
index 00000000..71c9c534
--- /dev/null
+++ b/layouts/shortcodes/event.html
@@ -0,0 +1,20 @@
+{{/*
+
+ This template is used to render an event.
+
+ It expects to be passed an `event` parameter, which is the name of a schema file under
+ "data/event-schemas/schema". The file extension is omitted. For example:
+
+ {{% event event="m.accepted_terms" %}}
+
+ This template replaces the old {{*_event}} template.
+
+*/}}
+
+{{ $event_data := index .Site.Data "event-schemas" "schema" .Params.event }}
+{{ $path := "event-schemas/schema" }}
+
+{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+{{ partial "events/render-event" (dict "event_name" .Params.event "event_data" $event_data)}}
diff --git a/layouts/shortcodes/http-api.html b/layouts/shortcodes/http-api.html
new file mode 100644
index 00000000..b4bc6d0d
--- /dev/null
+++ b/layouts/shortcodes/http-api.html
@@ -0,0 +1,26 @@
+{{/*
+
+ This template is used to render an HTTP API, given an OpenAPI/Swagger definition.
+
+ It expects to be passed two parameters:
+
+ * a `spec` parameter identifying the spec, which must be the name of
+ a directory under /data/api
+ * an `api` parameter, identifying an OpenAPI/Swagger definition,
+ which is the name of a schema file under "data/api/$spec".
+ The file extension is omitted. For example:
+
+ {{% http-api spec="server-server" api="public_rooms" %}}
+
+ This template replaces the old {{*_http_api}} template.
+
+*/}}
+
+{{ $spec := .Params.spec}}
+{{ $api := .Params.api}}
+
+{{ $api_data := index .Site.Data.api .Params.spec .Params.api }}
+{{ $base_url := replace $api_data.basePath "%CLIENT_MAJOR_VERSION%" "r0" }}
+{{ $path := delimit (slice "api" $spec) "/" }}
+
+{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "path" $path) }}
diff --git a/layouts/shortcodes/msgtypes.html b/layouts/shortcodes/msgtypes.html
new file mode 100644
index 00000000..695e9bb7
--- /dev/null
+++ b/layouts/shortcodes/msgtypes.html
@@ -0,0 +1,48 @@
+{{/*
+
+ This template is used to render the `m.room.message` events.
+
+ It replaces the old {{msgtype_events}} template.
+
+*/}}
+
+{{ $path := "event-schemas/schema" }}
+{{ $compact := false }}
+
+{{/*
+ The old template starts with an explicit list of events, presumably
+ to define the order in which they are rendered.
+*/}}
+{{ $msgtypes := (slice "m.room.message$m.text" "m.room.message$m.emote" "m.room.message$m.notice" "m.room.message$m.image" "m.room.message$m.file") }}
+
+{{/*
+ It excludes `m.room.message$m.server_notice`
+*/}}
+{{ $excluded := slice "m.room.message$m.server_notice" }}
+
+{{/*
+ It then adds any other events that start `m.room.message`.
+*/}}
+{{ $events := index .Site.Data "event-schemas" "schema" }}
+{{ $expected_prefix := "m.room.message$"}}
+{{ range $object_name, $event_data := $events }}
+
+ {{ $prefix := substr $object_name 0 (len $expected_prefix) }}
+ {{ if and (eq $prefix $expected_prefix) (not (in $excluded $object_name)) (not (in $msgtypes $object_name)) }}
+ {{ $msgtypes = $msgtypes | append $object_name }}
+ {{ end }}
+
+{{ end }}
+
+{{ $site_data := .Site.Data }}
+
+{{ range $msgtypes }}
+
+ {{ $event_data := index $site_data "event-schemas" "schema" . }}
+ {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+ {{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+ {{ $event_name := index (split . "$") 1 }}
+ {{ partial "events/render-event" (dict "event_name" $event_name "desired_example_name" . "event_data" $event_data)}}
+
+{{ end }}