Issues (4967)

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.

src/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 ) {
0 ignored issues
show
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 );
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 ) )
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 );
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 );
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. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
325
 *                            a WP_Post object, an associative array, or a numeric array, respectively. Default OBJECT.
326
 * @param string      $filter Optional sanitation filter. See sanitize_post().
327
 * @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
328
 */
329
function wp_get_post_revision(&$post, $output = OBJECT, $filter = 'raw') {
330
	if ( !$revision = get_post( $post, OBJECT, $filter ) )
331
		return $revision;
332
	if ( 'revision' !== $revision->post_type )
333
		return null;
334
335
	if ( $output == OBJECT ) {
336
		return $revision;
337
	} elseif ( $output == ARRAY_A ) {
338
		$_revision = get_object_vars($revision);
339
		return $_revision;
340
	} elseif ( $output == ARRAY_N ) {
341
		$_revision = array_values(get_object_vars($revision));
342
		return $_revision;
343
	}
344
345
	return $revision;
346
}
347
348
/**
349
 * Restores a post to the specified revision.
350
 *
351
 * Can restore a past revision using all fields of the post revision, or only selected fields.
352
 *
353
 * @since 2.6.0
354
 *
355
 * @param int|WP_Post $revision_id Revision ID or revision object.
356
 * @param array       $fields      Optional. What fields to restore from. Defaults to all.
0 ignored issues
show
Should the type for parameter $fields not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
357
 * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success.
0 ignored issues
show
Should the return type not be array|null|false|WP_Error|integer? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
358
 */
359
function wp_restore_post_revision( $revision_id, $fields = null ) {
360
	if ( !$revision = wp_get_post_revision( $revision_id, ARRAY_A ) )
361
		return $revision;
362
363
	if ( !is_array( $fields ) )
364
		$fields = array_keys( _wp_post_revision_fields( $revision ) );
365
366
	$update = array();
367
	foreach ( array_intersect( array_keys( $revision ), $fields ) as $field ) {
368
		$update[$field] = $revision[$field];
369
	}
370
371
	if ( !$update )
0 ignored issues
show
Bug Best Practice introduced by
The expression $update of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
372
		return false;
373
374
	$update['ID'] = $revision['post_parent'];
375
376
	$update = wp_slash( $update ); //since data is from db
377
378
	$post_id = wp_update_post( $update );
379
	if ( ! $post_id || is_wp_error( $post_id ) )
380
		return $post_id;
381
382
	// Update last edit user
383
	update_post_meta( $post_id, '_edit_last', get_current_user_id() );
384
385
	/**
386
	 * Fires after a post revision has been restored.
387
	 *
388
	 * @since 2.6.0
389
	 *
390
	 * @param int $post_id     Post ID.
391
	 * @param int $revision_id Post revision ID.
392
	 */
393
	do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] );
394
395
	return $post_id;
396
}
397
398
/**
399
 * Deletes a revision.
400
 *
401
 * Deletes the row from the posts table corresponding to the specified revision.
402
 *
403
 * @since 2.6.0
404
 *
405
 * @param int|WP_Post $revision_id Revision ID or revision object.
406
 * @return array|false|WP_Post|WP_Error|null Null or WP_Error if error, deleted post if success.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array|null|WP_Post|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
407
 */
408
function wp_delete_post_revision( $revision_id ) {
409
	if ( ! $revision = wp_get_post_revision( $revision_id ) ) {
410
		return $revision;
411
	}
412
413
	$delete = wp_delete_post( $revision->ID );
414
	if ( $delete ) {
415
		/**
416
		 * Fires once a post revision has been deleted.
417
		 *
418
		 * @since 2.6.0
419
		 *
420
		 * @param int          $revision_id Post revision ID.
421
		 * @param object|array $revision    Post revision object or array.
422
		 */
423
		do_action( 'wp_delete_post_revision', $revision->ID, $revision );
424
	}
425
426
	return $delete;
427
}
428
429
/**
430
 * Returns all revisions of specified post.
431
 *
432
 * @since 2.6.0
433
 *
434
 * @see get_children()
435
 *
436
 * @param int|WP_Post $post_id Optional. Post ID or WP_Post object. Default is global `$post`.
0 ignored issues
show
Consider making the type for parameter $post_id a bit more specific; maybe use integer.
Loading history...
437
 * @param array|null  $args    Optional. Arguments for retrieving post revisions. Default null.
438
 * @return array An array of revisions, or an empty array if none.
439
 */
