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/post-template.php (22 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
 * WordPress Post Template Functions.
4
 *
5
 * Gets content for the current post in the loop.
6
 *
7
 * @package WordPress
8
 * @subpackage Template
9
 */
10
11
/**
12
 * Display the ID of the current item in the WordPress Loop.
13
 *
14
 * @since 0.71
15
 */
16
function the_ID() {
17
	echo get_the_ID();
18
}
19
20
/**
21
 * Retrieve the ID of the current item in the WordPress Loop.
22
 *
23
 * @since 2.1.0
24
 *
25
 * @return int|false The ID of the current item in the WordPress Loop. False if $post is not set.
26
 */
27
function get_the_ID() {
28
	$post = get_post();
29
	return ! empty( $post ) ? $post->ID : false;
30
}
31
32
/**
33
 * Display or retrieve the current post title with optional markup.
34
 *
35
 * @since 0.71
36
 *
37
 * @param string $before Optional. Markup to prepend to the title. Default empty.
38
 * @param string $after  Optional. Markup to append to the title. Default empty.
39
 * @param bool   $echo   Optional. Whether to echo or return the title. Default true for echo.
40
 * @return string|void Current post title if $echo is false.
41
 */
42
function the_title( $before = '', $after = '', $echo = true ) {
43
	$title = get_the_title();
44
45
	if ( strlen($title) == 0 )
46
		return;
47
48
	$title = $before . $title . $after;
49
50
	if ( $echo )
51
		echo $title;
52
	else
53
		return $title;
54
}
55
56
/**
57
 * Sanitize the current title when retrieving or displaying.
58
 *
59
 * Works like the_title(), except the parameters can be in a string or
60
 * an array. See the function for what can be override in the $args parameter.
61
 *
62
 * The title before it is displayed will have the tags stripped and esc_attr()
63
 * before it is passed to the user or displayed. The default as with the_title(),
64
 * is to display the title.
65
 *
66
 * @since 2.3.0
67
 *
68
 * @param string|array $args {
69
 *     Title attribute arguments. Optional.
70
 *
71
 *     @type string  $before Markup to prepend to the title. Default empty.
72
 *     @type string  $after  Markup to append to the title. Default empty.
73
 *     @type bool    $echo   Whether to echo or return the title. Default true for echo.
74
 *     @type WP_Post $post   Current post object to retrieve the title for.
75
 * }
76
 * @return string|void String when echo is false.
77
 */
78
function the_title_attribute( $args = '' ) {
79
	$defaults = array( 'before' => '', 'after' =>  '', 'echo' => true, 'post' => get_post() );
80
	$r = wp_parse_args( $args, $defaults );
81
82
	$title = get_the_title( $r['post'] );
83
84
	if ( strlen( $title ) == 0 ) {
85
		return;
86
	}
87
88
	$title = $r['before'] . $title . $r['after'];
89
	$title = esc_attr( strip_tags( $title ) );
90
91
	if ( $r['echo'] ) {
92
		echo $title;
93
	} else {
94
		return $title;
95
	}
96
}
97
98
/**
99
 * Retrieve post title.
100
 *
101
 * If the post is protected and the visitor is not an admin, then "Protected"
102
 * will be displayed before the post title. If the post is private, then
103
 * "Private" will be located before the post title.
104
 *
105
 * @since 0.71
106
 *
107
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
108
 * @return string
109
 */
110
function get_the_title( $post = 0 ) {
111
	$post = get_post( $post );
112
113
	$title = isset( $post->post_title ) ? $post->post_title : '';
114
	$id = isset( $post->ID ) ? $post->ID : 0;
115
116
	if ( ! is_admin() ) {
117
		if ( ! empty( $post->post_password ) ) {
118
119
			/**
120
			 * Filters the text prepended to the post title for protected posts.
121
			 *
122
			 * The filter is only applied on the front end.
123
			 *
124
			 * @since 2.8.0
125
			 *
126
			 * @param string  $prepend Text displayed before the post title.
127
			 *                         Default 'Protected: %s'.
128
			 * @param WP_Post $post    Current post object.
129
			 */
130
			$protected_title_format = apply_filters( 'protected_title_format', __( 'Protected: %s' ), $post );
131
			$title = sprintf( $protected_title_format, $title );
132
		} elseif ( isset( $post->post_status ) && 'private' == $post->post_status ) {
133
134
			/**
135
			 * Filters the text prepended to the post title of private posts.
136
			 *
137
			 * The filter is only applied on the front end.
138
			 *
139
			 * @since 2.8.0
140
			 *
141
			 * @param string  $prepend Text displayed before the post title.
142
			 *                         Default 'Private: %s'.
143
			 * @param WP_Post $post    Current post object.
144
			 */
145
			$private_title_format = apply_filters( 'private_title_format', __( 'Private: %s' ), $post );
146
			$title = sprintf( $private_title_format, $title );
147
		}
148
	}
149
150
	/**
151
	 * Filters the post title.
152
	 *
153
	 * @since 0.71
154
	 *
155
	 * @param string $title The post title.
156
	 * @param int    $id    The post ID.
157
	 */
158
	return apply_filters( 'the_title', $title, $id );
159
}
160
161
/**
162
 * Display the Post Global Unique Identifier (guid).
163
 *
164
 * The guid will appear to be a link, but should not be used as a link to the
165
 * post. The reason you should not use it as a link, is because of moving the
166
 * blog across domains.
167
 *
168
 * URL is escaped to make it XML-safe.
169
 *
170
 * @since 1.5.0
171
 *
172
 * @param int|WP_Post $post Optional. Post ID or post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
173
 */
174 View Code Duplication
function the_guid( $post = 0 ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
	$post = get_post( $post );
176
177
	$guid = isset( $post->guid ) ? get_the_guid( $post ) : '';
178
	$id   = isset( $post->ID ) ? $post->ID : 0;
179
180
	/**
181
	 * Filters the escaped Global Unique Identifier (guid) of the post.
182
	 *
183
	 * @since 4.2.0
184
	 *
185
	 * @see get_the_guid()
186
	 *
187
	 * @param string $guid Escaped Global Unique Identifier (guid) of the post.
188
	 * @param int    $id   The post ID.
189
	 */
190
	echo apply_filters( 'the_guid', $guid, $id );
191
}
192
193
/**
194
 * Retrieve the Post Global Unique Identifier (guid).
195
 *
196
 * The guid will appear to be a link, but should not be used as an link to the
197
 * post. The reason you should not use it as a link, is because of moving the
198
 * blog across domains.
199
 *
200
 * @since 1.5.0
201
 *
202
 * @param int|WP_Post $post Optional. Post ID or post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
203
 * @return string
204
 */
205 View Code Duplication
function get_the_guid( $post = 0 ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
	$post = get_post( $post );
207
208
	$guid = isset( $post->guid ) ? $post->guid : '';
209
	$id   = isset( $post->ID ) ? $post->ID : 0;
210
211
	/**
212
	 * Filters the Global Unique Identifier (guid) of the post.
213
	 *
214
	 * @since 1.5.0
215
	 *
216
	 * @param string $guid Global Unique Identifier (guid) of the post.
217
	 * @param int    $id   The post ID.
218
	 */
219
	return apply_filters( 'get_the_guid', $guid, $id );
220
}
221
222
/**
223
 * Display the post content.
224
 *
225
 * @since 0.71
226
 *
227
 * @param string $more_link_text Optional. Content for when there is more text.
228
 * @param bool   $strip_teaser   Optional. Strip teaser content before the more text. Default is false.
229
 */
230
function the_content( $more_link_text = null, $strip_teaser = false) {
231
	$content = get_the_content( $more_link_text, $strip_teaser );
232
233
	/**
234
	 * Filters the post content.
235
	 *
236
	 * @since 0.71
237
	 *
238
	 * @param string $content Content of the current post.
239
	 */
240
	$content = apply_filters( 'the_content', $content );
241
	$content = str_replace( ']]>', ']]&gt;', $content );
242
	echo $content;
243
}
244
245
/**
246
 * Retrieve the post content.
247
 *
248
 * @since 0.71
249
 *
250
 * @global int   $page      Page number of a single post/page.
251
 * @global int   $more      Boolean indicator for whether single post/page is being viewed.
252
 * @global bool  $preview   Whether post/page is in preview mode.
253
 * @global array $pages     Array of all pages in post/page. Each array element contains part of the content separated by the <!--nextpage--> tag.
254
 * @global int   $multipage Boolean indicator for whether multiple pages are in play.
255
 *
256
 * @param string $more_link_text Optional. Content for when there is more text.
257
 * @param bool   $strip_teaser   Optional. Strip teaser content before the more text. Default is false.
258
 * @return string
259
 */
260
function get_the_content( $more_link_text = null, $strip_teaser = false ) {
261
	global $page, $more, $preview, $pages, $multipage;
262
263
	$post = get_post();
264
265
	if ( null === $more_link_text ) {
266
		$more_link_text = sprintf(
267
			'<span aria-label="%1$s">%2$s</span>',
268
			sprintf(
269
				/* translators: %s: Name of current post */
270
				__( 'Continue reading %s' ),
271
				the_title_attribute( array( 'echo' => false ) )
272
			),
273
			__( '(more&hellip;)' )
274
		);
275
	}
276
277
	$output = '';
278
	$has_teaser = false;
279
280
	// If post password required and it doesn't match the cookie.
281
	if ( post_password_required( $post ) )
282
		return get_the_password_form( $post );
283
284
	if ( $page > count( $pages ) ) // if the requested page doesn't exist
285
		$page = count( $pages ); // give them the highest numbered page that DOES exist
286
287
	$content = $pages[$page - 1];
288
	if ( preg_match( '/<!--more(.*?)?-->/', $content, $matches ) ) {
289
		$content = explode( $matches[0], $content, 2 );
290
		if ( ! empty( $matches[1] ) && ! empty( $more_link_text ) )
291
			$more_link_text = strip_tags( wp_kses_no_null( trim( $matches[1] ) ) );
292
293
		$has_teaser = true;
294
	} else {
295
		$content = array( $content );
296
	}
297
298
	if ( false !== strpos( $post->post_content, '<!--noteaser-->' ) && ( ! $multipage || $page == 1 ) )
299
		$strip_teaser = true;
300
301
	$teaser = $content[0];
302
303
	if ( $more && $strip_teaser && $has_teaser )
304
		$teaser = '';
305
306
	$output .= $teaser;
307
308
	if ( count( $content ) > 1 ) {
309
		if ( $more ) {
310
			$output .= '<span id="more-' . $post->ID . '"></span>' . $content[1];
311
		} else {
312
			if ( ! empty( $more_link_text ) )
313
314
				/**
315
				 * Filters the Read More link text.
316
				 *
317
				 * @since 2.8.0
318
				 *
319
				 * @param string $more_link_element Read More link element.
320
				 * @param string $more_link_text    Read More text.
321
				 */
322
				$output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink() . "#more-{$post->ID}\" class=\"more-link\">$more_link_text</a>", $more_link_text );
323
			$output = force_balance_tags( $output );
324
		}
325
	}
326
327
	if ( $preview ) // Preview fix for JavaScript bug with foreign languages.
328
		$output =	preg_replace_callback( '/\%u([0-9A-F]{4})/', '_convert_urlencoded_to_entities', $output );
329
330
	return $output;
331
}
332
333
/**
334
 * Preview fix for JavaScript bug with foreign languages.
335
 *
336
 * @since 3.1.0
337
 * @access private
338
 *
339
 * @param array $match Match array from preg_replace_callback.
340
 * @return string
341
 */
