Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/revision.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Post revision functions.
4
 *
5
 * @package WordPress
6
 * @subpackage Post_Revisions
7
 */
8
9
/**
10
 * Determines which fields of posts are to be saved in revisions.
11
 *
12
 * @since 2.6.0
13
 * @since 4.5.0 A `WP_Post` object can now be passed to the `$post` parameter.
14
 * @since 4.5.0 The optional `$autosave` parameter was deprecated and renamed to `$deprecated`.
15
 * @access private
16
 *
17
 * @staticvar array $fields
18
 *
19
 * @param array|WP_Post $post       Optional. A post array or a WP_Post object being processed
20
 *                                  for insertion as a post revision. Default empty array.
21
 * @param bool          $deprecated Not used.
22
 * @return array Array of fields that can be versioned.
23
 */
24
function _wp_post_revision_fields( $post = array(), $deprecated = false ) {
25
	static $fields = null;
26
27
	if ( ! is_array( $post ) ) {
28
		$post = get_post( $post, ARRAY_A );
29
	}
30
31
	if ( is_null( $fields ) ) {
32
		// Allow these to be versioned
33
		$fields = array(
34
			'post_title' => __( 'Title' ),
35
			'post_content' => __( 'Content' ),
36
			'post_excerpt' => __( 'Excerpt' ),
37
		);
38
	}
39
40
	/**
41
	 * Filters the list of fields saved in post revisions.
42
	 *
43
	 * Included by default: 'post_title', 'post_content' and 'post_excerpt'.
44
	 *
45
	 * Disallowed fields: 'ID', 'post_name', 'post_parent', 'post_date',
46
	 * 'post_date_gmt', 'post_status', 'post_type', 'comment_count',
47
	 * and 'post_author'.
48
	 *
49
	 * @since 2.6.0
50
	 * @since 4.5.0 The `$post` parameter was added.
51
	 *
52
	 * @param array $fields List of fields to revision. Contains 'post_title',
53
	 *                      'post_content', and 'post_excerpt' by default.
54
	 * @param array $post   A post array being processed for insertion as a post revision.
55
	 */
56
	$fields = apply_filters( '_wp_post_revision_fields', $fields, $post );
57
58
	// WP uses these internally either in versioning or elsewhere - they cannot be versioned
59
	foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect ) {
60
		unset( $fields[ $protect ] );
61
	}
62
63
64
	return $fields;
65
}
66
67
/**
68
 * Returns a post array ready to be inserted into the posts table as a post revision.
69
 *
70
 * @since 4.5.0
71
 * @access private
72
 *
73
 * @param array|WP_Post $post     Optional. A post array or a WP_Post object to be processed
74
 *                                for insertion as a post revision. Default empty array.
75
 * @param bool          $autosave Optional. Is the revision an autosave? Default false.
76
 * @return array Post array ready to be inserted as a post revision.
77
 */
78
function _wp_post_revision_data( $post = array(), $autosave = false ) {
79
	if ( ! is_array( $post ) ) {
80
		$post = get_post( $post, ARRAY_A );
81
	}
82
83
	$fields = _wp_post_revision_fields( $post );
0 ignored issues
show
It seems like $post defined by get_post($post, ARRAY_A) on line 80 can also be of type null; however, _wp_post_revision_fields() does only seem to accept array|object<WP_Post>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
84
85
	$revision_data = array();
86
87
	foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field ) {
88
		$revision_data[ $field ] = $post[ $field ];
89
	}
90
91
	$revision_data['post_parent']   = $post['ID'];
92
	$revision_data['post_status']   = 'inherit';
93
	$revision_data['post_type']     = 'revision';
94
	$revision_data['post_name']     = $autosave ? "$post[ID]-autosave-v1" : "$post[ID]-revision-v1"; // "1" is the revisioning system version
95
	$revision_data['post_date']     = isset( $post['post_modified'] ) ? $post['post_modified'] : '';
96
	$revision_data['post_date_gmt'] = isset( $post['post_modified_gmt'] ) ? $post['post_modified_gmt'] : '';
97
98
	return $revision_data;
99
}
100
101
/**
102
 * Creates a revision for the current version of a post.
103
 *
104
 * Typically used immediately after a post update, as every update is a revision,
105
 * and the most recent revision always matches the current post.
106
 *
107
 * @since 2.6.0
108
 *
109
 * @param int $post_id The ID of the post to save as a revision.
110
 * @return int|WP_Error|void Void or 0 if error, new revision ID, if success.
111
 */
