The aim of this plugin is to provide a system for versioning any post type.
Features
The basic features to support are:
- Allow for any post type to be versioned (excluding revisions)
- Allow versions to be tagged with custom numbers (e.g. continuous
1, 2, 3 ... or semver 1.0.0, 1.1.0, 2.0.0 ...)
Advanced features to consider are:
- Resolve posts to the latest version
- Allow working in "unreleased" versions
How it might work
We'll use revisions to control the changes, as they already are used in WordPress to provide the same feature. We'll bend the working of revisions to allow for:
- Tagging revisions
- Preventing delete of tagged revisions
- Collapse untagged revisions between versions
- Hide previous versions
- Delete previous versions
Versions
Versions are numbered sequentially starting from 1. All versions are stored as a meta_key with the format:
Labeling versions
If there is a need for a custom version label, the value of that label should be the value of the version meta_key.
Statuses
We'll need to add a custom unreleased status to handle a specific state a post can be in. This status represents a post that has a previous version, but the current is not yet finalized.
A post is set tu unreleased when performing an action to create a new version, which means V1 is never unreleased.
The existence of this status is to prevent issues with queries that would exclude the post from results if w used draft or other statuses.
The First Version (V1)
When creating a post, a version is started (V1). All editing processes are the same, changes as draft, publishing or updating. When the post is published a new action appears: "Create new version".
What happens after V1?
New versions are born with the "Create new version" action. A new version is created with the following steps:
- Determine current version
- Remove old version
meta_key
- Add new version
meta_key
- Change status to
unreleased
- Mark the latest revision, from the previous version, as
publish
Keeping revisions clean
WordPress has a builtin system to clean old revisions. We interfere with this system to keep the latest revision of a given version from being deleted. (TODO: check if we don't need to disable revisions and implement our own system to prevent a loop in revision cleanup checks.)
Creating revisions
Revisions are potential versions in time. WordPress creates revisions when any of the post data is changed (this is only the data stored in the posts table). This means that meta fields and term associations are not stored with revision, i.e., these values are not versioned.
To be more in line with expectations we're going to add support for versioning these external data points. Whenever there is a meta/term operation, apart from read, related to a post, we force a revision creation. On all revisions we preform the following:
- Go to the revision and add all relation to terms that the parent currently has
- Go to the revision and add all meta values that the parent currently has
Note: ACF forces revision creation when saving meta. We should either (1) prevent our system from copying meta for all ACF fields; or (2) prevent the ACF revision system from firing.
Version management
For all previous version we'll provide a set of operations:
delete: users will be allowed to destroy the revision for a given version, but only if it's not the latest. Deleting a version doesn't trigger a version number recount.
hide: users will be able to hide version from the history. Hiding a version is an operation to change it's status to draft, which effectively removes it from public availability.
Queries
When queries are performed we'll update the value of the default statuses to include unreleased in the results. After the posts are fetched we'll perform the following filter on the results:
- Check if the option to "hide unreleased"
post_version_hide_unreleased is false? IF it is, we don't change anything
- Loop through results and, for each
unreleased status, replace with get_current_version() (Note: take care to match the expected result structure)
API
Below are some operations that should be available as an API to be used by developers of Plugins and Themes, or internally.
get_versions( int $post_id ) : WP_Post
Given an ID it'll return a list of WP_Posts for all revisions that represent a version for the post. A revision is a version when:
- It has the highest ID for a given
post_version_# meta key.
get_current_version( int $post_id ) : WP_Post
Given a post ID it'll fetch the current revision ID. The current revision is determined as follows:
- Is the post itself in a status
publish? If true, the post ID is returned
- Call
get_versions( $post_id )
- Loop through versions (latest to oldest) to find the first that is
publishand return it
get_version( int $post_id, int|string $version ) : WP_Post
Retrieve a version based on it's ID or label.
- If
$version is an int fetch the revisions with meta key post_version_[$version]
- If
$version is a string fetch the revisions with meta value to $version
- Return ID
- If multiple revisions exist with the same version number, return the largest ID (latest)
The aim of this plugin is to provide a system for versioning any post type.
Features
The basic features to support are:
1, 2, 3 ...or semver1.0.0, 1.1.0, 2.0.0 ...)Advanced features to consider are:
How it might work
We'll use revisions to control the changes, as they already are used in WordPress to provide the same feature. We'll bend the working of revisions to allow for:
Versions
Versions are numbered sequentially starting from
1. All versions are stored as ameta_keywith the format:Labeling versions
If there is a need for a custom version label, the value of that label should be the value of the version
meta_key.Statuses
We'll need to add a custom
unreleasedstatus to handle a specific state a post can be in. This status represents a post that has a previous version, but the current is not yet finalized.A post is set tu
unreleasedwhen performing an action to create a new version, which means V1 is neverunreleased.The existence of this status is to prevent issues with queries that would exclude the post from results if w used
draftor other statuses.The First Version (V1)
When creating a post, a version is started (V1). All editing processes are the same, changes as draft, publishing or updating. When the post is published a new action appears: "Create new version".
What happens after V1?
New versions are born with the "Create new version" action. A new version is created with the following steps:
meta_keymeta_keyunreleasedpublishKeeping revisions clean
WordPress has a builtin system to clean old revisions. We interfere with this system to keep the latest revision of a given version from being deleted. (TODO: check if we don't need to disable revisions and implement our own system to prevent a loop in revision cleanup checks.)
Creating revisions
Revisions are potential versions in time. WordPress creates revisions when any of the post data is changed (this is only the data stored in the posts table). This means that meta fields and term associations are not stored with revision, i.e., these values are not versioned.
To be more in line with expectations we're going to add support for versioning these external data points. Whenever there is a meta/term operation, apart from read, related to a post, we force a revision creation. On all revisions we preform the following:
Note: ACF forces revision creation when saving meta. We should either (1) prevent our system from copying meta for all ACF fields; or (2) prevent the ACF revision system from firing.
Version management
For all previous version we'll provide a set of operations:
delete: users will be allowed to destroy the revision for a given version, but only if it's not the latest. Deleting a version doesn't trigger a version number recount.hide: users will be able to hide version from the history. Hiding a version is an operation to change it's status todraft, which effectively removes it from public availability.Queries
When queries are performed we'll update the value of the default statuses to include
unreleasedin the results. After the posts are fetched we'll perform the following filter on the results:post_version_hide_unreleasedisfalse? IF it is, we don't change anythingunreleasedstatus, replace withget_current_version()(Note: take care to match the expected result structure)API
Below are some operations that should be available as an API to be used by developers of Plugins and Themes, or internally.
get_versions( int $post_id ) : WP_PostGiven an ID it'll return a list of
WP_Posts for all revisions that represent a version for the post. A revision is a version when:post_version_#meta key.get_current_version( int $post_id ) : WP_PostGiven a post ID it'll fetch the current revision ID. The current revision is determined as follows:
publish? If true, the post ID is returnedget_versions( $post_id )publishand return itget_version( int $post_id, int|string $version ) : WP_PostRetrieve a version based on it's ID or label.
$versionis anintfetch the revisions with meta keypost_version_[$version]$versionis a string fetch the revisions with meta value to$version