SilverStripe Versioned Snapshots
Enables snapshots for enhanced history and modification status for deeply nested ownership structures. It's solving an important UX issue with versioning, which is particularly visible in content blocks implementations.
This module enables the data model, you might also be interested in silverstripe/versioned-snapshot-admin to expose these snapshots through the "History" tab of the CMS.
WARNING: This module is experimental, and not considered stable.
$ composer require silverstripe/versioned-snapshots
You'll also need to run
What does this do?
Imagine you have a content model that relies on an ownership structure, using the
BlockPage (has_many) Blocks (has_one) Gallery (many_many) Image
Ownership between each of those nodes affords publication of the entire graph through one commmand
(or click of a button). But it is not apparent to the user what owned content, if any, will
be published. If the Gallery is modified,
BlockPage will not show a modified state.
This module aims to make these modification states and implicit edit history more transparent.
What does it not do?
Currently, rolling back a record that owns other content is not supported and will produce unexpected results. Further, comparing owned changes between two versions of a parent is not supported.
SnapshotPublishable extension offers a large API surface, there are only a few primary methods
that are relevant to the end user:
$myDataObject->hasOwnedModifications(): boolreturns true if the record owns records that have changes
$myDataObject->getPublishableObjects(): ArrayList: returns a list of
DataObjectinstances that will be published along with the owner.
$myDataObject->getActivityFeed(): ArrayListProvides a collection of objects that can be rendered on a template to create a human-readable activity feed. Returns an array of
ActivityEntryobjects containing the following:
DataObjectrecord that instantiated the activity
Action: One of:
Owner: Only defined in
many_manyreltionships. Provides information on what the record was linked to. Informs the
The snapshot functionality is provided through the
SnapshotPublishable extension, which
is a drop-in replacement for
RecursivePublishable. By default, this module will replace
RecursivePublishable, which is added to all dataobjects by
this custom subclass.
For CMS views, use the
SnapshotSiteTreeExtension to provide notifications about
owned modification state (WORK IN PROGRESS, POC ONLY)
How it works
When a dataobject is written, an
onAfterWrite handler opens a snapshot by writing
VersionedSnapshot record. As long as this snapshot is open, any successive dataobject
writes will add themselves to the open snapshot, on the
VersionedSnapshotItem table. The dataobject
that opens the snapshot is stored as the
Origin on the
VersionedSnapshot table (a polymorphic
It then looks up the ownership chain using
findOwners() and puts each of its owners into the snapshot.
Each snapshot item contains its version, class, and ID at the time of the snapshot. This provides enough information to query what snapshots a given dataobject was involved in since a given version or date.
For the most part, the snapshot tables are considered immutable historical records, but there are a few cases when snapshots are retroactively updated
- When changes are reverted to live, any snapshots those changes made are deleted.
- When the ownership structure is changed, the previous owners are surgically removed from the graph and the new ones stitched in.
- Adds significant overhead to all
DataObject::write()calls. (Benchmarking TBD)
many_manyrelationships must use "through" objects.
This library follows Semver. According to Semver, you will be able to upgrade to any minor or patch version of this library without any breaking changes to the public API. Semver also requires that we clearly define the public API for this library.
All methods, with
public visibility, are part of the public API. All
other methods are not part of the public API. Where possible, we'll try
protected methods backwards-compatible in minor/patch versions,
but if you're overriding methods then please test your work before upgrading.
Please create an issue for any bugs you've found, or features you're missing.
This module is released under the BSD 3-Clause License