112
function wp_save_post_revision( $post_id ) {
113
	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
114
		return;
115
116
	if ( ! $post = get_post( $post_id ) )
117
		return;
118
119
	if ( ! post_type_supports( $post->post_type, 'revisions' ) )
120
		return;
121
122
	if ( 'auto-draft' == $post->post_status )
123
		return;
124
125
	if ( ! wp_revisions_enabled( $post ) )
0 ignored issues
show
It seems like $post defined by get_post($post_id) on line 116 can also be of type array; however, wp_revisions_enabled() does only seem to accept object<WP_Post>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
126
		return;
127
128
	// Compare the proposed update with the last stored revision verifying that
129
	// they are different, unless a plugin tells us to always save regardless.
130
	// If no previous revisions, save one
131
	if ( $revisions = wp_get_post_revisions( $post_id ) ) {
132
		// grab the last revision, but not an autosave
133
		foreach ( $revisions as $revision ) {
134
			if ( false !== strpos( $revision->post_name, "{$revision->post_parent}-revision" ) ) {
135
				$last_revision = $revision;
136
				break;
137
			}
138
		}
139
140
		/**
141
		 * Filters whether the post has changed since the last revision.
142
		 *
143
		 * By default a revision is saved only if one of the revisioned fields has changed.
144
		 * This filter can override that so a revision is saved even if nothing has changed.
145
		 *
146
		 * @since 3.6.0
147
		 *
148
		 * @param bool    $check_for_changes Whether to check for changes before saving a new revision.
149
		 *                                   Default true.
150
		 * @param WP_Post $last_revision     The last revision post object.
151
		 * @param WP_Post $post              The post object.
152
		 *
153
		 */
154
		if ( isset( $last_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', $check_for_changes = true, $last_revision, $post ) ) {
155
			$post_has_changed = false;
156
157
			foreach ( array_keys( _wp_post_revision_fields( $post ) ) as $field ) {
158
				if ( normalize_whitespace( $post->$field ) != normalize_whitespace( $last_revision->$field ) ) {
159
					$post_has_changed = true;
160
					break;
161
				}
162
			}
163
164
			/**
165
			 * Filters whether a post has changed.
166
			 *
167
			 * By default a revision is saved only if one of the revisioned fields has changed.
168
			 * This filter allows for additional checks to determine if there were changes.
169
			 *
170
			 * @since 4.1.0
171
			 *
172
			 * @param bool    $post_has_changed Whether the post has changed.
173
			 * @param WP_Post $last_revision    The last revision post object.
174
			 * @param WP_Post $post             The post object.
175
			 *
176
			 */
177
			$post_has_changed = (bool) apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $last_revision, $post );
178
179
			//don't save revision if post unchanged
180
			if ( ! $post_has_changed ) {
181
				return;
182
			}
183
		}
184
	}
185
186
	$return = _wp_put_post_revision( $post );
187
188
	// If a limit for the number of revisions to keep has been set,
189
	// delete the oldest ones.
190
	$revisions_to_keep = wp_revisions_to_keep( $post );
0 ignored issues
show
It seems like $post defined by get_post($post_id) on line 116 can also be of type array; however, wp_revisions_to_keep() does only seem to accept object<WP_Post>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
191
192
	if ( $revisions_to_keep < 0 )
193
		return $return;
194
195
	$revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) );
196
197
	$delete = count($revisions) - $revisions_to_keep;
198
199
	if ( $delete < 1 )
200
		return $return;
201
202
	$revisions = array_slice( $revisions, 0, $delete );
203
204
	for ( $i = 0; isset( $revisions[$i] ); $i++ ) {
205
		if ( false !== strpos( $revisions[ $i ]->post_name, 'autosave' ) )
206
			continue;
207
208
		wp_delete_post_revision( $revisions[ $i ]->ID );
209
	}
210
211
	return $return;
212
}
213
214
/**
215
 * Retrieve the autosaved data of the specified post.
216
 *
217
 * Returns a post object containing the information that was autosaved for the
218
 * specified post. If the optional $user_id is passed, returns the autosave for that user
219
 * otherwise returns the latest autosave.
220
 *
221
 * @since 2.6.0
222
 *
223
 * @param int $post_id The post ID.
224
 * @param int $user_id Optional The post author ID.
225
 * @return WP_Post|false The autosaved data or false on failure or when no autosave exists.
226
 */
