<?php 

/*
*  Revisions
*
*  This Class contains all the functionality for adding ACF fields to the WP revisions interface
*
*  @type	class
*  @date	11/08/13
*/

class acf_revisions
{

	/*
	*  __construct
	*
	*  A good place to add actions / filters
	*
	*  @type	function
	*  @date	11/08/13
	*
	*  @param	N/A
	*  @return	N/A
	*/
	
	function __construct()
	{
		// actions		
		add_action('wp_restore_post_revision', array($this, 'wp_restore_post_revision'), 10, 2 );
		
		
		// filters
		add_filter('_wp_post_revision_fields', array($this, 'wp_post_revision_fields') );
		add_filter('wp_save_post_revision_check_for_changes', array($this, 'force_save_revision'), 10, 3);
	}
	
	
	/*
	*  force_save_revision
	*
	*  This filter will return false and force WP to save a revision. This is required due to
	*  WP checking only post_title, post_excerpt and post_content values, not custom fields.
	*
	*  @type	filter
	*  @date	19/09/13
	*
	*  @param	$return (boolean) defaults to true
	*  @param	$last_revision (object) the last revision that WP will compare against
	*  @param	$post (object) the $post that WP will compare against
	*  @return	$return (boolean)
	*/
	
	function force_save_revision( $return, $last_revision, $post )
	{
		// preview hack
		if( isset($_POST['acf_has_changed']) && $_POST['acf_has_changed'] == '1' )
		{
			$return = false;
		}
		
		
		// return
		return $return;
	}
	
	
	/*
	*  wp_post_revision_fields
	*
	*  This filter will add the ACF fields to the returned array
	*  Versions 3.5 and 3.6 of WP feature different uses of the revisions filters, so there are
	*  some hacks to allow both versions to work correctly
	*
	*  @type	filter
	*  @date	11/08/13
	*
	*  @param	$post_id (int)
	*  @return	$post_id (int)
	*/
		
	function wp_post_revision_fields( $return ) {
		
		
		//globals
		global $post, $pagenow;
		

		// validate
		$allowed = false;
		
		
		// Normal revisions page
		if( $pagenow == 'revision.php' )
		{
			$allowed = true;
		}
		
		
		// WP 3.6 AJAX revision
		if( $pagenow == 'admin-ajax.php' && isset($_POST['action']) && $_POST['action'] == 'get-revision-diffs' )
		{
			$allowed = true;
		}
		
		
		// bail
		if( !$allowed ) 
		{
			return $return;
		}
		
		
		// vars
		$post_id = 0;
		
		
		// determine $post_id
		if( isset($_POST['post_id']) )
		{
			$post_id = $_POST['post_id'];
		}
		elseif( isset($post->ID) )
		{
			$post_id = $post->ID;
		}
		else
		{
			return $return;
		}
		
		
		// get field objects
		$fields = get_field_objects( $post_id, array('format_value' => false ) );
		
		
		if( $fields )
		{
			foreach( $fields as $field )
			{
				// dud field?
				if( !$field || !isset($field['name']) || !$field['name'] )
				{
					continue;
				}
				
				
				// Add field key / label
				$return[ $field['name'] ] = $field['label'];


				// load value
				add_filter('_wp_post_revision_field_' . $field['name'], array($this, 'wp_post_revision_field'), 10, 4);
				
				
				// WP 3.5: left vs right
				// Add a value of the revision ID (as there is no way to determine this within the '_wp_post_revision_field_' filter!)
				if( isset($_GET['action'], $_GET['left'], $_GET['right']) && $_GET['action'] == 'diff' )
				{
					global $left_revision, $right_revision;
					
					$left_revision->$field['name'] = 'revision_id=' . $_GET['left'];
					$right_revision->$field['name'] = 'revision_id=' . $_GET['right'];
				}
								
			}
		}
		
		
		return $return;
	
	}
	
	
	/*
	*  wp_post_revision_field
	*
	*  This filter will load the value for the given field and return it for rendering
	*
	*  @type	filter
	*  @date	11/08/13
	*
	*  @param	$value (mixed) should be false as it has not yet been loaded
	*  @param	$field_name (string) The name of the field
	*  @param	$post (mixed) Holds the $post object to load from - in WP 3.5, this is not passed!
	*  @param	$direction (string) to / from - not used
	*  @return	$value (string)
	*/
	
	function wp_post_revision_field( $value, $field_name, $post = null, $direction = false)
	{
		// vars
		$post_id = 0;
		
		
		// determine $post_id
		if( isset($post->ID) )
		{
			// WP 3.6
			$post_id = $post->ID;
		}
		elseif( isset($_GET['revision']) )
		{
			// WP 3.5
			$post_id = (int) $_GET['revision'];
		}
		elseif( strpos($value, 'revision_id=') !== false )
		{
			// WP 3.5 (left vs right)
			$post_id = (int) str_replace('revision_id=', '', $value);
		}
		
		
		// load field
		$field = get_field_object($field_name, $post_id, array('format_value' => false ));
		$value = $field['value'];
		
		
		// default formatting
		if( is_array($value) )
		{
			$value = implode(', ', $value);
		}
		
		
		// format
		if( $value )
		{
			// image?
			if( $field['type'] == 'image' || $field['type'] == 'file' )
			{
				$url = wp_get_attachment_url($value);
				$value = $value . ' (' . $url . ')';
			}
		}
		
		
		// return
		return $value;
	}
	
	
	/*
	*  wp_restore_post_revision
	*
	*  This action will copy and paste the metadata from a revision to the post
	*
	*  @type	action
	*  @date	11/08/13
	*
	*  @param	$parent_id (int) the destination post
	*  @return	$revision_id (int) the source post
	*/
	
	function wp_restore_post_revision( $post_id, $revision_id ) {
	
		// global
		global $wpdb;
		
		
		// vars
		$fields = array();
		
		
		// get field from postmeta
		$rows = $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM $wpdb->postmeta WHERE post_id=%d", 
			$revision_id
		), ARRAY_A);
		
		
		// populate $fields
		if( $rows )
		{
			foreach( $rows as $row )
			{
				// meta_key must start with '_'
				if( substr($row['meta_key'], 0, 1) !== '_' )
				{
					continue;
				}
				
				
				// meta_value must start with 'field_'
				if( substr($row['meta_value'], 0, 6) !== 'field_' )
				{
					continue;
				}
				
				
				// this is an ACF field, append to $fields
				$fields[] = substr($row['meta_key'], 1);
				
			}
		}
		
		
		// save data
		if( $rows )
		{
			foreach( $rows as $row )
			{
				if( in_array($row['meta_key'], $fields) )
				{
					update_post_meta( $post_id, $row['meta_key'], $row['meta_value'] );
				}
			}
		}
			
	}
	
			
}

new acf_revisions();

?>