wp/wp-admin/includes/revision.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    18  *
    18  *
    19  * @return array|bool Associative array of a post's revisioned fields and their diffs.
    19  * @return array|bool Associative array of a post's revisioned fields and their diffs.
    20  *                    Or, false on failure.
    20  *                    Or, false on failure.
    21  */
    21  */
    22 function wp_get_revision_ui_diff( $post, $compare_from, $compare_to ) {
    22 function wp_get_revision_ui_diff( $post, $compare_from, $compare_to ) {
    23 	if ( ! $post = get_post( $post ) )
    23 	if ( ! $post = get_post( $post ) ) {
    24 		return false;
    24 		return false;
       
    25 	}
    25 
    26 
    26 	if ( $compare_from ) {
    27 	if ( $compare_from ) {
    27 		if ( ! $compare_from = get_post( $compare_from ) )
    28 		if ( ! $compare_from = get_post( $compare_from ) ) {
    28 			return false;
    29 			return false;
       
    30 		}
    29 	} else {
    31 	} else {
    30 		// If we're dealing with the first revision...
    32 		// If we're dealing with the first revision...
    31 		$compare_from = false;
    33 		$compare_from = false;
    32 	}
    34 	}
    33 
    35 
    34 	if ( ! $compare_to = get_post( $compare_to ) )
    36 	if ( ! $compare_to = get_post( $compare_to ) ) {
    35 		return false;
    37 		return false;
       
    38 	}
    36 
    39 
    37 	// If comparing revisions, make sure we're dealing with the right post parent.
    40 	// If comparing revisions, make sure we're dealing with the right post parent.
    38 	// The parent post may be a 'revision' when revisions are disabled and we're looking at autosaves.
    41 	// The parent post may be a 'revision' when revisions are disabled and we're looking at autosaves.
    39 	if ( $compare_from && $compare_from->post_parent !== $post->ID && $compare_from->ID !== $post->ID )
    42 	if ( $compare_from && $compare_from->post_parent !== $post->ID && $compare_from->ID !== $post->ID ) {
    40 		return false;
    43 		return false;
    41 	if ( $compare_to->post_parent !== $post->ID && $compare_to->ID !== $post->ID )
    44 	}
       
    45 	if ( $compare_to->post_parent !== $post->ID && $compare_to->ID !== $post->ID ) {
    42 		return false;
    46 		return false;
       
    47 	}
    43 
    48 
    44 	if ( $compare_from && strtotime( $compare_from->post_date_gmt ) > strtotime( $compare_to->post_date_gmt ) ) {
    49 	if ( $compare_from && strtotime( $compare_from->post_date_gmt ) > strtotime( $compare_to->post_date_gmt ) ) {
    45 		$temp = $compare_from;
    50 		$temp         = $compare_from;
    46 		$compare_from = $compare_to;
    51 		$compare_from = $compare_to;
    47 		$compare_to = $temp;
    52 		$compare_to   = $temp;
    48 	}
    53 	}
    49 
    54 
    50 	// Add default title if title field is empty
    55 	// Add default title if title field is empty
    51 	if ( $compare_from && empty( $compare_from->post_title ) )
    56 	if ( $compare_from && empty( $compare_from->post_title ) ) {
    52 		$compare_from->post_title = __( '(no title)' );
    57 		$compare_from->post_title = __( '(no title)' );
    53 	if ( empty( $compare_to->post_title ) )
    58 	}
       
    59 	if ( empty( $compare_to->post_title ) ) {
    54 		$compare_to->post_title = __( '(no title)' );
    60 		$compare_to->post_title = __( '(no title)' );
       
    61 	}
    55 
    62 
    56 	$return = array();
    63 	$return = array();
    57 
    64 
    58 	foreach ( _wp_post_revision_fields( $post ) as $field => $name ) {
    65 	foreach ( _wp_post_revision_fields( $post ) as $field => $name ) {
    59 		/**
    66 		/**
    74 
    81 
    75 		/** This filter is documented in wp-admin/includes/revision.php */
    82 		/** This filter is documented in wp-admin/includes/revision.php */
    76 		$content_to = apply_filters( "_wp_post_revision_field_{$field}", $compare_to->$field, $field, $compare_to, 'to' );
    83 		$content_to = apply_filters( "_wp_post_revision_field_{$field}", $compare_to->$field, $field, $compare_to, 'to' );
    77 
    84 
    78 		$args = array(
    85 		$args = array(
    79 			'show_split_view' => true
    86 			'show_split_view' => true,
    80 		);
    87 		);
    81 
    88 
    82 		/**
    89 		/**
    83 		 * Filters revisions text diff options.
    90 		 * Filters revisions text diff options.
    84 		 *
    91 		 *
   102 
   109 
   103 		if ( ! $diff && 'post_title' === $field ) {
   110 		if ( ! $diff && 'post_title' === $field ) {
   104 			// It's a better user experience to still show the Title, even if it didn't change.
   111 			// It's a better user experience to still show the Title, even if it didn't change.
   105 			// No, you didn't see this.
   112 			// No, you didn't see this.
   106 			$diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>';
   113 			$diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>';
   107 			$diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td><td></td><td>' . esc_html( $compare_to->post_title ) . '</td>';
   114 
       
   115 			// In split screen mode, show the title before/after side by side.
       
   116 			if ( true === $args['show_split_view'] ) {
       
   117 				$diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td><td></td><td>' . esc_html( $compare_to->post_title ) . '</td>';
       
   118 			} else {
       
   119 				$diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td>';
       
   120 
       
   121 				// In single column mode, only show the title once if unchanged.
       
   122 				if ( $compare_from->post_title !== $compare_to->post_title ) {
       
   123 					$diff .= '</tr><tr><td>' . esc_html( $compare_to->post_title ) . '</td>';
       
   124 				}
       
   125 			}
       
   126 
   108 			$diff .= '</tr></tbody>';
   127 			$diff .= '</tr></tbody>';
   109 			$diff .= '</table>';
   128 			$diff .= '</table>';
   110 		}
   129 		}
   111 
   130 
   112 		if ( $diff ) {
   131 		if ( $diff ) {
   113 			$return[] = array(
   132 			$return[] = array(
   114 				'id' => $field,
   133 				'id'   => $field,
   115 				'name' => $name,
   134 				'name' => $name,
   116 				'diff' => $diff,
   135 				'diff' => $diff,
   117 			);
   136 			);
   118 		}
   137 		}
   119 	}
   138 	}
   121 	/**
   140 	/**
   122 	 * Filters the fields displayed in the post revision diff UI.
   141 	 * Filters the fields displayed in the post revision diff UI.
   123 	 *
   142 	 *
   124 	 * @since 4.1.0
   143 	 * @since 4.1.0
   125 	 *
   144 	 *
   126 	 * @param array   $return       Revision UI fields. Each item is an array of id, name and diff.
   145 	 * @param array[] $return       Array of revision UI fields. Each item is an array of id, name, and diff.
   127 	 * @param WP_Post $compare_from The revision post to compare from.
   146 	 * @param WP_Post $compare_from The revision post to compare from.
   128 	 * @param WP_Post $compare_to   The revision post to compare to.
   147 	 * @param WP_Post $compare_to   The revision post to compare to.
   129 	 */
   148 	 */
   130 	return apply_filters( 'wp_get_revision_ui_diff', $return, $compare_from, $compare_to );
   149 	return apply_filters( 'wp_get_revision_ui_diff', $return, $compare_from, $compare_to );
   131 
   150 
   141  * @param int        $from                 Optional. The revision ID to compare from.
   160  * @param int        $from                 Optional. The revision ID to compare from.
   142  *
   161  *
   143  * @return array An associative array of revision data and related settings.
   162  * @return array An associative array of revision data and related settings.
   144  */
   163  */
   145 function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) {
   164 function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) {
   146 	$post = get_post( $post );
   165 	$post    = get_post( $post );
   147 	$authors = array();
   166 	$authors = array();
   148 	$now_gmt = time();
   167 	$now_gmt = time();
   149 
   168 
   150 	$revisions = wp_get_post_revisions( $post->ID, array( 'order' => 'ASC', 'check_enabled' => false ) );
   169 	$revisions = wp_get_post_revisions(
       
   170 		$post->ID,
       
   171 		array(
       
   172 			'order'         => 'ASC',
       
   173 			'check_enabled' => false,
       
   174 		)
       
   175 	);
   151 	// If revisions are disabled, we only want autosaves and the current post.
   176 	// If revisions are disabled, we only want autosaves and the current post.
   152 	if ( ! wp_revisions_enabled( $post ) ) {
   177 	if ( ! wp_revisions_enabled( $post ) ) {
   153 		foreach ( $revisions as $revision_id => $revision ) {
   178 		foreach ( $revisions as $revision_id => $revision ) {
   154 			if ( ! wp_is_post_autosave( $revision ) )
   179 			if ( ! wp_is_post_autosave( $revision ) ) {
   155 				unset( $revisions[ $revision_id ] );
   180 				unset( $revisions[ $revision_id ] );
       
   181 			}
   156 		}
   182 		}
   157 		$revisions = array( $post->ID => $post ) + $revisions;
   183 		$revisions = array( $post->ID => $post ) + $revisions;
   158 	}
   184 	}
   159 
   185 
   160 	$show_avatars = get_option( 'show_avatars' );
   186 	$show_avatars = get_option( 'show_avatars' );
   161 
   187 
   162 	cache_users( wp_list_pluck( $revisions, 'post_author' ) );
   188 	cache_users( wp_list_pluck( $revisions, 'post_author' ) );
   163 
   189 
   164 	$can_restore = current_user_can( 'edit_post', $post->ID );
   190 	$can_restore = current_user_can( 'edit_post', $post->ID );
   165 	$current_id = false;
   191 	$current_id  = false;
   166 
   192 
   167 	foreach ( $revisions as $revision ) {
   193 	foreach ( $revisions as $revision ) {
   168 		$modified = strtotime( $revision->post_modified );
   194 		$modified     = strtotime( $revision->post_modified );
   169 		$modified_gmt = strtotime( $revision->post_modified_gmt . ' +0000' );
   195 		$modified_gmt = strtotime( $revision->post_modified_gmt . ' +0000' );
   170 		if ( $can_restore ) {
   196 		if ( $can_restore ) {
   171 			$restore_link = str_replace( '&amp;', '&', wp_nonce_url(
   197 			$restore_link = str_replace(
   172 				add_query_arg(
   198 				'&amp;',
   173 					array( 'revision' => $revision->ID,
   199 				'&',
   174 						'action' => 'restore' ),
   200 				wp_nonce_url(
       
   201 					add_query_arg(
       
   202 						array(
       
   203 							'revision' => $revision->ID,
       
   204 							'action'   => 'restore',
       
   205 						),
   175 						admin_url( 'revision.php' )
   206 						admin_url( 'revision.php' )
   176 				),
   207 					),
   177 				"restore-post_{$revision->ID}"
   208 					"restore-post_{$revision->ID}"
   178 			) );
   209 				)
       
   210 			);
   179 		}
   211 		}
   180 
   212 
   181 		if ( ! isset( $authors[ $revision->post_author ] ) ) {
   213 		if ( ! isset( $authors[ $revision->post_author ] ) ) {
   182 			$authors[ $revision->post_author ] = array(
   214 			$authors[ $revision->post_author ] = array(
   183 				'id' => (int) $revision->post_author,
   215 				'id'     => (int) $revision->post_author,
   184 				'avatar' => $show_avatars ? get_avatar( $revision->post_author, 32 ) : '',
   216 				'avatar' => $show_avatars ? get_avatar( $revision->post_author, 32 ) : '',
   185 				'name' => get_the_author_meta( 'display_name', $revision->post_author ),
   217 				'name'   => get_the_author_meta( 'display_name', $revision->post_author ),
   186 			);
   218 			);
   187 		}
   219 		}
   188 
   220 
   189 		$autosave = (bool) wp_is_post_autosave( $revision );
   221 		$autosave = (bool) wp_is_post_autosave( $revision );
   190 		$current = ! $autosave && $revision->post_modified_gmt === $post->post_modified_gmt;
   222 		$current  = ! $autosave && $revision->post_modified_gmt === $post->post_modified_gmt;
   191 		if ( $current && ! empty( $current_id ) ) {
   223 		if ( $current && ! empty( $current_id ) ) {
   192 			// If multiple revisions have the same post_modified_gmt, highest ID is current.
   224 			// If multiple revisions have the same post_modified_gmt, highest ID is current.
   193 			if ( $current_id < $revision->ID ) {
   225 			if ( $current_id < $revision->ID ) {
   194 				$revisions[ $current_id ]['current'] = false;
   226 				$revisions[ $current_id ]['current'] = false;
   195 				$current_id = $revision->ID;
   227 				$current_id                          = $revision->ID;
   196 			} else {
   228 			} else {
   197 				$current = false;
   229 				$current = false;
   198 			}
   230 			}
   199 		} elseif ( $current ) {
   231 		} elseif ( $current ) {
   200 			$current_id = $revision->ID;
   232 			$current_id = $revision->ID;
   251 			'timeAgo'    => sprintf( __( '%s ago' ), human_time_diff( strtotime( $post->post_modified_gmt ), $now_gmt ) ),
   283 			'timeAgo'    => sprintf( __( '%s ago' ), human_time_diff( strtotime( $post->post_modified_gmt ), $now_gmt ) ),
   252 			'autosave'   => false,
   284 			'autosave'   => false,
   253 			'current'    => true,
   285 			'current'    => true,
   254 			'restoreUrl' => false,
   286 			'restoreUrl' => false,
   255 		);
   287 		);
   256 		$current_id = $post->ID;
   288 		$current_id             = $post->ID;
   257 	}
   289 	}
   258 
   290 
   259 	/*
   291 	/*
   260 	 * If a post has been saved since the last revision (no revisioned fields
   292 	 * If a post has been saved since the last revision (no revisioned fields
   261 	 * were changed), we may not have a "current" revision. Mark the latest
   293 	 * were changed), we may not have a "current" revision. Mark the latest
   286 		}
   318 		}
   287 	}
   319 	}
   288 
   320 
   289 	$from = absint( $from );
   321 	$from = absint( $from );
   290 
   322 
   291 	$diffs = array( array(
   323 	$diffs = array(
   292 		'id' => $from . ':' . $selected_revision_id,
   324 		array(
   293 		'fields' => wp_get_revision_ui_diff( $post->ID, $from, $selected_revision_id ),
   325 			'id'     => $from . ':' . $selected_revision_id,
   294 	));
   326 			'fields' => wp_get_revision_ui_diff( $post->ID, $from, $selected_revision_id ),
       
   327 		),
       
   328 	);
   295 
   329 
   296 	return array(
   330 	return array(
   297 		'postId'           => $post->ID,
   331 		'postId'         => $post->ID,
   298 		'nonce'            => wp_create_nonce( 'revisions-ajax-nonce' ),
   332 		'nonce'          => wp_create_nonce( 'revisions-ajax-nonce' ),
   299 		'revisionData'     => array_values( $revisions ),
   333 		'revisionData'   => array_values( $revisions ),
   300 		'to'               => $selected_revision_id,
   334 		'to'             => $selected_revision_id,
   301 		'from'             => $from,
   335 		'from'           => $from,
   302 		'diffData'         => $diffs,
   336 		'diffData'       => $diffs,
   303 		'baseUrl'          => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ),
   337 		'baseUrl'        => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ),
   304 		'compareTwoMode'   => absint( $compare_two_mode ), // Apparently booleans are not allowed
   338 		'compareTwoMode' => absint( $compare_two_mode ), // Apparently booleans are not allowed
   305 		'revisionIds'      => array_keys( $revisions ),
   339 		'revisionIds'    => array_keys( $revisions ),
   306 	);
   340 	);
   307 }
   341 }
   308 
   342 
   309 /**
   343 /**
   310  * Print JavaScript templates required for the revisions experience.
   344  * Print JavaScript templates required for the revisions experience.
   355 				<# } #>
   389 				<# } #>
   356 				<div class="author-card<# if ( data.attributes.autosave ) { #> autosave<# } #>">
   390 				<div class="author-card<# if ( data.attributes.autosave ) { #> autosave<# } #>">
   357 					{{{ data.attributes.author.avatar }}}
   391 					{{{ data.attributes.author.avatar }}}
   358 					<div class="author-info">
   392 					<div class="author-info">
   359 					<# if ( data.attributes.autosave ) { #>
   393 					<# if ( data.attributes.autosave ) { #>
   360 						<span class="byline"><?php printf( __( 'Autosave by %s' ),
   394 						<span class="byline">
   361 							'<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
   395 						<?php
       
   396 						printf(
       
   397 							__( 'Autosave by %s' ),
       
   398 							'<span class="author-name">{{ data.attributes.author.name }}</span>'
       
   399 						);
       
   400 						?>
       
   401 							</span>
   362 					<# } else if ( data.attributes.current ) { #>
   402 					<# } else if ( data.attributes.current ) { #>
   363 						<span class="byline"><?php printf( __( 'Current Revision by %s' ),
   403 						<span class="byline">
   364 							'<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
   404 						<?php
       
   405 						printf(
       
   406 							__( 'Current Revision by %s' ),
       
   407 							'<span class="author-name">{{ data.attributes.author.name }}</span>'
       
   408 						);
       
   409 						?>
       
   410 							</span>
   365 					<# } else { #>
   411 					<# } else { #>
   366 						<span class="byline"><?php printf( __( 'Revision by %s' ),
   412 						<span class="byline">
   367 							'<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
   413 						<?php
       
   414 						printf(
       
   415 							__( 'Revision by %s' ),
       
   416 							'<span class="author-name">{{ data.attributes.author.name }}</span>'
       
   417 						);
       
   418 						?>
       
   419 							</span>
   368 					<# } #>
   420 					<# } #>
   369 						<span class="time-ago">{{ data.attributes.timeAgo }}</span>
   421 						<span class="time-ago">{{ data.attributes.timeAgo }}</span>
   370 						<span class="date">({{ data.attributes.dateShort }})</span>
   422 						<span class="date">({{ data.attributes.dateShort }})</span>
   371 					</div>
   423 					</div>
   372 				<# if ( 'to' === data.type && data.attributes.restoreUrl ) { #>
   424 				<# if ( 'to' === data.type && data.attributes.restoreUrl ) { #>
   397 		<# _.each( data.fields, function( field ) { #>
   449 		<# _.each( data.fields, function( field ) { #>
   398 			<h3>{{ field.name }}</h3>
   450 			<h3>{{ field.name }}</h3>
   399 			{{{ field.diff }}}
   451 			{{{ field.diff }}}
   400 		<# }); #>
   452 		<# }); #>
   401 		</div>
   453 		</div>
   402 	</script><?php
   454 	</script>
       
   455 	<?php
   403 }
   456 }