440
function wp_get_post_revisions( $post_id = 0, $args = null ) {
441
	$post = get_post( $post_id );
442
	if ( ! $post || empty( $post->ID ) )
443
		return array();
444
445
	$defaults = array( 'order' => 'DESC', 'orderby' => 'date ID', 'check_enabled' => true );
446
	$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|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...
447
448
	if ( $args['check_enabled'] && ! wp_revisions_enabled( $post ) )
449
		return array();
450
451
	$args = array_merge( $args, array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit' ) );
452
453
	if ( ! $revisions = get_children( $args ) )
454
		return array();
455
456
	return $revisions;
457
}
458
459
/**
460
 * Determine if revisions are enabled for a given post.
461
 *
462
 * @since 3.6.0
463
 *
464
 * @param WP_Post $post The post object.
465
 * @return bool True if number of revisions to keep isn't zero, false otherwise.
466
 */
467
function wp_revisions_enabled( $post ) {
468
	return wp_revisions_to_keep( $post ) !== 0;
469
}
470
471
/**
472
 * Determine how many revisions to retain for a given post.
473
 *
474
 * By default, an infinite number of revisions are kept.
475
 *
476
 * The constant WP_POST_REVISIONS can be set in wp-config to specify the limit
477
 * of revisions to keep.
478
 *
479
 * @since 3.6.0
480
 *
481
 * @param WP_Post $post The post object.
482
 * @return int The number of revisions to keep.
483
 */
484
function wp_revisions_to_keep( $post ) {
485
	$num = WP_POST_REVISIONS;
486
487
	if ( true === $num )
488
		$num = -1;
489
	else
490
		$num = intval( $num );
491
492
	if ( ! post_type_supports( $post->post_type, 'revisions' ) )
493
		$num = 0;
494
495
	/**
496
	 * Filters the number of revisions to save for the given post.
497
	 *
498
	 * Overrides the value of WP_POST_REVISIONS.
499
	 *
500
	 * @since 3.6.0
501
	 *
502
	 * @param int     $num  Number of revisions to store.
503
	 * @param WP_Post $post Post object.
504
	 */
505
	return (int) apply_filters( 'wp_revisions_to_keep', $num, $post );
506
}
507
508
/**
509
 * Sets up the post object for preview based on the post autosave.
510
 *
511
 * @since 2.7.0
512
 * @access private
513
 *
514
 * @param WP_Post $post
515
 * @return WP_Post|false
516
 */
517
function _set_preview( $post ) {
518
	if ( ! is_object( $post ) ) {
519
		return $post;
520
	}
521
522
	$preview = wp_get_post_autosave( $post->ID );
523
	if ( ! is_object( $preview ) ) {
524
		return $post;
525
	}
526
527
	$preview = sanitize_post( $preview );
528
529
	$post->post_content = $preview->post_content;
530
	$post->post_title = $preview->post_title;
531
	$post->post_excerpt = $preview->post_excerpt;
532
533
	add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
534
	add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
535
536
	return $post;
537
}
538
539
/**
540
 * Filters the latest content for preview from the post autosave.
541
 *
542
 * @since 2.7.0
543
 * @access private
544
 */
545
function _show_post_preview() {
546
	if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) ) {
547
		$id = (int) $_GET['preview_id'];
548
549
		if ( false === wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $id ) )
550
			wp_die( __('Sorry, you are not allowed to preview drafts.') );
551
552
		add_filter('the_preview', '_set_preview');
553
	}
554
}
555
556
/**
557
 * Filters terms lookup to set the post format.
558
 *
559
 * @since 3.6.0
560
 * @access private
561
 *
562
 * @param array  $terms
563
 * @param int    $post_id
564
 * @param string $taxonomy
565
 * @return array
566
 */
567
function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) {
568
	if ( ! $post = get_post() )
569
		return $terms;
570
571
	if ( empty( $_REQUEST['post_format'] ) || $post->ID != $post_id || 'post_format' != $taxonomy || 'revision' == $post->post_type )
572
		return $terms;
573
574
	if ( 'standard' == $_REQUEST['post_format'] )
575
		$terms = array();
576
	elseif ( $term = get_term_by( 'slug', 'post-format-' . sanitize_key( $_REQUEST['post_format'] ), 'post_format' ) )
577
		$terms = array( $term ); // Can only have one post format
578
579
	return $terms;
580
}
581
582
/**
583
 * Filters post thumbnail lookup to set the post thumbnail.
584
 *
585
 * @since 4.6.0
586
 * @access private
587
 *
588
 * @param null|array|string $value    The value to return - a single metadata value, or an array of values.
589
 * @param int               $post_id  Post ID.
590
 * @param string            $meta_key Meta key.
591
 * @return null|array The default return value or the post thumbnail meta array.
0 ignored issues
show
Should the return type not be null|array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
592
 */
