Skip to content
Tracks version history and modification state in DataObject ownership structures
Branch: master
Clone or download
unclecheese Merge pull request #8 from creative-commoners/pulls/1.0/disable-snaps…

NEW Allow pausing snapshots globally
Latest commit dde4965 Jul 16, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
_config NEW Allow pausing snapshots globally Jul 1, 2019
src Add documentation for pause and resume methods Jul 1, 2019
tests Fix travis config, code style issues, postgres issues Jun 12, 2019
.codecov.yml Initial commit Mar 5, 2019
.editorconfig Initial commit Mar 5, 2019
.gitattributes Initial commit Mar 5, 2019
.travis.yml Fix travis config, code style issues, postgres issues Jun 12, 2019 Initial commit Mar 5, 2019
LICENSE Removed wrong badgets, added note about admin Jun 25, 2019
composer.json Fix travis config, code style issues, postgres issues Jun 12, 2019
phpcs.xml.dist Initial commit Mar 5, 2019
phpunit.xml.dist Initial commit Mar 5, 2019

SilverStripe Versioned Snapshots

Build Status Latest Stable Version Latest Unstable Version Total Downloads License Dependency Status


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 dev/build.

What does this do?

Imagine you have a content model that relies on an ownership structure, using the $owns setting.

  (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.


While the SnapshotPublishable extension offers a large API surface, there are only a few primary methods that are relevant to the end user:

  • $myDataObject->hasOwnedModifications(): bool returns true if the record owns records that have changes
  • $myDataObject->getPublishableObjects(): ArrayList: returns a list of DataObject instances that will be published along with the owner.
  • $myDataObject->getActivityFeed(): ArrayList Provides a collection of objects that can be rendered on a template to create a human-readable activity feed. Returns an array of ActivityEntry objects containing the following:
    • Subject: The DataObject record that instantiated the activity
    • Owner: Only defined in many_many reltionships. Provides information on what the record was linked to. Informs the ADDED and REMOVED actions.


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 silverstripe-versioned, with 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 a new 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 has_one). 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_many relationships 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 to keep protected methods backwards-compatible in minor/patch versions, but if you're overriding methods then please test your work before upgrading.

Reporting Issues

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

You can’t perform that action at this time.