Saving revisions for custom post type meta data

I was recently asked to create a membership listing site and although there are may great plugins for this, none offered the specific functionality needed.

The solution had to be to create a custom post type with custom fields. My favourite plugin to do this is CustomPress by WPMUDEV. Apart from the convenience of using a plugin to create the CPT, a key requirement of the project was that the client needed to create additional fields herself. The plugin provides an excellent easy to use interface for this.

Another key requirement was that there would be different users who could update the records of the members and the system admin would need to be able to keep track of the changes made by the other users.

WordPress by default does not keep revisions of post meta data so I set about devising a solution. Searching online I came across an excellent post by John Blackbourn with many helpful pointers. So here is my solution for revisions of custom fields of a custom post type.

Create the CPT and fields

Using CustomPress I created a ‘member’ post type and assigned a number of custom fields to it.  The first task is to create a function to retrieve those fields.

function ca_members_fields(){
 global $CustomPress_Core;
  $member_fields=$CustomPress_Core->get_custom_fields_set('member');
  return $member_fields;
 }

Add the custom fields to the revisions screen

By default WordPress makes only the main post fields available in the revisions screen.
So we need to add our meta data fields to the revision screen array.

add_filter( '_wp_post_revision_fields','ca_member_fields', 10, 1 );

function ca_member_fields( $fields ) {
  $members_fields=ca_members_fields();
   foreach($members_fields as $fieldname=>$members_field){ 
     $fields['ct_'.$fieldname] = $members_field['field_title']; 
   }
 return $fields;
}

CustomPress stores the key for a metadata field prefixed with ‘ct_’  so notice in the code that the field name is prefixed as such.

To show the revision data on the screen we need to create a function to get the meta data for each field.

function ca_field( $value, $field_name,$post ) { 
  $members_fields=ca_members_fields(); 
   foreach($members_fields as $fieldname=>$members_field){ 
    if($field_name==$fieldname){ 
     $value= get_metadata( 'post',$post->ID, 'ct_'.$fieldname , true ); 
    } 
   } 
  return $value; 
}

Each of the custom fields needs to be added to the revision screen. So we add the filter _wp_post_revision_field_{customfield} to each of these custom fields. Then hook this into the admin_head filter so that it gets run on page load.

function ca_custom_admin_head() {
 $members_fields=ca_members_fields(); 
  foreach($members_fields as $fieldname=>$members_field){ 
   add_filter( '_wp_post_revision_field_'.'ct_'.$fieldname, 'ca_field', 10, 4 ); 
 }
}
add_action( 'admin_head', 'ca_custom_admin_head' );

Saving revisions

Tell WordPress to save the revision data for each of the custom fields

add_action( 'save_post','ca_save_member_revision', 10, 2 );
function ca_save_member_revision( $post_id, $post ) {
   if ( $parent_id = wp_is_post_revision( $post_id ) ) {
    $parent = get_post( $parent_id );
         if($parent->post_type='member'){
                $members_fields=ca_members_fields();
                foreach($members_fields as $fieldname=>$members_field){      
                    $meta = get_post_meta( $parent->ID, 'ct_'.$fieldname, true );
                    if ( false !== $meta ){
                         add_metadata( 'post', $post_id, 'ct_'.$fieldname, $meta );   
                    }
               }
           }     
      }
}

Finally allow each of the custom fields to be restored.

add_action( 'wp_restore_post_revision',    'ca_restore_revision', 10, 2 );
function ca_restore_revision( $post_id, $revision_id ) {
    $post     = get_post( $post_id );
    $revision = get_post( $revision_id );        
    if($post->post_type='member'){
        $members_fields=ca_members_fields();
        foreach($members_fields as $fieldname=>$members_field){         
           $meta = get_metadata( 'post', $revision->ID, 'ct_'.$fieldname, true );
           if ( false !== $meta ){    
            update_post_meta( $post_id, 'ct_'.$fieldname, $meta );           
           }
        }   
    }    
}