342
function _convert_urlencoded_to_entities( $match ) {
343
	return '&#' . base_convert( $match[1], 16, 10 ) . ';';
344
}
345
346
/**
347
 * Display the post excerpt.
348
 *
349
 * @since 0.71
350
 */
351
function the_excerpt() {
352
353
	/**
354
	 * Filters the displayed post excerpt.
355
	 *
356
	 * @since 0.71
357
	 *
358
	 * @see get_the_excerpt()
359
	 *
360
	 * @param string $post_excerpt The post excerpt.
361
	 */
362
	echo apply_filters( 'the_excerpt', get_the_excerpt() );
363
}
364
365
/**
366
 * Retrieves the post excerpt.
367
 *
368
 * @since 0.71
369
 * @since 4.5.0 Introduced the `$post` parameter.
370
 *
371
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
372
 * @return string Post excerpt.
373
 */
374
function get_the_excerpt( $post = null ) {
375
	if ( is_bool( $post ) ) {
376
		_deprecated_argument( __FUNCTION__, '2.3.0' );
377
	}
378
379
	$post = get_post( $post );
380
	if ( empty( $post ) ) {
381
		return '';
382
	}
383
384
	if ( post_password_required( $post ) ) {
385
		return __( 'There is no excerpt because this is a protected post.' );
386
	}
387
388
	/**
389
	 * Filters the retrieved post excerpt.
390
	 *
391
	 * @since 1.2.0
392
	 * @since 4.5.0 Introduced the `$post` parameter.
393
	 *
394
	 * @param string $post_excerpt The post excerpt.
395
	 * @param WP_Post $post Post object.
396
	 */
397
	return apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
398
}
399
400
/**
401
 * Whether the post has a custom excerpt.
402
 *
403
 * @since 2.3.0
404
 *
405
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
406
 * @return bool True if the post has a custom excerpt, false otherwise.
407
 */
408
function has_excerpt( $post = 0 ) {
409
	$post = get_post( $post );
410
	return ( !empty( $post->post_excerpt ) );
411
}
412
413
/**
414
 * Display the classes for the post div.
415
 *
416
 * @since 2.7.0
417
 *
418
 * @param string|array $class   One or more classes to add to the class list.
419
 * @param int|WP_Post  $post_id Optional. Post ID or post object. Defaults to the global `$post`.
420
 */
421
function post_class( $class = '', $post_id = null ) {
422
	// Separates classes with a single space, collates classes for post DIV
423
	echo 'class="' . join( ' ', get_post_class( $class, $post_id ) ) . '"';
424
}
425
426
/**
427
 * Retrieves the classes for the post div as an array.
428
 *
429
 * The class names are many. If the post is a sticky, then the 'sticky'
430
 * class name. The class 'hentry' is always added to each post. If the post has a
431
 * post thumbnail, 'has-post-thumbnail' is added as a class. For each taxonomy that
432
 * the post belongs to, a class will be added of the format '{$taxonomy}-{$slug}' -
433
 * eg 'category-foo' or 'my_custom_taxonomy-bar'.
434
 *
435
 * The 'post_tag' taxonomy is a special
436
 * case; the class has the 'tag-' prefix instead of 'post_tag-'. All classes are
437
 * passed through the filter, {@see 'post_class'}, with the list of classes, followed by
438
 * $class parameter value, with the post ID as the last parameter.
439
 *
440
 * @since 2.7.0
441
 * @since 4.2.0 Custom taxonomy classes were added.
442
 *
443
 * @param string|array $class   One or more classes to add to the class list.
444
 * @param int|WP_Post  $post_id Optional. Post ID or post object.
445
 * @return array Array of classes.
446
 */
447
function get_post_class( $class = '', $post_id = null ) {
448
	$post = get_post( $post_id );
449
450
	$classes = array();
451
452 View Code Duplication
	if ( $class ) {
453
		if ( ! is_array( $class ) ) {
454
			$class = preg_split( '#\s+#', $class );
455
		}
456
		$classes = array_map( 'esc_attr', $class );
457
	} else {
458
		// Ensure that we always coerce class to being an array.
459
		$class = array();
460
	}
461
462
	if ( ! $post ) {
463
		return $classes;
464
	}
465
466
	$classes[] = 'post-' . $post->ID;
467
	if ( ! is_admin() )
468
		$classes[] = $post->post_type;
469
	$classes[] = 'type-' . $post->post_type;
470
	$classes[] = 'status-' . $post->post_status;
471
472
	// Post Format
473 View Code Duplication
	if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
474
		$post_format = get_post_format( $post->ID );
475
476
		if ( $post_format && !is_wp_error($post_format) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $post_format of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
477
			$classes[] = 'format-' . sanitize_html_class( $post_format );
478
		else
479
			$classes[] = 'format-standard';
480
	}
481
482
	$post_password_required = post_password_required( $post->ID );
483
484
	// Post requires password.
485
	if ( $post_password_required ) {
486
		$classes[] = 'post-password-required';
487
	} elseif ( ! empty( $post->post_password ) ) {
488
		$classes[] = 'post-password-protected';
489
	}
490
491
	// Post thumbnails.
492
	if ( current_theme_supports( 'post-thumbnails' ) && has_post_thumbnail( $post->ID ) && ! is_attachment( $post ) && ! $post_password_required ) {
493
		$classes[] = 'has-post-thumbnail';
494
	}
495
496
	// sticky for Sticky Posts
497
	if ( is_sticky( $post->ID ) ) {
498
		if ( is_home() && ! is_paged() ) {
499
			$classes[] = 'sticky';
500
		} elseif ( is_admin() ) {
501
			$classes[] = 'status-sticky';
502
		}
503
	}
504
505
	// hentry for hAtom compliance
506
	$classes[] = 'hentry';
507
508
	// All public taxonomies
509
	$taxonomies = get_taxonomies( array( 'public' => true ) );
510
	foreach ( (array) $taxonomies as $taxonomy ) {
511
		if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {
512
			foreach ( (array) get_the_terms( $post->ID, $taxonomy ) as $term ) {
513
				if ( empty( $term->slug ) ) {
514
					continue;
515
				}
516
517
				$term_class = sanitize_html_class( $term->slug, $term->term_id );
518
				if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) {
519
					$term_class = $term->term_id;
520
				}
521
522
				// 'post_tag' uses the 'tag' prefix for backward compatibility.
523
				if ( 'post_tag' == $taxonomy ) {
524
					$classes[] = 'tag-' . $term_class;
525
				} else {
526
					$classes[] = sanitize_html_class( $taxonomy . '-' . $term_class, $taxonomy . '-' . $term->term_id );
527
				}
528
			}
529
		}
530
	}