227
function wp_get_post_autosave( $post_id, $user_id = 0 ) {
228
	$revisions = wp_get_post_revisions( $post_id, array( 'check_enabled' => false ) );
229
230
	foreach ( $revisions as $revision ) {
231
		if ( false !== strpos( $revision->post_name, "{$post_id}-autosave" ) ) {
232
			if ( $user_id && $user_id != $revision->post_author )
233
				continue;
234
235
			return $revision;
236
		}
237
	}
238
239
	return false;
240
}
241
242
/**
243
 * Determines if the specified post is a revision.
244
 *
245
 * @since 2.6.0
246
 *
247
 * @param int|WP_Post $post Post ID or post object.
248
 * @return false|int False if not a revision, ID of revision's parent otherwise.
249
 */
250
function wp_is_post_revision( $post ) {
251
	if ( !$post = wp_get_post_revision( $post ) )
252
		return false;
253
254
	return (int) $post->post_parent;
255
}
256
257
/**
258
 * Determines if the specified post is an autosave.
259
 *
260
 * @since 2.6.0
261
 *
262
 * @param int|WP_Post $post Post ID or post object.
263
 * @return false|int False if not a revision, ID of autosave's parent otherwise
264
 */
265
function wp_is_post_autosave( $post ) {
266
	if ( !$post = wp_get_post_revision( $post ) )
267
		return false;
268
269
	if ( false !== strpos( $post->post_name, "{$post->post_parent}-autosave" ) )
270
		return (int) $post->post_parent;
271
272
	return false;
273
}
274
275
/**
276
 * Inserts post data into the posts table as a post revision.
277
 *
278
 * @since 2.6.0
279
 * @access private
280
 *
281
 * @param int|WP_Post|array|null $post     Post ID, post object OR post array.
282
 * @param bool                   $autosave Optional. Is the revision an autosave?
283
 * @return int|WP_Error WP_Error or 0 if error, new revision ID if success.
284
 */
285
function _wp_put_post_revision( $post = null, $autosave = false ) {
286
	if ( is_object($post) )
287
		$post = get_object_vars( $post );
288
	elseif ( !is_array($post) )
289
		$post = get_post($post, ARRAY_A);
290
291
	if ( ! $post || empty($post['ID']) )
292
		return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
293
294
	if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
295
		return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
296
297
	$post = _wp_post_revision_data( $post, $autosave );
298
	$post = wp_slash($post); //since data is from db
299
300
	$revision_id = wp_insert_post( $post );
0 ignored issues
show
It seems like $post defined by wp_slash($post) on line 298 can also be of type string; however, wp_insert_post() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
301
	if ( is_wp_error($revision_id) )
302
		return $revision_id;
303
304
	if ( $revision_id ) {
305
		/**
306
		 * Fires once a revision has been saved.
307
		 *
308
		 * @since 2.6.0
309
		 *
310
		 * @param int $revision_id Post revision ID.
311
		 */
312
		do_action( '_wp_put_post_revision', $revision_id );
313
	}
314
315
	return $revision_id;
316
}
317
318
/**
319
 * Gets a post revision.
320
 *
321
 * @since 2.6.0
322
 *
323
 * @param int|WP_Post $post   The post ID or object.
324
 * @param string      $output Optional. OBJECT, ARRAY_A, or ARRAY_N.
325
 * @param string      $filter Optional sanitation filter. See sanitize_post().
326
 * @return WP_Post|array|null Null if error or post object if success.
327
 */
328
function wp_get_post_revision(&$post, $output = OBJECT, $filter = 'raw') {
329
	if ( !$revision = get_post( $post, OBJECT, $filter ) )
330
		return $revision;
331
	if ( 'revision' !== $revision->post_type )
332
		return null;
333
334
	if ( $output == OBJECT ) {
335
		return $revision;
336
	} elseif ( $output == ARRAY_A ) {
337
		$_revision = get_object_vars($revision);
338
		return $_revision;
339
	} elseif ( $output == ARRAY_N ) {
340
		$_revision = array_values(get_object_vars($revision));
341
		return $_revision;
342
	}
343
344
	return $revision;
345
}
346
347
/**
348
 * Restores a post to the specified revision.
349
 *
350
 * Can restore a past revision using all fields of the post revision, or only selected fields.
351
 *
352
 * @since 2.6.0
353
 *
354
 * @param int|WP_Post $revision_id Revision ID or revision object.
355
 * @param array       $fields      Optional. What fields to restore from. Defaults to all.
356
 * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success.
357
 */