593
function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) {
594
	if ( ! $post = get_post() ) {
595
		return $value;
596
	}
597
598
	if ( empty( $_REQUEST['_thumbnail_id'] ) ||
599
	     empty( $_REQUEST['preview_id'] ) ||
600
	     $post->ID != $post_id ||
601
	     '_thumbnail_id' != $meta_key ||
602
	     'revision' == $post->post_type ||
603
	     $post_id != $_REQUEST['preview_id']
604
	) {
605
		return $value;
606
	}
607
608
	$thumbnail_id = intval( $_REQUEST['_thumbnail_id'] );
609
	if ( $thumbnail_id <= 0 ) {
610
		return '';
611
	}
612
613
	return strval( $thumbnail_id );
614
}
615
616
/**
617
 * Gets the post revision version.
618
 *
619
 * @since 3.6.0
620
 * @access private
621
 *
622
 * @param WP_Post $revision
623
 * @return int|false
624
 */
625
function _wp_get_post_revision_version( $revision ) {
626
	if ( is_object( $revision ) )
627
		$revision = get_object_vars( $revision );
628
	elseif ( !is_array( $revision ) )
629
		return false;
630
631
	if ( preg_match( '/^\d+-(?:autosave|revision)-v(\d+)$/', $revision['post_name'], $matches ) )
632
		return (int) $matches[1];
633
634
	return 0;
635
}
636
637
/**
638
 * Upgrade the revisions author, add the current post as a revision and set the revisions version to 1
639
 *
640
 * @since 3.6.0
641
 * @access private
642
 *
643
 * @global wpdb $wpdb WordPress database abstraction object.
644
 *
645
 * @param WP_Post $post      Post object
646
 * @param array   $revisions Current revisions of the post
647
 * @return bool true if the revisions were upgraded, false if problems
648
 */
649
function _wp_upgrade_revisions_of_post( $post, $revisions ) {
650
	global $wpdb;
651
652
	// Add post option exclusively
653
	$lock = "revision-upgrade-{$post->ID}";
654
	$now = time();
655
	$result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no') /* LOCK */", $lock, $now ) );
656
	if ( ! $result ) {
657
		// If we couldn't get a lock, see how old the previous lock is
658
		$locked = get_option( $lock );
659
		if ( ! $locked ) {
660
			// Can't write to the lock, and can't read the lock.
661
			// Something broken has happened
662
			return false;
663
		}
664
665
		if ( $locked > $now - 3600 ) {
666
			// Lock is not too old: some other process may be upgrading this post.  Bail.
667
			return false;
668
		}
669
670
		// Lock is too old - update it (below) and continue
671
	}
672
673
	// If we could get a lock, re-"add" the option to fire all the correct filters.
674
	update_option( $lock, $now );
675
676
	reset( $revisions );
677
	$add_last = true;
678
679
	do {
680
		$this_revision = current( $revisions );
681
		$prev_revision = next( $revisions );
682
683
		$this_revision_version = _wp_get_post_revision_version( $this_revision );
684
685
		// Something terrible happened
686
		if ( false === $this_revision_version )
687
			continue;
688
689
		// 1 is the latest revision version, so we're already up to date.
690
		// No need to add a copy of the post as latest revision.
691
		if ( 0 < $this_revision_version ) {
692
			$add_last = false;
693
			continue;
694
		}
695
696
		// Always update the revision version
697
		$update = array(
698
			'post_name' => preg_replace( '/^(\d+-(?:autosave|revision))[\d-]*$/', '$1-v1', $this_revision->post_name ),
699
		);
700
701
		// If this revision is the oldest revision of the post, i.e. no $prev_revision,
702
		// the correct post_author is probably $post->post_author, but that's only a good guess.
703
		// Update the revision version only and Leave the author as-is.
704
		if ( $prev_revision ) {
705
			$prev_revision_version = _wp_get_post_revision_version( $prev_revision );
706
707
			// If the previous revision is already up to date, it no longer has the information we need :(
708
			if ( $prev_revision_version < 1 )
709
				$update['post_author'] = $prev_revision->post_author;
710
		}
711
712
		// Upgrade this revision
713
		$result = $wpdb->update( $wpdb->posts, $update, array( 'ID' => $this_revision->ID ) );
714
715
		if ( $result )
716
			wp_cache_delete( $this_revision->ID, 'posts' );
717
718
	} while ( $prev_revision );
719
720
	delete_option( $lock );
721
722
	// Add a copy of the post as latest revision.
723
	if ( $add_last )
724
		wp_save_post_revision( $post->ID );
725
726
	return true;
727
}
728