531
532
	$classes = array_map( 'esc_attr', $classes );
533
534
	/**
535
	 * Filters the list of CSS classes for the current post.
536
	 *
537
	 * @since 2.7.0
538
	 *
539
	 * @param array $classes An array of post classes.
540
	 * @param array $class   An array of additional classes added to the post.
541
	 * @param int   $post_id The post ID.
542
	 */
543
	$classes = apply_filters( 'post_class', $classes, $class, $post->ID );
544
545
	return array_unique( $classes );
546
}
547
548
/**
549
 * Display the classes for the body element.
550
 *
551
 * @since 2.8.0
552
 *
553
 * @param string|array $class One or more classes to add to the class list.
554
 */
555
function body_class( $class = '' ) {
556
	// Separates classes with a single space, collates classes for body element
557
	echo 'class="' . join( ' ', get_body_class( $class ) ) . '"';
558
}
559
560
/**
561
 * Retrieve the classes for the body element as an array.
562
 *
563
 * @since 2.8.0
564
 *
565
 * @global WP_Query $wp_query
566
 *
567
 * @param string|array $class One or more classes to add to the class list.
568
 * @return array Array of classes.
569
 */
570
function get_body_class( $class = '' ) {
571
	global $wp_query;
572
573
	$classes = array();
574
575
	if ( is_rtl() )
576
		$classes[] = 'rtl';
577
578
	if ( is_front_page() )
579
		$classes[] = 'home';
580
	if ( is_home() )
581
		$classes[] = 'blog';
582
	if ( is_archive() )
583
		$classes[] = 'archive';
584
	if ( is_date() )
585
		$classes[] = 'date';
586
	if ( is_search() ) {
587
		$classes[] = 'search';
588
		$classes[] = $wp_query->posts ? 'search-results' : 'search-no-results';
589
	}
590
	if ( is_paged() )
591
		$classes[] = 'paged';
592
	if ( is_attachment() )
593
		$classes[] = 'attachment';
594
	if ( is_404() )
595
		$classes[] = 'error404';
596
597
	if ( is_singular() ) {
598
		$post_id = $wp_query->get_queried_object_id();
599
		$post = $wp_query->get_queried_object();
600
		$post_type = $post->post_type;
601
602
		if ( is_page_template() ) {
603
			$classes[] = "{$post_type}-template";
604
605
			$template_slug  = get_page_template_slug( $post_id );
606
			$template_parts = explode( '/', $template_slug );
607
608
			foreach ( $template_parts as $part ) {
609
				$classes[] = "{$post_type}-template-" . sanitize_html_class( str_replace( array( '.', '/' ), '-', basename( $part, '.php' ) ) );
610
			}
611
			$classes[] = "{$post_type}-template-" . sanitize_html_class( str_replace( '.', '-', $template_slug ) );
612
		} else {
613
			$classes[] = "{$post_type}-template-default";
614
		}
615
616
		if ( is_single() ) {
617
			$classes[] = 'single';
618
			if ( isset( $post->post_type ) ) {
619
				$classes[] = 'single-' . sanitize_html_class( $post->post_type, $post_id );
620
				$classes[] = 'postid-' . $post_id;
621
622
				// Post Format
623 View Code Duplication
				if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
624
					$post_format = get_post_format( $post->ID );
625
626
					if ( $post_format && !is_wp_error($post_format) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $post_format of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
627
						$classes[] = 'single-format-' . sanitize_html_class( $post_format );
628
					else
629
						$classes[] = 'single-format-standard';
630
				}
631
			}
632
		}
633
634
		if ( is_attachment() ) {
635
			$mime_type = get_post_mime_type($post_id);
636
			$mime_prefix = array( 'application/', 'image/', 'text/', 'audio/', 'video/', 'music/' );
637
			$classes[] = 'attachmentid-' . $post_id;
638
			$classes[] = 'attachment-' . str_replace( $mime_prefix, '', $mime_type );
639
		} elseif ( is_page() ) {
640
			$classes[] = 'page';
641
642
			$page_id = $wp_query->get_queried_object_id();
643
644
			$post = get_post($page_id);
645
646
			$classes[] = 'page-id-' . $page_id;
647
648
			if ( get_pages( array( 'parent' => $page_id, 'number' => 1 ) ) ) {
649
				$classes[] = 'page-parent';
650
			}
651
652
			if ( $post->post_parent ) {
653
				$classes[] = 'page-child';
654
				$classes[] = 'parent-pageid-' . $post->post_parent;
655
			}
656
		}
657
	} elseif ( is_archive() ) {
658
		if ( is_post_type_archive() ) {
659
			$classes[] = 'post-type-archive';
660
			$post_type = get_query_var( 'post_type' );
661
			if ( is_array( $post_type ) )
662
				$post_type = reset( $post_type );
663
			$classes[] = 'post-type-archive-' . sanitize_html_class( $post_type );
664
		} elseif ( is_author() ) {
665
			$author = $wp_query->get_queried_object();
666
			$classes[] = 'author';
667
			if ( isset( $author->user_nicename ) ) {
668
				$classes[] = 'author-' . sanitize_html_class( $author->user_nicename, $author->ID );
669
				$classes[] = 'author-' . $author->ID;
670
			}
671 View Code Duplication
		} elseif ( is_category() ) {
672
			$cat = $wp_query->get_queried_object();
673
			$classes[] = 'category';
674
			if ( isset( $cat->term_id ) ) {
675
				$cat_class = sanitize_html_class( $cat->slug, $cat->term_id );
676
				if ( is_numeric( $cat_class ) || ! trim( $cat_class, '-' ) ) {
677
					$cat_class = $cat->term_id;
678
				}
679
680
				$classes[] = 'category-' . $cat_class;
681
				$classes[] = 'category-' . $cat->term_id;
682
			}
683
		} elseif ( is_tag() ) {
684
			$tag = $wp_query->get_queried_object();
685
			$classes[] = 'tag';
686
			if ( isset( $tag->term_id ) ) {
687
				$tag_class = sanitize_html_class( $tag->slug, $tag->term_id );
688
				if ( is_numeric( $tag_class ) || ! trim( $tag_class, '-' ) ) {
689
					$tag_class = $tag->term_id;
690
				}
691
692
				$classes[] = 'tag-' . $tag_class;
693
				$classes[] = 'tag-' . $tag->term_id;
694
			}
695 View Code Duplication
		} elseif ( is_tax() ) {
696
			$term = $wp_query->get_queried_object();
697
			if ( isset( $term->term_id ) ) {
698
				$term_class = sanitize_html_class( $term->slug, $term->term_id );
699
				if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) {
700
					$term_class = $term->term_id;
701
				}
702
703
				$classes[] = 'tax-' . sanitize_html_class( $term->taxonomy );
704
				$classes[] = 'term-' . $term_class;
705
				$classes[] = 'term-' . $term->term_id;
706
			}
707
		}
708
	}
709
710
	if ( is_user_logged_in() )
711
		$classes[] = 'logged-in';
712
713
	if ( is_admin_bar_showing() ) {
714
		$classes[] = 'admin-bar';
715
		$classes[] = 'no-customize-support';
716
	}
717
718
	if ( get_background_color() !== get_theme_support( 'custom-background', 'default-color' ) || get_background_image() )
719
		$classes[] = 'custom-background';
720
721
	if ( has_custom_logo() ) {
722
		$classes[] = 'wp-custom-logo';
723
	}
724
725
	$page = $wp_query->get( 'page' );
726
727
	if ( ! $page || $page < 2 )
728
		$page = $wp_query->get( 'paged' );
729
730
	if ( $page && $page > 1 && ! is_404() ) {
731
		$classes[] = 'paged-' . $page;
732
733
		if ( is_single() )
734
			$classes[] = 'single-paged-' . $page;
735
		elseif ( is_page() )
736
			$classes[] = 'page-paged-' . $page;
737
		elseif ( is_category() )
738
			$classes[] = 'category-paged-' . $page;
739
		elseif ( is_tag() )
740
			$classes[] = 'tag-paged-' . $page;
741
		elseif ( is_date() )
742
			$classes[] = 'date-paged-' . $page;
743
		elseif ( is_author() )
744
			$classes[] = 'author-paged-' . $page;
745
		elseif ( is_search() )
746
			$classes[] = 'search-paged-' . $page;
747
		elseif ( is_post_type_archive() )
748
			$classes[] = 'post-type-paged-' . $page;
749
	}
750
751 View Code Duplication
	if ( ! empty( $class ) ) {
752
		if ( !is_array( $class ) )
753
			$class = preg_split( '#\s+#', $class );
754
		$classes = array_merge( $classes, $class );
755
	} else {
756
		// Ensure that we always coerce class to being an array.
757
		$class = array();
758
	}