358
function wp_restore_post_revision( $revision_id, $fields = null ) {
359
	if ( !$revision = wp_get_post_revision( $revision_id, ARRAY_A ) )
360
		return $revision;
361
362
	if ( !is_array( $fields ) )
363
		$fields = array_keys( _wp_post_revision_fields( $revision ) );
364
365
	$update = array();
366
	foreach ( array_intersect( array_keys( $revision ), $fields ) as $field ) {
367
		$update[$field] = $revision[$field];
368
	}
369
370
	if ( !$update )
371
		return false;
372
373
	$update['ID'] = $revision['post_parent'];
374
375
	$update = wp_slash( $update ); //since data is from db
376
377
	$post_id = wp_update_post( $update );
0 ignored issues
show
It seems like $update defined by wp_slash($update) on line 375 can also be of type string; however, wp_update_post() does only seem to accept array|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
378
	if ( ! $post_id || is_wp_error( $post_id ) )
379
		return $post_id;
380
381
	// Update last edit user
382
	update_post_meta( $post_id, '_edit_last', get_current_user_id() );
0 ignored issues
show
It seems like $post_id defined by wp_update_post($update) on line 377 can also be of type object<WP_Error>; however, update_post_meta() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
383
384
	/**
385
	 * Fires after a post revision has been restored.
386
	 *
387
	 * @since 2.6.0
388
	 *
389
	 * @param int $post_id     Post ID.
390
	 * @param int $revision_id Post revision ID.
391
	 */
392
	do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] );
393
394
	return $post_id;
395
}
396
397
/**
398
 * Deletes a revision.
399
 *
400
 * Deletes the row from the posts table corresponding to the specified revision.
401
 *
402
 * @since 2.6.0
403
 *
404
 * @param int|WP_Post $revision_id Revision ID or revision object.
405
 * @return array|false|WP_Post|WP_Error|null Null or WP_Error if error, deleted post if success.
406
 */
407
function wp_delete_post_revision( $revision_id ) {
408
	if ( ! $revision = wp_get_post_revision( $revision_id ) ) {
409
		return $revision;
410
	}
411
412
	$delete = wp_delete_post( $revision->ID );
413
	if ( $delete ) {
414
		/**
415
		 * Fires once a post revision has been deleted.
416
		 *
417
		 * @since 2.6.0
418
		 *
419
		 * @param int          $revision_id Post revision ID.
420
		 * @param object|array $revision    Post revision object or array.
421
		 */
422
		do_action( 'wp_delete_post_revision', $revision->ID, $revision );
423
	}
424
425
	return $delete;
426
}
427
428
/**
429
 * Returns all revisions of specified post.
430
 *
431
 * @since 2.6.0
432
 *
433
 * @see get_children()
434
 *
435
 * @param int|WP_Post $post_id Optional. Post ID or WP_Post object. Default is global `$post`.
436
 * @param array|null  $args    Optional. Arguments for retrieving post revisions. Default null.
437
 * @return array An array of revisions, or an empty array if none.
438
 */
