Make WordPress Core

Ticket #39848: post-template.php

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