759
760
	$classes = array_map( 'esc_attr', $classes );
761
762
	/**
763
	 * Filters the list of CSS body classes for the current post or page.
764
	 *
765
	 * @since 2.8.0
766
	 *
767
	 * @param array $classes An array of body classes.
768
	 * @param array $class   An array of additional classes added to the body.
769
	 */
770
	$classes = apply_filters( 'body_class', $classes, $class );
771
772
	return array_unique( $classes );
773
}
774
775
/**
776
 * Whether post requires password and correct password has been provided.
777
 *
778
 * @since 2.7.0
779
 *
780
 * @param int|WP_Post|null $post An optional post. Global $post used if not provided.
781
 * @return bool false if a password is not required or the correct password cookie is present, true otherwise.
782
 */
783
function post_password_required( $post = null ) {
784
	$post = get_post($post);
785
786
	if ( empty( $post->post_password ) ) {
787
		/** This filter is documented in wp-includes/post.php */
788
		return apply_filters( 'post_password_required', false, $post );
789
	}
790
791
	if ( ! isset( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ) ) {
792
		/** This filter is documented in wp-includes/post.php */
793
		return apply_filters( 'post_password_required', true, $post );
794
	}
795
796
	require_once ABSPATH . WPINC . '/class-phpass.php';
797
	$hasher = new PasswordHash( 8, true );
798
799
	$hash = wp_unslash( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] );
800
	if ( 0 !== strpos( $hash, '$P$B' ) ) {
801
		$required = true;
802
	} else {
803
		$required = ! $hasher->CheckPassword( $post->post_password, $hash );
804
	}
805
806
	/**
807
	 * Filters whether a post requires the user to supply a password.
808
	 *
809
	 * @since 4.7.0
810
	 *
811
	 * @param bool    $required Whether the user needs to supply a password. True if password has not been
812
	 *                          provided or is incorrect, false if password has been supplied or is not required.
813
	 * @param WP_Post $post     Post data.
814
	 */
815
	return apply_filters( 'post_password_required', $required, $post );
816
}
817
818
//
819
// Page Template Functions for usage in Themes
820
//
821
822
/**
823
 * The formatted output of a list of pages.
824
 *
825
 * Displays page links for paginated posts (i.e. includes the <!--nextpage-->.
826
 * Quicktag one or more times). This tag must be within The Loop.
827
 *
828
 * @since 1.2.0
829
 *
830
 * @global int $page
831
 * @global int $numpages
832
 * @global int $multipage
833
 * @global int $more
834
 *
835
 * @param string|array $args {
836
 *     Optional. Array or string of default arguments.
837
 *
838
 *     @type string       $before           HTML or text to prepend to each link. Default is `<p> Pages:`.
839
 *     @type string       $after            HTML or text to append to each link. Default is `</p>`.
840
 *     @type string       $link_before      HTML or text to prepend to each link, inside the `<a>` tag.
841
 *                                          Also prepended to the current item, which is not linked. Default empty.
842
 *     @type string       $link_after       HTML or text to append to each Pages link inside the `<a>` tag.
843
 *                                          Also appended to the current item, which is not linked. Default empty.
844
 *     @type string       $next_or_number   Indicates whether page numbers should be used. Valid values are number
845
 *                                          and next. Default is 'number'.
846
 *     @type string       $separator        Text between pagination links. Default is ' '.
847
 *     @type string       $nextpagelink     Link text for the next page link, if available. Default is 'Next Page'.
848
 *     @type string       $previouspagelink Link text for the previous page link, if available. Default is 'Previous Page'.
849
 *     @type string       $pagelink         Format string for page numbers. The % in the parameter string will be
850
 *                                          replaced with the page number, so 'Page %' generates "Page 1", "Page 2", etc.
851
 *                                          Defaults to '%', just the page number.
852
 *     @type int|bool     $echo             Whether to echo or not. Accepts 1|true or 0|false. Default 1|true.
853
 * }
854
 * @return string Formatted output in HTML.
855
 */
856
function wp_link_pages( $args = '' ) {
857
	global $page, $numpages, $multipage, $more;
858
859
	$defaults = array(
860
		'before'           => '<p>' . __( 'Pages:' ),
861
		'after'            => '</p>',
862
		'link_before'      => '',
863
		'link_after'       => '',
864
		'next_or_number'   => 'number',
865
		'separator'        => ' ',
866
		'nextpagelink'     => __( 'Next page' ),
867
		'previouspagelink' => __( 'Previous page' ),
868
		'pagelink'         => '%',
869
		'echo'             => 1
870
	);
871
872
	$params = wp_parse_args( $args, $defaults );
873
874
	/**
875
	 * Filters the arguments used in retrieving page links for paginated posts.
876
	 *
877
	 * @since 3.0.0
878
	 *
879
	 * @param array $params An array of arguments for page links for paginated posts.
880
	 */
881
	$r = apply_filters( 'wp_link_pages_args', $params );
882
883
	$output = '';
884
	if ( $multipage ) {
885
		if ( 'number' == $r['next_or_number'] ) {
886
			$output .= $r['before'];
887
			for ( $i = 1; $i <= $numpages; $i++ ) {
888
				$link = $r['link_before'] . str_replace( '%', $i, $r['pagelink'] ) . $r['link_after'];
889
				if ( $i != $page || ! $more && 1 == $page ) {
890
					$link = _wp_link_page( $i ) . $link . '</a>';
891
				}
892
				/**
893
				 * Filters the HTML output of individual page number links.
894
				 *
895
				 * @since 3.6.0
896
				 *
897
				 * @param string $link The page number HTML output.
898
				 * @param int    $i    Page number for paginated posts' page links.
899
				 */
900
				$link = apply_filters( 'wp_link_pages_link', $link, $i );
901
902
				// Use the custom links separator beginning with the second link.
903
				$output .= ( 1 === $i ) ? ' ' : $r['separator'];
904
				$output .= $link;
905
			}
906
			$output .= $r['after'];
907
		} elseif ( $more ) {
908
			$output .= $r['before'];
909
			$prev = $page - 1;
910
			if ( $prev > 0 ) {
911
				$link = _wp_link_page( $prev ) . $r['link_before'] . $r['previouspagelink'] . $r['link_after'] . '</a>';
912
913
				/** This filter is documented in wp-includes/post-template.php */
914
				$output .= apply_filters( 'wp_link_pages_link', $link, $prev );
915
			}
916
			$next = $page + 1;
917
			if ( $next <= $numpages ) {
918
				if ( $prev ) {
919
					$output .= $r['separator'];
920
				}
921
				$link = _wp_link_page( $next ) . $r['link_before'] . $r['nextpagelink'] . $r['link_after'] . '</a>';
922
923
				/** This filter is documented in wp-includes/post-template.php */
924
				$output .= apply_filters( 'wp_link_pages_link', $link, $next );
925
			}
926
			$output .= $r['after'];
927
		}
928
	}
929
930
	/**
931
	 * Filters the HTML output of page links for paginated posts.
932
	 *
933
	 * @since 3.6.0
934
	 *
935
	 * @param string $output HTML output of paginated posts' page links.
936
	 * @param array  $args   An array of arguments.
937
	 */
938
	$html = apply_filters( 'wp_link_pages', $output, $args );
939
940
	if ( $r['echo'] ) {
941
		echo $html;
942
	}
943
	return $html;
944
}
945
946
/**
947
 * Helper function for wp_link_pages().
948
 *
949
 * @since 3.1.0
950
 * @access private
951
 *
952
 * @global WP_Rewrite $wp_rewrite
953
 *
954
 * @param int $i Page number.
955
 * @return string Link.
956
 */