439
function wp_get_post_revisions( $post_id = 0, $args = null ) {
440
	$post = get_post( $post_id );
441
	if ( ! $post || empty( $post->ID ) )
442
		return array();
443
444
	$defaults = array( 'order' => 'DESC', 'orderby' => 'date ID', 'check_enabled' => true );
445
	$args = wp_parse_args( $args, $defaults );
0 ignored issues
show
It seems like $args can also be of type null; however, wp_parse_args() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
446
447
	if ( $args['check_enabled'] && ! wp_revisions_enabled( $post ) )
0 ignored issues
show
It seems like $post defined by get_post($post_id) on line 440 can also be of type array; however, wp_revisions_enabled() does only seem to accept object<WP_Post>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
448
		return array();
449
450
	$args = array_merge( $args, array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit' ) );
451
452
	if ( ! $revisions = get_children( $args ) )
453
		return array();
454
455
	return $revisions;
456
}
457
458
/**
459
 * Determine if revisions are enabled for a given post.
460
 *
461
 * @since 3.6.0
462
 *
463
 * @param WP_Post $post The post object.
464
 * @return bool True if number of revisions to keep isn't zero, false otherwise.
465
 */
466
function wp_revisions_enabled( $post ) {
467
	return wp_revisions_to_keep( $post ) !== 0;
468
}
469
470
/**
471
 * Determine how many revisions to retain for a given post.
472
 *
473
 * By default, an infinite number of revisions are kept.
474
 *
475
 * The constant WP_POST_REVISIONS can be set in wp-config to specify the limit
476
 * of revisions to keep.
477
 *
478
 * @since 3.6.0
479
 *
480
 * @param WP_Post $post The post object.
481
 * @return int The number of revisions to keep.
482
 */
483
function wp_revisions_to_keep( $post ) {
484
	$num = WP_POST_REVISIONS;
485
486
	if ( true === $num )
487
		$num = -1;
488
	else
489
		$num = intval( $num );
490
491
	if ( ! post_type_supports( $post->post_type, 'revisions' ) )
492
		$num = 0;
493
494
	/**
495
	 * Filters the number of revisions to save for the given post.
496
	 *
497
	 * Overrides the value of WP_POST_REVISIONS.
498
	 *
499
	 * @since 3.6.0
500
	 *
501
	 * @param int     $num  Number of revisions to store.
502
	 * @param WP_Post $post Post object.
503
	 */
504
	return (int) apply_filters( 'wp_revisions_to_keep', $num, $post );
505
}
506
507
/**
508
 * Sets up the post object for preview based on the post autosave.
509
 *
510
 * @since 2.7.0
511
 * @access private
512
 *
513
 * @param WP_Post $post
514
 * @return WP_Post|false
515
 */
516
function _set_preview( $post ) {
517
	if ( ! is_object( $post ) ) {
518
		return $post;
519
	}
520
521
	$preview = wp_get_post_autosave( $post->ID );
522
	if ( ! is_object( $preview ) ) {
523
		return $post;
524
	}
525
526
	$preview = sanitize_post( $preview );
527
528
	$post->post_content = $preview->post_content;
529
	$post->post_title = $preview->post_title;
530
	$post->post_excerpt = $preview->post_excerpt;
531
532
	add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
533
	add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
534
535
	return $post;
536
}
537
538
/**
539
 * Filters the latest content for preview from the post autosave.
540
 *
541
 * @since 2.7.0
542
 * @access private
543
 */
544
function _show_post_preview() {
545
	if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) ) {
546
		$id = (int) $_GET['preview_id'];
547
548
		if ( false === wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $id ) )
549
			wp_die( __('Sorry, you are not allowed to preview drafts.') );
550
551
		add_filter('the_preview', '_set_preview');
552
	}
553
}
554
555
/**
556
 * Filters terms lookup to set the post format.
557
 *
558
 * @since 3.6.0
559
 * @access private
560
 *
561
 * @param array  $terms
562
 * @param int    $post_id
563
 * @param string $taxonomy
564
 * @return array
565
 */
566
function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) {
567
	if ( ! $post = get_post() )
568
		return $terms;
569
570 View Code Duplication
	if ( empty( $_REQUEST['post_format'] ) || $post->ID != $post_id || 'post_format' != $taxonomy || 'revision' == $post->post_type )
571
		return $terms;
572
573
	if ( 'standard' == $_REQUEST['post_format'] )
574
		$terms = array();
575
	elseif ( $term = get_term_by( 'slug', 'post-format-' . sanitize_key( $_REQUEST['post_format'] ), 'post_format' ) )
576
		$terms = array( $term ); // Can only have one post format
577
578
	return $terms;
579
}
580
581
/**
582
 * Filters post thumbnail lookup to set the post thumbnail.
583
 *
584
 * @since 4.6.0
585
 * @access private
586
 *
587
 * @param null|array|string $value    The value to return - a single metadata value, or an array of values.
588
 * @param int               $post_id  Post ID.
589
 * @param string            $meta_key Meta key.
590
 * @return null|array The default return value or the post thumbnail meta array.
591
 */
