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