Reference

Snapshot Storage

All history is stored in one table, resource_version, created by sql/install.sql. There are no foreign keys into Omeka's core tables, so history survives the deletion of the resource, the user, or anything the snapshot refers to.

Table columns

Column Type Description
id INT UNSIGNED Version row id. This is the id used in admin URLs.
resource_type VARCHAR(20) items, media, or item_sets
resource_id INT UNSIGNED Omeka resource id
version_num INT UNSIGNED Sequential per resource, starting at 1
operation VARCHAR(20) create, update, delete, restore, or baseline
payload MEDIUMTEXT Full JSON-LD of the resource, as returned by its API representation
payload_hash CHAR(64) SHA-256 of the payload with volatile keys removed, used for deduplication
resource_title VARCHAR(255) Display title at capture time (o:title, then dcterms:title, then o:source, else [untitled])
user_id, user_name INT, VARCHAR(255) The acting user's id and name (or email), or NULL for system saves
is_batch TINYINT(1) 1 when the save was a partial update (Bulk Edit, batchUpdate, REST PATCH)
batch_key VARCHAR(64) 28-character hex key shared by every capture in the same PHP request or job
restored_from INT UNSIGNED For restore operations, the version row id that was restored
created_at DATETIME Capture time in the server's PHP time zone

Indexes cover (resource_type, resource_id, version_num), (user_id, created_at), created_at, and batch_key.

Payloads are stored as MEDIUMTEXT, so a single snapshot can be up to 16 MB. Payloads are not compressed.

Operations

Operation When it is written
create After api.create.post for a versioned resource type
update After api.update.post, unless the payload hash equals the latest version's
delete After api.delete.post; always written, even when the payload matches
restore An update performed by the module's restore service, single or batch
baseline Before the first update of a resource that has no versions yet, capturing its pre-edit state

Deduplication

Before inserting, the module hashes the payload with the keys @context, @id, o:created, and o:modified removed and all object keys sorted recursively (list order is kept, because value order inside a property is meaningful). If the hash matches the latest version of that resource, and the operation is not delete, nothing is written.

Capture hooks

The module listens on the shared event identifier Omeka\Api\Adapter\AbstractAdapter for api.create.post, api.update.post, api.delete.post (capture) and api.update.pre (baseline), all at priority -100 so that other listeners run first. Any exception in a capture handler is written to the PHP error log with the prefix [ResourceVersioning] and swallowed; a capture failure never causes the save to fail.

Saves that bypass the API manager are not captured. In particular, media edited inline within an item's edit form are hydrated directly by the item adapter, so they produce no media version; the item's own version still records the list of attached media.

Admin URLs

All routes are under /admin/resource-versioning and are handled by ResourceVersioning\Controller\Admin\VersionController.

URL Method Action
/admin/resource-versioning GET Browse all versions; query parameters resource_type, user_id, operation, date_from, date_to, page
/admin/resource-versioning/resource/:resource-type/:id GET History for one resource (items, media, or item_sets)
/admin/resource-versioning/version/:id GET One version with its raw JSON-LD snapshot
/admin/resource-versioning/compare/:a[/:b] GET Diff two version rows; b defaults to the version before a
/admin/resource-versioning/restore/:id GET, POST Confirmation page, then execute the restore
/admin/resource-versioning/batch/:key GET All versions written under one batch key
/admin/resource-versioning/rollback GET, POST Batch rollback wizard; POST with step=preview or step=execute
/admin/resource-versioning/prune POST Dispatch the prune job

POST actions require the CSRF token from the module's confirmation form, which the pages include automatically.

Log in for Support