957
function _wp_link_page( $i ) {
958
	global $wp_rewrite;
959
	$post = get_post();
960
	$query_args = array();
961
962
	if ( 1 == $i ) {
963
		$url = get_permalink();
964
	} else {
965
		if ( '' == get_option('permalink_structure') || in_array($post->post_status, array('draft', 'pending')) )
966
			$url = add_query_arg( 'page', $i, get_permalink() );
967
		elseif ( 'page' == get_option('show_on_front') && get_option('page_on_front') == $post->ID )
968
			$url = trailingslashit(get_permalink()) . user_trailingslashit("$wp_rewrite->pagination_base/" . $i, 'single_paged');
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
969
		else
970
			$url = trailingslashit(get_permalink()) . user_trailingslashit($i, 'single_paged');
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
971
	}
972
973
	if ( is_preview() ) {
974
975
		if ( ( 'draft' !== $post->post_status ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) {
976
			$query_args['preview_id'] = wp_unslash( $_GET['preview_id'] );
977
			$query_args['preview_nonce'] = wp_unslash( $_GET['preview_nonce'] );
978
		}
979
980
		$url = get_preview_post_link( $post, $query_args, $url );
0 ignored issues
show
It seems like $url defined by get_preview_post_link($post, $query_args, $url) on line 980 can also be of type false; however, get_preview_post_link() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
981
	}
982
983
	return '<a href="' . esc_url( $url ) . '">';
0 ignored issues
show
It seems like $url can also be of type false or null; however, esc_url() does only seem to accept string, 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...
984
}
985
986
//
987
// Post-meta: Custom per-post fields.
988
//
989
990
/**
991
 * Retrieve post custom meta data field.
992
 *
993
 * @since 1.5.0
994
 *
995
 * @param string $key Meta data key name.
996
 * @return false|string|array Array of values or single value, if only one element exists. False will be returned if key does not exist.
997
 */
998
function post_custom( $key = '' ) {
999
	$custom = get_post_custom();
1000
1001
	if ( !isset( $custom[$key] ) )
1002
		return false;
1003
	elseif ( 1 == count($custom[$key]) )
1004
		return $custom[$key][0];
1005
	else
1006
		return $custom[$key];
1007
}
1008
1009
/**
1010
 * Display list of post custom fields.
1011
 *
1012
 * @since 1.2.0
1013
 *
1014
 * @internal This will probably change at some point...
1015
 *
1016
 */
1017
function the_meta() {
1018
	if ( $keys = get_post_custom_keys() ) {
1019
		echo "<ul class='post-meta'>\n";
1020
		foreach ( (array) $keys as $key ) {
1021
			$keyt = trim($key);
1022
			if ( is_protected_meta( $keyt, 'post' ) )
1023
				continue;
1024
			$values = array_map('trim', get_post_custom_values($key));
1025
			$value = implode($values,', ');
1026
1027
			/**
1028
			 * Filters the HTML output of the li element in the post custom fields list.
1029
			 *
1030
			 * @since 2.2.0
1031
			 *
1032
			 * @param string $html  The HTML output for the li element.
1033
			 * @param string $key   Meta key.
1034
			 * @param string $value Meta value.
1035
			 */
1036
			echo apply_filters( 'the_meta_key', "<li><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value );
1037
		}
1038
		echo "</ul>\n";
1039
	}
1040
}
1041
1042
//
1043
// Pages
1044
//
1045
1046
/**
1047
 * Retrieve or display list of pages as a dropdown (select list).
1048
 *
1049
 * @since 2.1.0
1050
 * @since 4.2.0 The `$value_field` argument was added.
1051
 * @since 4.3.0 The `$class` argument was added.
1052
 *
1053
 * @param array|string $args {
1054
 *     Optional. Array or string of arguments to generate a pages drop-down element.
1055
 *
1056
 *     @type int          $depth                 Maximum depth. Default 0.
1057
 *     @type int          $child_of              Page ID to retrieve child pages of. Default 0.
1058
 *     @type int|string   $selected              Value of the option that should be selected. Default 0.
1059
 *     @type bool|int     $echo                  Whether to echo or return the generated markup. Accepts 0, 1,
1060
 *                                               or their bool equivalents. Default 1.
1061
 *     @type string       $name                  Value for the 'name' attribute of the select element.
1062
 *                                               Default 'page_id'.
1063
 *     @type string       $id                    Value for the 'id' attribute of the select element.
1064
 *     @type string       $class                 Value for the 'class' attribute of the select element. Default: none.
1065
 *                                               Defaults to the value of `$name`.
1066
 *     @type string       $show_option_none      Text to display for showing no pages. Default empty (does not display).
1067
 *     @type string       $show_option_no_change Text to display for "no change" option. Default empty (does not display).
1068
 *     @type string       $option_none_value     Value to use when no page is selected. Default empty.
1069
 *     @type string       $value_field           Post field used to populate the 'value' attribute of the option
1070
 *                                               elements. Accepts any valid post field. Default 'ID'.
1071
 * }
1072
 * @return string HTML content, if not displaying.
1073
 */
1074
function wp_dropdown_pages( $args = '' ) {
1075
	$defaults = array(
1076
		'depth' => 0, 'child_of' => 0,
1077
		'selected' => 0, 'echo' => 1,
1078
		'name' => 'page_id', 'id' => '',
1079
		'class' => '',
1080
		'show_option_none' => '', 'show_option_no_change' => '',
1081
		'option_none_value' => '',
1082
		'value_field' => 'ID',
1083
	);
1084
1085
	$r = wp_parse_args( $args, $defaults );
1086
1087
	$pages = get_pages( $r );
1088
	$output = '';
1089
	// Back-compat with old system where both id and name were based on $name argument
1090
	if ( empty( $r['id'] ) ) {
1091
		$r['id'] = $r['name'];
1092
	}
1093
1094
	if ( ! empty( $pages ) ) {
1095
		$class = '';
1096
		if ( ! empty( $r['class'] ) ) {
1097
			$class = " class='" . esc_attr( $r['class'] ) . "'";
1098
		}
1099
1100
		$output = "<select name='" . esc_attr( $r['name'] ) . "'" . $class . " id='" . esc_attr( $r['id'] ) . "'>\n";
1101
		if ( $r['show_option_no_change'] ) {
1102
			$output .= "\t<option value=\"-1\">" . $r['show_option_no_change'] . "</option>\n";
1103
		}
1104
		if ( $r['show_option_none'] ) {
1105
			$output .= "\t<option value=\"" . esc_attr( $r['option_none_value'] ) . '">' . $r['show_option_none'] . "</option>\n";
1106
		}
1107
		$output .= walk_page_dropdown_tree( $pages, $r['depth'], $r );
1108
		$output .= "</select>\n";
1109
	}
1110
1111
	/**
1112
	 * Filters the HTML output of a list of pages as a drop down.
1113
	 *
1114
	 * @since 2.1.0
1115
	 * @since 4.4.0 `$r` and `$pages` added as arguments.
1116
	 *
1117
	 * @param string $output HTML output for drop down list of pages.
1118
	 * @param array  $r      The parsed arguments array.
1119
	 * @param array  $pages  List of WP_Post objects returned by `get_pages()`
1120
 	 */
1121
	$html = apply_filters( 'wp_dropdown_pages', $output, $r, $pages );
1122
1123
	if ( $r['echo'] ) {
1124
		echo $html;
1125
	}
1126
	return $html;
1127
}
1128
1129
/**
1130
 * Retrieve or display list of pages (or hierarchical post type items) in list (li) format.
1131
 *
1132
 * @since 1.5.0
1133
 * @since 4.7.0 Added the `item_spacing` argument.
1134
 *
1135
 * @see get_pages()
1136
 *
1137
 * @global WP_Query $wp_query
1138
 *
1139
 * @param array|string $args {
1140
 *     Array or string of arguments. Optional.
1141
 *
1142
 *     @type int          $child_of     Display only the sub-pages of a single page by ID. Default 0 (all pages).
1143
 *     @type string       $authors      Comma-separated list of author IDs. Default empty (all authors).
1144
 *     @type string       $date_format  PHP date format to use for the listed pages. Relies on the 'show_date' parameter.
1145
 *                                      Default is the value of 'date_format' option.
1146
 *     @type int          $depth        Number of levels in the hierarchy of pages to include in the generated list.
1147
 *                                      Accepts -1 (any depth), 0 (all pages), 1 (top-level pages only), and n (pages to
1148
 *                                      the given n depth). Default 0.
1149
 *     @type bool         $echo         Whether or not to echo the list of pages. Default true.
1150
 *     @type string       $exclude      Comma-separated list of page IDs to exclude. Default empty.
1151
 *     @type array        $include      Comma-separated list of page IDs to include. Default empty.
1152
 *     @type string       $link_after   Text or HTML to follow the page link label. Default null.
1153
 *     @type string       $link_before  Text or HTML to precede the page link label. Default null.
1154
 *     @type string       $post_type    Post type to query for. Default 'page'.
1155
 *     @type string|array $post_status  Comma-separated list or array of post statuses to include. Default 'publish'.
1156
 *     @type string       $show_date    Whether to display the page publish or modified date for each page. Accepts
1157
 *                                      'modified' or any other value. An empty value hides the date. Default empty.
1158
 *     @type string       $sort_column  Comma-separated list of column names to sort the pages by. Accepts 'post_author',
1159
 *                                      'post_date', 'post_title', 'post_name', 'post_modified', 'post_modified_gmt',
1160
 *                                      'menu_order', 'post_parent', 'ID', 'rand', or 'comment_count'. Default 'post_title'.
1161
 *     @type string       $title_li     List heading. Passing a null or empty value will result in no heading, and the list
1162
 *                                      will not be wrapped with unordered list `<ul>` tags. Default 'Pages'.
1163
 *     @type string       $item_spacing Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' or 'discard'.
1164
 *                                      Default 'preserve'.
1165
 *     @type Walker       $walker       Walker instance to use for listing pages. Default empty (Walker_Page).
1166
 * }
1167
 * @return string|void HTML list of pages.
1168
 */
1169
function wp_list_pages( $args = '' ) {
1170
	$defaults = array(
1171
		'depth'        => 0,
1172
		'show_date'    => '',
1173
		'date_format'  => get_option( 'date_format' ),
1174
		'child_of'     => 0,
1175
		'exclude'      => '',
1176
		'title_li'     => __( 'Pages' ),
1177
		'echo'         => 1,
1178
		'authors'      => '',
1179
		'sort_column'  => 'menu_order, post_title',
1180
		'link_before'  => '',
1181
		'link_after'   => '',
1182
		'item_spacing' => 'preserve',
1183
		'walker'       => '',
1184
	);
1185
1186
	$r = wp_parse_args( $args, $defaults );
1187
1188 View Code Duplication
	if ( ! in_array( $r['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
1189
		// invalid value, fall back to default.
1190
		$r['item_spacing'] = $defaults['item_spacing'];
1191
	}
1192
1193
	$output = '';
1194
	$current_page = 0;
1195
1196
	// sanitize, mostly to keep spaces out
1197
	$r['exclude'] = preg_replace( '/[^0-9,]/', '', $r['exclude'] );
1198
1199
	// Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
1200
	$exclude_array = ( $r['exclude'] ) ? explode( ',', $r['exclude'] ) : array();
1201
1202
	/**
1203
	 * Filters the array of pages to exclude from the pages list.
1204
	 *
1205
	 * @since 2.1.0
1206
	 *
1207
	 * @param array $exclude_array An array of page IDs to exclude.
1208
	 */
1209
	$r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) );
1210
1211
	// Query pages.
1212
	$r['hierarchical'] = 0;
1213
	$pages = get_pages( $r );
1214
1215
	if ( ! empty( $pages ) ) {
1216
		if ( $r['title_li'] ) {
1217
			$output .= '<li class="pagenav">' . $r['title_li'] . '<ul>';
1218
		}
1219
		global $wp_query;
1220
		if ( is_page() || is_attachment() || $wp_query->is_posts_page ) {
1221
			$current_page = get_queried_object_id();
1222
		} elseif ( is_singular() ) {
1223
			$queried_object = get_queried_object();
1224
			if ( is_post_type_hierarchical( $queried_object->post_type ) ) {
1225
				$current_page = $queried_object->ID;
1226
			}
1227
		}
1228
1229
		$output .= walk_page_tree( $pages, $r['depth'], $current_page, $r );
1230
1231
		if ( $r['title_li'] ) {
1232
			$output .= '</ul></li>';
1233
		}
1234
	}
1235
1236
	/**
1237
	 * Filters the HTML output of the pages to list.
1238
	 *
1239
	 * @since 1.5.1
1240
	 * @since 4.4.0 `$pages` added as arguments.
1241
	 *
1242
	 * @see wp_list_pages()
1243
	 *
1244
	 * @param string $output HTML output of the pages list.
1245
	 * @param array  $r      An array of page-listing arguments.
1246
	 * @param array  $pages  List of WP_Post objects returned by `get_pages()`
1247
	 */
1248
	$html = apply_filters( 'wp_list_pages', $output, $r, $pages );
1249
1250
	if ( $r['echo'] ) {
1251
		echo $html;
1252
	} else {
1253
		return $html;
1254
	}
1255
}
1256
1257
/**
1258
 * Displays or retrieves a list of pages with an optional home link.
1259
 *
1260
 * The arguments are listed below and part of the arguments are for wp_list_pages()} function.
1261
 * Check that function for more info on those arguments.
1262
 *
1263
 * @since 2.7.0
1264
 * @since 4.4.0 Added `menu_id`, `container`, `before`, `after`, and `walker` arguments.
1265
 * @since 4.7.0 Added the `item_spacing` argument.
1266
 *
1267
 * @param array|string $args {
1268
 *     Optional. Arguments to generate a page menu. See wp_list_pages() for additional arguments.
1269
 *
1270
 *     @type string          $sort_column  How to sort the list of pages. Accepts post column names.
1271
 *                                         Default 'menu_order, post_title'.
1272
 *     @type string          $menu_id      ID for the div containing the page list. Default is empty string.
1273
 *     @type string          $menu_class   Class to use for the element containing the page list. Default 'menu'.
1274
 *     @type string          $container    Element to use for the element containing the page list. Default 'div'.
1275
 *     @type bool            $echo         Whether to echo the list or return it. Accepts true (echo) or false (return).
1276
 *                                         Default true.
1277
 *     @type int|bool|string $show_home    Whether to display the link to the home page. Can just enter the text
1278
 *                                         you'd like shown for the home link. 1|true defaults to 'Home'.
1279
 *     @type string          $link_before  The HTML or text to prepend to $show_home text. Default empty.
1280
 *     @type string          $link_after   The HTML or text to append to $show_home text. Default empty.
1281
 *     @type string          $before       The HTML or text to prepend to the menu. Default is '<ul>'.
1282
 *     @type string          $after        The HTML or text to append to the menu. Default is '</ul>'.
1283
 *     @type string          $item_spacing Whether to preserve whitespace within the menu's HTML. Accepts 'preserve' or 'discard'. Default 'discard'.
1284
 *     @type Walker          $walker       Walker instance to use for listing pages. Default empty (Walker_Page).
1285
 * }
1286
 * @return string|void HTML menu
1287
 */
1288
function wp_page_menu( $args = array() ) {
1289
	$defaults = array(
1290
		'sort_column'  => 'menu_order, post_title',
1291
		'menu_id'      => '',
1292
		'menu_class'   => 'menu',
1293
		'container'    => 'div',
1294
		'echo'         => true,
1295
		'link_before'  => '',
1296
		'link_after'   => '',
1297
		'before'       => '<ul>',
1298
		'after'        => '</ul>',
1299
		'item_spacing' => 'discard',
1300
		'walker'       => '',
1301
	);
1302
	$args = wp_parse_args( $args, $defaults );
1303
1304 View Code Duplication
	if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ) ) ) {
1305
		// invalid value, fall back to default.
1306
		$args['item_spacing'] = $defaults['item_spacing'];
1307
	}
