WordPress 4.4
[autoinstalls/wordpress.git] / wp-includes / post-template.php
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 content.
34  *
35  * @since 0.71
36  *
37  * @param string $before Optional. Content to prepend to the title.
38  * @param string $after  Optional. Content to append to the title.
39  * @param bool   $echo   Optional, default to true.Whether to display or return.
40  * @return string|void String if $echo parameter 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 {@link 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 {@link
63  * esc_attr()} before it is passed to the user or displayed. The default
64  * as with {@link the_title()}, 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.
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                          * Filter 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                          * Filter 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          * Filter 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 an 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 $id Optional. Post ID or post object.
173  */
174 function the_guid( $id = 0 ) {
175         /**
176          * Filter the escaped Global Unique Identifier (guid) of the post.
177          *
178          * @since 4.2.0
179          *
180          * @see get_the_guid()
181          *
182          * @param string $post_guid Escaped Global Unique Identifier (guid) of the post.
183          */
184         echo apply_filters( 'the_guid', get_the_guid( $id ) );
185 }
186
187 /**
188  * Retrieve the Post Global Unique Identifier (guid).
189  *
190  * The guid will appear to be a link, but should not be used as an link to the
191  * post. The reason you should not use it as a link, is because of moving the
192  * blog across domains.
193  *
194  * @since 1.5.0
195  *
196  * @param int|WP_Post $id Optional. Post ID or post object.
197  * @return string
198  */
199 function get_the_guid( $id = 0 ) {
200         $post = get_post($id);
201
202         /**
203          * Filter the Global Unique Identifier (guid) of the post.
204          *
205          * @since 1.5.0
206          *
207          * @param string $post_guid Global Unique Identifier (guid) of the post.
208          */
209         return apply_filters( 'get_the_guid', $post->guid );
210 }
211
212 /**
213  * Display the post content.
214  *
215  * @since 0.71
216  *
217  * @param string $more_link_text Optional. Content for when there is more text.
218  * @param bool   $strip_teaser   Optional. Strip teaser content before the more text. Default is false.
219  */
220 function the_content( $more_link_text = null, $strip_teaser = false) {
221         $content = get_the_content( $more_link_text, $strip_teaser );
222
223         /**
224          * Filter the post content.
225          *
226          * @since 0.71
227          *
228          * @param string $content Content of the current post.
229          */
230         $content = apply_filters( 'the_content', $content );
231         $content = str_replace( ']]>', ']]&gt;', $content );
232         echo $content;
233 }
234
235 /**
236  * Retrieve the post content.
237  *
238  * @since 0.71
239  *
240  * @global int   $page
241  * @global int   $more
242  * @global bool  $preview
243  * @global array $pages
244  * @global int   $multipage
245  *
246  * @param string $more_link_text Optional. Content for when there is more text.
247  * @param bool   $strip_teaser   Optional. Strip teaser content before the more text. Default is false.
248  * @return string
249  */
250 function get_the_content( $more_link_text = null, $strip_teaser = false ) {
251         global $page, $more, $preview, $pages, $multipage;
252
253         $post = get_post();
254
255         if ( null === $more_link_text )
256                 $more_link_text = __( '(more&hellip;)' );
257
258         $output = '';
259         $has_teaser = false;
260
261         // If post password required and it doesn't match the cookie.
262         if ( post_password_required( $post ) )
263                 return get_the_password_form( $post );
264
265         if ( $page > count( $pages ) ) // if the requested page doesn't exist
266                 $page = count( $pages ); // give them the highest numbered page that DOES exist
267
268         $content = $pages[$page - 1];
269         if ( preg_match( '/<!--more(.*?)?-->/', $content, $matches ) ) {
270                 $content = explode( $matches[0], $content, 2 );
271                 if ( ! empty( $matches[1] ) && ! empty( $more_link_text ) )
272                         $more_link_text = strip_tags( wp_kses_no_null( trim( $matches[1] ) ) );
273
274                 $has_teaser = true;
275         } else {
276                 $content = array( $content );
277         }
278
279         if ( false !== strpos( $post->post_content, '<!--noteaser-->' ) && ( ! $multipage || $page == 1 ) )
280                 $strip_teaser = true;
281
282         $teaser = $content[0];
283
284         if ( $more && $strip_teaser && $has_teaser )
285                 $teaser = '';
286
287         $output .= $teaser;
288
289         if ( count( $content ) > 1 ) {
290                 if ( $more ) {
291                         $output .= '<span id="more-' . $post->ID . '"></span>' . $content[1];
292                 } else {
293                         if ( ! empty( $more_link_text ) )
294
295                                 /**
296                                  * Filter the Read More link text.
297                                  *
298                                  * @since 2.8.0
299                                  *
300                                  * @param string $more_link_element Read More link element.
301                                  * @param string $more_link_text    Read More text.
302                                  */
303                                 $output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink() . "#more-{$post->ID}\" class=\"more-link\">$more_link_text</a>", $more_link_text );
304                         $output = force_balance_tags( $output );
305                 }
306         }
307
308         if ( $preview ) // Preview fix for JavaScript bug with foreign languages.
309                 $output =       preg_replace_callback( '/\%u([0-9A-F]{4})/', '_convert_urlencoded_to_entities', $output );
310
311         return $output;
312 }
313
314 /**
315  * Preview fix for JavaScript bug with foreign languages.
316  *
317  * @since 3.1.0
318  * @access private
319  *
320  * @param array $match Match array from preg_replace_callback.
321  * @return string
322  */
323 function _convert_urlencoded_to_entities( $match ) {
324         return '&#' . base_convert( $match[1], 16, 10 ) . ';';
325 }
326
327 /**
328  * Display the post excerpt.
329  *
330  * @since 0.71
331  */
332 function the_excerpt() {
333
334         /**
335          * Filter the displayed post excerpt.
336          *
337          * @since 0.71
338          *
339          * @see get_the_excerpt()
340          *
341          * @param string $post_excerpt The post excerpt.
342          */
343         echo apply_filters( 'the_excerpt', get_the_excerpt() );
344 }
345
346 /**
347  * Retrieve the post excerpt.
348  *
349  * @since 0.71
350  *
351  * @param mixed $deprecated Not used.
352  * @return string
353  */
354 function get_the_excerpt( $deprecated = '' ) {
355         if ( !empty( $deprecated ) )
356                 _deprecated_argument( __FUNCTION__, '2.3' );
357
358         $post = get_post();
359         if ( empty( $post ) ) {
360                 return '';
361         }
362
363         if ( post_password_required() ) {
364                 return __( 'There is no excerpt because this is a protected post.' );
365         }
366
367         /**
368          * Filter the retrieved post excerpt.
369          *
370          * @since 1.2.0
371          *
372          * @param string $post_excerpt The post excerpt.
373          */
374         return apply_filters( 'get_the_excerpt', $post->post_excerpt );
375 }
376
377 /**
378  * Whether post has excerpt.
379  *
380  * @since 2.3.0
381  *
382  * @param int|WP_Post $id Optional. Post ID or post object.
383  * @return bool
384  */
385 function has_excerpt( $id = 0 ) {
386         $post = get_post( $id );
387         return ( !empty( $post->post_excerpt ) );
388 }
389
390 /**
391  * Display the classes for the post div.
392  *
393  * @since 2.7.0
394  *
395  * @param string|array $class   One or more classes to add to the class list.
396  * @param int|WP_Post  $post_id Optional. Post ID or post object. Defaults to the global `$post`.
397  */
398 function post_class( $class = '', $post_id = null ) {
399         // Separates classes with a single space, collates classes for post DIV
400         echo 'class="' . join( ' ', get_post_class( $class, $post_id ) ) . '"';
401 }
402
403 /**
404  * Retrieve the classes for the post div as an array.
405  *
406  * The class names are many. If the post is a sticky, then the 'sticky'
407  * class name. The class 'hentry' is always added to each post. If the post has a
408  * post thumbnail, 'has-post-thumbnail' is added as a class. For each taxonomy that
409  * the post belongs to, a class will be added of the format '{$taxonomy}-{$slug}' -
410  * eg 'category-foo' or 'my_custom_taxonomy-bar'. The 'post_tag' taxonomy is a special
411  * case; the class has the 'tag-' prefix instead of 'post_tag-'. All classes are
412  * passed through the filter, 'post_class' with the list of classes, followed by
413  * $class parameter value, with the post ID as the last parameter.
414  *
415  * @since 2.7.0
416  * @since 4.2.0 Custom taxonomy classes were added.
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.
420  * @return array Array of classes.
421  */
422 function get_post_class( $class = '', $post_id = null ) {
423         $post = get_post( $post_id );
424
425         $classes = array();
426
427         if ( $class ) {
428                 if ( ! is_array( $class ) ) {
429                         $class = preg_split( '#\s+#', $class );
430                 }
431                 $classes = array_map( 'esc_attr', $class );
432         } else {
433                 // Ensure that we always coerce class to being an array.
434                 $class = array();
435         }
436
437         if ( ! $post ) {
438                 return $classes;
439         }
440
441         $classes[] = 'post-' . $post->ID;
442         if ( ! is_admin() )
443                 $classes[] = $post->post_type;
444         $classes[] = 'type-' . $post->post_type;
445         $classes[] = 'status-' . $post->post_status;
446
447         // Post Format
448         if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
449                 $post_format = get_post_format( $post->ID );
450
451                 if ( $post_format && !is_wp_error($post_format) )
452                         $classes[] = 'format-' . sanitize_html_class( $post_format );
453                 else
454                         $classes[] = 'format-standard';
455         }
456
457         $post_password_required = post_password_required( $post->ID );
458
459         // Post requires password.
460         if ( $post_password_required ) {
461                 $classes[] = 'post-password-required';
462         } elseif ( ! empty( $post->post_password ) ) {
463                 $classes[] = 'post-password-protected';
464         }
465
466         // Post thumbnails.
467         if ( current_theme_supports( 'post-thumbnails' ) && has_post_thumbnail( $post->ID ) && ! is_attachment( $post ) && ! $post_password_required ) {
468                 $classes[] = 'has-post-thumbnail';
469         }
470
471         // sticky for Sticky Posts
472         if ( is_sticky( $post->ID ) ) {
473                 if ( is_home() && ! is_paged() ) {
474                         $classes[] = 'sticky';
475                 } elseif ( is_admin() ) {
476                         $classes[] = 'status-sticky';
477                 }
478         }
479
480         // hentry for hAtom compliance
481         $classes[] = 'hentry';
482
483         // All public taxonomies
484         $taxonomies = get_taxonomies( array( 'public' => true ) );
485         foreach ( (array) $taxonomies as $taxonomy ) {
486                 if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {
487                         foreach ( (array) get_the_terms( $post->ID, $taxonomy ) as $term ) {
488                                 if ( empty( $term->slug ) ) {
489                                         continue;
490                                 }
491
492                                 $term_class = sanitize_html_class( $term->slug, $term->term_id );
493                                 if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) {
494                                         $term_class = $term->term_id;
495                                 }
496
497                                 // 'post_tag' uses the 'tag' prefix for backward compatibility.
498                                 if ( 'post_tag' == $taxonomy ) {
499                                         $classes[] = 'tag-' . $term_class;
500                                 } else {
501                                         $classes[] = sanitize_html_class( $taxonomy . '-' . $term_class, $taxonomy . '-' . $term->term_id );
502                                 }
503                         }
504                 }
505         }
506
507         $classes = array_map( 'esc_attr', $classes );
508
509         /**
510          * Filter the list of CSS classes for the current post.
511          *
512          * @since 2.7.0
513          *
514          * @param array $classes An array of post classes.
515          * @param array $class   An array of additional classes added to the post.
516          * @param int   $post_id The post ID.
517          */
518         $classes = apply_filters( 'post_class', $classes, $class, $post->ID );
519
520         return array_unique( $classes );
521 }
522
523 /**
524  * Display the classes for the body element.
525  *
526  * @since 2.8.0
527  *
528  * @param string|array $class One or more classes to add to the class list.
529  */
530 function body_class( $class = '' ) {
531         // Separates classes with a single space, collates classes for body element
532         echo 'class="' . join( ' ', get_body_class( $class ) ) . '"';
533 }
534
535 /**
536  * Retrieve the classes for the body element as an array.
537  *
538  * @since 2.8.0
539  *
540  * @global WP_Query $wp_query
541  *
542  * @param string|array $class One or more classes to add to the class list.
543  * @return array Array of classes.
544  */
545 function get_body_class( $class = '' ) {
546         global $wp_query;
547
548         $classes = array();
549
550         if ( is_rtl() )
551                 $classes[] = 'rtl';
552
553         if ( is_front_page() )
554                 $classes[] = 'home';
555         if ( is_home() )
556                 $classes[] = 'blog';
557         if ( is_archive() )
558                 $classes[] = 'archive';
559         if ( is_date() )
560                 $classes[] = 'date';
561         if ( is_search() ) {
562                 $classes[] = 'search';
563                 $classes[] = $wp_query->posts ? 'search-results' : 'search-no-results';
564         }
565         if ( is_paged() )
566                 $classes[] = 'paged';
567         if ( is_attachment() )
568                 $classes[] = 'attachment';
569         if ( is_404() )
570                 $classes[] = 'error404';
571
572         if ( is_single() ) {
573                 $post_id = $wp_query->get_queried_object_id();
574                 $post = $wp_query->get_queried_object();
575
576                 $classes[] = 'single';
577                 if ( isset( $post->post_type ) ) {
578                         $classes[] = 'single-' . sanitize_html_class($post->post_type, $post_id);
579                         $classes[] = 'postid-' . $post_id;
580
581                         // Post Format
582                         if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
583                                 $post_format = get_post_format( $post->ID );
584
585                                 if ( $post_format && !is_wp_error($post_format) )
586                                         $classes[] = 'single-format-' . sanitize_html_class( $post_format );
587                                 else
588                                         $classes[] = 'single-format-standard';
589                         }
590                 }
591
592                 if ( is_attachment() ) {
593                         $mime_type = get_post_mime_type($post_id);
594                         $mime_prefix = array( 'application/', 'image/', 'text/', 'audio/', 'video/', 'music/' );
595                         $classes[] = 'attachmentid-' . $post_id;
596                         $classes[] = 'attachment-' . str_replace( $mime_prefix, '', $mime_type );
597                 }
598         } elseif ( is_archive() ) {
599                 if ( is_post_type_archive() ) {
600                         $classes[] = 'post-type-archive';
601                         $post_type = get_query_var( 'post_type' );
602                         if ( is_array( $post_type ) )
603                                 $post_type = reset( $post_type );
604                         $classes[] = 'post-type-archive-' . sanitize_html_class( $post_type );
605                 } elseif ( is_author() ) {
606                         $author = $wp_query->get_queried_object();
607                         $classes[] = 'author';
608                         if ( isset( $author->user_nicename ) ) {
609                                 $classes[] = 'author-' . sanitize_html_class( $author->user_nicename, $author->ID );
610                                 $classes[] = 'author-' . $author->ID;
611                         }
612                 } elseif ( is_category() ) {
613                         $cat = $wp_query->get_queried_object();
614                         $classes[] = 'category';
615                         if ( isset( $cat->term_id ) ) {
616                                 $cat_class = sanitize_html_class( $cat->slug, $cat->term_id );
617                                 if ( is_numeric( $cat_class ) || ! trim( $cat_class, '-' ) ) {
618                                         $cat_class = $cat->term_id;
619                                 }
620
621                                 $classes[] = 'category-' . $cat_class;
622                                 $classes[] = 'category-' . $cat->term_id;
623                         }
624                 } elseif ( is_tag() ) {
625                         $tag = $wp_query->get_queried_object();
626                         $classes[] = 'tag';
627                         if ( isset( $tag->term_id ) ) {
628                                 $tag_class = sanitize_html_class( $tag->slug, $tag->term_id );
629                                 if ( is_numeric( $tag_class ) || ! trim( $tag_class, '-' ) ) {
630                                         $tag_class = $tag->term_id;
631                                 }
632
633                                 $classes[] = 'tag-' . $tag_class;
634                                 $classes[] = 'tag-' . $tag->term_id;
635                         }
636                 } elseif ( is_tax() ) {
637                         $term = $wp_query->get_queried_object();
638                         if ( isset( $term->term_id ) ) {
639                                 $term_class = sanitize_html_class( $term->slug, $term->term_id );
640                                 if ( is_numeric( $term_class ) || ! trim( $term_class, '-' ) ) {
641                                         $term_class = $term->term_id;
642                                 }
643
644                                 $classes[] = 'tax-' . sanitize_html_class( $term->taxonomy );
645                                 $classes[] = 'term-' . $term_class;
646                                 $classes[] = 'term-' . $term->term_id;
647                         }
648                 }
649         } elseif ( is_page() ) {
650                 $classes[] = 'page';
651
652                 $page_id = $wp_query->get_queried_object_id();
653
654                 $post = get_post($page_id);
655
656                 $classes[] = 'page-id-' . $page_id;
657
658                 if ( get_pages( array( 'parent' => $page_id, 'number' => 1 ) ) ) {
659                         $classes[] = 'page-parent';
660                 }
661
662                 if ( $post->post_parent ) {
663                         $classes[] = 'page-child';
664                         $classes[] = 'parent-pageid-' . $post->post_parent;
665                 }
666                 if ( is_page_template() ) {
667                         $classes[] = 'page-template';
668
669                         $template_slug  = get_page_template_slug( $page_id );
670                         $template_parts = explode( '/', $template_slug );
671
672                         foreach ( $template_parts as $part ) {
673                                 $classes[] = 'page-template-' . sanitize_html_class( str_replace( array( '.', '/' ), '-', basename( $part, '.php' ) ) );
674                         }
675                         $classes[] = 'page-template-' . sanitize_html_class( str_replace( '.', '-', $template_slug ) );
676                 } else {
677                         $classes[] = 'page-template-default';
678                 }
679         }
680
681         if ( is_user_logged_in() )
682                 $classes[] = 'logged-in';
683
684         if ( is_admin_bar_showing() ) {
685                 $classes[] = 'admin-bar';
686                 $classes[] = 'no-customize-support';
687         }
688
689         if ( get_background_color() !== get_theme_support( 'custom-background', 'default-color' ) || get_background_image() )
690                 $classes[] = 'custom-background';
691
692         $page = $wp_query->get( 'page' );
693
694         if ( ! $page || $page < 2 )
695                 $page = $wp_query->get( 'paged' );
696
697         if ( $page && $page > 1 && ! is_404() ) {
698                 $classes[] = 'paged-' . $page;
699
700                 if ( is_single() )
701                         $classes[] = 'single-paged-' . $page;
702                 elseif ( is_page() )
703                         $classes[] = 'page-paged-' . $page;
704                 elseif ( is_category() )
705                         $classes[] = 'category-paged-' . $page;
706                 elseif ( is_tag() )
707                         $classes[] = 'tag-paged-' . $page;
708                 elseif ( is_date() )
709                         $classes[] = 'date-paged-' . $page;
710                 elseif ( is_author() )
711                         $classes[] = 'author-paged-' . $page;
712                 elseif ( is_search() )
713                         $classes[] = 'search-paged-' . $page;
714                 elseif ( is_post_type_archive() )
715                         $classes[] = 'post-type-paged-' . $page;
716         }
717
718         if ( ! empty( $class ) ) {
719                 if ( !is_array( $class ) )
720                         $class = preg_split( '#\s+#', $class );
721                 $classes = array_merge( $classes, $class );
722         } else {
723                 // Ensure that we always coerce class to being an array.
724                 $class = array();
725         }
726
727         $classes = array_map( 'esc_attr', $classes );
728
729         /**
730          * Filter the list of CSS body classes for the current post or page.
731          *
732          * @since 2.8.0
733          *
734          * @param array $classes An array of body classes.
735          * @param array $class   An array of additional classes added to the body.
736          */
737         $classes = apply_filters( 'body_class', $classes, $class );
738
739         return array_unique( $classes );
740 }
741
742 /**
743  * Whether post requires password and correct password has been provided.
744  *
745  * @since 2.7.0
746  *
747  * @param int|WP_Post|null $post An optional post. Global $post used if not provided.
748  * @return bool false if a password is not required or the correct password cookie is present, true otherwise.
749  */
750 function post_password_required( $post = null ) {
751         $post = get_post($post);
752
753         if ( empty( $post->post_password ) )
754                 return false;
755
756         if ( ! isset( $_COOKIE['wp-postpass_' . COOKIEHASH] ) )
757                 return true;
758
759         require_once ABSPATH . WPINC . '/class-phpass.php';
760         $hasher = new PasswordHash( 8, true );
761
762         $hash = wp_unslash( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] );
763         if ( 0 !== strpos( $hash, '$P$B' ) )
764                 return true;
765
766         return ! $hasher->CheckPassword( $post->post_password, $hash );
767 }
768
769 //
770 // Page Template Functions for usage in Themes
771 //
772
773 /**
774  * The formatted output of a list of pages.
775  *
776  * Displays page links for paginated posts (i.e. includes the <!--nextpage-->.
777  * Quicktag one or more times). This tag must be within The Loop.
778  *
779  * @since 1.2.0
780  *
781  * @global int $page
782  * @global int $numpages
783  * @global int $multipage
784  * @global int $more
785  *
786  * @param string|array $args {
787  *     Optional. Array or string of default arguments.
788  *
789  *     @type string       $before           HTML or text to prepend to each link. Default is `<p> Pages:`.
790  *     @type string       $after            HTML or text to append to each link. Default is `</p>`.
791  *     @type string       $link_before      HTML or text to prepend to each link, inside the `<a>` tag.
792  *                                          Also prepended to the current item, which is not linked. Default empty.
793  *     @type string       $link_after       HTML or text to append to each Pages link inside the `<a>` tag.
794  *                                          Also appended to the current item, which is not linked. Default empty.
795  *     @type string       $next_or_number   Indicates whether page numbers should be used. Valid values are number
796  *                                          and next. Default is 'number'.
797  *     @type string       $separator        Text between pagination links. Default is ' '.
798  *     @type string       $nextpagelink     Link text for the next page link, if available. Default is 'Next Page'.
799  *     @type string       $previouspagelink Link text for the previous page link, if available. Default is 'Previous Page'.
800  *     @type string       $pagelink         Format string for page numbers. The % in the parameter string will be
801  *                                          replaced with the page number, so 'Page %' generates "Page 1", "Page 2", etc.
802  *                                          Defaults to '%', just the page number.
803  *     @type int|bool     $echo             Whether to echo or not. Accepts 1|true or 0|false. Default 1|true.
804  * }
805  * @return string Formatted output in HTML.
806  */
807 function wp_link_pages( $args = '' ) {
808         global $page, $numpages, $multipage, $more;
809
810         $defaults = array(
811                 'before'           => '<p>' . __( 'Pages:' ),
812                 'after'            => '</p>',
813                 'link_before'      => '',
814                 'link_after'       => '',
815                 'next_or_number'   => 'number',
816                 'separator'        => ' ',
817                 'nextpagelink'     => __( 'Next page' ),
818                 'previouspagelink' => __( 'Previous page' ),
819                 'pagelink'         => '%',
820                 'echo'             => 1
821         );
822
823         $params = wp_parse_args( $args, $defaults );
824
825         /**
826          * Filter the arguments used in retrieving page links for paginated posts.
827          *
828          * @since 3.0.0
829          *
830          * @param array $params An array of arguments for page links for paginated posts.
831          */
832         $r = apply_filters( 'wp_link_pages_args', $params );
833
834         $output = '';
835         if ( $multipage ) {
836                 if ( 'number' == $r['next_or_number'] ) {
837                         $output .= $r['before'];
838                         for ( $i = 1; $i <= $numpages; $i++ ) {
839                                 $link = $r['link_before'] . str_replace( '%', $i, $r['pagelink'] ) . $r['link_after'];
840                                 if ( $i != $page || ! $more && 1 == $page ) {
841                                         $link = _wp_link_page( $i ) . $link . '</a>';
842                                 }
843                                 /**
844                                  * Filter the HTML output of individual page number links.
845                                  *
846                                  * @since 3.6.0
847                                  *
848                                  * @param string $link The page number HTML output.
849                                  * @param int    $i    Page number for paginated posts' page links.
850                                  */
851                                 $link = apply_filters( 'wp_link_pages_link', $link, $i );
852
853                                 // Use the custom links separator beginning with the second link.
854                                 $output .= ( 1 === $i ) ? ' ' : $r['separator'];
855                                 $output .= $link;
856                         }
857                         $output .= $r['after'];
858                 } elseif ( $more ) {
859                         $output .= $r['before'];
860                         $prev = $page - 1;
861                         if ( $prev > 0 ) {
862                                 $link = _wp_link_page( $prev ) . $r['link_before'] . $r['previouspagelink'] . $r['link_after'] . '</a>';
863
864                                 /** This filter is documented in wp-includes/post-template.php */
865                                 $output .= apply_filters( 'wp_link_pages_link', $link, $prev );
866                         }
867                         $next = $page + 1;
868                         if ( $next <= $numpages ) {
869                                 if ( $prev ) {
870                                         $output .= $r['separator'];
871                                 }
872                                 $link = _wp_link_page( $next ) . $r['link_before'] . $r['nextpagelink'] . $r['link_after'] . '</a>';
873
874                                 /** This filter is documented in wp-includes/post-template.php */
875                                 $output .= apply_filters( 'wp_link_pages_link', $link, $next );
876                         }
877                         $output .= $r['after'];
878                 }
879         }
880
881         /**
882          * Filter the HTML output of page links for paginated posts.
883          *
884          * @since 3.6.0
885          *
886          * @param string $output HTML output of paginated posts' page links.
887          * @param array  $args   An array of arguments.
888          */
889         $html = apply_filters( 'wp_link_pages', $output, $args );
890
891         if ( $r['echo'] ) {
892                 echo $html;
893         }
894         return $html;
895 }
896
897 /**
898  * Helper function for wp_link_pages().
899  *
900  * @since 3.1.0
901  * @access private
902  *
903  * @global WP_Rewrite $wp_rewrite
904  *
905  * @param int $i Page number.
906  * @return string Link.
907  */
908 function _wp_link_page( $i ) {
909         global $wp_rewrite;
910         $post = get_post();
911         $query_args = array();
912
913         if ( 1 == $i ) {
914                 $url = get_permalink();
915         } else {
916                 if ( '' == get_option('permalink_structure') || in_array($post->post_status, array('draft', 'pending')) )
917                         $url = add_query_arg( 'page', $i, get_permalink() );
918                 elseif ( 'page' == get_option('show_on_front') && get_option('page_on_front') == $post->ID )
919                         $url = trailingslashit(get_permalink()) . user_trailingslashit("$wp_rewrite->pagination_base/" . $i, 'single_paged');
920                 else
921                         $url = trailingslashit(get_permalink()) . user_trailingslashit($i, 'single_paged');
922         }
923
924         if ( is_preview() ) {
925
926                 if ( ( 'draft' !== $post->post_status ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) {
927                         $query_args['preview_id'] = wp_unslash( $_GET['preview_id'] );
928                         $query_args['preview_nonce'] = wp_unslash( $_GET['preview_nonce'] );
929                 }
930
931                 $url = get_preview_post_link( $post, $query_args, $url );
932         }
933
934         return '<a href="' . esc_url( $url ) . '">';
935 }
936
937 //
938 // Post-meta: Custom per-post fields.
939 //
940
941 /**
942  * Retrieve post custom meta data field.
943  *
944  * @since 1.5.0
945  *
946  * @param string $key Meta data key name.
947  * @return false|string|array Array of values or single value, if only one element exists. False will be returned if key does not exist.
948  */
949 function post_custom( $key = '' ) {
950         $custom = get_post_custom();
951
952         if ( !isset( $custom[$key] ) )
953                 return false;
954         elseif ( 1 == count($custom[$key]) )
955                 return $custom[$key][0];
956         else
957                 return $custom[$key];
958 }
959
960 /**
961  * Display list of post custom fields.
962  *
963  * @since 1.2.0
964  *
965  * @internal This will probably change at some point...
966  *
967  */
968 function the_meta() {
969         if ( $keys = get_post_custom_keys() ) {
970                 echo "<ul class='post-meta'>\n";
971                 foreach ( (array) $keys as $key ) {
972                         $keyt = trim($key);
973                         if ( is_protected_meta( $keyt, 'post' ) )
974                                 continue;
975                         $values = array_map('trim', get_post_custom_values($key));
976                         $value = implode($values,', ');
977
978                         /**
979                          * Filter the HTML output of the li element in the post custom fields list.
980                          *
981                          * @since 2.2.0
982                          *
983                          * @param string $html  The HTML output for the li element.
984                          * @param string $key   Meta key.
985                          * @param string $value Meta value.
986                          */
987                         echo apply_filters( 'the_meta_key', "<li><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value );
988                 }
989                 echo "</ul>\n";
990         }
991 }
992
993 //
994 // Pages
995 //
996
997 /**
998  * Retrieve or display list of pages as a dropdown (select list).
999  *
1000  * @since 2.1.0
1001  * @since 4.2.0 The `$value_field` argument was added.
1002  * @since 4.3.0 The `$class` argument was added.
1003  *
1004  * @param array|string $args {
1005  *     Optional. Array or string of arguments to generate a pages drop-down element.
1006  *
1007  *     @type int          $depth                 Maximum depth. Default 0.
1008  *     @type int          $child_of              Page ID to retrieve child pages of. Default 0.
1009  *     @type int|string   $selected              Value of the option that should be selected. Default 0.
1010  *     @type bool|int     $echo                  Whether to echo or return the generated markup. Accepts 0, 1,
1011  *                                               or their bool equivalents. Default 1.
1012  *     @type string       $name                  Value for the 'name' attribute of the select element.
1013  *                                               Default 'page_id'.
1014  *     @type string       $id                    Value for the 'id' attribute of the select element.
1015  *     @type string       $class                 Value for the 'class' attribute of the select element. Default: none.
1016  *                                               Defaults to the value of `$name`.
1017  *     @type string       $show_option_none      Text to display for showing no pages. Default empty (does not display).
1018  *     @type string       $show_option_no_change Text to display for "no change" option. Default empty (does not display).
1019  *     @type string       $option_none_value     Value to use when no page is selected. Default empty.
1020  *     @type string       $value_field           Post field used to populate the 'value' attribute of the option
1021  *                                               elements. Accepts any valid post field. Default 'ID'.
1022  * }
1023  * @return string HTML content, if not displaying.
1024  */
1025 function wp_dropdown_pages( $args = '' ) {
1026         $defaults = array(
1027                 'depth' => 0, 'child_of' => 0,
1028                 'selected' => 0, 'echo' => 1,
1029                 'name' => 'page_id', 'id' => '',
1030                 'class' => '',
1031                 'show_option_none' => '', 'show_option_no_change' => '',
1032                 'option_none_value' => '',
1033                 'value_field' => 'ID',
1034         );
1035
1036         $r = wp_parse_args( $args, $defaults );
1037
1038         $pages = get_pages( $r );
1039         $output = '';
1040         // Back-compat with old system where both id and name were based on $name argument
1041         if ( empty( $r['id'] ) ) {
1042                 $r['id'] = $r['name'];
1043         }
1044
1045         if ( ! empty( $pages ) ) {
1046                 $class = '';
1047                 if ( ! empty( $r['class'] ) ) {
1048                         $class = " class='" . esc_attr( $r['class'] ) . "'";
1049                 }
1050
1051                 $output = "<select name='" . esc_attr( $r['name'] ) . "'" . $class . " id='" . esc_attr( $r['id'] ) . "'>\n";
1052                 if ( $r['show_option_no_change'] ) {
1053                         $output .= "\t<option value=\"-1\">" . $r['show_option_no_change'] . "</option>\n";
1054                 }
1055                 if ( $r['show_option_none'] ) {
1056                         $output .= "\t<option value=\"" . esc_attr( $r['option_none_value'] ) . '">' . $r['show_option_none'] . "</option>\n";
1057                 }
1058                 $output .= walk_page_dropdown_tree( $pages, $r['depth'], $r );
1059                 $output .= "</select>\n";
1060         }
1061
1062         /**
1063          * Filter the HTML output of a list of pages as a drop down.
1064          *
1065          * @since 2.1.0
1066          * @since 4.4.0 `$r` and `$pages` added as arguments.
1067          *
1068          * @param string $output HTML output for drop down list of pages.
1069          * @param array  $r      The parsed arguments array.
1070          * @param array  $pages  List of WP_Post objects returned by `get_pages()`
1071          */
1072         $html = apply_filters( 'wp_dropdown_pages', $output, $r, $pages );
1073
1074         if ( $r['echo'] ) {
1075                 echo $html;
1076         }
1077         return $html;
1078 }
1079
1080 /**
1081  * Retrieve or display list of pages in list (li) format.
1082  *
1083  * @since 1.5.0
1084  *
1085  * @see get_pages()
1086  *
1087  * @global WP_Query $wp_query
1088  *
1089  * @param array|string $args {
1090  *     Array or string of arguments. Optional.
1091  *
1092  *     @type int    $child_of     Display only the sub-pages of a single page by ID. Default 0 (all pages).
1093  *     @type string $authors      Comma-separated list of author IDs. Default empty (all authors).
1094  *     @type string $date_format  PHP date format to use for the listed pages. Relies on the 'show_date' parameter.
1095  *                                Default is the value of 'date_format' option.
1096  *     @type int    $depth        Number of levels in the hierarchy of pages to include in the generated list.
1097  *                                Accepts -1 (any depth), 0 (all pages), 1 (top-level pages only), and n (pages to
1098  *                                the given n depth). Default 0.
1099  *     @type bool   $echo         Whether or not to echo the list of pages. Default true.
1100  *     @type string $exclude      Comma-separated list of page IDs to exclude. Default empty.
1101  *     @type array  $include      Comma-separated list of page IDs to include. Default empty.
1102  *     @type string $link_after   Text or HTML to follow the page link label. Default null.
1103  *     @type string $link_before  Text or HTML to precede the page link label. Default null.
1104  *     @type string $post_type    Post type to query for. Default 'page'.
1105  *     @type string $post_status  Comma-separated list of post statuses to include. Default 'publish'.
1106  *     @type string $show_date    Whether to display the page publish or modified date for each page. Accepts
1107  *                                'modified' or any other value. An empty value hides the date. Default empty.
1108  *     @type string $sort_column  Comma-separated list of column names to sort the pages by. Accepts 'post_author',
1109  *                                'post_date', 'post_title', 'post_name', 'post_modified', 'post_modified_gmt',
1110  *                                'menu_order', 'post_parent', 'ID', 'rand', or 'comment_count'. Default 'post_title'.
1111  *     @type string $title_li     List heading. Passing a null or empty value will result in no heading, and the list
1112  *                                will not be wrapped with unordered list `<ul>` tags. Default 'Pages'.
1113  *     @type Walker $walker       Walker instance to use for listing pages. Default empty (Walker_Page).
1114  * }
1115  * @return string|void HTML list of pages.
1116  */
1117 function wp_list_pages( $args = '' ) {
1118         $defaults = array(
1119                 'depth' => 0, 'show_date' => '',
1120                 'date_format' => get_option( 'date_format' ),
1121                 'child_of' => 0, 'exclude' => '',
1122                 'title_li' => __( 'Pages' ), 'echo' => 1,
1123                 'authors' => '', 'sort_column' => 'menu_order, post_title',
1124                 'link_before' => '', 'link_after' => '', 'walker' => '',
1125         );
1126
1127         $r = wp_parse_args( $args, $defaults );
1128
1129         $output = '';
1130         $current_page = 0;
1131
1132         // sanitize, mostly to keep spaces out
1133         $r['exclude'] = preg_replace( '/[^0-9,]/', '', $r['exclude'] );
1134
1135         // Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
1136         $exclude_array = ( $r['exclude'] ) ? explode( ',', $r['exclude'] ) : array();
1137
1138         /**
1139          * Filter the array of pages to exclude from the pages list.
1140          *
1141          * @since 2.1.0
1142          *
1143          * @param array $exclude_array An array of page IDs to exclude.
1144          */
1145         $r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) );
1146
1147         // Query pages.
1148         $r['hierarchical'] = 0;
1149         $pages = get_pages( $r );
1150
1151         if ( ! empty( $pages ) ) {
1152                 if ( $r['title_li'] ) {
1153                         $output .= '<li class="pagenav">' . $r['title_li'] . '<ul>';
1154                 }
1155                 global $wp_query;
1156                 if ( is_page() || is_attachment() || $wp_query->is_posts_page ) {
1157                         $current_page = get_queried_object_id();
1158                 } elseif ( is_singular() ) {
1159                         $queried_object = get_queried_object();
1160                         if ( is_post_type_hierarchical( $queried_object->post_type ) ) {
1161                                 $current_page = $queried_object->ID;
1162                         }
1163                 }
1164
1165                 $output .= walk_page_tree( $pages, $r['depth'], $current_page, $r );
1166
1167                 if ( $r['title_li'] ) {
1168                         $output .= '</ul></li>';
1169                 }
1170         }
1171
1172         /**
1173          * Filter the HTML output of the pages to list.
1174          *
1175          * @since 1.5.1
1176          * @since 4.4.0 `$pages` added as arguments.
1177          *
1178          * @see wp_list_pages()
1179          *
1180          * @param string $output HTML output of the pages list.
1181          * @param array  $r      An array of page-listing arguments.
1182          * @param array  $pages  List of WP_Post objects returned by `get_pages()`
1183          */
1184         $html = apply_filters( 'wp_list_pages', $output, $r, $pages );
1185
1186         if ( $r['echo'] ) {
1187                 echo $html;
1188         } else {
1189                 return $html;
1190         }
1191 }
1192
1193 /**
1194  * Display or retrieve list of pages with optional home link.
1195  *
1196  * The arguments are listed below and part of the arguments are for {@link
1197  * wp_list_pages()} function. Check that function for more info on those
1198  * arguments.
1199  *
1200  * @since 2.7.0
1201  * @since 4.4.0 Added `menu_id`, `container`, `before`, `after`, and `walker` arguments.
1202  *
1203  * @param array|string $args {
1204  *     Optional. Arguments to generate a page menu. See wp_list_pages() for additional arguments.
1205  *
1206  *     @type string          $sort_column How to short the list of pages. Accepts post column names.
1207  *                                        Default 'menu_order, post_title'.
1208  *     @type string          $menu_id     ID for the div containing the page list. Default is empty string.
1209  *     @type string          $menu_class  Class to use for the element containing the page list. Default 'menu'.
1210  *     @type string          $container   Element to use for the element containing the page list. Default 'div'.
1211  *     @type bool            $echo        Whether to echo the list or return it. Accepts true (echo) or false (return).
1212  *                                        Default true.
1213  *     @type int|bool|string $show_home   Whether to display the link to the home page. Can just enter the text
1214  *                                        you'd like shown for the home link. 1|true defaults to 'Home'.
1215  *     @type string          $link_before The HTML or text to prepend to $show_home text. Default empty.
1216  *     @type string          $link_after  The HTML or text to append to $show_home text. Default empty.
1217  *     @type string          $before      The HTML or text to prepend to the menu. Default is '<ul>'.
1218  *     @type string          $after       The HTML or text to append to the menu. Default is '</ul>'.
1219  *     @type Walker          $walker      Walker instance to use for listing pages. Default empty (Walker_Page).
1220  * }
1221  * @return string|void HTML menu
1222  */
1223 function wp_page_menu( $args = array() ) {
1224         $defaults = array(
1225                 'sort_column' => 'menu_order, post_title',
1226                 'menu_id'     => '',
1227                 'menu_class'  => 'menu',
1228                 'container'   => 'div',
1229                 'echo'        => true,
1230                 'link_before' => '',
1231                 'link_after'  => '',
1232                 'before'      => '<ul>',
1233                 'after'       => '</ul>',
1234                 'walker'      => '',
1235         );
1236         $args = wp_parse_args( $args, $defaults );
1237
1238         /**
1239          * Filter the arguments used to generate a page-based menu.
1240          *
1241          * @since 2.7.0
1242          *
1243          * @see wp_page_menu()
1244          *
1245          * @param array $args An array of page menu arguments.
1246          */
1247         $args = apply_filters( 'wp_page_menu_args', $args );
1248
1249         $menu = '';
1250
1251         $list_args = $args;
1252
1253         // Show Home in the menu
1254         if ( ! empty($args['show_home']) ) {
1255                 if ( true === $args['show_home'] || '1' === $args['show_home'] || 1 === $args['show_home'] )
1256                         $text = __('Home');
1257                 else
1258                         $text = $args['show_home'];
1259                 $class = '';
1260                 if ( is_front_page() && !is_paged() )
1261                         $class = 'class="current_page_item"';
1262                 $menu .= '<li ' . $class . '><a href="' . home_url( '/' ) . '">' . $args['link_before'] . $text . $args['link_after'] . '</a></li>';
1263                 // If the front page is a page, add it to the exclude list
1264                 if (get_option('show_on_front') == 'page') {
1265                         if ( !empty( $list_args['exclude'] ) ) {
1266                                 $list_args['exclude'] .= ',';
1267                         } else {
1268                                 $list_args['exclude'] = '';
1269                         }
1270                         $list_args['exclude'] .= get_option('page_on_front');
1271                 }
1272         }
1273
1274         $list_args['echo'] = false;
1275         $list_args['title_li'] = '';
1276         $menu .= str_replace( array( "\r", "\n", "\t" ), '', wp_list_pages($list_args) );
1277
1278         $container = sanitize_text_field( $args['container'] );
1279
1280         // Fallback in case `wp_nav_menu()` was called without a container.
1281         if ( empty( $container ) ) {
1282                 $container = 'div';
1283         }
1284
1285         if ( $menu ) {
1286
1287                 // wp_nav_menu doesn't set before and after
1288                 if ( isset( $args['fallback_cb'] ) &&
1289                         'wp_page_menu' === $args['fallback_cb'] &&
1290                         'ul' !== $container ) {
1291                         $args['before'] = '<ul>';
1292                         $args['after'] = '</ul>';
1293                 }
1294
1295                 $menu = $args['before'] . $menu . $args['after'];
1296         }
1297
1298         $attrs = '';
1299         if ( ! empty( $args['menu_id'] ) ) {
1300                 $attrs .= ' id="' . esc_attr( $args['menu_id'] ) . '"';
1301         }
1302
1303         if ( ! empty( $args['menu_class'] ) ) {
1304                 $attrs .= ' class="' . esc_attr( $args['menu_class'] ) . '"';
1305         }
1306
1307         $menu = "<{$container}{$attrs}>" . $menu . "</{$container}>\n";
1308
1309         /**
1310          * Filter the HTML output of a page-based menu.
1311          *
1312          * @since 2.7.0
1313          *
1314          * @see wp_page_menu()
1315          *
1316          * @param string $menu The HTML output.
1317          * @param array  $args An array of arguments.
1318          */
1319         $menu = apply_filters( 'wp_page_menu', $menu, $args );
1320         if ( $args['echo'] )
1321                 echo $menu;
1322         else
1323                 return $menu;
1324 }
1325
1326 //
1327 // Page helpers
1328 //
1329
1330 /**
1331  * Retrieve HTML list content for page list.
1332  *
1333  * @uses Walker_Page to create HTML list content.
1334  * @since 2.1.0
1335  *
1336  * @param array $pages
1337  * @param int   $depth
1338  * @param int   $current_page
1339  * @param array $r
1340  * @return string
1341  */
1342 function walk_page_tree( $pages, $depth, $current_page, $r ) {
1343         if ( empty($r['walker']) )
1344                 $walker = new Walker_Page;
1345         else
1346                 $walker = $r['walker'];
1347
1348         foreach ( (array) $pages as $page ) {
1349                 if ( $page->post_parent )
1350                         $r['pages_with_children'][ $page->post_parent ] = true;
1351         }
1352
1353         $args = array($pages, $depth, $r, $current_page);
1354         return call_user_func_array(array($walker, 'walk'), $args);
1355 }
1356
1357 /**
1358  * Retrieve HTML dropdown (select) content for page list.
1359  *
1360  * @uses Walker_PageDropdown to create HTML dropdown content.
1361  * @since 2.1.0
1362  * @see Walker_PageDropdown::walk() for parameters and return description.
1363  *
1364  * @return string
1365  */
1366 function walk_page_dropdown_tree() {
1367         $args = func_get_args();
1368         if ( empty($args[2]['walker']) ) // the user's options are the third parameter
1369                 $walker = new Walker_PageDropdown;
1370         else
1371                 $walker = $args[2]['walker'];
1372
1373         return call_user_func_array(array($walker, 'walk'), $args);
1374 }
1375
1376 //
1377 // Attachments
1378 //
1379
1380 /**
1381  * Display an attachment page link using an image or icon.
1382  *
1383  * @since 2.0.0
1384  *
1385  * @param int|WP_Post $id Optional. Post ID or post object.
1386  * @param bool        $fullsize     Optional, default is false. Whether to use full size.
1387  * @param bool        $deprecated   Deprecated. Not used.
1388  * @param bool        $permalink    Optional, default is false. Whether to include permalink.
1389  */
1390 function the_attachment_link( $id = 0, $fullsize = false, $deprecated = false, $permalink = false ) {
1391         if ( !empty( $deprecated ) )
1392                 _deprecated_argument( __FUNCTION__, '2.5' );
1393
1394         if ( $fullsize )
1395                 echo wp_get_attachment_link($id, 'full', $permalink);
1396         else
1397                 echo wp_get_attachment_link($id, 'thumbnail', $permalink);
1398 }
1399
1400 /**
1401  * Retrieve an attachment page link using an image or icon, if possible.
1402  *
1403  * @since 2.5.0
1404  * @since 4.4.0 The `$id` parameter can now accept either a post ID or `WP_Post` object.
1405  *
1406  * @param int|WP_Post  $id        Optional. Post ID or post object.
1407  * @param string|array $size      Optional. Image size. Accepts any valid image size, or an array
1408  *                                of width and height values in pixels (in that order).
1409  *                                Default 'thumbnail'.
1410  * @param bool         $permalink Optional, Whether to add permalink to image. Default false.
1411  * @param bool         $icon      Optional. Whether the attachment is an icon. Default false.
1412  * @param string|false $text      Optional. Link text to use. Activated by passing a string, false otherwise.
1413  *                                Default false.
1414  * @param array|string $attr      Optional. Array or string of attributes. Default empty.
1415  * @return string HTML content.
1416  */
1417 function wp_get_attachment_link( $id = 0, $size = 'thumbnail', $permalink = false, $icon = false, $text = false, $attr = '' ) {
1418         $_post = get_post( $id );
1419
1420         if ( empty( $_post ) || ( 'attachment' != $_post->post_type ) || ! $url = wp_get_attachment_url( $_post->ID ) )
1421                 return __( 'Missing Attachment' );
1422
1423         if ( $permalink )
1424                 $url = get_attachment_link( $_post->ID );
1425
1426         if ( $text ) {
1427                 $link_text = $text;
1428         } elseif ( $size && 'none' != $size ) {
1429                 $link_text = wp_get_attachment_image( $_post->ID, $size, $icon, $attr );
1430         } else {
1431                 $link_text = '';
1432         }
1433
1434         if ( trim( $link_text ) == '' )
1435                 $link_text = $_post->post_title;
1436
1437         /**
1438          * Filter a retrieved attachment page link.
1439          *
1440          * @since 2.7.0
1441          *
1442          * @param string       $link_html The page link HTML output.
1443          * @param int          $id        Post ID.
1444          * @param string|array $size      Size of the image. Image size or array of width and height values (in that order).
1445          *                                Default 'thumbnail'.
1446          * @param bool         $permalink Whether to add permalink to image. Default false.
1447          * @param bool         $icon      Whether to include an icon. Default false.
1448          * @param string|bool  $text      If string, will be link text. Default false.
1449          */
1450         return apply_filters( 'wp_get_attachment_link', "<a href='$url'>$link_text</a>", $id, $size, $permalink, $icon, $text );
1451 }
1452
1453 /**
1454  * Wrap attachment in paragraph tag before content.
1455  *
1456  * @since 2.0.0
1457  *
1458  * @param string $content
1459  * @return string
1460  */
1461 function prepend_attachment($content) {
1462         $post = get_post();
1463
1464         if ( empty($post->post_type) || $post->post_type != 'attachment' )
1465                 return $content;
1466
1467         if ( wp_attachment_is( 'video', $post ) ) {
1468                 $meta = wp_get_attachment_metadata( get_the_ID() );
1469                 $atts = array( 'src' => wp_get_attachment_url() );
1470                 if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) {
1471                         $atts['width'] = (int) $meta['width'];
1472                         $atts['height'] = (int) $meta['height'];
1473                 }
1474                 if ( has_post_thumbnail() ) {
1475                         $atts['poster'] = wp_get_attachment_url( get_post_thumbnail_id() );
1476                 }
1477                 $p = wp_video_shortcode( $atts );
1478         } elseif ( wp_attachment_is( 'audio', $post ) ) {
1479                 $p = wp_audio_shortcode( array( 'src' => wp_get_attachment_url() ) );
1480         } else {
1481                 $p = '<p class="attachment">';
1482                 // show the medium sized image representation of the attachment if available, and link to the raw file
1483                 $p .= wp_get_attachment_link(0, 'medium', false);
1484                 $p .= '</p>';
1485         }
1486
1487         /**
1488          * Filter the attachment markup to be prepended to the post content.
1489          *
1490          * @since 2.0.0
1491          *
1492          * @see prepend_attachment()
1493          *
1494          * @param string $p The attachment HTML output.
1495          */
1496         $p = apply_filters( 'prepend_attachment', $p );
1497
1498         return "$p\n$content";
1499 }
1500
1501 //
1502 // Misc
1503 //
1504
1505 /**
1506  * Retrieve protected post password form content.
1507  *
1508  * @since 1.0.0
1509  *
1510  * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
1511  * @return string HTML content for password form for password protected post.
1512  */
1513 function get_the_password_form( $post = 0 ) {
1514         $post = get_post( $post );
1515         $label = 'pwbox-' . ( empty($post->ID) ? rand() : $post->ID );
1516         $output = '<form action="' . esc_url( site_url( 'wp-login.php?action=postpass', 'login_post' ) ) . '" class="post-password-form" method="post">
1517         <p>' . __( 'This content is password protected. To view it please enter your password below:' ) . '</p>
1518         <p><label for="' . $label . '">' . __( 'Password:' ) . ' <input name="post_password" id="' . $label . '" type="password" size="20" /></label> <input type="submit" name="Submit" value="' . esc_attr__( 'Submit' ) . '" /></p></form>
1519         ';
1520
1521         /**
1522          * Filter the HTML output for the protected post password form.
1523          *
1524          * If modifying the password field, please note that the core database schema
1525          * limits the password field to 20 characters regardless of the value of the
1526          * size attribute in the form input.
1527          *
1528          * @since 2.7.0
1529          *
1530          * @param string $output The password form HTML output.
1531          */
1532         return apply_filters( 'the_password_form', $output );
1533 }
1534
1535 /**
1536  * Whether currently in a page template.
1537  *
1538  * This template tag allows you to determine if you are in a page template.
1539  * You can optionally provide a template name or array of template names
1540  * and then the check will be specific to that template.
1541  *
1542  * @since 2.5.0
1543  * @since 4.2.0 The `$template` parameter was changed to also accept an array of page templates.
1544  *
1545  * @param string|array $template The specific template name or array of templates to match.
1546  * @return bool True on success, false on failure.
1547  */
1548 function is_page_template( $template = '' ) {
1549         if ( ! is_page() )
1550                 return false;
1551
1552         $page_template = get_page_template_slug( get_queried_object_id() );
1553
1554         if ( empty( $template ) )
1555                 return (bool) $page_template;
1556
1557         if ( $template == $page_template )
1558                 return true;
1559
1560         if ( is_array( $template ) ) {
1561                 if ( ( in_array( 'default', $template, true ) && ! $page_template )
1562                         || in_array( $page_template, $template, true )
1563                 ) {
1564                         return true;
1565                 }
1566         }
1567
1568         return ( 'default' === $template && ! $page_template );
1569 }
1570
1571 /**
1572  * Get the specific template name for a page.
1573  *
1574  * @since 3.4.0
1575  *
1576  * @param int $post_id Optional. The page ID to check. Defaults to the current post, when used in the loop.
1577  * @return string|false Page template filename. Returns an empty string when the default page template
1578  *      is in use. Returns false if the post is not a page.
1579  */
1580 function get_page_template_slug( $post_id = null ) {
1581         $post = get_post( $post_id );
1582         if ( ! $post || 'page' != $post->post_type )
1583                 return false;
1584         $template = get_post_meta( $post->ID, '_wp_page_template', true );
1585         if ( ! $template || 'default' == $template )
1586                 return '';
1587         return $template;
1588 }
1589
1590 /**
1591  * Retrieve formatted date timestamp of a revision (linked to that revisions's page).
1592  *
1593  * @since 2.6.0
1594  *
1595  * @param int|object $revision Revision ID or revision object.
1596  * @param bool       $link     Optional, default is true. Link to revisions's page?
1597  * @return string|false i18n formatted datetimestamp or localized 'Current Revision'.
1598  */
1599 function wp_post_revision_title( $revision, $link = true ) {
1600         if ( !$revision = get_post( $revision ) )
1601                 return $revision;
1602
1603         if ( !in_array( $revision->post_type, array( 'post', 'page', 'revision' ) ) )
1604                 return false;
1605
1606         /* translators: revision date format, see http://php.net/date */
1607         $datef = _x( 'F j, Y @ H:i:s', 'revision date format' );
1608         /* translators: 1: date */
1609         $autosavef = _x( '%1$s [Autosave]', 'post revision title extra' );
1610         /* translators: 1: date */
1611         $currentf  = _x( '%1$s [Current Revision]', 'post revision title extra' );
1612
1613         $date = date_i18n( $datef, strtotime( $revision->post_modified ) );
1614         if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
1615                 $date = "<a href='$link'>$date</a>";
1616
1617         if ( !wp_is_post_revision( $revision ) )
1618                 $date = sprintf( $currentf, $date );
1619         elseif ( wp_is_post_autosave( $revision ) )
1620                 $date = sprintf( $autosavef, $date );
1621
1622         return $date;
1623 }
1624
1625 /**
1626  * Retrieve formatted date timestamp of a revision (linked to that revisions's page).
1627  *
1628  * @since 3.6.0
1629  *
1630  * @param int|object $revision Revision ID or revision object.
1631  * @param bool       $link     Optional, default is true. Link to revisions's page?
1632  * @return string|false gravatar, user, i18n formatted datetimestamp or localized 'Current Revision'.
1633  */
1634 function wp_post_revision_title_expanded( $revision, $link = true ) {
1635         if ( !$revision = get_post( $revision ) )
1636                 return $revision;
1637
1638         if ( !in_array( $revision->post_type, array( 'post', 'page', 'revision' ) ) )
1639                 return false;
1640
1641         $author = get_the_author_meta( 'display_name', $revision->post_author );
1642         /* translators: revision date format, see http://php.net/date */
1643         $datef = _x( 'F j, Y @ H:i:s', 'revision date format' );
1644
1645         $gravatar = get_avatar( $revision->post_author, 24 );
1646
1647         $date = date_i18n( $datef, strtotime( $revision->post_modified ) );
1648         if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
1649                 $date = "<a href='$link'>$date</a>";
1650
1651         $revision_date_author = sprintf(
1652                 /* translators: post revision title: 1: author avatar, 2: author name, 3: time ago, 4: date */
1653                 _x( '%1$s %2$s, %3$s ago (%4$s)', 'post revision title' ),
1654                 $gravatar,
1655                 $author,
1656                 human_time_diff( strtotime( $revision->post_modified ), current_time( 'timestamp' ) ),
1657                 $date
1658         );
1659
1660         $autosavef = __( '%1$s [Autosave]' );
1661         $currentf  = __( '%1$s [Current Revision]' );
1662
1663         if ( !wp_is_post_revision( $revision ) )
1664                 $revision_date_author = sprintf( $currentf, $revision_date_author );
1665         elseif ( wp_is_post_autosave( $revision ) )
1666                 $revision_date_author = sprintf( $autosavef, $revision_date_author );
1667
1668         /**
1669          * Filter the formatted author and date for a revision.
1670          *
1671          * @since 4.4.0
1672          *
1673          * @param string  $revision_date_author The formatted string.
1674          * @param WP_Post $revision             The revision object.
1675          * @param bool    $link                 Whether to link to the revisions page, as passed into
1676          *                                      wp_post_revision_title_expanded().
1677          */
1678         return apply_filters( 'wp_post_revision_title_expanded', $revision_date_author, $revision, $link );
1679 }
1680
1681 /**
1682  * Display list of a post's revisions.
1683  *
1684  * Can output either a UL with edit links or a TABLE with diff interface, and
1685  * restore action links.
1686  *
1687  * @since 2.6.0
1688  *
1689  * @param int|WP_Post $post_id Optional. Post ID or WP_Post object. Default is global $post.
1690  * @param string      $type    'all' (default), 'revision' or 'autosave'
1691  */
1692 function wp_list_post_revisions( $post_id = 0, $type = 'all' ) {
1693         if ( ! $post = get_post( $post_id ) )
1694                 return;
1695
1696         // $args array with (parent, format, right, left, type) deprecated since 3.6
1697         if ( is_array( $type ) ) {
1698                 $type = ! empty( $type['type'] ) ? $type['type']  : $type;
1699                 _deprecated_argument( __FUNCTION__, '3.6' );
1700         }
1701
1702         if ( ! $revisions = wp_get_post_revisions( $post->ID ) )
1703                 return;
1704
1705         $rows = '';
1706         foreach ( $revisions as $revision ) {
1707                 if ( ! current_user_can( 'read_post', $revision->ID ) )
1708                         continue;
1709
1710                 $is_autosave = wp_is_post_autosave( $revision );
1711                 if ( ( 'revision' === $type && $is_autosave ) || ( 'autosave' === $type && ! $is_autosave ) )
1712                         continue;
1713
1714                 $rows .= "\t<li>" . wp_post_revision_title_expanded( $revision ) . "</li>\n";
1715         }
1716
1717         echo "<div class='hide-if-js'><p>" . __( 'JavaScript must be enabled to use this feature.' ) . "</p></div>\n";
1718
1719         echo "<ul class='post-revisions hide-if-no-js'>\n";
1720         echo $rows;
1721         echo "</ul>";
1722 }