Add an OcflExporter service that builds the complete object for a node:
node.json plus the binaries of
- media pointing at the node through the "media of" field (Islandora:
field_media_of), stored under media/{media uuid}/,
- media referenced from the node's own media reference fields, and
- files in the node's own file/image fields, under files/{field}/.
node.json gains a media list (each media's metadata, how it is
associated with the node and the logical paths of its files) and a files
list. Standalone media objects include their own files too.
Automatic exports are queued and run once per node when the request ends
(needs_destruction), so saving a node together with several media creates
a single version. Media inserts, updates and deletes queue the media's
parent nodes, before and after the change, so derivatives and removed
media are reflected in the node's object. A successful export invalidates
the entity's cache tags so the export status shown on its page is current.
The controller, bulk actions and hooks now go through the exporter.
New settings, with an update hook to add them to existing sites:
include_files, media_of_field, member_of_field and model_field (the
latter two are used by the node serializer for membership data).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rework OcflStorageService so an object can hold binaries alongside
node.json, and fix several conformance problems in the generated objects.
- saveObject() accepts a map of logical path => source URI. Files whose
sha512 is already in the manifest are referenced from the new version's
state instead of being copied again (forward-delta deduplication); new
copies are fixity-checked after copying.
- File digests are cached by URI, size and mtime in key/value storage so
unchanged files aren't rehashed on every export.
- Unreadable files are skipped and listed in _metadata.missing_files
rather than failing the whole export.
- Fix the manifest overwriting an existing digest's path, which left
content files on disk that the manifest didn't list.
- Fix the 0= conformance declarations, which must contain only the
version string (E007).
- Add a version user, the contentDirectory key, and replace the root
inventory last via rename so a failed export can't advance the head.
Leftover version directories from interrupted exports are removed.
- Object IDs are urn:uuid URIs, and objects are stored using the
0003-hash-and-id-n-tuple-storage-layout extension; the module refuses to
write into a root that declares a different layout.
- Resolve the latest node.json through the inventory, since unchanged
content may live in an earlier version.
- Restrict the view_export route's uuid parameter to a UUID pattern, as
it is now used to build a filesystem path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>