1308
1309
	if ( 'preserve' === $args['item_spacing'] ) {
1310
		$t = "\t";
1311
		$n = "\n";
1312
	} else {
1313
		$t = '';
1314
		$n = '';
1315
	}
1316
1317
	/**
1318
	 * Filters the arguments used to generate a page-based menu.
1319
	 *
1320
	 * @since 2.7.0
1321
	 *
1322
	 * @see wp_page_menu()
1323
	 *
1324
	 * @param array $args An array of page menu arguments.
1325
	 */
1326
	$args = apply_filters( 'wp_page_menu_args', $args );
1327
1328
	$menu = '';
1329
1330
	$list_args = $args;
1331
1332
	// Show Home in the menu
1333
	if ( ! empty($args['show_home']) ) {
1334
		if ( true === $args['show_home'] || '1' === $args['show_home'] || 1 === $args['show_home'] )
1335
			$text = __('Home');
1336
		else
1337
			$text = $args['show_home'];
1338
		$class = '';
1339
		if ( is_front_page() && !is_paged() )
1340
			$class = 'class="current_page_item"';
1341
		$menu .= '<li ' . $class . '><a href="' . home_url( '/' ) . '">' . $args['link_before'] . $text . $args['link_after'] . '</a></li>';
1342
		// If the front page is a page, add it to the exclude list
1343
		if (get_option('show_on_front') == 'page') {
1344
			if ( !empty( $list_args['exclude'] ) ) {
1345
				$list_args['exclude'] .= ',';
1346
			} else {
1347
				$list_args['exclude'] = '';
1348
			}
1349
			$list_args['exclude'] .= get_option('page_on_front');
1350
		}
1351
	}
1352
1353
	$list_args['echo'] = false;
1354
	$list_args['title_li'] = '';
1355
	$menu .= wp_list_pages( $list_args );
1356
1357
	$container = sanitize_text_field( $args['container'] );
1358
1359
	// Fallback in case `wp_nav_menu()` was called without a container.
1360
	if ( empty( $container ) ) {
1361
		$container = 'div';
1362
	}
1363
1364
	if ( $menu ) {
1365
1366
		// wp_nav_menu doesn't set before and after
1367
		if ( isset( $args['fallback_cb'] ) &&
1368
			'wp_page_menu' === $args['fallback_cb'] &&
1369
			'ul' !== $container ) {
1370
			$args['before'] = "<ul>{$n}";
1371
			$args['after'] = '</ul>';
1372
		}
1373
1374
		$menu = $args['before'] . $menu . $args['after'];
1375
	}
1376
1377
	$attrs = '';
1378
	if ( ! empty( $args['menu_id'] ) ) {
1379
		$attrs .= ' id="' . esc_attr( $args['menu_id'] ) . '"';
1380
	}
1381
1382
	if ( ! empty( $args['menu_class'] ) ) {
1383
		$attrs .= ' class="' . esc_attr( $args['menu_class'] ) . '"';
1384
	}
1385
1386
	$menu = "<{$container}{$attrs}>" . $menu . "</{$container}>{$n}";
