Skip to content

Instantly share code, notes, and snippets.

@mattheu
Last active August 29, 2015 14:07
Show Gist options
  • Save mattheu/3720c50a506f067e408a to your computer and use it in GitHub Desktop.
Save mattheu/3720c50a506f067e408a to your computer and use it in GitHub Desktop.
Register Revisions for Meta and Taxonomies
<?php
/*
Plugin Name: HM Post Meta Revisions
Description: Register Revisions for meta keys.
Version: 1.0
Author: Matthew Haines-Young (building on code from John Blackbourn)
*/
class HM_Post_Meta_Revisions {
protected static $instance;
private $fields = array();
private $taxonomies = array();
function __construct() {
add_action( 'save_post', array( $this, 'save_revision_meta' ), 10 );
add_action( 'save_post', array( $this, 'save_revision_taxonomies' ), 10 );
add_action( 'wp_restore_post_revision', array( $this, 'restore_revision_meta' ), 10, 2 );
add_action( 'wp_restore_post_revision', array( $this, 'restore_revision_taxonomies' ), 10, 2 );
// Filter the post revision fields.
add_filter( '_wp_post_revision_fields', array( $this, 'post_revision_fields' ) );
// Always do a revision on save.
// @todo This could be a bit cleverer and check for changes in meta/taxonomies.
add_action( 'wp_save_post_revision_check_for_changes', '__return_false' );
}
/**
* Creates or returns an instance of this class.
*
* @return HM_Post_Meta_Revisions A single instance of this class.
*/
public static function get_instance() {
if ( null == self::$instance ) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Register revisions for meta key.
* Enable post revisions for a given key.
*
* @param string $id meta key
* @param string $label label. Used in revisions UI.
* @param string|closure $format_callback UI Format callback. Format raw meta value to be used in revisions UI.
* @return null
*/
function register_revisions_for_meta_key( $meta_key, $label, $format_callback = null ) {
$this->fields[ $meta_key ] = $label;
if ( $format_callback ) {
add_filter( "_wp_post_revision_field_$meta_key", $format_callback, 10, 3 );
}
}
/**
* Register revisions for taxonomy.
*
* @param string $id meta key
* @param string $label label. Used in revisions UI.
* @param string|closure $format_callback UI Format callback. Format raw meta value to be used in revisions UI.
* @return null
*/
function register_revisions_for_taxonomy( $taxonomy, $format_callback = null ) {
$taxonomy_object = get_taxonomy( $taxonomy );
$this->taxonomies[ $taxonomy_object->name ] = $taxonomy_object->label;
if ( $format_callback ) {
add_filter( "_wp_post_revision_field_$taxonomy", $format_callback, 10, 3 );
} else {
add_filter( "_wp_post_revision_field_$taxonomy", array( $this, 'taxonomy_format_callback' ), 10, 3 );
}
}
/**
* Filter the post revision fields to add the new meta/tax revisions.
*
* @param array $fields array key => label.
* @return array
*/
function post_revision_fields( $fields ) {
return array_merge( $fields, $this->fields, $this->taxonomies );
}
/**
* Restore metadata.
* This should be hooked in on wp_restore_post_revision
*
* @param int $post_id
* @param int $revision_id
* @return null
*/
function restore_revision_meta( $post_id, $revision_id ) {
foreach ( array_keys( $this->fields ) as $meta_key ) {
// Delete all existing meta for this key.
delete_post_meta( $post_id, $meta_key );
// Copy meta from revision to parent.
$values = get_metadata( 'post', $revision_id, $meta_key, false );
foreach ( $values as $value ) {
add_post_meta( $post_id, $meta_key, $value );
}
}
}
/**
* Save metadata revision. (hooked in on save_post)
* Notes
* Uses crazy closure to hook the actual meta data save in after the save post action of the parent has fired.
* I wanted to avoid directly saving the $_POST data as plugins may be modifying this before save.
*
* @param int $post_id
* @param int $revision_id
* @return null
*/
function save_revision_meta( $post_id ) {
$revision_id = $post_id;
$parent_id = wp_is_post_revision( $revision_id );
$closure = function( $post_id ) use ( $revision_id, $parent_id, &$closure ) {
if ( $post_id != $parent_id ) {
return;
}
remove_action( 'wp_insert_post', $closure, 100 );
foreach ( array_keys( $this->fields ) as $meta_key ) {
$values = get_metadata( 'post', $parent_id, $meta_key );
delete_metadata( 'post', $revision_id, $meta_key );
foreach ( $values as $value ) {
add_metadata( 'post', $revision_id, $meta_key, $value );
}
}
};
add_action( 'wp_insert_post', $closure, 100 );
}
/**
* Restore taxonomies.
* This should be hooked in on wp_restore_post_revision
*
* @param int $post_id
* @param int $revision_id
* @return null
*/
function restore_revision_taxonomies( $post_id, $revision_id ) {
foreach ( array_keys( $this->taxonomies ) as $taxonomy ) {
$terms = wp_get_object_terms( $revision_id, $taxonomy );
if ( empty( $terms ) ) {
wp_set_object_terms( $post_id, null, $taxonomy );
} else {
$terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
wp_set_object_terms( $post_id, $terms, $taxonomy );
}
}
}
/**
* Save taxonomy revision.
* This should be hooked in on save_post.
* Note - uses a slightly crazy closure to hook the actual meta data save in
* after all the save post actions of the parent have fired.
*
* @param int $post_id
* @param int $revision_id
* @return null
*/
function save_revision_taxonomies( $post_id ) {
$revision_id = $post_id;
$parent_id = wp_is_post_revision( $revision_id );
$closure = function( $post_id ) use ( $revision_id, $parent_id, &$closure ) {
if ( $post_id != $parent_id ) {
return;
}
remove_action( 'wp_insert_post', $closure, 100 );
foreach ( array_keys( $this->taxonomies ) as $taxonomy ) {
$terms = wp_get_object_terms( $parent_id, $taxonomy );
if ( ! empty( $terms ) ) {
$terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
wp_set_object_terms( $revision_id, $terms, $taxonomy );
}
}
};
add_action( 'wp_insert_post', $closure, 100 );
}
function taxonomy_format_callback( $value, $field, $parent ) {
$terms = wp_get_object_terms( $parent->ID, $field );
if ( ! empty( $terms ) ) {
return implode( "\r", wp_list_pluck( $terms, 'name' ) );
}
}
}
add_action( 'plugins_loaded', function() {
HM_Post_Meta_Revisions::get_instance();
} );
/**
* Register revisions for a meta key.
*
* @param string $meta_key meta key
* @param string $label label used in revisions UI
* @param mixed $format_callback callback to format data for use in revisions UI.
* @return null
*/
function hmpmr_register_revisions_for_meta_key( $meta_key, $label, $format_callback = null ) {
HM_Post_Meta_Revisions::get_instance()->register_revisions_for_meta_key( $meta_key, $label, $format_callback );
}
/**
* Register revisions for a taxonomy.
*
* @param string $meta_key meta key
* @param string $label label used in revisions UI
* @param mixed $format_callback callback to format data for use in revisions UI.
* @return null
*/
function hmpmr_register_revisions_for_taxonomy( $taxonomy, $format_callback = null ) {
HM_Post_Meta_Revisions::get_instance()->register_revisions_for_taxonomy( $taxonomy, $format_callback );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment