56 |
56 |
57 foreach ( _wp_post_revision_fields() as $field => $name ) { |
57 foreach ( _wp_post_revision_fields() as $field => $name ) { |
58 /** |
58 /** |
59 * Contextually filter a post revision field. |
59 * Contextually filter a post revision field. |
60 * |
60 * |
61 * The dynamic portion of the hook name, $field, corresponds to each of the post |
61 * The dynamic portion of the hook name, `$field`, corresponds to each of the post |
62 * fields of the revision object being iterated over in a foreach statement. |
62 * fields of the revision object being iterated over in a foreach statement. |
63 * |
63 * |
64 * @since 3.6.0 |
64 * @since 3.6.0 |
65 * |
65 * |
66 * @param string $compare_from->$field The current revision field to compare to or from. |
66 * @param string $compare_from->$field The current revision field to compare to or from. |
67 * @param string $field The current revision field. |
67 * @param string $field The current revision field. |
68 * @param WP_Post $compare_from The revision post object to compare to or from. |
68 * @param WP_Post $compare_from The revision post object to compare to or from. |
69 * @param string null The context of whether the current revision is the old or the new one. Values are 'to' or 'from'. |
69 * @param string null The context of whether the current revision is the old |
|
70 * or the new one. Values are 'to' or 'from'. |
70 */ |
71 */ |
71 $content_from = $compare_from ? apply_filters( "_wp_post_revision_field_$field", $compare_from->$field, $field, $compare_from, 'from' ) : ''; |
72 $content_from = $compare_from ? apply_filters( "_wp_post_revision_field_$field", $compare_from->$field, $field, $compare_from, 'from' ) : ''; |
72 |
73 |
73 /** This filter is documented in wp-admin/includes/revision.php */ |
74 /** This filter is documented in wp-admin/includes/revision.php */ |
74 $content_to = apply_filters( "_wp_post_revision_field_$field", $compare_to->$field, $field, $compare_to, 'to' ); |
75 $content_to = apply_filters( "_wp_post_revision_field_$field", $compare_to->$field, $field, $compare_to, 'to' ); |
75 |
76 |
76 $diff = wp_text_diff( $content_from, $content_to, array( 'show_split_view' => true ) ); |
77 $args = array( |
|
78 'show_split_view' => true |
|
79 ); |
|
80 |
|
81 /** |
|
82 * Filter revisions text diff options. |
|
83 * |
|
84 * Filter the options passed to {@see wp_text_diff()} when viewing a post revision. |
|
85 * |
|
86 * @since 4.1.0 |
|
87 * |
|
88 * @param array $args { |
|
89 * Associative array of options to pass to {@see wp_text_diff()}. |
|
90 * |
|
91 * @type bool $show_split_view True for split view (two columns), false for |
|
92 * un-split view (single column). Default true. |
|
93 * } |
|
94 * @param string $field The current revision field. |
|
95 * @param WP_Post $compare_from The revision post to compare from. |
|
96 * @param WP_Post $compare_to The revision post to compare to. |
|
97 */ |
|
98 $args = apply_filters( 'revision_text_diff_options', $args, $field, $compare_from, $compare_to ); |
|
99 |
|
100 $diff = wp_text_diff( $content_from, $content_to, $args ); |
77 |
101 |
78 if ( ! $diff && 'post_title' === $field ) { |
102 if ( ! $diff && 'post_title' === $field ) { |
79 // It's a better user experience to still show the Title, even if it didn't change. |
103 // It's a better user experience to still show the Title, even if it didn't change. |
80 // No, you didn't see this. |
104 // No, you didn't see this. |
81 $diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>'; |
105 $diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>'; |
106 * |
141 * |
107 * @return array An associative array of revision data and related settings. |
142 * @return array An associative array of revision data and related settings. |
108 */ |
143 */ |
109 function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) { |
144 function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) { |
110 $post = get_post( $post ); |
145 $post = get_post( $post ); |
111 $revisions = $authors = array(); |
146 $authors = array(); |
112 $now_gmt = time(); |
147 $now_gmt = time(); |
113 |
148 |
114 $revisions = wp_get_post_revisions( $post->ID, array( 'order' => 'ASC', 'check_enabled' => false ) ); |
149 $revisions = wp_get_post_revisions( $post->ID, array( 'order' => 'ASC', 'check_enabled' => false ) ); |
115 // If revisions are disabled, we only want autosaves and the current post. |
150 // If revisions are disabled, we only want autosaves and the current post. |
116 if ( ! wp_revisions_enabled( $post ) ) { |
151 if ( ! wp_revisions_enabled( $post ) ) { |
124 $show_avatars = get_option( 'show_avatars' ); |
159 $show_avatars = get_option( 'show_avatars' ); |
125 |
160 |
126 cache_users( wp_list_pluck( $revisions, 'post_author' ) ); |
161 cache_users( wp_list_pluck( $revisions, 'post_author' ) ); |
127 |
162 |
128 $can_restore = current_user_can( 'edit_post', $post->ID ); |
163 $can_restore = current_user_can( 'edit_post', $post->ID ); |
|
164 $current_id = false; |
129 |
165 |
130 foreach ( $revisions as $revision ) { |
166 foreach ( $revisions as $revision ) { |
131 $modified = strtotime( $revision->post_modified ); |
167 $modified = strtotime( $revision->post_modified ); |
132 $modified_gmt = strtotime( $revision->post_modified_gmt ); |
168 $modified_gmt = strtotime( $revision->post_modified_gmt ); |
133 if ( $can_restore ) { |
169 if ( $can_restore ) { |
165 |
201 |
166 $revisions[ $revision->ID ] = array( |
202 $revisions[ $revision->ID ] = array( |
167 'id' => $revision->ID, |
203 'id' => $revision->ID, |
168 'title' => get_the_title( $post->ID ), |
204 'title' => get_the_title( $post->ID ), |
169 'author' => $authors[ $revision->post_author ], |
205 'author' => $authors[ $revision->post_author ], |
170 'date' => date_i18n( __( 'M j, Y @ G:i' ), $modified ), |
206 'date' => date_i18n( __( 'M j, Y @ H:i' ), $modified ), |
171 'dateShort' => date_i18n( _x( 'j M @ G:i', 'revision date short format' ), $modified ), |
207 'dateShort' => date_i18n( _x( 'j M @ H:i', 'revision date short format' ), $modified ), |
172 'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( $modified_gmt, $now_gmt ) ), |
208 'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( $modified_gmt, $now_gmt ) ), |
173 'autosave' => $autosave, |
209 'autosave' => $autosave, |
174 'current' => $current, |
210 'current' => $current, |
175 'restoreUrl' => $can_restore ? $restore_link : false, |
211 'restoreUrl' => $can_restore ? $restore_link : false, |
176 ); |
212 ); |
177 } |
213 } |
178 |
214 |
179 // If a post has been saved since the last revision (no revisioned fields were changed) |
215 /** |
180 // we may not have a "current" revision. Mark the latest revision as "current". |
216 * If we only have one revision, the initial revision is missing; This happens |
|
217 * when we have an autsosave and the user has clicked 'View the Autosave' |
|
218 */ |
|
219 if ( 1 === sizeof( $revisions ) ) { |
|
220 $revisions[ $post->ID ] = array( |
|
221 'id' => $post->ID, |
|
222 'title' => get_the_title( $post->ID ), |
|
223 'author' => $authors[ $post->post_author ], |
|
224 'date' => date_i18n( __( 'M j, Y @ H:i' ), strtotime( $post->post_modified ) ), |
|
225 'dateShort' => date_i18n( _x( 'j M @ H:i', 'revision date short format' ), strtotime( $post->post_modified ) ), |
|
226 'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( strtotime( $post->post_modified_gmt ), $now_gmt ) ), |
|
227 'autosave' => false, |
|
228 'current' => true, |
|
229 'restoreUrl' => false, |
|
230 ); |
|
231 $current_id = $post->ID; |
|
232 } |
|
233 |
|
234 /* |
|
235 * If a post has been saved since the last revision (no revisioned fields |
|
236 * were changed), we may not have a "current" revision. Mark the latest |
|
237 * revision as "current". |
|
238 */ |
181 if ( empty( $current_id ) ) { |
239 if ( empty( $current_id ) ) { |
182 if ( $revisions[ $revision->ID ]['autosave'] ) { |
240 if ( $revisions[ $revision->ID ]['autosave'] ) { |
183 $revision = end( $revisions ); |
241 $revision = end( $revisions ); |
184 while ( $revision['autosave'] ) { |
242 while ( $revision['autosave'] ) { |
185 $revision = prev( $revisions ); |
243 $revision = prev( $revisions ); |
189 $current_id = $revision->ID; |
247 $current_id = $revision->ID; |
190 } |
248 } |
191 $revisions[ $current_id ]['current'] = true; |
249 $revisions[ $current_id ]['current'] = true; |
192 } |
250 } |
193 |
251 |
194 // Now, grab the initial diff |
252 // Now, grab the initial diff. |
195 $compare_two_mode = is_numeric( $from ); |
253 $compare_two_mode = is_numeric( $from ); |
196 if ( ! $compare_two_mode ) { |
254 if ( ! $compare_two_mode ) { |
197 $found = array_search( $selected_revision_id, array_keys( $revisions ) ); |
255 $found = array_search( $selected_revision_id, array_keys( $revisions ) ); |
198 if ( $found ) { |
256 if ( $found ) { |
199 $from = array_keys( array_slice( $revisions, $found - 1, 1, true ) ); |
257 $from = array_keys( array_slice( $revisions, $found - 1, 1, true ) ); |
220 'baseUrl' => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ), |
278 'baseUrl' => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ), |
221 'compareTwoMode' => absint( $compare_two_mode ), // Apparently booleans are not allowed |
279 'compareTwoMode' => absint( $compare_two_mode ), // Apparently booleans are not allowed |
222 'revisionIds' => array_keys( $revisions ), |
280 'revisionIds' => array_keys( $revisions ), |
223 ); |
281 ); |
224 } |
282 } |
|
283 |
|
284 /** |
|
285 * Print JavaScript templates required for the revisions experience. |
|
286 * |
|
287 * @since 4.1.0 |
|
288 * |
|
289 * @global WP_Post $post The global `$post` object. |
|
290 */ |
|
291 function wp_print_revision_templates() { |
|
292 global $post; |
|
293 ?><script id="tmpl-revisions-frame" type="text/html"> |
|
294 <div class="revisions-control-frame"></div> |
|
295 <div class="revisions-diff-frame"></div> |
|
296 </script> |
|
297 |
|
298 <script id="tmpl-revisions-buttons" type="text/html"> |
|
299 <div class="revisions-previous"> |
|
300 <input class="button" type="button" value="<?php echo esc_attr_x( 'Previous', 'Button label for a previous revision' ); ?>" /> |
|
301 </div> |
|
302 |
|
303 <div class="revisions-next"> |
|
304 <input class="button" type="button" value="<?php echo esc_attr_x( 'Next', 'Button label for a next revision' ); ?>" /> |
|
305 </div> |
|
306 </script> |
|
307 |
|
308 <script id="tmpl-revisions-checkbox" type="text/html"> |
|
309 <div class="revision-toggle-compare-mode"> |
|
310 <label> |
|
311 <input type="checkbox" class="compare-two-revisions" |
|
312 <# |
|
313 if ( 'undefined' !== typeof data && data.model.attributes.compareTwoMode ) { |
|
314 #> checked="checked"<# |
|
315 } |
|
316 #> |
|
317 /> |
|
318 <?php esc_attr_e( 'Compare any two revisions' ); ?> |
|
319 </label> |
|
320 </div> |
|
321 </script> |
|
322 |
|
323 <script id="tmpl-revisions-meta" type="text/html"> |
|
324 <# if ( ! _.isUndefined( data.attributes ) ) { #> |
|
325 <div class="diff-title"> |
|
326 <# if ( 'from' === data.type ) { #> |
|
327 <strong><?php _ex( 'From:', 'Followed by post revision info' ); ?></strong> |
|
328 <# } else if ( 'to' === data.type ) { #> |
|
329 <strong><?php _ex( 'To:', 'Followed by post revision info' ); ?></strong> |
|
330 <# } #> |
|
331 <div class="author-card<# if ( data.attributes.autosave ) { #> autosave<# } #>"> |
|
332 {{{ data.attributes.author.avatar }}} |
|
333 <div class="author-info"> |
|
334 <# if ( data.attributes.autosave ) { #> |
|
335 <span class="byline"><?php printf( __( 'Autosave by %s' ), |
|
336 '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span> |
|
337 <# } else if ( data.attributes.current ) { #> |
|
338 <span class="byline"><?php printf( __( 'Current Revision by %s' ), |
|
339 '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span> |
|
340 <# } else { #> |
|
341 <span class="byline"><?php printf( __( 'Revision by %s' ), |
|
342 '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span> |
|
343 <# } #> |
|
344 <span class="time-ago">{{ data.attributes.timeAgo }}</span> |
|
345 <span class="date">({{ data.attributes.dateShort }})</span> |
|
346 </div> |
|
347 <# if ( 'to' === data.type && data.attributes.restoreUrl ) { #> |
|
348 <input <?php if ( wp_check_post_lock( $post->ID ) ) { ?> |
|
349 disabled="disabled" |
|
350 <?php } else { ?> |
|
351 <# if ( data.attributes.current ) { #> |
|
352 disabled="disabled" |
|
353 <# } #> |
|
354 <?php } ?> |
|
355 <# if ( data.attributes.autosave ) { #> |
|
356 type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Autosave' ); ?>" /> |
|
357 <# } else { #> |
|
358 type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Revision' ); ?>" /> |
|
359 <# } #> |
|
360 <# } #> |
|
361 </div> |
|
362 <# if ( 'tooltip' === data.type ) { #> |
|
363 <div class="revisions-tooltip-arrow"><span></span></div> |
|
364 <# } #> |
|
365 <# } #> |
|
366 </script> |
|
367 |
|
368 <script id="tmpl-revisions-diff" type="text/html"> |
|
369 <div class="loading-indicator"><span class="spinner"></span></div> |
|
370 <div class="diff-error"><?php _e( 'Sorry, something went wrong. The requested comparison could not be loaded.' ); ?></div> |
|
371 <div class="diff"> |
|
372 <# _.each( data.fields, function( field ) { #> |
|
373 <h3>{{ field.name }}</h3> |
|
374 {{{ field.diff }}} |
|
375 <# }); #> |
|
376 </div> |
|
377 </script><?php |
|
378 } |