1387
1388
	/**
1389
	 * Filters the HTML output of a page-based menu.
1390
	 *
1391
	 * @since 2.7.0
1392
	 *
1393
	 * @see wp_page_menu()
1394
	 *
1395
	 * @param string $menu The HTML output.
1396
	 * @param array  $args An array of arguments.
1397
	 */
1398
	$menu = apply_filters( 'wp_page_menu', $menu, $args );
1399
	if ( $args['echo'] )
1400
		echo $menu;
1401
	else
1402
		return $menu;
1403
}
1404
1405
//
1406
// Page helpers
1407
//
1408
1409
/**
1410
 * Retrieve HTML list content for page list.
1411
 *
1412
 * @uses Walker_Page to create HTML list content.
1413
 * @since 2.1.0
1414
 *
1415
 * @param array $pages
1416
 * @param int   $depth
1417
 * @param int   $current_page
1418
 * @param array $r
1419
 * @return string
1420
 */
1421
function walk_page_tree( $pages, $depth, $current_page, $r ) {
1422
	if ( empty($r['walker']) )
1423
		$walker = new Walker_Page;
1424
	else
1425
		$walker = $r['walker'];
1426
1427
	foreach ( (array) $pages as $page ) {
1428
		if ( $page->post_parent )
1429
			$r['pages_with_children'][ $page->post_parent ] = true;
1430
	}
1431
1432
	$args = array($pages, $depth, $r, $current_page);
1433
	return call_user_func_array(array($walker, 'walk'), $args);
1434
}
1435
1436
/**
1437
 * Retrieve HTML dropdown (select) content for page list.
1438
 *
1439
 * @uses Walker_PageDropdown to create HTML dropdown content.
1440
 * @since 2.1.0
1441
 * @see Walker_PageDropdown::walk() for parameters and return description.
1442
 *
1443
 * @return string
1444
 */
1445
function walk_page_dropdown_tree() {
1446
	$args = func_get_args();
1447
	if ( empty($args[2]['walker']) ) // the user's options are the third parameter
1448
		$walker = new Walker_PageDropdown;
1449
	else
1450
		$walker = $args[2]['walker'];
1451
1452
	return call_user_func_array(array($walker, 'walk'), $args);
1453
}
1454
1455
//
1456
// Attachments
1457
//
1458
1459
/**
1460
 * Display an attachment page link using an image or icon.
1461
 *
1462
 * @since 2.0.0
1463
 *
1464
 * @param int|WP_Post $id Optional. Post ID or post object.
0 ignored issues
show
Consider making the type for parameter $id a bit more specific; maybe use integer.
Loading history...
1465
 * @param bool        $fullsize     Optional, default is false. Whether to use full size.
1466
 * @param bool        $deprecated   Deprecated. Not used.
1467
 * @param bool        $permalink    Optional, default is false. Whether to include permalink.
1468
 */
1469
function the_attachment_link( $id = 0, $fullsize = false, $deprecated = false, $permalink = false ) {
1470
	if ( !empty( $deprecated ) )
1471
		_deprecated_argument( __FUNCTION__, '2.5.0' );
1472
1473
	if ( $fullsize )
1474
		echo wp_get_attachment_link($id, 'full', $permalink);
1475
	else
1476
		echo wp_get_attachment_link($id, 'thumbnail', $permalink);
1477
}
1478
1479
/**
1480
 * Retrieve an attachment page link using an image or icon, if possible.
1481
 *
1482
 * @since 2.5.0
1483
 * @since 4.4.0 The `$id` parameter can now accept either a post ID or `WP_Post` object.
1484
 *
1485
 * @param int|WP_Post  $id        Optional. Post ID or post object.
0 ignored issues
show
Consider making the type for parameter $id a bit more specific; maybe use integer.
Loading history...
1486
 * @param string|array $size      Optional. Image size. Accepts any valid image size, or an array
1487
 *                                of width and height values in pixels (in that order).
1488
 *                                Default 'thumbnail'.
1489
 * @param bool         $permalink Optional, Whether to add permalink to image. Default false.
1490
 * @param bool         $icon      Optional. Whether the attachment is an icon. Default false.
1491
 * @param string|false $text      Optional. Link text to use. Activated by passing a string, false otherwise.
1492
 *                                Default false.
1493
 * @param array|string $attr      Optional. Array or string of attributes. Default empty.
1494
 * @return string HTML content.
1495
 */
1496
function wp_get_attachment_link( $id = 0, $size = 'thumbnail', $permalink = false, $icon = false, $text = false, $attr = '' ) {
1497
	$_post = get_post( $id );
1498
1499 View Code Duplication
	if ( empty( $_post ) || ( 'attachment' !== $_post->post_type ) || ! $url = wp_get_attachment_url( $_post->ID ) ) {
1500
		return __( 'Missing Attachment' );
1501
	}
1502
1503
	if ( $permalink ) {
1504
		$url = get_attachment_link( $_post->ID );
1505
	}
1506
1507
	if ( $text ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $text of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1508
		$link_text = $text;
1509
	} elseif ( $size && 'none' != $size ) {
1510
		$link_text = wp_get_attachment_image( $_post->ID, $size, $icon, $attr );
1511
	} else {
1512
		$link_text = '';
1513
	}
1514
1515
	if ( '' === trim( $link_text ) ) {
1516
		$link_text = $_post->post_title;
1517
	}
1518
1519
	if ( '' === trim( $link_text ) ) {
1520
		$link_text = esc_html( pathinfo( get_attached_file( $_post->ID ), PATHINFO_FILENAME ) );
1521
	}
1522
	/**
1523
	 * Filters a retrieved attachment page link.
1524
	 *
1525
	 * @since 2.7.0
1526
	 *
1527
	 * @param string       $link_html The page link HTML output.
1528
	 * @param int          $id        Post ID.
1529
	 * @param string|array $size      Size of the image. Image size or array of width and height values (in that order).
1530
	 *                                Default 'thumbnail'.
1531
	 * @param bool         $permalink Whether to add permalink to image. Default false.
1532
	 * @param bool         $icon      Whether to include an icon. Default false.
1533
	 * @param string|bool  $text      If string, will be link text. Default false.
1534
	 */
1535
	return apply_filters( 'wp_get_attachment_link', "<a href='" . esc_url( $url ) . "'>$link_text</a>", $id, $size, $permalink, $icon, $text );
1536
}
1537
1538
/**
1539
 * Wrap attachment in paragraph tag before content.
1540
 *
1541
 * @since 2.0.0
1542
 *
1543
 * @param string $content
1544
 * @return string
1545
 */
1546
function prepend_attachment($content) {
1547
	$post = get_post();
1548
1549
	if ( empty($post->post_type) || $post->post_type != 'attachment' )
1550
		return $content;
1551
1552
	if ( wp_attachment_is( 'video', $post ) ) {
1553
		$meta = wp_get_attachment_metadata( get_the_ID() );
0 ignored issues
show
It seems like get_the_ID() can also be of type false; however, wp_get_attachment_metadata() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
1554
		$atts = array( 'src' => wp_get_attachment_url() );
1555
		if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) {
1556
			$atts['width'] = (int) $meta['width'];
1557
			$atts['height'] = (int) $meta['height'];
1558
		}
1559
		if ( has_post_thumbnail() ) {
1560
			$atts['poster'] = wp_get_attachment_url( get_post_thumbnail_id() );
1561
		}
1562
		$p = wp_video_shortcode( $atts );
1563
	} elseif ( wp_attachment_is( 'audio', $post ) ) {
1564
		$p = wp_audio_shortcode( array( 'src' => wp_get_attachment_url() ) );
1565
	} else {
1566
		$p = '<p class="attachment">';
1567
		// show the medium sized image representation of the attachment if available, and link to the raw file
1568
		$p .= wp_get_attachment_link(0, 'medium', false);
1569
		$p .= '</p>';
1570
	}
1571
1572
	/**
1573
	 * Filters the attachment markup to be prepended to the post content.
1574
	 *
1575
	 * @since 2.0.0
1576
	 *
1577
	 * @see prepend_attachment()
1578
	 *
1579
	 * @param string $p The attachment HTML output.
1580
	 */
1581
	$p = apply_filters( 'prepend_attachment', $p );
1582
1583
	return "$p\n$content";
1584
}
1585
1586
//
1587
// Misc
1588
//
1589
1590
/**
1591
 * Retrieve protected post password form content.
1592
 *
1593
 * @since 1.0.0
1594
 *
1595
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
1596
 * @return string HTML content for password form for password protected post.
1597
 */
