Skip to content

Commit 466dfe3

Browse files
authored
Merge branch 'master' into 0103-poetry
2 parents 0521bcc + 6b73726 commit 466dfe3

File tree

15 files changed

+184
-11
lines changed

15 files changed

+184
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The fields are as follows:
3535
* `tags` this is used in the recipe listing page and is currently uncontrolled and optional. It is shown on the [full list of recipes](https://iiif.io/api/cookbook/recipe/all/)
3636
* `summary` a short summary of the recipe. It is shown on the [full list of recipes](https://iiif.io/api/cookbook/recipe/all/)
3737
* `viewers` see further details below but this drives the Viewer Matrix
38-
* `topic` a controlled list of headings that are used on the viewer matrix. Allowed values are **basic, property, structure, image, AV, annotation or geo-recipes**. A recipe may have multiple topics and these would be expressed as a list for example:
38+
* `topic` a controlled list of headings that are used on the viewer matrix. Allowed values are **basic, property, note, structure, annotation, image, AV, realWorldObject, geo-recipes, content-state**. (The current set of topics at any time are in the [`topics.yml`](_data/topics.yml) file.) A recipe may have multiple topics and these would be expressed as a list for example:
3939
```
4040
topic:
4141
- basic

_includes/links.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@
3535
[0040]: {{ site.cookbook_url | absolute_url }}/recipe/0040-image-rotation-service/ "Image Rotation Two Ways"
3636

3737
[0047]: {{ site.cookbook_url | absolute_url }}/recipe/0047-homepage/ "Linking to Web Page of an Object"
38-
[0053]: {{ site.cookbook_url | absolute_url }}/recipe/0053-seeAlso/ "Linking to Structured Metadata"
3938
[0046]: {{ site.cookbook_url | absolute_url }}/recipe/0046-rendering/ "Providing Alternative Representations"
39+
[0053]: {{ site.cookbook_url | absolute_url }}/recipe/0053-seeAlso/ "Linking to Structured Metadata"
40+
[0057]: {{ site.cookbook_url | absolute_url }}/recipe/0057-publishing-v2-and-v3/ "Making IIIF Presentation API v2 and v3 manifests available at the same URL"
4041
[0064]: {{ site.cookbook_url | absolute_url }}/recipe/0064-opera-one-canvas/ "Table of Contents for Multiple A/V Files on a Single Canvas"
4142
[0065]: {{ site.cookbook_url | absolute_url }}/recipe/0065-opera-multiple-canvases/ "Table of Contents for Multiple A/V Files on Multiple Canvases"
4243

_includes/viewer_link.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@
6161
https://theseusviewer.org/?iiif-content={{manifest_url |strip}}
6262
{% endcapture %}
6363
{% assign default_text="View in Theseus" %}
64+
{% elsif include.type == 'liiive' %}
65+
{% capture viewer_url %}
66+
https://liiive.now/?iiif-content={{manifest_url |strip}}
67+
{% endcapture %}
68+
{% assign default_text="View in liiive" %}
6469
{% else %}
6570
{% capture default_text %}Unknown Viewer type '{{ include.type}}'{% endcapture %}
6671
{% capture viewer_url %}{{manifest_url |strip}}{% endcapture %}

index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ Recipes using [Content State API](https://iiif.io/api/content-state/1.0/)
156156
* extensions (18)
157157
* services (9,10)
158158
* Mixed version scenarios (Prezi 3+Image 2)
159-
* Publishing v2 and v3 versions
159+
* [Making IIIF Presentation API v2 and v3 manifests available at the same URL][0057]
160160

161161
## Real-world complex objects (ideally taken from actual collections)
162162

recipe/0001-mvm-image/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ viewers:
1212
- Glycerine Viewer
1313
- Theseus
1414
- Curation
15+
- liiive
1516
topic:
1617
- basic
1718
- image

recipe/0005-image-service/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ viewers:
1111
- Glycerine Viewer
1212
- Theseus
1313
- Curation
14+
- liiive
1415
topic:
1516
- basic
1617
- image
@@ -38,7 +39,7 @@ Though a version 3 Manifest may specify a service using the version 2 `@id` and
3839

3940
## Example
4041

41-
{% include manifest_links.html viewers="Mirador, Annona, Clover, Glycerine Viewer, Theseus, Curation" manifest="manifest.json" %}
42+
{% include manifest_links.html viewers="Mirador, Annona, Clover, Glycerine Viewer, Theseus, Curation, liiive" manifest="manifest.json" %}
4243

4344
{% include jsonviewer.html src="manifest.json" config='data-line="36-42"' %}
4445

recipe/0007-string-formats/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ viewers:
1212
- Glycerine Viewer
1313
- Theseus
1414
- Curation
15+
- liiive
1516
topic: property
1617
property: label, summary, metadata, requiredStatement
1718
code:
@@ -34,7 +35,7 @@ For security reasons, clients are expected to allow only `a`, `b`, `br`, `i`, `i
3435

3536
## Example
3637

37-
{% include manifest_links.html viewers="UV, Mirador, Annona, Clover, Glycerine Viewer, Theseus, Curation" manifest="manifest.json" %}
38+
{% include manifest_links.html viewers="UV, Mirador, Annona, Clover, Glycerine Viewer, Theseus, Curation, liiive" manifest="manifest.json" %}
3839

3940
{% include jsonviewer.html src="manifest.json" config='data-line="7,12,24,38"' %}
4041

recipe/0021-tagging/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ viewers:
99
- Annona
1010
- Glycerine Viewer
1111
- Theseus
12+
- liiive
1213
topic: annotation
1314
code:
1415
- iiif-prezi3
@@ -34,7 +35,7 @@ In this Manifest, we use a photograph of Göttingen from the 2019 IIIF annual co
3435

3536
Because the statue is not the sole or dominant element of the photo, we've targeted the tag to a portion of the photo using fragment selector syntax.
3637

37-
{% include manifest_links.html viewers="Mirador,Annona,Glycerine Viewer, Theseus" manifest="manifest.json" %}
38+
{% include manifest_links.html viewers="Mirador,Annona,Glycerine Viewer, Theseus, liiive" manifest="manifest.json" %}
3839

3940
{% include jsonviewer.html src="manifest.json" config='data-line="44-63"' %}
4041

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: Making IIIF Presentation API v2 and v3 manifests available at the same URL
3+
id: 57
4+
layout: recipe
5+
tags: [implementation_note]
6+
summary: "tbc"
7+
viewers:
8+
topic: note
9+
---
10+
11+
12+
## Use Case
13+
As a manifest creator or host, or as a viewer or client developer, you may want to allow access to multiple versions of a manifest, each conforming to a different IIIF Presentation API major version. For the manifest creator or host, you may want to allow a wider range of clients the ability to view the resources in the manifests. For a client developer, you may want your client software to privilege one version of the API while allowing users to view manifests from a wide range of hosts with unpredictable and possibly uneven API conformance.
14+
15+
## Implementation notes
16+
17+
This recipe describes publishing IIIF v2 and v3 resources using the Presentation API at the same URL by using HTTP Content Negotiation. It is presented as an alternative approach both to publishing version-specific URLs and either requiring direct requests for retrieving the desired resource version or redirecting manifest requests from an neutral URL to a version-specific one. (HTTP Content Negotiation can be used with the Image API, but is a different matter.)
18+
19+
Using Content Negotiation is useful in cases where changing the location of these resources would cause annotations targeting those resources, particularly those created and stored by third-party users, to no longer work. For example, an annotation targeting a region of a canvas that relies on resolving that canvas and any media (image, video, or audio) to show to end users. Using multiple URLs risks losing any work your users have done to annotate these canvases. Content Negotiation also provides a stable URL to reference a IIIF Manifest that will work, even as providers transition through
20+
supported IIIF versions.
21+
22+
[Content Negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation) is an established method of requesting varying responses from a server. In this case, the server will be asked to vary its response for either a version 2 or version 3 of a IIIF resource formatted in JSON-LD. This can be accomplished by using the `Accept` HTTP header. The value of this header contains a `profile` section that varies according to the IIIF version that is desired. Servers that implement this method should also provide a default response if the request does not contain these values. The examples below illustrate this process.
23+
24+
The [IIIF API specification](https://iiif.io/api/presentation/3.0/#63-responses) gives the values for the header. It follows a straightforward format:
25+
26+
`application/ld+json;profile="http://iiif.io/api/presentation/{VERSION}/context.json"`
27+
28+
If successful, the server should respond with a value in the `Content-Type` header that mirrors the requested value.
29+
30+
If the server is unable to respond as requested — for example, it does not support the version requested — then it may either choose to return its default response (a manifest for a different IIIF Presentation version), or it may choose to return a `406 Not Acceptable` status code. Returning such an error implies support for Content Negotiation.
31+
32+
Where possible, servers are encouraged to return by default the latest IIIF Presentation API version. Since the server may substitute a different version than the one requested if the requested one is not available, clients will need to check the version information in the returned manifest. Simultaneously, client software must account for the response indicating the server does not support Content Negotiation and is responding with the manifest available at the requested URL. Put another way, clients must consider multiple possibilities and inconsistent affordances when requesting IIIF manifests from a server.
33+
34+
## Restrictions
35+
36+
There are several restrictions to using this pattern. Perhaps the most notable is that typical people using a web browser to view IIIF resources cannot be expected to know how to vary their `Accept` headers, so their viewing will likely be dependent on the client they are using. To view varying responses, a person must have the ability to set the HTTP request headers. A web-based client can provide this capability, but none currently do. People with sufficient combination of knowledge, access, and support can also use non-browser tools such as [Postman](https://www.postman.com/).
37+
38+
This is an active area of work in web specifications, and may change as methodologies develop. The W3C has a current working group looking at [Content Negotiation by Profile](https://www.w3.org/TR/dx-prof-conneg/.). This specification offers dedicated `Accept-Profile` and `Content-Profile` headers; however, these recommendations are
39+
still in draft form.
40+
41+
## Example
42+
43+
These examples will use the `cURL` command-line HTTP client to control the request and view the response from the server. The `-H` flag controls the value of the request header. The `-v` flag makes the process "verbose" so that the response headers can be inspected. The leading `$` is used to illustrate a prompt and is not part of the command.
44+
45+
The first example shows a basic request to an HTTP service, but with an explicitly-set `Accept` header:
46+
47+
$ curl -v -H "Accept: application/ld+json;profile=http://iiif.io/api/presentation/2/context.json" "https://iiif.io/api/cookbook/recipe/0057-publishing-v2-and-v3/manifest.json"
48+
49+
This provides a default response of a IIIF v2 manifest. Looking at some of the request (`>`) and response (`<`) values from cURL:
50+
51+
> GET /api/cookbook/recipe/0057-publishing-v2-and-v3/manifest.json HTTP/2
52+
> Host: iiif.io
53+
> User-Agent: curl/8.7.1
54+
> Accept: application/ld+json;profile=http://iiif.io/api/presentation/2/context.json
55+
56+
< HTTP/2 200
57+
< server: nginx
58+
< date: Fri, 07 Mar 2025 15:49:39 GMT
59+
< content-type: application/json
60+
61+
The response content should be a v2 manifest:
62+
63+
{% include jsonviewer.html src="manifest-v2.json" %}
64+
65+
To request a IIIF v3 manifest at the same URL the `Accept` header value can be adjusted:
66+
67+
$ curl -v -H "Accept: application/ld+json;profile=http://iiif.io/api/presentation/3/context.json" "https://iiif.io/api/cookbook/recipe/0057-publishing-v2-and-v3/manifest.json"
68+
69+
Looking at the same request (`>`) and response (`<`) values from cURL as for the previous example, we see the difference in the request's `Accept` header:
70+
71+
> GET /api/cookbook/recipe/0057-publishing-v2-and-v3/manifest.json HTTP/2
72+
> Host: iiif.io
73+
> User-Agent: curl/8.7.1
74+
> Accept: application/ld+json;profile=http://iiif.io/api/presentation/3/context.json
75+
76+
< HTTP/2 200
77+
< server: nginx
78+
< date: Fri, 07 Mar 2025 15:54:11 GMT
79+
< content-type: application/json
80+
81+
The response now should be a v3 manifest. Note that the value of the manifest's `id` field is the same as in the v2 manifest.
82+
83+
{% include jsonviewer.html src="manifest-v3.json" %}
84+
85+
{% include acronyms.md %}
86+
{% include links.md %}
87+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"@context": "http://iiif.io/api/presentation/2/context.json",
3+
"@id": "https://iiif.io/api/cookbook/recipe/0057-publishing-v2-and-v3/manifest.json",
4+
"@type": "sc:Manifest",
5+
"label": "IIIF Presentation Version 3 Minimum Viable Manifest",
6+
"sequences": [
7+
{
8+
"@id": "{{ id.path }}/sequences/s1",
9+
"@type": "sc:Sequence",
10+
"label": "Default",
11+
"canvases": [
12+
{
13+
"@id": "{{ id.path }}/canvas/p1",
14+
"@type": "sc:Canvas",
15+
"height": 1800,
16+
"width": 1200,
17+
"images": [
18+
{
19+
"@id": "{{ id.path }}/annotation/p0001-image",
20+
"@type": "oa:Annotation",
21+
"motivation": "sc:painting",
22+
"resource": {
23+
"@id": "https://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png",
24+
"@type": "dctypes:Image",
25+
"format": "image/png",
26+
"height": 1800,
27+
"width": 1200
28+
},
29+
"on": "{{ id.path }}/canvas/p1"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
]
36+
}

0 commit comments

Comments
 (0)