592
function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) {
593
	if ( ! $post = get_post() ) {
594
		return $value;
595
	}
596
597 View Code Duplication
	if ( empty( $_REQUEST['_thumbnail_id'] ) || $post->ID != $post_id || '_thumbnail_id' != $meta_key || 'revision' == $post->post_type ) {
598
		return $value;
599
	}
600
601
	$thumbnail_id = intval( $_REQUEST['_thumbnail_id'] );
602
	if ( $thumbnail_id <= 0 ) {
603
		return '';
604
	}
605
606
	return strval( $thumbnail_id );
607
}
608
609
/**
610
 * Gets the post revision version.
611
 *
612
 * @since 3.6.0
613
 * @access private
614
 *
615
 * @param WP_Post $revision
616
 * @return int|false
617
 */
618
function _wp_get_post_revision_version( $revision ) {
619
	if ( is_object( $revision ) )
620
		$revision = get_object_vars( $revision );
621
	elseif ( !is_array( $revision ) )
622
		return false;
623
624
	if ( preg_match( '/^\d+-(?:autosave|revision)-v(\d+)$/', $revision['post_name'], $matches ) )
625
		return (int) $matches[1];
626
627
	return 0;
628
}
629
630
/**
631
 * Upgrade the revisions author, add the current post as a revision and set the revisions version to 1
632
 *
633
 * @since 3.6.0
634
 * @access private
635
 *
636
 * @global wpdb $wpdb WordPress database abstraction object.
637
 *
638
 * @param WP_Post $post      Post object
639
 * @param array   $revisions Current revisions of the post
640
 * @return bool true if the revisions were upgraded, false if problems
641
 */
642
function _wp_upgrade_revisions_of_post( $post, $revisions ) {
643
	global $wpdb;
644
645
	// Add post option exclusively
646
	$lock = "revision-upgrade-{$post->ID}";
647
	$now = time();
648
	$result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no') /* LOCK */", $lock, $now ) );
649
	if ( ! $result ) {
650
		// If we couldn't get a lock, see how old the previous lock is
651
		$locked = get_option( $lock );
652
		if ( ! $locked ) {
653
			// Can't write to the lock, and can't read the lock.
654
			// Something broken has happened
655
			return false;
656
		}
657
658
		if ( $locked > $now - 3600 ) {
659
			// Lock is not too old: some other process may be upgrading this post.  Bail.
660
			return false;
661
		}
662
663
		// Lock is too old - update it (below) and continue
664
	}
665
666
	// If we could get a lock, re-"add" the option to fire all the correct filters.
667
	update_option( $lock, $now );
668
669
	reset( $revisions );
670
	$add_last = true;
671
672
	do {
673
		$this_revision = current( $revisions );
674
		$prev_revision = next( $revisions );
675
676
		$this_revision_version = _wp_get_post_revision_version( $this_revision );
677
678
		// Something terrible happened
679
		if ( false === $this_revision_version )
680
			continue;
681
682
		// 1 is the latest revision version, so we're already up to date.
683
		// No need to add a copy of the post as latest revision.
684
		if ( 0 < $this_revision_version ) {
685
			$add_last = false;
686
			continue;
687
		}
688
689
		// Always update the revision version
690
		$update = array(
691
			'post_name' => preg_replace( '/^(\d+-(?:autosave|revision))[\d-]*$/', '$1-v1', $this_revision->post_name ),
692
		);
693
694
		// If this revision is the oldest revision of the post, i.e. no $prev_revision,
695
		// the correct post_author is probably $post->post_author, but that's only a good guess.
696
		// Update the revision version only and Leave the author as-is.
697
		if ( $prev_revision ) {
698
			$prev_revision_version = _wp_get_post_revision_version( $prev_revision );
699
700
			// If the previous revision is already up to date, it no longer has the information we need :(
701
			if ( $prev_revision_version < 1 )
702
				$update['post_author'] = $prev_revision->post_author;
703
		}
704
705
		// Upgrade this revision
706
		$result = $wpdb->update( $wpdb->posts, $update, array( 'ID' => $this_revision->ID ) );
707
708
		if ( $result )
709
			wp_cache_delete( $this_revision->ID, 'posts' );
710
711
	} while ( $prev_revision );
712
713
	delete_option( $lock );
714
715
	// Add a copy of the post as latest revision.
716
	if ( $add_last )
717
		wp_save_post_revision( $post->ID );
718
719
	return true;
720
}
721