1598
function get_the_password_form( $post = 0 ) {
1599
	$post = get_post( $post );
1600
	$label = 'pwbox-' . ( empty($post->ID) ? rand() : $post->ID );
1601
	$output = '<form action="' . esc_url( site_url( 'wp-login.php?action=postpass', 'login_post' ) ) . '" class="post-password-form" method="post">
1602
	<p>' . __( 'This content is password protected. To view it please enter your password below:' ) . '</p>
1603
	<p><label for="' . $label . '">' . __( 'Password:' ) . ' <input name="post_password" id="' . $label . '" type="password" size="20" /></label> <input type="submit" name="Submit" value="' . esc_attr_x( 'Enter', 'post password form' ) . '" /></p></form>
1604
	';
1605
1606
	/**
1607
	 * Filters the HTML output for the protected post password form.
1608
	 *
1609
	 * If modifying the password field, please note that the core database schema
1610
	 * limits the password field to 20 characters regardless of the value of the
1611
	 * size attribute in the form input.
1612
	 *
1613
	 * @since 2.7.0
1614
	 *
1615
	 * @param string $output The password form HTML output.
1616
	 */
1617
	return apply_filters( 'the_password_form', $output );
1618
}
1619
1620
/**
1621
 * Whether currently in a page template.
1622
 *
1623
 * This template tag allows you to determine if you are in a page template.
1624
 * You can optionally provide a template name or array of template names
1625
 * and then the check will be specific to that template.
1626
 *
1627
 * @since 2.5.0
1628
 * @since 4.2.0 The `$template` parameter was changed to also accept an array of page templates.
1629
 * @since 4.7.0 Now works with any post type, not just pages.
1630
 *
1631
 * @param string|array $template The specific template name or array of templates to match.
1632
 * @return bool True on success, false on failure.
1633
 */
1634
function is_page_template( $template = '' ) {
1635
	if ( ! is_singular() ) {
1636
		return false;
1637
	}
1638
1639
	$page_template = get_page_template_slug( get_queried_object_id() );
1640
1641
	if ( empty( $template ) )
1642
		return (bool) $page_template;
1643
1644
	if ( $template == $page_template )
1645
		return true;
1646
1647
	if ( is_array( $template ) ) {
1648
		if ( ( in_array( 'default', $template, true ) && ! $page_template )
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_template of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1649
			|| in_array( $page_template, $template, true )
1650
		) {
1651
			return true;
1652
		}
1653
	}
1654
1655
	return ( 'default' === $template && ! $page_template );
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_template of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1656
}
1657
1658
/**
1659
 * Get the specific template name for a given post.
1660
 *
1661
 * @since 3.4.0
1662
 * @since 4.7.0 Now works with any post type, not just pages.
1663
 *
1664
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
1665
 * @return string|false Page template filename. Returns an empty string when the default page template
1666
 * 	is in use. Returns false if the post does not exist.
1667
 */
1668
function get_page_template_slug( $post = null ) {
1669
	$post = get_post( $post );
1670
1671
	if ( ! $post ) {
1672
		return false;
1673
	}
1674
1675
	$template = get_post_meta( $post->ID, '_wp_page_template', true );
1676
1677
	if ( ! $template || 'default' == $template ) {
1678
		return '';
1679
	}
1680
1681
	return $template;
1682
}
1683
1684
/**
1685
 * Retrieve formatted date timestamp of a revision (linked to that revisions's page).
1686
 *
1687
 * @since 2.6.0
1688
 *
1689
 * @param int|object $revision Revision ID or revision object.
1690
 * @param bool       $link     Optional, default is true. Link to revisions's page?
1691
 * @return string|false i18n formatted datetimestamp or localized 'Current Revision'.
1692
 */
1693
function wp_post_revision_title( $revision, $link = true ) {
1694
	if ( !$revision = get_post( $revision ) )
1695
		return $revision;
1696
1697 View Code Duplication
	if ( !in_array( $revision->post_type, array( 'post', 'page', 'revision' ) ) )
1698
		return false;
1699
1700
	/* translators: revision date format, see https://secure.php.net/date */
1701
	$datef = _x( 'F j, Y @ H:i:s', 'revision date format' );
1702
	/* translators: %s: revision date */
1703
	$autosavef = __( '%s [Autosave]' );
1704
	/* translators: %s: revision date */
1705
	$currentf  = __( '%s [Current Revision]' );
1706
1707
	$date = date_i18n( $datef, strtotime( $revision->post_modified ) );
1708 View Code Duplication
	if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
1709
		$date = "<a href='$link'>$date</a>";
1710
1711 View Code Duplication
	if ( !wp_is_post_revision( $revision ) )
1712
		$date = sprintf( $currentf, $date );
1713
	elseif ( wp_is_post_autosave( $revision ) )
1714
		$date = sprintf( $autosavef, $date );
1715
1716
	return $date;
1717
}
1718
1719
/**
1720
 * Retrieve formatted date timestamp of a revision (linked to that revisions's page).
1721
 *
1722
 * @since 3.6.0
1723
 *
1724
 * @param int|object $revision Revision ID or revision object.
1725
 * @param bool       $link     Optional, default is true. Link to revisions's page?
1726
 * @return string|false gravatar, user, i18n formatted datetimestamp or localized 'Current Revision'.
1727
 */
1728
function wp_post_revision_title_expanded( $revision, $link = true ) {
1729
	if ( !$revision = get_post( $revision ) )
1730
		return $revision;
1731
1732 View Code Duplication
	if ( !in_array( $revision->post_type, array( 'post', 'page', 'revision' ) ) )
1733
		return false;
1734
1735
	$author = get_the_author_meta( 'display_name', $revision->post_author );
1736
	/* translators: revision date format, see https://secure.php.net/date */
1737
	$datef = _x( 'F j, Y @ H:i:s', 'revision date format' );
1738
1739
	$gravatar = get_avatar( $revision->post_author, 24 );
1740
1741
	$date = date_i18n( $datef, strtotime( $revision->post_modified ) );
1742 View Code Duplication
	if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
1743
		$date = "<a href='$link'>$date</a>";
1744
1745
	$revision_date_author = sprintf(
1746
		/* translators: post revision title: 1: author avatar, 2: author name, 3: time ago, 4: date */
1747
		__( '%1$s %2$s, %3$s ago (%4$s)' ),
1748
		$gravatar,
1749
		$author,
1750
		human_time_diff( strtotime( $revision->post_modified ), current_time( 'timestamp' ) ),
1751
		$date
1752
	);
1753
1754
	/* translators: %s: revision date with author avatar */
1755
	$autosavef = __( '%s [Autosave]' );
1756
	/* translators: %s: revision date with author avatar */
1757
	$currentf  = __( '%s [Current Revision]' );
1758
1759 View Code Duplication
	if ( !wp_is_post_revision( $revision ) )
1760
		$revision_date_author = sprintf( $currentf, $revision_date_author );
1761
	elseif ( wp_is_post_autosave( $revision ) )
1762
		$revision_date_author = sprintf( $autosavef, $revision_date_author );
1763
1764
	/**
1765
	 * Filters the formatted author and date for a revision.
1766
	 *
1767
	 * @since 4.4.0
1768
	 *
1769
	 * @param string  $revision_date_author The formatted string.
1770
	 * @param WP_Post $revision             The revision object.
1771
	 * @param bool    $link                 Whether to link to the revisions page, as passed into
1772
	 *                                      wp_post_revision_title_expanded().
1773
	 */
1774
	return apply_filters( 'wp_post_revision_title_expanded', $revision_date_author, $revision, $link );
1775
}
1776
1777
/**
1778
 * Display list of a post's revisions.
1779
 *
1780
 * Can output either a UL with edit links or a TABLE with diff interface, and
1781
 * restore action links.
1782
 *
1783
 * @since 2.6.0
1784
 *
1785
 * @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...
1786
 * @param string      $type    'all' (default), 'revision' or 'autosave'
1787
 */
1788
function wp_list_post_revisions( $post_id = 0, $type = 'all' ) {
1789
	if ( ! $post = get_post( $post_id ) )
1790
		return;
1791
1792
	// $args array with (parent, format, right, left, type) deprecated since 3.6
1793
	if ( is_array( $type ) ) {
1794
		$type = ! empty( $type['type'] ) ? $type['type']  : $type;
1795
		_deprecated_argument( __FUNCTION__, '3.6.0' );
1796
	}
1797
1798
	if ( ! $revisions = wp_get_post_revisions( $post->ID ) )
1799
		return;
1800
1801
	$rows = '';
1802
	foreach ( $revisions as $revision ) {
1803
		if ( ! current_user_can( 'read_post', $revision->ID ) )
1804
			continue;
1805
1806
		$is_autosave = wp_is_post_autosave( $revision );
1807
		if ( ( 'revision' === $type && $is_autosave ) || ( 'autosave' === $type && ! $is_autosave ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_autosave of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $is_autosave of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1808
			continue;
1809
1810
		$rows .= "\t<li>" . wp_post_revision_title_expanded( $revision ) . "</li>\n";
1811
	}
1812
1813
	echo "<div class='hide-if-js'><p>" . __( 'JavaScript must be enabled to use this feature.' ) . "</p></div>\n";
1814
1815
	echo "<ul class='post-revisions hide-if-no-js'>\n";
1816
	echo $rows;
1817
	echo "</ul>";
1818
}
1819