WordPress.org

Make WordPress Core

Ticket #41152: link-template.php

File link-template.php, 136.3 KB (added by rinkuyadav999, 2 years ago)

Functino in Core file

Line 
1<?php
2/**
3 * WordPress Link Template Functions
4 *
5 * @package WordPress
6 * @subpackage Template
7 */
8
9/**
10 * Displays the permalink for the current post.
11 *
12 * @since 1.2.0
13 * @since 4.4.0 Added the `$post` parameter.
14 *
15 * @param int|WP_Post $post Optional. Post ID or post object. Default is the global `$post`.
16 */
17function the_permalink( $post = 0 ) {
18        /**
19         * Filters the display of the permalink for the current post.
20         *
21         * @since 1.5.0
22         * @since 4.4.0 Added the `$post` parameter.
23         *
24         * @param string      $permalink The permalink for the current post.
25         * @param int|WP_Post $post      Post ID, WP_Post object, or 0. Default 0.
26         */
27        echo esc_url( apply_filters( 'the_permalink', get_permalink( $post ), $post ) );
28}
29
30/**
31 * Retrieves a trailing-slashed string if the site is set for adding trailing slashes.
32 *
33 * Conditionally adds a trailing slash if the permalink structure has a trailing
34 * slash, strips the trailing slash if not. The string is passed through the
35 * {@see 'user_trailingslashit'} filter. Will remove trailing slash from string, if
36 * site is not set to have them.
37 *
38 * @since 2.2.0
39 *
40 * @global WP_Rewrite $wp_rewrite
41 *
42 * @param string $string      URL with or without a trailing slash.
43 * @param string $type_of_url Optional. The type of URL being considered (e.g. single, category, etc)
44 *                            for use in the filter. Default empty string.
45 * @return string The URL with the trailing slash appended or stripped.
46 */
47function user_trailingslashit($string, $type_of_url = '') {
48        global $wp_rewrite;
49        if ( $wp_rewrite->use_trailing_slashes )
50                $string = trailingslashit($string);
51        else
52                $string = untrailingslashit($string);
53
54        /**
55         * Filters the trailing-slashed string, depending on whether the site is set to use trailing slashes.
56         *
57         * @since 2.2.0
58         *
59         * @param string $string      URL with or without a trailing slash.
60         * @param string $type_of_url The type of URL being considered. Accepts 'single', 'single_trackback',
61         *                            'single_feed', 'single_paged', 'commentpaged', 'paged', 'home', 'feed',
62         *                            'category', 'page', 'year', 'month', 'day', 'post_type_archive'.
63         */
64        return apply_filters( 'user_trailingslashit', $string, $type_of_url );
65}
66
67/**
68 * Displays the permalink anchor for the current post.
69 *
70 * The permalink mode title will use the post title for the 'a' element 'id'
71 * attribute. The id mode uses 'post-' with the post ID for the 'id' attribute.
72 *
73 * @since 0.71
74 *
75 * @param string $mode Optional. Permalink mode. Accepts 'title' or 'id'. Default 'id'.
76 */
77function permalink_anchor( $mode = 'id' ) {
78        $post = get_post();
79        switch ( strtolower( $mode ) ) {
80                case 'title':
81                        $title = sanitize_title( $post->post_title ) . '-' . $post->ID;
82                        echo '<a id="'.$title.'"></a>';
83                        break;
84                case 'id':
85                default:
86                        echo '<a id="post-' . $post->ID . '"></a>';
87                        break;
88        }
89}
90
91/**
92 * Retrieves the full permalink for the current post or post ID.
93 *
94 * This function is an alias for get_permalink().
95 *
96 * @since 3.9.0
97 *
98 * @see get_permalink()
99 *
100 * @param int|WP_Post $post      Optional. Post ID or post object. Default is the global `$post`.
101 * @param bool        $leavename Optional. Whether to keep post name or page name. Default false.
102 *
103 * @return string|false The permalink URL or false if post does not exist.
104 */
105function get_the_permalink( $post = 0, $leavename = false ) {
106        return get_permalink( $post, $leavename );
107}
108
109/**
110 * Retrieves the full permalink for the current post or post ID.
111 *
112 * @since 1.0.0
113 *
114 * @param int|WP_Post $post      Optional. Post ID or post object. Default is the global `$post`.
115 * @param bool        $leavename Optional. Whether to keep post name or page name. Default false.
116 * @return string|false The permalink URL or false if post does not exist.
117 */
118function get_permalink( $post = 0, $leavename = false ) {
119        $rewritecode = array(
120                '%year%',
121                '%monthnum%',
122                '%day%',
123                '%hour%',
124                '%minute%',
125                '%second%',
126                $leavename? '' : '%postname%',
127                '%post_id%',
128                '%category%',
129                '%author%',
130                $leavename? '' : '%pagename%',
131        );
132
133        if ( is_object( $post ) && isset( $post->filter ) && 'sample' == $post->filter ) {
134                $sample = true;
135        } else {
136                $post = get_post( $post );
137                $sample = false;
138        }
139
140        if ( empty($post->ID) )
141                return false;
142
143        if ( $post->post_type == 'page' )
144                return get_page_link($post, $leavename, $sample);
145        elseif ( $post->post_type == 'attachment' )
146                return get_attachment_link( $post, $leavename );
147        elseif ( in_array($post->post_type, get_post_types( array('_builtin' => false) ) ) )
148                return get_post_permalink($post, $leavename, $sample);
149
150        $permalink = get_option('permalink_structure');
151
152        /**
153         * Filters the permalink structure for a post before token replacement occurs.
154         *
155         * Only applies to posts with post_type of 'post'.
156         *
157         * @since 3.0.0
158         *
159         * @param string  $permalink The site's permalink structure.
160         * @param WP_Post $post      The post in question.
161         * @param bool    $leavename Whether to keep the post name.
162         */
163        $permalink = apply_filters( 'pre_post_link', $permalink, $post, $leavename );
164
165        if ( '' != $permalink && !in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft', 'future' ) ) ) {
166                $unixtime = strtotime($post->post_date);
167
168                $category = '';
169                if ( strpos($permalink, '%category%') !== false ) {
170                        $cats = get_the_category($post->ID);
171                        if ( $cats ) {
172                                $cats = wp_list_sort( $cats, array(
173                                        'term_id' => 'ASC',
174                                ) );
175
176                                /**
177                                 * Filters the category that gets used in the %category% permalink token.
178                                 *
179                                 * @since 3.5.0
180                                 *
181                                 * @param WP_Term  $cat  The category to use in the permalink.
182                                 * @param array    $cats Array of all categories (WP_Term objects) associated with the post.
183                                 * @param WP_Post  $post The post in question.
184                                 */
185                                $category_object = apply_filters( 'post_link_category', $cats[0], $cats, $post );
186
187                                $category_object = get_term( $category_object, 'category' );
188                                $category = $category_object->slug;
189                                if ( $parent = $category_object->parent )
190                                        $category = get_category_parents($parent, false, '/', true) . $category;
191                        }
192                        // show default category in permalinks, without
193                        // having to assign it explicitly
194                        if ( empty($category) ) {
195                                $default_category = get_term( get_option( 'default_category' ), 'category' );
196                                if ( $default_category && ! is_wp_error( $default_category ) ) {
197                                        $category = $default_category->slug;
198                                }
199                        }
200                }
201
202                $author = '';
203                if ( strpos($permalink, '%author%') !== false ) {
204                        $authordata = get_userdata($post->post_author);
205                        $author = $authordata->user_nicename;
206                }
207
208                $date = explode(" ",date('Y m d H i s', $unixtime));
209                $rewritereplace =
210                array(
211                        $date[0],
212                        $date[1],
213                        $date[2],
214                        $date[3],
215                        $date[4],
216                        $date[5],
217                        $post->post_name,
218                        $post->ID,
219                        $category,
220                        $author,
221                        $post->post_name,
222                );
223                $permalink = home_url( str_replace($rewritecode, $rewritereplace, $permalink) );
224                $permalink = user_trailingslashit($permalink, 'single');
225        } else { // if they're not using the fancy permalink option
226                $permalink = home_url('?p=' . $post->ID);
227        }
228
229        /**
230         * Filters the permalink for a post.
231         *
232         * Only applies to posts with post_type of 'post'.
233         *
234         * @since 1.5.0
235         *
236         * @param string  $permalink The post's permalink.
237         * @param WP_Post $post      The post in question.
238         * @param bool    $leavename Whether to keep the post name.
239         */
240        return apply_filters( 'post_link', $permalink, $post, $leavename );
241}
242
243/**
244 * Retrieves the permalink for a post of a custom post type.
245 *
246 * @since 3.0.0
247 *
248 * @global WP_Rewrite $wp_rewrite
249 *
250 * @param int $id         Optional. Post ID. Default uses the global `$post`.
251 * @param bool $leavename Optional, defaults to false. Whether to keep post name. Default false.
252 * @param bool $sample    Optional, defaults to false. Is it a sample permalink. Default false.
253 * @return string|WP_Error The post permalink.
254 */
255function get_post_permalink( $id = 0, $leavename = false, $sample = false ) {
256        global $wp_rewrite;
257
258        $post = get_post($id);
259
260        if ( is_wp_error( $post ) )
261                return $post;
262
263        $post_link = $wp_rewrite->get_extra_permastruct($post->post_type);
264
265        $slug = $post->post_name;
266
267        $draft_or_pending = get_post_status( $id ) && in_array( get_post_status( $id ), array( 'draft', 'pending', 'auto-draft', 'future' ) );
268
269        $post_type = get_post_type_object($post->post_type);
270
271        if ( $post_type->hierarchical ) {
272                $slug = get_page_uri( $id );
273        }
274
275        if ( !empty($post_link) && ( !$draft_or_pending || $sample ) ) {
276                if ( ! $leavename ) {
277                        $post_link = str_replace("%$post->post_type%", $slug, $post_link);
278                }
279                $post_link = home_url( user_trailingslashit($post_link) );
280        } else {
281                if ( $post_type->query_var && ( isset($post->post_status) && !$draft_or_pending ) )
282                        $post_link = add_query_arg($post_type->query_var, $slug, '');
283                else
284                        $post_link = add_query_arg(array('post_type' => $post->post_type, 'p' => $post->ID), '');
285                $post_link = home_url($post_link);
286        }
287
288        /**
289         * Filters the permalink for a post of a custom post type.
290         *
291         * @since 3.0.0
292         *
293         * @param string  $post_link The post's permalink.
294         * @param WP_Post $post      The post in question.
295         * @param bool    $leavename Whether to keep the post name.
296         * @param bool    $sample    Is it a sample permalink.
297         */
298        return apply_filters( 'post_type_link', $post_link, $post, $leavename, $sample );
299}
300
301/**
302 * Retrieves the permalink for the current page or page ID.
303 *
304 * Respects page_on_front. Use this one.
305 *
306 * @since 1.5.0
307 *
308 * @param int|WP_Post $post      Optional. Post ID or object. Default uses the global `$post`.
309 * @param bool        $leavename Optional. Whether to keep the page name. Default false.
310 * @param bool        $sample    Optional. Whether it should be treated as a sample permalink.
311 *                               Default false.
312 * @return string The page permalink.
313 */
314function get_page_link( $post = false, $leavename = false, $sample = false ) {
315        $post = get_post( $post );
316
317        if ( 'page' == get_option( 'show_on_front' ) && $post->ID == get_option( 'page_on_front' ) )
318                $link = home_url('/');
319        else
320                $link = _get_page_link( $post, $leavename, $sample );
321
322        /**
323         * Filters the permalink for a page.
324         *
325         * @since 1.5.0
326         *
327         * @param string $link    The page's permalink.
328         * @param int    $post_id The ID of the page.
329         * @param bool   $sample  Is it a sample permalink.
330         */
331        return apply_filters( 'page_link', $link, $post->ID, $sample );
332}
333
334/**
335 * Retrieves the page permalink.
336 *
337 * Ignores page_on_front. Internal use only.
338 *
339 * @since 2.1.0
340 * @access private
341 *
342 * @global WP_Rewrite $wp_rewrite
343 *
344 * @param int|WP_Post $post      Optional. Post ID or object. Default uses the global `$post`.
345 * @param bool        $leavename Optional. Whether to keep the page name. Default false.
346 * @param bool        $sample    Optional. Whether it should be treated as a sample permalink.
347 *                               Default false.
348 * @return string The page permalink.
349 */
350function _get_page_link( $post = false, $leavename = false, $sample = false ) {
351        global $wp_rewrite;
352
353        $post = get_post( $post );
354
355        $draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
356
357        $link = $wp_rewrite->get_page_permastruct();
358
359        if ( !empty($link) && ( ( isset($post->post_status) && !$draft_or_pending ) || $sample ) ) {
360                if ( ! $leavename ) {
361                        $link = str_replace('%pagename%', get_page_uri( $post ), $link);
362                }
363
364                $link = home_url($link);
365                $link = user_trailingslashit($link, 'page');
366        } else {
367                $link = home_url( '?page_id=' . $post->ID );
368        }
369
370        /**
371         * Filters the permalink for a non-page_on_front page.
372         *
373         * @since 2.1.0
374         *
375         * @param string $link    The page's permalink.
376         * @param int    $post_id The ID of the page.
377         */
378        return apply_filters( '_get_page_link', $link, $post->ID );
379}
380
381/**
382 * Retrieves the permalink for an attachment.
383 *
384 * This can be used in the WordPress Loop or outside of it.
385 *
386 * @since 2.0.0
387 *
388 * @global WP_Rewrite $wp_rewrite
389 *
390 * @param int|object $post      Optional. Post ID or object. Default uses the global `$post`.
391 * @param bool       $leavename Optional. Whether to keep the page name. Default false.
392 * @return string The attachment permalink.
393 */
394function get_attachment_link( $post = null, $leavename = false ) {
395        global $wp_rewrite;
396
397        $link = false;
398
399        $post = get_post( $post );
400        $parent = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false;
401        if ( $parent && ! in_array( $parent->post_type, get_post_types() ) ) {
402                $parent = false;
403        }
404
405        if ( $wp_rewrite->using_permalinks() && $parent ) {
406                if ( 'page' == $parent->post_type )
407                        $parentlink = _get_page_link( $post->post_parent ); // Ignores page_on_front
408                else
409                        $parentlink = get_permalink( $post->post_parent );
410
411                if ( is_numeric($post->post_name) || false !== strpos(get_option('permalink_structure'), '%category%') )
412                        $name = 'attachment/' . $post->post_name; // <permalink>/<int>/ is paged so we use the explicit attachment marker
413                else
414                        $name = $post->post_name;
415
416                if ( strpos($parentlink, '?') === false )
417                        $link = user_trailingslashit( trailingslashit($parentlink) . '%postname%' );
418
419                if ( ! $leavename )
420                        $link = str_replace( '%postname%', $name, $link );
421        } elseif ( $wp_rewrite->using_permalinks() && ! $leavename ) {
422                $link = home_url( user_trailingslashit( $post->post_name ) );
423        }
424
425        if ( ! $link )
426                $link = home_url( '/?attachment_id=' . $post->ID );
427
428        /**
429         * Filters the permalink for an attachment.
430         *
431         * @since 2.0.0
432         *
433         * @param string $link    The attachment's permalink.
434         * @param int    $post_id Attachment ID.
435         */
436        return apply_filters( 'attachment_link', $link, $post->ID );
437}
438
439/**
440 * Retrieves the permalink for the year archives.
441 *
442 * @since 1.5.0
443 *
444 * @global WP_Rewrite $wp_rewrite
445 *
446 * @param int|bool $year False for current year or year for permalink.
447 * @return string The permalink for the specified year archive.
448 */
449function get_year_link( $year ) {
450        global $wp_rewrite;
451        if ( !$year )
452                $year = gmdate('Y', current_time('timestamp'));
453        $yearlink = $wp_rewrite->get_year_permastruct();
454        if ( !empty($yearlink) ) {
455                $yearlink = str_replace('%year%', $year, $yearlink);
456                $yearlink = home_url( user_trailingslashit( $yearlink, 'year' ) );
457        } else {
458                $yearlink = home_url( '?m=' . $year );
459        }
460
461        /**
462         * Filters the year archive permalink.
463         *
464         * @since 1.5.0
465         *
466         * @param string $yearlink Permalink for the year archive.
467         * @param int    $year     Year for the archive.
468         */
469        return apply_filters( 'year_link', $yearlink, $year );
470}
471
472/**
473 * Retrieves the permalink for the month archives with year.
474 *
475 * @since 1.0.0
476 *
477 * @global WP_Rewrite $wp_rewrite
478 *
479 * @param bool|int $year  False for current year. Integer of year.
480 * @param bool|int $month False for current month. Integer of month.
481 * @return string The permalink for the specified month and year archive.
482 */
483function get_month_link($year, $month) {
484        global $wp_rewrite;
485        if ( !$year )
486                $year = gmdate('Y', current_time('timestamp'));
487        if ( !$month )
488                $month = gmdate('m', current_time('timestamp'));
489        $monthlink = $wp_rewrite->get_month_permastruct();
490        if ( !empty($monthlink) ) {
491                $monthlink = str_replace('%year%', $year, $monthlink);
492                $monthlink = str_replace('%monthnum%', zeroise(intval($month), 2), $monthlink);
493                $monthlink = home_url( user_trailingslashit( $monthlink, 'month' ) );
494        } else {
495                $monthlink = home_url( '?m=' . $year . zeroise( $month, 2 ) );
496        }
497
498        /**
499         * Filters the month archive permalink.
500         *
501         * @since 1.5.0
502         *
503         * @param string $monthlink Permalink for the month archive.
504         * @param int    $year      Year for the archive.
505         * @param int    $month     The month for the archive.
506         */
507        return apply_filters( 'month_link', $monthlink, $year, $month );
508}
509
510/**
511 * Retrieves the permalink for the day archives with year and month.
512 *
513 * @since 1.0.0
514 *
515 * @global WP_Rewrite $wp_rewrite
516 *
517 * @param bool|int $year  False for current year. Integer of year.
518 * @param bool|int $month False for current month. Integer of month.
519 * @param bool|int $day   False for current day. Integer of day.
520 * @return string The permalink for the specified day, month, and year archive.
521 */
522function get_day_link($year, $month, $day) {
523        global $wp_rewrite;
524        if ( !$year )
525                $year = gmdate('Y', current_time('timestamp'));
526        if ( !$month )
527                $month = gmdate('m', current_time('timestamp'));
528        if ( !$day )
529                $day = gmdate('j', current_time('timestamp'));
530
531        $daylink = $wp_rewrite->get_day_permastruct();
532        if ( !empty($daylink) ) {
533                $daylink = str_replace('%year%', $year, $daylink);
534                $daylink = str_replace('%monthnum%', zeroise(intval($month), 2), $daylink);
535                $daylink = str_replace('%day%', zeroise(intval($day), 2), $daylink);
536                $daylink = home_url( user_trailingslashit( $daylink, 'day' ) );
537        } else {
538                $daylink = home_url( '?m=' . $year . zeroise( $month, 2 ) . zeroise( $day, 2 ) );
539        }
540
541        /**
542         * Filters the day archive permalink.
543         *
544         * @since 1.5.0
545         *
546         * @param string $daylink Permalink for the day archive.
547         * @param int    $year    Year for the archive.
548         * @param int    $month   Month for the archive.
549         * @param int    $day     The day for the archive.
550         */
551        return apply_filters( 'day_link', $daylink, $year, $month, $day );
552}
553
554/**
555 * Displays the permalink for the feed type.
556 *
557 * @since 3.0.0
558 *
559 * @param string $anchor The link's anchor text.
560 * @param string $feed   Optional. Feed type. Default empty.
561 */
562function the_feed_link( $anchor, $feed = '' ) {
563        $link = '<a href="' . esc_url( get_feed_link( $feed ) ) . '">' . $anchor . '</a>';
564
565        /**
566         * Filters the feed link anchor tag.
567         *
568         * @since 3.0.0
569         *
570         * @param string $link The complete anchor tag for a feed link.
571         * @param string $feed The feed type, or an empty string for the
572         *                     default feed type.
573         */
574        echo apply_filters( 'the_feed_link', $link, $feed );
575}
576
577/**
578 * Retrieves the permalink for the feed type.
579 *
580 * @since 1.5.0
581 *
582 * @global WP_Rewrite $wp_rewrite
583 *
584 * @param string $feed Optional. Feed type. Default empty.
585 * @return string The feed permalink.
586 */
587function get_feed_link( $feed = '' ) {
588        global $wp_rewrite;
589
590        $permalink = $wp_rewrite->get_feed_permastruct();
591        if ( '' != $permalink ) {
592                if ( false !== strpos($feed, 'comments_') ) {
593                        $feed = str_replace('comments_', '', $feed);
594                        $permalink = $wp_rewrite->get_comment_feed_permastruct();
595                }
596
597                if ( get_default_feed() == $feed )
598                        $feed = '';
599
600                $permalink = str_replace('%feed%', $feed, $permalink);
601                $permalink = preg_replace('#/+#', '/', "/$permalink");
602                $output =  home_url( user_trailingslashit($permalink, 'feed') );
603        } else {
604                if ( empty($feed) )
605                        $feed = get_default_feed();
606
607                if ( false !== strpos($feed, 'comments_') )
608                        $feed = str_replace('comments_', 'comments-', $feed);
609
610                $output = home_url("?feed={$feed}");
611        }
612
613        /**
614         * Filters the feed type permalink.
615         *
616         * @since 1.5.0
617         *
618         * @param string $output The feed permalink.
619         * @param string $feed   Feed type.
620         */
621        return apply_filters( 'feed_link', $output, $feed );
622}
623
624/**
625 * Retrieves the permalink for the post comments feed.
626 *
627 * @since 2.2.0
628 *
629 * @param int    $post_id Optional. Post ID. Default is the ID of the global `$post`.
630 * @param string $feed    Optional. Feed type. Default empty.
631 * @return string The permalink for the comments feed for the given post.
632 */
633function get_post_comments_feed_link( $post_id = 0, $feed = '' ) {
634        $post_id = absint( $post_id );
635
636        if ( ! $post_id )
637                $post_id = get_the_ID();
638
639        if ( empty( $feed ) )
640                $feed = get_default_feed();
641
642        $post = get_post( $post_id );
643        $unattached = 'attachment' === $post->post_type && 0 === (int) $post->post_parent;
644
645        if ( '' != get_option('permalink_structure') ) {
646                if ( 'page' == get_option('show_on_front') && $post_id == get_option('page_on_front') )
647                        $url = _get_page_link( $post_id );
648                else
649                        $url = get_permalink($post_id);
650
651                if ( $unattached ) {
652                        $url =  home_url( '/feed/' );
653                        if ( $feed !== get_default_feed() ) {
654                                $url .= "$feed/";
655                        }
656                        $url = add_query_arg( 'attachment_id', $post_id, $url );
657                } else {
658                        $url = trailingslashit($url) . 'feed';
659                        if ( $feed != get_default_feed() )
660                                $url .= "/$feed";
661                        $url = user_trailingslashit($url, 'single_feed');
662                }
663        } else {
664                if ( $unattached ) {
665                        $url = add_query_arg( array( 'feed' => $feed, 'attachment_id' => $post_id ), home_url( '/' ) );
666                } elseif ( 'page' == $post->post_type ) {
667                        $url = add_query_arg( array( 'feed' => $feed, 'page_id' => $post_id ), home_url( '/' ) );
668                } else {
669                        $url = add_query_arg( array( 'feed' => $feed, 'p' => $post_id ), home_url( '/' ) );
670                }
671        }
672
673        /**
674         * Filters the post comments feed permalink.
675         *
676         * @since 1.5.1
677         *
678         * @param string $url Post comments feed permalink.
679         */
680        return apply_filters( 'post_comments_feed_link', $url );
681}
682
683/**
684 * Displays the comment feed link for a post.
685 *
686 * Prints out the comment feed link for a post. Link text is placed in the
687 * anchor. If no link text is specified, default text is used. If no post ID is
688 * specified, the current post is used.
689 *
690 * @since 2.5.0
691 *
692 * @param string $link_text Optional. Descriptive link text. Default 'Comments Feed'.
693 * @param int    $post_id   Optional. Post ID. Default is the ID of the global `$post`.
694 * @param string $feed      Optional. Feed format. Default empty.
695 */
696function post_comments_feed_link( $link_text = '', $post_id = '', $feed = '' ) {
697        $url = get_post_comments_feed_link( $post_id, $feed );
698        if ( empty( $link_text ) ) {
699                $link_text = __('Comments Feed');
700        }
701
702        $link = '<a href="' . esc_url( $url ) . '">' . $link_text . '</a>';
703        /**
704         * Filters the post comment feed link anchor tag.
705         *
706         * @since 2.8.0
707         *
708         * @param string $link    The complete anchor tag for the comment feed link.
709         * @param int    $post_id Post ID.
710         * @param string $feed    The feed type, or an empty string for the default feed type.
711         */
712        echo apply_filters( 'post_comments_feed_link_html', $link, $post_id, $feed );
713}
714
715/**
716 * Retrieves the feed link for a given author.
717 *
718 * Returns a link to the feed for all posts by a given author. A specific feed
719 * can be requested or left blank to get the default feed.
720 *
721 * @since 2.5.0
722 *
723 * @param int    $author_id Author ID.
724 * @param string $feed      Optional. Feed type. Default empty.
725 * @return string Link to the feed for the author specified by $author_id.
726 */
727function get_author_feed_link( $author_id, $feed = '' ) {
728        $author_id = (int) $author_id;
729        $permalink_structure = get_option('permalink_structure');
730
731        if ( empty($feed) )
732                $feed = get_default_feed();
733
734        if ( '' == $permalink_structure ) {
735                $link = home_url("?feed=$feed&amp;author=" . $author_id);
736        } else {
737                $link = get_author_posts_url($author_id);
738                if ( $feed == get_default_feed() )
739                        $feed_link = 'feed';
740                else
741                        $feed_link = "feed/$feed";
742
743                $link = trailingslashit($link) . user_trailingslashit($feed_link, 'feed');
744        }
745
746        /**
747         * Filters the feed link for a given author.
748         *
749         * @since 1.5.1
750         *
751         * @param string $link The author feed link.
752         * @param string $feed Feed type.
753         */
754        $link = apply_filters( 'author_feed_link', $link, $feed );
755
756        return $link;
757}
758
759/**
760 * Retrieves the feed link for a category.
761 *
762 * Returns a link to the feed for all posts in a given category. A specific feed
763 * can be requested or left blank to get the default feed.
764 *
765 * @since 2.5.0
766 *
767 * @param int    $cat_id Category ID.
768 * @param string $feed   Optional. Feed type. Default empty.
769 * @return string Link to the feed for the category specified by $cat_id.
770 */
771function get_category_feed_link( $cat_id, $feed = '' ) {
772        return get_term_feed_link( $cat_id, 'category', $feed );
773}
774
775/**
776 * Retrieves the feed link for a term.
777 *
778 * Returns a link to the feed for all posts in a given term. A specific feed
779 * can be requested or left blank to get the default feed.
780 *
781 * @since 3.0.0
782 *
783 * @param int    $term_id  Term ID.
784 * @param string $taxonomy Optional. Taxonomy of `$term_id`. Default 'category'.
785 * @param string $feed     Optional. Feed type. Default empty.
786 * @return string|false Link to the feed for the term specified by $term_id and $taxonomy.
787 */
788function get_term_feed_link( $term_id, $taxonomy = 'category', $feed = '' ) {
789        $term_id = ( int ) $term_id;
790
791        $term = get_term( $term_id, $taxonomy  );
792
793        if ( empty( $term ) || is_wp_error( $term ) )
794                return false;
795
796        if ( empty( $feed ) )
797                $feed = get_default_feed();
798
799        $permalink_structure = get_option( 'permalink_structure' );
800
801        if ( '' == $permalink_structure ) {
802                if ( 'category' == $taxonomy ) {
803                        $link = home_url("?feed=$feed&amp;cat=$term_id");
804                }
805                elseif ( 'post_tag' == $taxonomy ) {
806                        $link = home_url("?feed=$feed&amp;tag=$term->slug");
807                } else {
808                        $t = get_taxonomy( $taxonomy );
809                        $link = home_url("?feed=$feed&amp;$t->query_var=$term->slug");
810                }
811        } else {
812                $link = get_term_link( $term_id, $term->taxonomy );
813                if ( $feed == get_default_feed() )
814                        $feed_link = 'feed';
815                else
816                        $feed_link = "feed/$feed";
817
818                $link = trailingslashit( $link ) . user_trailingslashit( $feed_link, 'feed' );
819        }
820
821        if ( 'category' == $taxonomy ) {
822                /**
823                 * Filters the category feed link.
824                 *
825                 * @since 1.5.1
826                 *
827                 * @param string $link The category feed link.
828                 * @param string $feed Feed type.
829                 */
830                $link = apply_filters( 'category_feed_link', $link, $feed );
831        } elseif ( 'post_tag' == $taxonomy ) {
832                /**
833                 * Filters the post tag feed link.
834                 *
835                 * @since 2.3.0
836                 *
837                 * @param string $link The tag feed link.
838                 * @param string $feed Feed type.
839                 */
840                $link = apply_filters( 'tag_feed_link', $link, $feed );
841        } else {
842                /**
843                 * Filters the feed link for a taxonomy other than 'category' or 'post_tag'.
844                 *
845                 * @since 3.0.0
846                 *
847                 * @param string $link The taxonomy feed link.
848                 * @param string $feed Feed type.
849                 * @param string $taxonomy The taxonomy name.
850                 */
851                $link = apply_filters( 'taxonomy_feed_link', $link, $feed, $taxonomy );
852        }
853
854        return $link;
855}
856
857/**
858 * Retrieves the permalink for a tag feed.
859 *
860 * @since 2.3.0
861 *
862 * @param int    $tag_id Tag ID.
863 * @param string $feed   Optional. Feed type. Default empty.
864 * @return string The feed permalink for the given tag.
865 */
866function get_tag_feed_link( $tag_id, $feed = '' ) {
867        return get_term_feed_link( $tag_id, 'post_tag', $feed );
868}
869
870/**
871 * Retrieves the edit link for a tag.
872 *
873 * @since 2.7.0
874 *
875 * @param int    $tag_id   Tag ID.
876 * @param string $taxonomy Optional. Taxonomy slug. Default 'post_tag'.
877 * @return string The edit tag link URL for the given tag.
878 */
879function get_edit_tag_link( $tag_id, $taxonomy = 'post_tag' ) {
880        /**
881         * Filters the edit link for a tag (or term in another taxonomy).
882         *
883         * @since 2.7.0
884         *
885         * @param string $link The term edit link.
886         */
887        return apply_filters( 'get_edit_tag_link', get_edit_term_link( $tag_id, $taxonomy ) );
888}
889
890/**
891 * Displays or retrieves the edit link for a tag with formatting.
892 *
893 * @since 2.7.0
894 *
895 * @param string  $link   Optional. Anchor text. Default empty.
896 * @param string  $before Optional. Display before edit link. Default empty.
897 * @param string  $after  Optional. Display after edit link. Default empty.
898 * @param WP_Term $tag    Optional. Term object. If null, the queried object will be inspected.
899 *                        Default null.
900 */
901function edit_tag_link( $link = '', $before = '', $after = '', $tag = null ) {
902        $link = edit_term_link( $link, '', '', $tag, false );
903
904        /**
905         * Filters the anchor tag for the edit link for a tag (or term in another taxonomy).
906         *
907         * @since 2.7.0
908         *
909         * @param string $link The anchor tag for the edit link.
910         */
911        echo $before . apply_filters( 'edit_tag_link', $link ) . $after;
912}
913
914/**
915 * Retrieves the URL for editing a given term.
916 *
917 * @since 3.1.0
918 * @since 4.5.0 The `$taxonomy` argument was made optional.
919 *
920 * @param int    $term_id     Term ID.
921 * @param string $taxonomy    Optional. Taxonomy. Defaults to the taxonomy of the term identified
922 *                            by `$term_id`.
923 * @param string $object_type Optional. The object type. Used to highlight the proper post type
924 *                            menu on the linked page. Defaults to the first object_type associated
925 *                            with the taxonomy.
926 * @return string|null The edit term link URL for the given term, or null on failure.
927 */
928function get_edit_term_link( $term_id, $taxonomy = '', $object_type = '' ) {
929        $term = get_term( $term_id, $taxonomy );
930        if ( ! $term || is_wp_error( $term ) ) {
931                return;
932        }
933
934        $tax = get_taxonomy( $term->taxonomy );
935        if ( ! $tax || ! current_user_can( 'edit_term', $term->term_id ) ) {
936                return;
937        }
938
939        $args = array(
940                'taxonomy' => $taxonomy,
941                'tag_ID'   => $term->term_id,
942        );
943
944        if ( $object_type ) {
945                $args['post_type'] = $object_type;
946        } elseif ( ! empty( $tax->object_type ) ) {
947                $args['post_type'] = reset( $tax->object_type );
948        }
949
950        if ( $tax->show_ui ) {
951                $location = add_query_arg( $args, admin_url( 'term.php' ) );
952        } else {
953                $location = '';
954        }
955
956        /**
957         * Filters the edit link for a term.
958         *
959         * @since 3.1.0
960         *
961         * @param string $location    The edit link.
962         * @param int    $term_id     Term ID.
963         * @param string $taxonomy    Taxonomy name.
964         * @param string $object_type The object type (eg. the post type).
965         */
966        return apply_filters( 'get_edit_term_link', $location, $term_id, $taxonomy, $object_type );
967}
968
969/**
970 * Displays or retrieves the edit term link with formatting.
971 *
972 * @since 3.1.0
973 *
974 * @param string $link   Optional. Anchor text. Default empty.
975 * @param string $before Optional. Display before edit link. Default empty.
976 * @param string $after  Optional. Display after edit link. Default empty.
977 * @param object $term   Optional. Term object. If null, the queried object will be inspected. Default null.
978 * @param bool   $echo   Optional. Whether or not to echo the return. Default true.
979 * @return string|void HTML content.
980 */
981function edit_term_link( $link = '', $before = '', $after = '', $term = null, $echo = true ) {
982        if ( is_null( $term ) )
983                $term = get_queried_object();
984
985        if ( ! $term )
986                return;
987
988        $tax = get_taxonomy( $term->taxonomy );
989        if ( ! current_user_can( 'edit_term', $term->term_id ) ) {
990                return;
991        }
992
993        if ( empty( $link ) )
994                $link = __('Edit This');
995
996        $link = '<a href="' . get_edit_term_link( $term->term_id, $term->taxonomy ) . '">' . $link . '</a>';
997
998        /**
999         * Filters the anchor tag for the edit link of a term.
1000         *
1001         * @since 3.1.0
1002         *
1003         * @param string $link    The anchor tag for the edit link.
1004         * @param int    $term_id Term ID.
1005         */
1006        $link = $before . apply_filters( 'edit_term_link', $link, $term->term_id ) . $after;
1007
1008        if ( $echo )
1009                echo $link;
1010        else
1011                return $link;
1012}
1013
1014/**
1015 * Retrieves the permalink for a search.
1016 *
1017 * @since  3.0.0
1018 *
1019 * @global WP_Rewrite $wp_rewrite
1020 *
1021 * @param string $query Optional. The query string to use. If empty the current query is used. Default empty.
1022 * @return string The search permalink.
1023 */
1024function get_search_link( $query = '' ) {
1025        global $wp_rewrite;
1026
1027        if ( empty($query) )
1028                $search = get_search_query( false );
1029        else
1030                $search = stripslashes($query);
1031
1032        $permastruct = $wp_rewrite->get_search_permastruct();
1033
1034        if ( empty( $permastruct ) ) {
1035                $link = home_url('?s=' . urlencode($search) );
1036        } else {
1037                $search = urlencode($search);
1038                $search = str_replace('%2F', '/', $search); // %2F(/) is not valid within a URL, send it un-encoded.
1039                $link = str_replace( '%search%', $search, $permastruct );
1040                $link = home_url( user_trailingslashit( $link, 'search' ) );
1041        }
1042
1043        /**
1044         * Filters the search permalink.
1045         *
1046         * @since 3.0.0
1047         *
1048         * @param string $link   Search permalink.
1049         * @param string $search The URL-encoded search term.
1050         */
1051        return apply_filters( 'search_link', $link, $search );
1052}
1053
1054/**
1055 * Retrieves the permalink for the search results feed.
1056 *
1057 * @since 2.5.0
1058 *
1059 * @global WP_Rewrite $wp_rewrite
1060 *
1061 * @param string $search_query Optional. Search query. Default empty.
1062 * @param string $feed         Optional. Feed type. Default empty.
1063 * @return string The search results feed permalink.
1064 */
1065function get_search_feed_link($search_query = '', $feed = '') {
1066        global $wp_rewrite;
1067        $link = get_search_link($search_query);
1068
1069        if ( empty($feed) )
1070                $feed = get_default_feed();
1071
1072        $permastruct = $wp_rewrite->get_search_permastruct();
1073
1074        if ( empty($permastruct) ) {
1075                $link = add_query_arg('feed', $feed, $link);
1076        } else {
1077                $link = trailingslashit($link);
1078                $link .= "feed/$feed/";
1079        }
1080
1081        /**
1082         * Filters the search feed link.
1083         *
1084         * @since 2.5.0
1085         *
1086         * @param string $link Search feed link.
1087         * @param string $feed Feed type.
1088         * @param string $type The search type. One of 'posts' or 'comments'.
1089         */
1090        return apply_filters( 'search_feed_link', $link, $feed, 'posts' );
1091}
1092
1093/**
1094 * Retrieves the permalink for the search results comments feed.
1095 *
1096 * @since 2.5.0
1097 *
1098 * @global WP_Rewrite $wp_rewrite
1099 *
1100 * @param string $search_query Optional. Search query. Default empty.
1101 * @param string $feed         Optional. Feed type. Default empty.
1102 * @return string The comments feed search results permalink.
1103 */
1104function get_search_comments_feed_link($search_query = '', $feed = '') {
1105        global $wp_rewrite;
1106
1107        if ( empty($feed) )
1108                $feed = get_default_feed();
1109
1110        $link = get_search_feed_link($search_query, $feed);
1111
1112        $permastruct = $wp_rewrite->get_search_permastruct();
1113
1114        if ( empty($permastruct) )
1115                $link = add_query_arg('feed', 'comments-' . $feed, $link);
1116        else
1117                $link = add_query_arg('withcomments', 1, $link);
1118
1119        /** This filter is documented in wp-includes/link-template.php */
1120        return apply_filters( 'search_feed_link', $link, $feed, 'comments' );
1121}
1122
1123/**
1124 * Retrieves the permalink for a post type archive.
1125 *
1126 * @since 3.1.0
1127 * @since 4.5.0 Support for posts was added.
1128 *
1129 * @global WP_Rewrite $wp_rewrite
1130 *
1131 * @param string $post_type Post type.
1132 * @return string|false The post type archive permalink.
1133 */
1134function get_post_type_archive_link( $post_type ) {
1135        global $wp_rewrite;
1136        if ( ! $post_type_obj = get_post_type_object( $post_type ) )
1137                return false;
1138
1139        if ( 'post' === $post_type ) {
1140                $show_on_front = get_option( 'show_on_front' );
1141                $page_for_posts  = get_option( 'page_for_posts' );
1142
1143                if ( 'page' == $show_on_front && $page_for_posts ) {
1144                        $link = get_permalink( $page_for_posts );
1145                } else {
1146                        $link = get_home_url();
1147                }
1148                /** This filter is documented in wp-includes/link-template.php */
1149                return apply_filters( 'post_type_archive_link', $link, $post_type );
1150        }
1151
1152        if ( ! $post_type_obj->has_archive )
1153                return false;
1154
1155        if ( get_option( 'permalink_structure' ) && is_array( $post_type_obj->rewrite ) ) {
1156                $struct = ( true === $post_type_obj->has_archive ) ? $post_type_obj->rewrite['slug'] : $post_type_obj->has_archive;
1157                if ( $post_type_obj->rewrite['with_front'] )
1158                        $struct = $wp_rewrite->front . $struct;
1159                else
1160                        $struct = $wp_rewrite->root . $struct;
1161                $link = home_url( user_trailingslashit( $struct, 'post_type_archive' ) );
1162        } else {
1163                $link = home_url( '?post_type=' . $post_type );
1164        }
1165
1166        /**
1167         * Filters the post type archive permalink.
1168         *
1169         * @since 3.1.0
1170         *
1171         * @param string $link      The post type archive permalink.
1172         * @param string $post_type Post type name.
1173         */
1174        return apply_filters( 'post_type_archive_link', $link, $post_type );
1175}
1176
1177/**
1178 * Retrieves the permalink for a post type archive feed.
1179 *
1180 * @since 3.1.0
1181 *
1182 * @param string $post_type Post type
1183 * @param string $feed      Optional. Feed type. Default empty.
1184 * @return string|false The post type feed permalink.
1185 */
1186function get_post_type_archive_feed_link( $post_type, $feed = '' ) {
1187        $default_feed = get_default_feed();
1188        if ( empty( $feed ) )
1189                $feed = $default_feed;
1190
1191        if ( ! $link = get_post_type_archive_link( $post_type ) )
1192                return false;
1193
1194        $post_type_obj = get_post_type_object( $post_type );
1195        if ( get_option( 'permalink_structure' ) && is_array( $post_type_obj->rewrite ) && $post_type_obj->rewrite['feeds'] ) {
1196                $link = trailingslashit( $link );
1197                $link .= 'feed/';
1198                if ( $feed != $default_feed )
1199                        $link .= "$feed/";
1200        } else {
1201                $link = add_query_arg( 'feed', $feed, $link );
1202        }
1203
1204        /**
1205         * Filters the post type archive feed link.
1206         *
1207         * @since 3.1.0
1208         *
1209         * @param string $link The post type archive feed link.
1210         * @param string $feed Feed type.
1211         */
1212        return apply_filters( 'post_type_archive_feed_link', $link, $feed );
1213}
1214
1215/**
1216 * Retrieves the URL used for the post preview.
1217 *
1218 * Allows additional query args to be appended.
1219 *
1220 * @since 4.4.0
1221 *
1222 * @param int|WP_Post $post         Optional. Post ID or `WP_Post` object. Defaults to global `$post`.
1223 * @param array       $query_args   Optional. Array of additional query args to be appended to the link.
1224 *                                  Default empty array.
1225 * @param string      $preview_link Optional. Base preview link to be used if it should differ from the
1226 *                                  post permalink. Default empty.
1227 * @return string|null URL used for the post preview, or null if the post does not exist.
1228 */
1229function get_preview_post_link( $post = null, $query_args = array(), $preview_link = '' ) {
1230        $post = get_post( $post );
1231        if ( ! $post ) {
1232                return;
1233        }
1234
1235        $post_type_object = get_post_type_object( $post->post_type );
1236        if ( is_post_type_viewable( $post_type_object ) ) {
1237                if ( ! $preview_link ) {
1238                        $preview_link = set_url_scheme( get_permalink( $post ) );
1239                }
1240
1241                $query_args['preview'] = 'true';
1242                $preview_link = add_query_arg( $query_args, $preview_link );
1243        }
1244
1245        /**
1246         * Filters the URL used for a post preview.
1247         *
1248         * @since 2.0.5
1249         * @since 4.0.0 Added the `$post` parameter.
1250         *
1251         * @param string  $preview_link URL used for the post preview.
1252         * @param WP_Post $post         Post object.
1253         */
1254        return apply_filters( 'preview_post_link', $preview_link, $post );
1255}
1256
1257/**
1258 * Retrieves the edit post link for post.
1259 *
1260 * Can be used within the WordPress loop or outside of it. Can be used with
1261 * pages, posts, attachments, and revisions.
1262 *
1263 * @since 2.3.0
1264 *
1265 * @param int    $id      Optional. Post ID. Default is the ID of the global `$post`.
1266 * @param string $context Optional. How to output the '&' character. Default '&amp;'.
1267 * @return string|null The edit post link for the given post. null if the post type is invalid or does
1268 *                     not allow an editing UI.
1269 */
1270function get_edit_post_link( $id = 0, $context = 'display' ) {
1271        if ( ! $post = get_post( $id ) )
1272                return;
1273
1274        if ( 'revision' === $post->post_type )
1275                $action = '';
1276        elseif ( 'display' == $context )
1277                $action = '&amp;action=edit';
1278        else
1279                $action = '&action=edit';
1280
1281        $post_type_object = get_post_type_object( $post->post_type );
1282        if ( !$post_type_object )
1283                return;
1284
1285        if ( !current_user_can( 'edit_post', $post->ID ) )
1286                return;
1287
1288        if ( $post_type_object->_edit_link ) {
1289                $link = admin_url( sprintf( $post_type_object->_edit_link . $action, $post->ID ) );
1290        } else {
1291                $link = '';
1292        }
1293
1294        /**
1295         * Filters the post edit link.
1296         *
1297         * @since 2.3.0
1298         *
1299         * @param string $link    The edit link.
1300         * @param int    $post_id Post ID.
1301         * @param string $context The link context. If set to 'display' then ampersands
1302         *                        are encoded.
1303         */
1304        return apply_filters( 'get_edit_post_link', $link, $post->ID, $context );
1305}
1306
1307/**
1308 * Displays the edit post link for post.
1309 *
1310 * @since 1.0.0
1311 * @since 4.4.0 The `$class` argument was added.
1312 *
1313 * @param string $text   Optional. Anchor text. If null, default is 'Edit This'. Default null.
1314 * @param string $before Optional. Display before edit link. Default empty.
1315 * @param string $after  Optional. Display after edit link. Default empty.
1316 * @param int    $id     Optional. Post ID. Default is the ID of the global `$post`.
1317 * @param string $class  Optional. Add custom class to link. Default 'post-edit-link'.
1318 */
1319function edit_post_link( $text = null, $before = '', $after = '', $id = 0, $class = 'post-edit-link' ) {
1320        if ( ! $post = get_post( $id ) ) {
1321                return;
1322        }
1323
1324        if ( ! $url = get_edit_post_link( $post->ID ) ) {
1325                return;
1326        }
1327
1328        if ( null === $text ) {
1329                $text = __( 'Edit This' );
1330        }
1331
1332        $link = '<a class="' . esc_attr( $class ) . '" href="' . esc_url( $url ) . '">' . $text . '</a>';
1333
1334        /**
1335         * Filters the post edit link anchor tag.
1336         *
1337         * @since 2.3.0
1338         *
1339         * @param string $link    Anchor tag for the edit link.
1340         * @param int    $post_id Post ID.
1341         * @param string $text    Anchor text.
1342         */
1343        echo $before . apply_filters( 'edit_post_link', $link, $post->ID, $text ) . $after;
1344}
1345
1346/**
1347 * Retrieves the delete posts link for post.
1348 *
1349 * Can be used within the WordPress loop or outside of it, with any post type.
1350 *
1351 * @since 2.9.0
1352 *
1353 * @param int    $id           Optional. Post ID. Default is the ID of the global `$post`.
1354 * @param string $deprecated   Not used.
1355 * @param bool   $force_delete Optional. Whether to bypass trash and force deletion. Default false.
1356 * @return string|void The delete post link URL for the given post.
1357 */
1358function get_delete_post_link( $id = 0, $deprecated = '', $force_delete = false ) {
1359        if ( ! empty( $deprecated ) )
1360                _deprecated_argument( __FUNCTION__, '3.0.0' );
1361
1362        if ( !$post = get_post( $id ) )
1363                return;
1364
1365        $post_type_object = get_post_type_object( $post->post_type );
1366        if ( !$post_type_object )
1367                return;
1368
1369        if ( !current_user_can( 'delete_post', $post->ID ) )
1370                return;
1371
1372        $action = ( $force_delete || !EMPTY_TRASH_DAYS ) ? 'delete' : 'trash';
1373
1374        $delete_link = add_query_arg( 'action', $action, admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) );
1375
1376        /**
1377         * Filters the post delete link.
1378         *
1379         * @since 2.9.0
1380         *
1381         * @param string $link         The delete link.
1382         * @param int    $post_id      Post ID.
1383         * @param bool   $force_delete Whether to bypass the trash and force deletion. Default false.
1384         */
1385        return apply_filters( 'get_delete_post_link', wp_nonce_url( $delete_link, "$action-post_{$post->ID}" ), $post->ID, $force_delete );
1386}
1387
1388/**
1389 * Retrieves the edit comment link.
1390 *
1391 * @since 2.3.0
1392 *
1393 * @param int|WP_Comment $comment_id Optional. Comment ID or WP_Comment object.
1394 * @return string|void The edit comment link URL for the given comment.
1395 */
1396function get_edit_comment_link( $comment_id = 0 ) {
1397        $comment = get_comment( $comment_id );
1398
1399        if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
1400                return;
1401
1402        $location = admin_url('comment.php?action=editcomment&amp;c=') . $comment->comment_ID;
1403
1404        /**
1405         * Filters the comment edit link.
1406         *
1407         * @since 2.3.0
1408         *
1409         * @param string $location The edit link.
1410         */
1411        return apply_filters( 'get_edit_comment_link', $location );
1412}
1413
1414/**
1415 * Displays the edit comment link with formatting.
1416 *
1417 * @since 1.0.0
1418 *
1419 * @param string $text   Optional. Anchor text. If null, default is 'Edit This'. Default null.
1420 * @param string $before Optional. Display before edit link. Default empty.
1421 * @param string $after  Optional. Display after edit link. Default empty.
1422 */
1423function edit_comment_link( $text = null, $before = '', $after = '' ) {
1424        $comment = get_comment();
1425
1426        if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) {
1427                return;
1428        }
1429
1430        if ( null === $text ) {
1431                $text = __( 'Edit This' );
1432        }
1433
1434        $link = '<a class="comment-edit-link" href="' . esc_url( get_edit_comment_link( $comment ) ) . '">' . $text . '</a>';
1435
1436        /**
1437         * Filters the comment edit link anchor tag.
1438         *
1439         * @since 2.3.0
1440         *
1441         * @param string $link       Anchor tag for the edit link.
1442         * @param int    $comment_id Comment ID.
1443         * @param string $text       Anchor text.
1444         */
1445        echo $before . apply_filters( 'edit_comment_link', $link, $comment->comment_ID, $text ) . $after;
1446}
1447
1448/**
1449 * Displays the edit bookmark link.
1450 *
1451 * @since 2.7.0
1452 *
1453 * @param int|stdClass $link Optional. Bookmark ID. Default is the id of the current bookmark.
1454 * @return string|void The edit bookmark link URL.
1455 */
1456function get_edit_bookmark_link( $link = 0 ) {
1457        $link = get_bookmark( $link );
1458
1459        if ( !current_user_can('manage_links') )
1460                return;
1461
1462        $location = admin_url('link.php?action=edit&amp;link_id=') . $link->link_id;
1463
1464        /**
1465         * Filters the bookmark edit link.
1466         *
1467         * @since 2.7.0
1468         *
1469         * @param string $location The edit link.
1470         * @param int    $link_id  Bookmark ID.
1471         */
1472        return apply_filters( 'get_edit_bookmark_link', $location, $link->link_id );
1473}
1474
1475/**
1476 * Displays the edit bookmark link anchor content.
1477 *
1478 * @since 2.7.0
1479 *
1480 * @param string $link     Optional. Anchor text. Default empty.
1481 * @param string $before   Optional. Display before edit link. Default empty.
1482 * @param string $after    Optional. Display after edit link. Default empty.
1483 * @param int    $bookmark Optional. Bookmark ID. Default is the current bookmark.
1484 */
1485function edit_bookmark_link( $link = '', $before = '', $after = '', $bookmark = null ) {
1486        $bookmark = get_bookmark($bookmark);
1487
1488        if ( !current_user_can('manage_links') )
1489                return;
1490
1491        if ( empty($link) )
1492                $link = __('Edit This');
1493
1494        $link = '<a href="' . esc_url( get_edit_bookmark_link( $bookmark ) ) . '">' . $link . '</a>';
1495
1496        /**
1497         * Filters the bookmark edit link anchor tag.
1498         *
1499         * @since 2.7.0
1500         *
1501         * @param string $link    Anchor tag for the edit link.
1502         * @param int    $link_id Bookmark ID.
1503         */
1504        echo $before . apply_filters( 'edit_bookmark_link', $link, $bookmark->link_id ) . $after;
1505}
1506
1507/**
1508 * Retrieves the edit user link.
1509 *
1510 * @since 3.5.0
1511 *
1512 * @param int $user_id Optional. User ID. Defaults to the current user.
1513 * @return string URL to edit user page or empty string.
1514 */
1515function get_edit_user_link( $user_id = null ) {
1516        if ( ! $user_id )
1517                $user_id = get_current_user_id();
1518
1519        if ( empty( $user_id ) || ! current_user_can( 'edit_user', $user_id ) )
1520                return '';
1521
1522        $user = get_userdata( $user_id );
1523
1524        if ( ! $user )
1525                return '';
1526
1527        if ( get_current_user_id() == $user->ID )
1528                $link = get_edit_profile_url( $user->ID );
1529        else
1530                $link = add_query_arg( 'user_id', $user->ID, self_admin_url( 'user-edit.php' ) );
1531
1532        /**
1533         * Filters the user edit link.
1534         *
1535         * @since 3.5.0
1536         *
1537         * @param string $link    The edit link.
1538         * @param int    $user_id User ID.
1539         */
1540        return apply_filters( 'get_edit_user_link', $link, $user->ID );
1541}
1542
1543// Navigation links
1544
1545/**
1546 * Retrieves the previous post that is adjacent to the current post.
1547 *
1548 * @since 1.5.0
1549 *
1550 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1551 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1552 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1553 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1554 *                             corresponding post exists.
1555 */
1556function get_previous_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1557        return get_adjacent_post( $in_same_term, $excluded_terms, true, $taxonomy );
1558}
1559
1560/**
1561 * Retrieves the next post that is adjacent to the current post.
1562 *
1563 * @since 1.5.0
1564 *
1565 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1566 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1567 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1568 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1569 *                             corresponding post exists.
1570 */
1571function get_next_post( $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1572        return get_adjacent_post( $in_same_term, $excluded_terms, false, $taxonomy );
1573}
1574
1575/**
1576 * Retrieves the adjacent post.
1577 *
1578 * Can either be next or previous post.
1579 *
1580 * @since 2.5.0
1581 *
1582 * @global wpdb $wpdb WordPress database abstraction object.
1583 *
1584 * @param bool         $in_same_term   Optional. Whether post should be in a same taxonomy term. Default false.
1585 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1586 * @param bool         $previous       Optional. Whether to retrieve previous post. Default true
1587 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1588 * @return null|string|WP_Post Post object if successful. Null if global $post is not set. Empty string if no
1589 *                             corresponding post exists.
1590 */
1591function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
1592        global $wpdb;
1593
1594        if ( ( ! $post = get_post() ) || ! taxonomy_exists( $taxonomy ) )
1595                return null;
1596
1597        $current_post_date = $post->post_date;
1598
1599        $join = '';
1600        $where = '';
1601        $adjacent = $previous ? 'previous' : 'next';
1602
1603        if ( $in_same_term || ! empty( $excluded_terms ) ) {
1604                if ( ! empty( $excluded_terms ) && ! is_array( $excluded_terms ) ) {
1605                        // back-compat, $excluded_terms used to be $excluded_terms with IDs separated by " and "
1606                        if ( false !== strpos( $excluded_terms, ' and ' ) ) {
1607                                _deprecated_argument( __FUNCTION__, '3.3.0', sprintf( __( 'Use commas instead of %s to separate excluded terms.' ), "'and'" ) );
1608                                $excluded_terms = explode( ' and ', $excluded_terms );
1609                        } else {
1610                                $excluded_terms = explode( ',', $excluded_terms );
1611                        }
1612
1613                        $excluded_terms = array_map( 'intval', $excluded_terms );
1614                }
1615
1616                if ( $in_same_term ) {
1617                        $join .= " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
1618                        $where .= $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
1619
1620                        if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) )
1621                                return '';
1622                        $term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
1623
1624                        // Remove any exclusions from the term array to include.
1625                        $term_array = array_diff( $term_array, (array) $excluded_terms );
1626                        $term_array = array_map( 'intval', $term_array );
1627
1628                        if ( ! $term_array || is_wp_error( $term_array ) )
1629                                return '';
1630
1631                        $where .= " AND tt.term_id IN (" . implode( ',', $term_array ) . ")";
1632                }
1633
1634                /**
1635                 * Filters the IDs of terms excluded from adjacent post queries.
1636                 *
1637                 * The dynamic portion of the hook name, `$adjacent`, refers to the type
1638                 * of adjacency, 'next' or 'previous'.
1639                 *
1640                 * @since 4.4.0
1641                 *
1642                 * @param string $excluded_terms Array of excluded term IDs.
1643                 */
1644                $excluded_terms = apply_filters( "get_{$adjacent}_post_excluded_terms", $excluded_terms );
1645
1646                if ( ! empty( $excluded_terms ) ) {
1647                        $where .= " AND p.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships tr LEFT JOIN $wpdb->term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE tt.term_id IN (" . implode( ',', array_map( 'intval', $excluded_terms ) ) . ') )';
1648                }
1649        }
1650
1651        // 'post_status' clause depends on the current user.
1652        if ( is_user_logged_in() ) {
1653                $user_id = get_current_user_id();
1654
1655                $post_type_object = get_post_type_object( $post->post_type );
1656                if ( empty( $post_type_object ) ) {
1657                        $post_type_cap    = $post->post_type;
1658                        $read_private_cap = 'read_private_' . $post_type_cap . 's';
1659                } else {
1660                        $read_private_cap = $post_type_object->cap->read_private_posts;
1661                }
1662
1663                /*
1664                 * Results should include private posts belonging to the current user, or private posts where the
1665                 * current user has the 'read_private_posts' cap.
1666                 */
1667                $private_states = get_post_stati( array( 'private' => true ) );
1668                $where .= " AND ( p.post_status = 'publish'";
1669                foreach ( (array) $private_states as $state ) {
1670                        if ( current_user_can( $read_private_cap ) ) {
1671                                $where .= $wpdb->prepare( " OR p.post_status = %s", $state );
1672                        } else {
1673                                $where .= $wpdb->prepare( " OR (p.post_author = %d AND p.post_status = %s)", $user_id, $state );
1674                        }
1675                }
1676                $where .= " )";
1677        } else {
1678                $where .= " AND p.post_status = 'publish'";
1679        }
1680
1681        $op = $previous ? '<' : '>';
1682        $order = $previous ? 'DESC' : 'ASC';
1683
1684        /**
1685         * Filters the JOIN clause in the SQL for an adjacent post query.
1686         *
1687         * The dynamic portion of the hook name, `$adjacent`, refers to the type
1688         * of adjacency, 'next' or 'previous'.
1689         *
1690         * @since 2.5.0
1691         * @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
1692         *
1693         * @param string  $join           The JOIN clause in the SQL.
1694         * @param bool    $in_same_term   Whether post should be in a same taxonomy term.
1695         * @param array   $excluded_terms Array of excluded term IDs.
1696         * @param string  $taxonomy       Taxonomy. Used to identify the term used when `$in_same_term` is true.
1697         * @param WP_Post $post           WP_Post object.
1698         */
1699        $join = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms, $taxonomy, $post );
1700
1701        /**
1702         * Filters the WHERE clause in the SQL for an adjacent post query.
1703         *
1704         * The dynamic portion of the hook name, `$adjacent`, refers to the type
1705         * of adjacency, 'next' or 'previous'.
1706         *
1707         * @since 2.5.0
1708         * @since 4.4.0 Added the `$taxonomy` and `$post` parameters.
1709         *
1710         * @param string $where          The `WHERE` clause in the SQL.
1711         * @param bool   $in_same_term   Whether post should be in a same taxonomy term.
1712         * @param array  $excluded_terms Array of excluded term IDs.
1713         * @param string $taxonomy       Taxonomy. Used to identify the term used when `$in_same_term` is true.
1714         * @param WP_Post $post           WP_Post object.
1715         */
1716        $where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s $where", $current_post_date, $post->post_type ), $in_same_term, $excluded_terms, $taxonomy, $post );
1717
1718        /**
1719         * Filters the ORDER BY clause in the SQL for an adjacent post query.
1720         *
1721         * The dynamic portion of the hook name, `$adjacent`, refers to the type
1722         * of adjacency, 'next' or 'previous'.
1723         *
1724         * @since 2.5.0
1725         * @since 4.4.0 Added the `$post` parameter.
1726         *
1727         * @param string $order_by The `ORDER BY` clause in the SQL.
1728         * @param WP_Post $post    WP_Post object.
1729         */
1730        $sort  = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1", $post );
1731
1732        $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";
1733        $query_key = 'adjacent_post_' . md5( $query );
1734        $result = wp_cache_get( $query_key, 'counts' );
1735        if ( false !== $result ) {
1736                if ( $result )
1737                        $result = get_post( $result );
1738                return $result;
1739        }
1740
1741        $result = $wpdb->get_var( $query );
1742        if ( null === $result )
1743                $result = '';
1744
1745        wp_cache_set( $query_key, $result, 'counts' );
1746
1747        if ( $result )
1748                $result = get_post( $result );
1749
1750        return $result;
1751}
1752
1753/**
1754 * Retrieves the adjacent post relational link.
1755 *
1756 * Can either be next or previous post relational link.
1757 *
1758 * @since 2.8.0
1759 *
1760 * @param string       $title          Optional. Link title format. Default '%title'.
1761 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1762 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1763 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
1764 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1765 * @return string|void The adjacent post relational link URL.
1766 */
1767function get_adjacent_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
1768        if ( $previous && is_attachment() && $post = get_post() )
1769                $post = get_post( $post->post_parent );
1770        else
1771                $post = get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
1772
1773        if ( empty( $post ) )
1774                return;
1775
1776        $post_title = the_title_attribute( array( 'echo' => false, 'post' => $post ) );
1777
1778        if ( empty( $post_title ) )
1779                $post_title = $previous ? __( 'Previous Post' ) : __( 'Next Post' );
1780
1781        $date = mysql2date( get_option( 'date_format' ), $post->post_date );
1782
1783        $title = str_replace( '%title', $post_title, $title );
1784        $title = str_replace( '%date', $date, $title );
1785
1786        $link = $previous ? "<link rel='prev' title='" : "<link rel='next' title='";
1787        $link .= esc_attr( $title );
1788        $link .= "' href='" . get_permalink( $post ) . "' />\n";
1789
1790        $adjacent = $previous ? 'previous' : 'next';
1791
1792        /**
1793         * Filters the adjacent post relational link.
1794         *
1795         * The dynamic portion of the hook name, `$adjacent`, refers to the type
1796         * of adjacency, 'next' or 'previous'.
1797         *
1798         * @since 2.8.0
1799         *
1800         * @param string $link The relational link.
1801         */
1802        return apply_filters( "{$adjacent}_post_rel_link", $link );
1803}
1804
1805/**
1806 * Displays the relational links for the posts adjacent to the current post.
1807 *
1808 * @since 2.8.0
1809 *
1810 * @param string       $title          Optional. Link title format. Default '%title'.
1811 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1812 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1813 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1814 */
1815function adjacent_posts_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1816        echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, true, $taxonomy );
1817        echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, false, $taxonomy );
1818}
1819
1820/**
1821 * Displays relational links for the posts adjacent to the current post for single post pages.
1822 *
1823 * This is meant to be attached to actions like 'wp_head'. Do not call this directly in plugins
1824 * or theme templates.
1825 *
1826 * @since 3.0.0
1827 *
1828 * @see adjacent_posts_rel_link()
1829 */
1830function adjacent_posts_rel_link_wp_head() {
1831        if ( ! is_single() || is_attachment() ) {
1832                return;
1833        }
1834        adjacent_posts_rel_link();
1835}
1836
1837/**
1838 * Displays the relational link for the next post adjacent to the current post.
1839 *
1840 * @since 2.8.0
1841 *
1842 * @see get_adjacent_post_rel_link()
1843 *
1844 * @param string       $title          Optional. Link title format. Default '%title'.
1845 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1846 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1847 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1848 */
1849function next_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1850        echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, false, $taxonomy );
1851}
1852
1853/**
1854 * Displays the relational link for the previous post adjacent to the current post.
1855 *
1856 * @since 2.8.0
1857 *
1858 * @see get_adjacent_post_rel_link()
1859 *
1860 * @param string       $title          Optional. Link title format. Default '%title'.
1861 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1862 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default true.
1863 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1864 */
1865function prev_post_rel_link( $title = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1866        echo get_adjacent_post_rel_link( $title, $in_same_term, $excluded_terms, true, $taxonomy );
1867}
1868
1869/**
1870 * Retrieves the boundary post.
1871 *
1872 * Boundary being either the first or last post by publish date within the constraints specified
1873 * by $in_same_term or $excluded_terms.
1874 *
1875 * @since 2.8.0
1876 *
1877 * @param bool         $in_same_term   Optional. Whether returned post should be in a same taxonomy term.
1878 *                                     Default false.
1879 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
1880 *                                     Default empty.
1881 * @param bool         $start          Optional. Whether to retrieve first or last post. Default true
1882 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1883 * @return null|array Array containing the boundary post object if successful, null otherwise.
1884 */
1885function get_boundary_post( $in_same_term = false, $excluded_terms = '', $start = true, $taxonomy = 'category' ) {
1886        $post = get_post();
1887        if ( ! $post || ! is_single() || is_attachment() || ! taxonomy_exists( $taxonomy ) )
1888                return null;
1889
1890        $query_args = array(
1891                'posts_per_page' => 1,
1892                'order' => $start ? 'ASC' : 'DESC',
1893                'update_post_term_cache' => false,
1894                'update_post_meta_cache' => false
1895        );
1896
1897        $term_array = array();
1898
1899        if ( ! is_array( $excluded_terms ) ) {
1900                if ( ! empty( $excluded_terms ) )
1901                        $excluded_terms = explode( ',', $excluded_terms );
1902                else
1903                        $excluded_terms = array();
1904        }
1905
1906        if ( $in_same_term || ! empty( $excluded_terms ) ) {
1907                if ( $in_same_term )
1908                        $term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
1909
1910                if ( ! empty( $excluded_terms ) ) {
1911                        $excluded_terms = array_map( 'intval', $excluded_terms );
1912                        $excluded_terms = array_diff( $excluded_terms, $term_array );
1913
1914                        $inverse_terms = array();
1915                        foreach ( $excluded_terms as $excluded_term )
1916                                $inverse_terms[] = $excluded_term * -1;
1917                        $excluded_terms = $inverse_terms;
1918                }
1919
1920                $query_args[ 'tax_query' ] = array( array(
1921                        'taxonomy' => $taxonomy,
1922                        'terms' => array_merge( $term_array, $excluded_terms )
1923                ) );
1924        }
1925
1926        return get_posts( $query_args );
1927}
1928
1929/**
1930 * Retrieves the previous post link that is adjacent to the current post.
1931 *
1932 * @since 3.7.0
1933 *
1934 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1935 * @param string       $link           Optional. Link permalink format. Default '%title%'.
1936 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1937 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1938 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1939 * @return string The link URL of the previous post in relation to the current post.
1940 */
1941function get_previous_post_link( $format = '&laquo; %link', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1942        return get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, true, $taxonomy );
1943}
1944
1945/**
1946 * Displays the previous post link that is adjacent to the current post.
1947 *
1948 * @since 1.5.0
1949 *
1950 * @see get_previous_post_link()
1951 *
1952 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1953 * @param string       $link           Optional. Link permalink format. Default '%title'.
1954 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1955 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1956 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1957 */
1958function previous_post_link( $format = '&laquo; %link', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1959        echo get_previous_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy );
1960}
1961
1962/**
1963 * Retrieves the next post link that is adjacent to the current post.
1964 *
1965 * @since 3.7.0
1966 *
1967 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1968 * @param string       $link           Optional. Link permalink format. Default '%title'.
1969 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1970 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1971 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1972 * @return string The link URL of the next post in relation to the current post.
1973 */
1974function get_next_post_link( $format = '%link &raquo;', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1975        return get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, false, $taxonomy );
1976}
1977
1978/**
1979 * Displays the next post link that is adjacent to the current post.
1980 *
1981 * @since 1.5.0
1982 * @see get_next_post_link()
1983 *
1984 * @param string       $format         Optional. Link anchor format. Default '&laquo; %link'.
1985 * @param string       $link           Optional. Link permalink format. Default '%title'
1986 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
1987 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded term IDs. Default empty.
1988 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
1989 */
1990function next_post_link( $format = '%link &raquo;', $link = '%title', $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
1991         echo get_next_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy );
1992}
1993
1994/**
1995 * Retrieves the adjacent post link.
1996 *
1997 * Can be either next post link or previous.
1998 *
1999 * @since 3.7.0
2000 *
2001 * @param string       $format         Link anchor format.
2002 * @param string       $link           Link permalink format.
2003 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
2004 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded terms IDs. Default empty.
2005 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
2006 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
2007 * @return string The link URL of the previous or next post in relation to the current post.
2008 */
2009function get_adjacent_post_link( $format, $link, $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
2010        if ( $previous && is_attachment() )
2011                $post = get_post( get_post()->post_parent );
2012        else
2013                $post = get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
2014
2015        if ( ! $post ) {
2016                $output = '';
2017        } else {
2018                $title = $post->post_title;
2019
2020                if ( empty( $post->post_title ) )
2021                        $title = $previous ? __( 'Previous Post' ) : __( 'Next Post' );
2022
2023                /** This filter is documented in wp-includes/post-template.php */
2024                $title = apply_filters( 'the_title', $title, $post->ID );
2025
2026                $date = mysql2date( get_option( 'date_format' ), $post->post_date );
2027                $rel = $previous ? 'prev' : 'next';
2028
2029                $string = '<a href="' . get_permalink( $post ) . '" rel="'.$rel.'">';
2030                $inlink = str_replace( '%title', $title, $link );
2031                $inlink = str_replace( '%date', $date, $inlink );
2032                $inlink = $string . $inlink . '</a>';
2033
2034                $output = str_replace( '%link', $inlink, $format );
2035        }
2036
2037        $adjacent = $previous ? 'previous' : 'next';
2038
2039        /**
2040         * Filters the adjacent post link.
2041         *
2042         * The dynamic portion of the hook name, `$adjacent`, refers to the type
2043         * of adjacency, 'next' or 'previous'.
2044         *
2045         * @since 2.6.0
2046         * @since 4.2.0 Added the `$adjacent` parameter.
2047         *
2048         * @param string  $output   The adjacent post link.
2049         * @param string  $format   Link anchor format.
2050         * @param string  $link     Link permalink format.
2051         * @param WP_Post $post     The adjacent post.
2052         * @param string  $adjacent Whether the post is previous or next.
2053         */
2054        return apply_filters( "{$adjacent}_post_link", $output, $format, $link, $post, $adjacent );
2055}
2056
2057/**
2058 * Displays the adjacent post link.
2059 *
2060 * Can be either next post link or previous.
2061 *
2062 * @since 2.5.0
2063 *
2064 * @param string       $format         Link anchor format.
2065 * @param string       $link           Link permalink format.
2066 * @param bool         $in_same_term   Optional. Whether link should be in a same taxonomy term. Default false.
2067 * @param array|string $excluded_terms Optional. Array or comma-separated list of excluded category IDs. Default empty.
2068 * @param bool         $previous       Optional. Whether to display link to previous or next post. Default true.
2069 * @param string       $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
2070 */
2071function adjacent_post_link( $format, $link, $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {
2072        echo get_adjacent_post_link( $format, $link, $in_same_term, $excluded_terms, $previous, $taxonomy );
2073}
2074
2075/**
2076 * Retrieves the link for a page number.
2077 *
2078 * @since 1.5.0
2079 *
2080 * @global WP_Rewrite $wp_rewrite
2081 *
2082 * @param int  $pagenum Optional. Page ID. Default 1.
2083 * @param bool $escape  Optional. Whether to escape the URL for display, with esc_url(). Defaults to true.
2084 *                          Otherwise, prepares the URL with esc_url_raw().
2085 * @return string The link URL for the given page number.
2086 */
2087function get_pagenum_link($pagenum = 1, $escape = true ) {
2088        global $wp_rewrite;
2089
2090        $pagenum = (int) $pagenum;
2091
2092        $request = remove_query_arg( 'paged' );
2093
2094        $home_root = parse_url(home_url());
2095        $home_root = ( isset($home_root['path']) ) ? $home_root['path'] : '';
2096        $home_root = preg_quote( $home_root, '|' );
2097
2098        $request = preg_replace('|^'. $home_root . '|i', '', $request);
2099        $request = preg_replace('|^/+|', '', $request);
2100
2101        if ( !$wp_rewrite->using_permalinks() || is_admin() ) {
2102                $base = trailingslashit( get_bloginfo( 'url' ) );
2103
2104                if ( $pagenum > 1 ) {
2105                        $result = add_query_arg( 'paged', $pagenum, $base . $request );
2106                } else {
2107                        $result = $base . $request;
2108                }
2109        } else {
2110                $qs_regex = '|\?.*?$|';
2111                preg_match( $qs_regex, $request, $qs_match );
2112
2113                if ( !empty( $qs_match[0] ) ) {
2114                        $query_string = $qs_match[0];
2115                        $request = preg_replace( $qs_regex, '', $request );
2116                } else {
2117                        $query_string = '';
2118                }
2119
2120                $request = preg_replace( "|$wp_rewrite->pagination_base/\d+/?$|", '', $request);
2121                $request = preg_replace( '|^' . preg_quote( $wp_rewrite->index, '|' ) . '|i', '', $request);
2122                $request = ltrim($request, '/');
2123
2124                $base = trailingslashit( get_bloginfo( 'url' ) );
2125
2126                if ( $wp_rewrite->using_index_permalinks() && ( $pagenum > 1 || '' != $request ) )
2127                        $base .= $wp_rewrite->index . '/';
2128
2129                if ( $pagenum > 1 ) {
2130                        $request = ( ( !empty( $request ) ) ? trailingslashit( $request ) : $request ) . user_trailingslashit( $wp_rewrite->pagination_base . "/" . $pagenum, 'paged' );
2131                }
2132
2133                $result = $base . $request . $query_string;
2134        }
2135
2136        /**
2137         * Filters the page number link for the current request.
2138         *
2139         * @since 2.5.0
2140         *
2141         * @param string $result The page number link.
2142         */
2143        $result = apply_filters( 'get_pagenum_link', $result );
2144
2145        if ( $escape )
2146                return esc_url( $result );
2147        else
2148                return esc_url_raw( $result );
2149}
2150
2151/**
2152 * Retrieves the next posts page link.
2153 *
2154 * Backported from 2.1.3 to 2.0.10.
2155 *
2156 * @since 2.0.10
2157 *
2158 * @global int $paged
2159 *
2160 * @param int $max_page Optional. Max pages. Default 0.
2161 * @return string|void The link URL for next posts page.
2162 */
2163function get_next_posts_page_link($max_page = 0) {
2164        global $paged;
2165
2166        if ( !is_single() ) {
2167                if ( !$paged )
2168                        $paged = 1;
2169                $nextpage = intval($paged) + 1;
2170                if ( !$max_page || $max_page >= $nextpage )
2171                        return get_pagenum_link($nextpage);
2172        }
2173}
2174
2175/**
2176 * Displays or retrieves the next posts page link.
2177 *
2178 * @since 0.71
2179 *
2180 * @param int   $max_page Optional. Max pages. Default 0.
2181 * @param bool  $echo     Optional. Whether to echo the link. Default true.
2182 * @return string|void The link URL for next posts page if `$echo = false`.
2183 */
2184function next_posts( $max_page = 0, $echo = true ) {
2185        $output = esc_url( get_next_posts_page_link( $max_page ) );
2186
2187        if ( $echo )
2188                echo $output;
2189        else
2190                return $output;
2191}
2192
2193/**
2194 * Retrieves the next posts page link.
2195 *
2196 * @since 2.7.0
2197 *
2198 * @global int      $paged
2199 * @global WP_Query $wp_query
2200 *
2201 * @param string $label    Content for link text.
2202 * @param int    $max_page Optional. Max pages. Default 0.
2203 * @return string|void HTML-formatted next posts page link.
2204 */
2205function get_next_posts_link( $label = null, $max_page = 0 ) {
2206        global $paged, $wp_query;
2207
2208        if ( !$max_page )
2209                $max_page = $wp_query->max_num_pages;
2210
2211        if ( !$paged )
2212                $paged = 1;
2213
2214        $nextpage = intval($paged) + 1;
2215
2216        if ( null === $label )
2217                $label = __( 'Next Page &raquo;' );
2218
2219        if ( !is_single() && ( $nextpage <= $max_page ) ) {
2220                /**
2221                 * Filters the anchor tag attributes for the next posts page link.
2222                 *
2223                 * @since 2.7.0
2224                 *
2225                 * @param string $attributes Attributes for the anchor tag.
2226                 */
2227                $attr = apply_filters( 'next_posts_link_attributes', '' );
2228
2229                return '<a href="' . next_posts( $max_page, false ) . "\" $attr>" . preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) . '</a>';
2230        }
2231}
2232
2233/**
2234 * Displays the next posts page link.
2235 *
2236 * @since 0.71
2237 *
2238 * @param string $label    Content for link text.
2239 * @param int    $max_page Optional. Max pages. Default 0.
2240 */
2241function next_posts_link( $label = null, $max_page = 0 ) {
2242        echo get_next_posts_link( $label, $max_page );
2243}
2244
2245/**
2246 * Retrieves the previous posts page link.
2247 *
2248 * Will only return string, if not on a single page or post.
2249 *
2250 * Backported to 2.0.10 from 2.1.3.
2251 *
2252 * @since 2.0.10
2253 *
2254 * @global int $paged
2255 *
2256 * @return string|void The link for the previous posts page.
2257 */
2258function get_previous_posts_page_link() {
2259        global $paged;
2260
2261        if ( !is_single() ) {
2262                $nextpage = intval($paged) - 1;
2263                if ( $nextpage < 1 )
2264                        $nextpage = 1;
2265                return get_pagenum_link($nextpage);
2266        }
2267}
2268
2269/**
2270 * Displays or retrieves the previous posts page link.
2271 *
2272 * @since 0.71
2273 *
2274 * @param bool $echo Optional. Whether to echo the link. Default true.
2275 * @return string|void The previous posts page link if `$echo = false`.
2276 */
2277function previous_posts( $echo = true ) {
2278        $output = esc_url( get_previous_posts_page_link() );
2279
2280        if ( $echo )
2281                echo $output;
2282        else
2283                return $output;
2284}
2285
2286/**
2287 * Retrieves the previous posts page link.
2288 *
2289 * @since 2.7.0
2290 *
2291 * @global int $paged
2292 *
2293 * @param string $label Optional. Previous page link text.
2294 * @return string|void HTML-formatted previous page link.
2295 */
2296function get_previous_posts_link( $label = null ) {
2297        global $paged;
2298
2299        if ( null === $label )
2300                $label = __( '&laquo; Previous Page' );
2301
2302        if ( !is_single() && $paged > 1 ) {
2303                /**
2304                 * Filters the anchor tag attributes for the previous posts page link.
2305                 *
2306                 * @since 2.7.0
2307                 *
2308                 * @param string $attributes Attributes for the anchor tag.
2309                 */
2310                $attr = apply_filters( 'previous_posts_link_attributes', '' );
2311                return '<a href="' . previous_posts( false ) . "\" $attr>". preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label ) .'</a>';
2312        }
2313}
2314
2315/**
2316 * Displays the previous posts page link.
2317 *
2318 * @since 0.71
2319 *
2320 * @param string $label Optional. Previous page link text.
2321 */
2322function previous_posts_link( $label = null ) {
2323        echo get_previous_posts_link( $label );
2324}
2325
2326/**
2327 * Retrieves the post pages link navigation for previous and next pages.
2328 *
2329 * @since 2.8.0
2330 *
2331 * @global WP_Query $wp_query
2332 *
2333 * @param string|array $args {
2334 *     Optional. Arguments to build the post pages link navigation.
2335 *
2336 *     @type string $sep      Separator character. Default '&#8212;'.
2337 *     @type string $prelabel Link text to display for the previous page link.
2338 *                            Default '&laquo; Previous Page'.
2339 *     @type string $nxtlabel Link text to display for the next page link.
2340 *                            Default 'Next Page &raquo;'.
2341 * }
2342 * @return string The posts link navigation.
2343 */
2344function get_posts_nav_link( $args = array() ) {
2345        global $wp_query;
2346
2347        $return = '';
2348
2349        if ( !is_singular() ) {
2350                $defaults = array(
2351                        'sep' => ' &#8212; ',
2352                        'prelabel' => __('&laquo; Previous Page'),
2353                        'nxtlabel' => __('Next Page &raquo;'),
2354                );
2355                $args = wp_parse_args( $args, $defaults );
2356
2357                $max_num_pages = $wp_query->max_num_pages;
2358                $paged = get_query_var('paged');
2359
2360                //only have sep if there's both prev and next results
2361                if ($paged < 2 || $paged >= $max_num_pages) {
2362                        $args['sep'] = '';
2363                }
2364
2365                if ( $max_num_pages > 1 ) {
2366                        $return = get_previous_posts_link($args['prelabel']);
2367                        $return .= preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $args['sep']);
2368                        $return .= get_next_posts_link($args['nxtlabel']);
2369                }
2370        }
2371        return $return;
2372
2373}
2374
2375/**
2376 * Displays the post pages link navigation for previous and next pages.
2377 *
2378 * @since 0.71
2379 *
2380 * @param string $sep      Optional. Separator for posts navigation links. Default empty.
2381 * @param string $prelabel Optional. Label for previous pages. Default empty.
2382 * @param string $nxtlabel Optional Label for next pages. Default empty.
2383 */
2384function posts_nav_link( $sep = '', $prelabel = '', $nxtlabel = '' ) {
2385        $args = array_filter( compact('sep', 'prelabel', 'nxtlabel') );
2386        echo get_posts_nav_link($args);
2387}
2388
2389/**
2390 * Retrieves the navigation to next/previous post, when applicable.
2391 *
2392 * @since 4.1.0
2393 * @since 4.4.0 Introduced the `in_same_term`, `excluded_terms`, and `taxonomy` arguments.
2394 *
2395 * @param array $args {
2396 *     Optional. Default post navigation arguments. Default empty array.
2397 *
2398 *     @type string       $prev_text          Anchor text to display in the previous post link. Default '%title'.
2399 *     @type string       $next_text          Anchor text to display in the next post link. Default '%title'.
2400 *     @type bool         $in_same_term       Whether link should be in a same taxonomy term. Default false.
2401 *     @type array|string $excluded_terms     Array or comma-separated list of excluded term IDs. Default empty.
2402 *     @type string       $taxonomy           Taxonomy, if `$in_same_term` is true. Default 'category'.
2403 *     @type string       $screen_reader_text Screen reader text for nav element. Default 'Post navigation'.
2404 * }
2405 * @return string Markup for post links.
2406 */
2407function get_the_post_navigation( $args = array() ) {
2408        $args = wp_parse_args( $args, array(
2409                'prev_text'          => '%title',
2410                'next_text'          => '%title',
2411                'in_same_term'       => false,
2412                'excluded_terms'     => '',
2413                'taxonomy'           => 'category',
2414                'screen_reader_text' => __( 'Post navigation' ),
2415        ) );
2416
2417        $navigation = '';
2418
2419        $previous = get_previous_post_link(
2420                '<div class="nav-previous">%link</div>',
2421                $args['prev_text'],
2422                $args['in_same_term'],
2423                $args['excluded_terms'],
2424                $args['taxonomy']
2425        );
2426
2427        $next = get_next_post_link(
2428                '<div class="nav-next">%link</div>',
2429                $args['next_text'],
2430                $args['in_same_term'],
2431                $args['excluded_terms'],
2432                $args['taxonomy']
2433        );
2434
2435        // Only add markup if there's somewhere to navigate to.
2436        if ( $previous || $next ) {
2437                $navigation = _navigation_markup( $previous . $next, 'post-navigation', $args['screen_reader_text'] );
2438        }
2439
2440        return $navigation;
2441}
2442
2443/**
2444 * Displays the navigation to next/previous post, when applicable.
2445 *
2446 * @since 4.1.0
2447 *
2448 * @param array $args Optional. See get_the_post_navigation() for available arguments.
2449 *                    Default empty array.
2450 */
2451function the_post_navigation( $args = array() ) {
2452        echo get_the_post_navigation( $args );
2453}
2454
2455/**
2456 * Returns the navigation to next/previous set of posts, when applicable.
2457 *
2458 * @since 4.1.0
2459 *
2460 * @global WP_Query $wp_query WordPress Query object.
2461 *
2462 * @param array $args {
2463 *     Optional. Default posts navigation arguments. Default empty array.
2464 *
2465 *     @type string $prev_text          Anchor text to display in the previous posts link.
2466 *                                      Default 'Older posts'.
2467 *     @type string $next_text          Anchor text to display in the next posts link.
2468 *                                      Default 'Newer posts'.
2469 *     @type string $screen_reader_text Screen reader text for nav element.
2470 *                                      Default 'Posts navigation'.
2471 * }
2472 * @return string Markup for posts links.
2473 */
2474function get_the_posts_navigation( $args = array() ) {
2475        $navigation = '';
2476
2477        // Don't print empty markup if there's only one page.
2478        if ( $GLOBALS['wp_query']->max_num_pages > 1 ) {
2479                $args = wp_parse_args( $args, array(
2480                        'prev_text'          => __( 'Older posts' ),
2481                        'next_text'          => __( 'Newer posts' ),
2482                        'screen_reader_text' => __( 'Posts navigation' ),
2483                ) );
2484
2485                $next_link = get_previous_posts_link( $args['next_text'] );
2486                $prev_link = get_next_posts_link( $args['prev_text'] );
2487
2488                if ( $prev_link ) {
2489                        $navigation .= '<div class="nav-previous">' . $prev_link . '</div>';
2490                }
2491
2492                if ( $next_link ) {
2493                        $navigation .= '<div class="nav-next">' . $next_link . '</div>';
2494                }
2495
2496                $navigation = _navigation_markup( $navigation, 'posts-navigation', $args['screen_reader_text'] );
2497        }
2498
2499        return $navigation;
2500}
2501
2502/**
2503 * Displays the navigation to next/previous set of posts, when applicable.
2504 *
2505 * @since 4.1.0
2506 *
2507 * @param array $args Optional. See get_the_posts_navigation() for available arguments.
2508 *                    Default empty array.
2509 */
2510function the_posts_navigation( $args = array() ) {
2511        echo get_the_posts_navigation( $args );
2512}
2513
2514/**
2515 * Retrieves a paginated navigation to next/previous set of posts, when applicable.
2516 *
2517 * @since 4.1.0
2518 *
2519 * @param array $args {
2520 *     Optional. Default pagination arguments, see paginate_links().
2521 *
2522 *     @type string $screen_reader_text Screen reader text for navigation element.
2523 *                                      Default 'Posts navigation'.
2524 * }
2525 * @return string Markup for pagination links.
2526 */
2527function get_the_posts_pagination( $args = array() ) {
2528        $navigation = '';
2529
2530        // Don't print empty markup if there's only one page.
2531        if ( $GLOBALS['wp_query']->max_num_pages > 1 ) {
2532                $args = wp_parse_args( $args, array(
2533                        'mid_size'           => 1,
2534                        'prev_text'          => _x( 'Previous', 'previous set of posts' ),
2535                        'next_text'          => _x( 'Next', 'next set of posts' ),
2536                        'screen_reader_text' => __( 'Posts navigation' ),
2537                ) );
2538
2539                // Make sure we get a string back. Plain is the next best thing.
2540                if ( isset( $args['type'] ) && 'array' == $args['type'] ) {
2541                        $args['type'] = 'plain';
2542                }
2543
2544                // Set up paginated links.
2545                $links = paginate_links( $args );
2546
2547                if ( $links ) {
2548                        $navigation = _navigation_markup( $links, 'pagination', $args['screen_reader_text'] );
2549                }
2550        }
2551
2552        return $navigation;
2553}
2554
2555/**
2556 * Displays a paginated navigation to next/previous set of posts, when applicable.
2557 *
2558 * @since 4.1.0
2559 *
2560 * @param array $args Optional. See get_the_posts_pagination() for available arguments.
2561 *                    Default empty array.
2562 */
2563function the_posts_pagination( $args = array() ) {
2564        echo get_the_posts_pagination( $args );
2565}
2566
2567/**
2568 * Wraps passed links in navigational markup.
2569 *
2570 * @since 4.1.0
2571 * @access private
2572 *
2573 * @param string $links              Navigational links.
2574 * @param string $class              Optional. Custom class for nav element. Default: 'posts-navigation'.
2575 * @param string $screen_reader_text Optional. Screen reader text for nav element. Default: 'Posts navigation'.
2576 * @return string Navigation template tag.
2577 */
2578function _navigation_markup( $links, $class = 'posts-navigation', $screen_reader_text = '' ) {
2579        if ( empty( $screen_reader_text ) ) {
2580                $screen_reader_text = __( 'Posts navigation' );
2581        }
2582
2583        $template = '
2584        <nav class="navigation %1$s" role="navigation">
2585                <h2 class="screen-reader-text">%2$s</h2>
2586                <div class="nav-links">%3$s</div>
2587        </nav>';
2588
2589        /**
2590         * Filters the navigation markup template.
2591         *
2592         * Note: The filtered template HTML must contain specifiers for the navigation
2593         * class (%1$s), the screen-reader-text value (%2$s), and placement of the
2594         * navigation links (%3$s):
2595         *
2596         *     <nav class="navigation %1$s" role="navigation">
2597         *         <h2 class="screen-reader-text">%2$s</h2>
2598         *         <div class="nav-links">%3$s</div>
2599         *     </nav>
2600         *
2601         * @since 4.4.0
2602         *
2603         * @param string $template The default template.
2604         * @param string $class    The class passed by the calling function.
2605         * @return string Navigation template.
2606         */
2607        $template = apply_filters( 'navigation_markup_template', $template, $class );
2608
2609        return sprintf( $template, sanitize_html_class( $class ), esc_html( $screen_reader_text ), $links );
2610}
2611
2612/**
2613 * Retrieves the comments page number link.
2614 *
2615 * @since 2.7.0
2616 *
2617 * @global WP_Rewrite $wp_rewrite
2618 *
2619 * @param int $pagenum  Optional. Page number. Default 1.
2620 * @param int $max_page Optional. The maximum number of comment pages. Default 0.
2621 * @return string The comments page number link URL.
2622 */
2623function get_comments_pagenum_link( $pagenum = 1, $max_page = 0 ) {
2624        global $wp_rewrite;
2625
2626        $pagenum = (int) $pagenum;
2627
2628        $result = get_permalink();
2629
2630        if ( 'newest' == get_option('default_comments_page') ) {
2631                if ( $pagenum != $max_page ) {
2632                        if ( $wp_rewrite->using_permalinks() )
2633                                $result = user_trailingslashit( trailingslashit($result) . $wp_rewrite->comments_pagination_base . '-' . $pagenum, 'commentpaged');
2634                        else
2635                                $result = add_query_arg( 'cpage', $pagenum, $result );
2636                }
2637        } elseif ( $pagenum > 1 ) {
2638                if ( $wp_rewrite->using_permalinks() )
2639                        $result = user_trailingslashit( trailingslashit($result) . $wp_rewrite->comments_pagination_base . '-' . $pagenum, 'commentpaged');
2640                else
2641                        $result = add_query_arg( 'cpage', $pagenum, $result );
2642        }
2643
2644        $result .= '#comments';
2645
2646        /**
2647         * Filters the comments page number link for the current request.
2648         *
2649         * @since 2.7.0
2650         *
2651         * @param string $result The comments page number link.
2652         */
2653        return apply_filters( 'get_comments_pagenum_link', $result );
2654}
2655
2656/**
2657 * Retrieves the link to the next comments page.
2658 *
2659 * @since 2.7.1
2660 *
2661 * @global WP_Query $wp_query
2662 *
2663 * @param string $label    Optional. Label for link text. Default empty.
2664 * @param int    $max_page Optional. Max page. Default 0.
2665 * @return string|void HTML-formatted link for the next page of comments.
2666 */
2667function get_next_comments_link( $label = '', $max_page = 0 ) {
2668        global $wp_query;
2669
2670        if ( ! is_singular() )
2671                return;
2672
2673        $page = get_query_var('cpage');
2674
2675        if ( ! $page ) {
2676                $page = 1;
2677        }
2678
2679        $nextpage = intval($page) + 1;
2680
2681        if ( empty($max_page) )
2682                $max_page = $wp_query->max_num_comment_pages;
2683
2684        if ( empty($max_page) )
2685                $max_page = get_comment_pages_count();
2686
2687        if ( $nextpage > $max_page )
2688                return;
2689
2690        if ( empty($label) )
2691                $label = __('Newer Comments &raquo;');
2692
2693        /**
2694         * Filters the anchor tag attributes for the next comments page link.
2695         *
2696         * @since 2.7.0
2697         *
2698         * @param string $attributes Attributes for the anchor tag.
2699         */
2700        return '<a href="' . esc_url( get_comments_pagenum_link( $nextpage, $max_page ) ) . '" ' . apply_filters( 'next_comments_link_attributes', '' ) . '>'. preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) .'</a>';
2701}
2702
2703/**
2704 * Displays the link to the next comments page.
2705 *
2706 * @since 2.7.0
2707 *
2708 * @param string $label    Optional. Label for link text. Default empty.
2709 * @param int    $max_page Optional. Max page. Default 0.
2710 */
2711function next_comments_link( $label = '', $max_page = 0 ) {
2712        echo get_next_comments_link( $label, $max_page );
2713}
2714
2715/**
2716 * Retrieves the link to the previous comments page.
2717 *
2718 * @since 2.7.1
2719 *
2720 * @param string $label Optional. Label for comments link text. Default empty.
2721 * @return string|void HTML-formatted link for the previous page of comments.
2722 */
2723function get_previous_comments_link( $label = '' ) {
2724        if ( ! is_singular() )
2725                return;
2726
2727        $page = get_query_var('cpage');
2728
2729        if ( intval($page) <= 1 )
2730                return;
2731
2732        $prevpage = intval($page) - 1;
2733
2734        if ( empty($label) )
2735                $label = __('&laquo; Older Comments');
2736
2737        /**
2738         * Filters the anchor tag attributes for the previous comments page link.
2739         *
2740         * @since 2.7.0
2741         *
2742         * @param string $attributes Attributes for the anchor tag.
2743         */
2744        return '<a href="' . esc_url( get_comments_pagenum_link( $prevpage ) ) . '" ' . apply_filters( 'previous_comments_link_attributes', '' ) . '>' . preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) .'</a>';
2745}
2746
2747/**
2748 * Displays the link to the previous comments page.
2749 *
2750 * @since 2.7.0
2751 *
2752 * @param string $label Optional. Label for comments link text. Default empty.
2753 */
2754function previous_comments_link( $label = '' ) {
2755        echo get_previous_comments_link( $label );
2756}
2757
2758/**
2759 * Displays or retrieves pagination links for the comments on the current post.
2760 *
2761 * @see paginate_links()
2762 * @since 2.7.0
2763 *
2764 * @global WP_Rewrite $wp_rewrite
2765 *
2766 * @param string|array $args Optional args. See paginate_links(). Default empty array.
2767 * @return string|void Markup for pagination links.
2768 */
2769function paginate_comments_links( $args = array() ) {
2770        global $wp_rewrite;
2771
2772        if ( ! is_singular() )
2773                return;
2774
2775        $page = get_query_var('cpage');
2776        if ( !$page )
2777                $page = 1;
2778        $max_page = get_comment_pages_count();
2779        $defaults = array(
2780                'base' => add_query_arg( 'cpage', '%#%' ),
2781                'format' => '',
2782                'total' => $max_page,
2783                'current' => $page,
2784                'echo' => true,
2785                'add_fragment' => '#comments'
2786        );
2787        if ( $wp_rewrite->using_permalinks() )
2788                $defaults['base'] = user_trailingslashit(trailingslashit(get_permalink()) . $wp_rewrite->comments_pagination_base . '-%#%', 'commentpaged');
2789
2790        $args = wp_parse_args( $args, $defaults );
2791        $page_links = paginate_links( $args );
2792
2793        if ( $args['echo'] )
2794                echo $page_links;
2795        else
2796                return $page_links;
2797}
2798
2799/**
2800 * Retrieves navigation to next/previous set of comments, when applicable.
2801 *
2802 * @since 4.4.0
2803 *
2804 * @param array $args {
2805 *     Optional. Default comments navigation arguments.
2806 *
2807 *     @type string $prev_text          Anchor text to display in the previous comments link.
2808 *                                      Default 'Older comments'.
2809 *     @type string $next_text          Anchor text to display in the next comments link.
2810 *                                      Default 'Newer comments'.
2811 *     @type string $screen_reader_text Screen reader text for nav element. Default 'Comments navigation'.
2812 * }
2813 * @return string Markup for comments links.
2814 */
2815function get_the_comments_navigation( $args = array() ) {
2816        $navigation = '';
2817
2818        // Are there comments to navigate through?
2819        if ( get_comment_pages_count() > 1 ) {
2820                $args = wp_parse_args( $args, array(
2821                        'prev_text'          => __( 'Older comments' ),
2822                        'next_text'          => __( 'Newer comments' ),
2823                        'screen_reader_text' => __( 'Comments navigation' ),
2824                ) );
2825
2826                $prev_link = get_previous_comments_link( $args['prev_text'] );
2827                $next_link = get_next_comments_link( $args['next_text'] );
2828
2829                if ( $prev_link ) {
2830                        $navigation .= '<div class="nav-previous">' . $prev_link . '</div>';
2831                }
2832
2833                if ( $next_link ) {
2834                        $navigation .= '<div class="nav-next">' . $next_link . '</div>';
2835                }
2836
2837                $navigation = _navigation_markup( $navigation, 'comment-navigation', $args['screen_reader_text'] );
2838        }
2839
2840        return $navigation;
2841}
2842
2843/**
2844 * Displays navigation to next/previous set of comments, when applicable.
2845 *
2846 * @since 4.4.0
2847 *
2848 * @param array $args See get_the_comments_navigation() for available arguments. Default empty array.
2849 */
2850function the_comments_navigation( $args = array() ) {
2851        echo get_the_comments_navigation( $args );
2852}
2853
2854/**
2855 * Retrieves a paginated navigation to next/previous set of comments, when applicable.
2856 *
2857 * @since 4.4.0
2858 *
2859 * @see paginate_comments_links()
2860 *
2861 * @param array $args {
2862 *     Optional. Default pagination arguments.
2863 *
2864 *     @type string $screen_reader_text Screen reader text for nav element. Default 'Comments navigation'.
2865 * }
2866 * @return string Markup for pagination links.
2867 */
2868function get_the_comments_pagination( $args = array() ) {
2869        $navigation = '';
2870        $args       = wp_parse_args( $args, array(
2871                'screen_reader_text' => __( 'Comments navigation' ),
2872        ) );
2873        $args['echo'] = false;
2874
2875        // Make sure we get plain links, so we get a string we can work with.
2876        $args['type'] = 'plain';
2877
2878        $links = paginate_comments_links( $args );
2879
2880        if ( $links ) {
2881                $navigation = _navigation_markup( $links, 'comments-pagination', $args['screen_reader_text'] );
2882        }
2883
2884        return $navigation;
2885}
2886
2887/**
2888 * Displays a paginated navigation to next/previous set of comments, when applicable.
2889 *
2890 * @since 4.4.0
2891 *
2892 * @param array $args See get_the_comments_pagination() for available arguments. Default empty array.
2893 */
2894function the_comments_pagination( $args = array() ) {
2895        echo get_the_comments_pagination( $args );
2896}
2897
2898/**
2899 * Retrieves the Press This bookmarklet link.
2900 *
2901 * @since 2.6.0
2902 *
2903 * @global bool          $is_IE      Whether the browser matches an Internet Explorer user agent.
2904 */
2905function get_shortcut_link() {
2906        global $is_IE;
2907
2908        include_once( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
2909
2910        $link = '';
2911
2912        if ( $is_IE ) {
2913                /*
2914                 * Return the old/shorter bookmarklet code for MSIE 8 and lower,
2915                 * since they only support a max length of ~2000 characters for
2916                 * bookmark[let] URLs, which is way to small for our smarter one.
2917                 * Do update the version number so users do not get the "upgrade your
2918                 * bookmarklet" notice when using PT in those browsers.
2919                 */
2920                $ua = $_SERVER['HTTP_USER_AGENT'];
2921
2922                if ( ! empty( $ua ) && preg_match( '/\bMSIE (\d)/', $ua, $matches ) && (int) $matches[1] <= 8 ) {
2923                        $url = wp_json_encode( admin_url( 'press-this.php' ) );
2924
2925                        $link = 'javascript:var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,' .
2926                                's=(e?e():(k)?k():(x?x.createRange().text:0)),f=' . $url . ',l=d.location,e=encodeURIComponent,' .
2927                                'u=f+"?u="+e(l.href)+"&t="+e(d.title)+"&s="+e(s)+"&v=' . WP_Press_This::VERSION . '";' .
2928                                'a=function(){if(!w.open(u,"t","toolbar=0,resizable=1,scrollbars=1,status=1,width=600,height=700"))l.href=u;};' .
2929                                'if(/Firefox/.test(navigator.userAgent))setTimeout(a,0);else a();void(0)';
2930                }
2931        }
2932
2933        if ( empty( $link ) ) {
2934                $src = @file_get_contents( ABSPATH . 'wp-admin/js/bookmarklet.min.js' );
2935
2936                if ( $src ) {
2937                        $url = wp_json_encode( admin_url( 'press-this.php' ) . '?v=' . WP_Press_This::VERSION );
2938                        $link = 'javascript:' . str_replace( 'window.pt_url', $url, $src );
2939                }
2940        }
2941
2942        $link = str_replace( array( "\r", "\n", "\t" ),  '', $link );
2943
2944        /**
2945         * Filters the Press This bookmarklet link.
2946         *
2947         * @since 2.6.0
2948         *
2949         * @param string $link The Press This bookmarklet link.
2950         */
2951        return apply_filters( 'shortcut_link', $link );
2952}
2953
2954/**
2955 * Retrieves the URL for the current site where the front end is accessible.
2956 *
2957 * Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
2958 * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
2959 * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
2960 *
2961 * @since 3.0.0
2962 *
2963 * @param  string      $path   Optional. Path relative to the home URL. Default empty.
2964 * @param  string|null $scheme Optional. Scheme to give the home URL context. Accepts
2965 *                             'http', 'https', 'relative', 'rest', or null. Default null.
2966 * @return string Home URL link with optional path appended.
2967 */
2968function home_url( $path = '', $scheme = null ) {
2969        return get_home_url( null, $path, $scheme );
2970}
2971
2972/**
2973 * Retrieves the URL for a given site where the front end is accessible.
2974 *
2975 * Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
2976 * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
2977 * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
2978 *
2979 * @since 3.0.0
2980 *
2981 * @global string $pagenow
2982 *
2983 * @param  int         $blog_id Optional. Site ID. Default null (current site).
2984 * @param  string      $path    Optional. Path relative to the home URL. Default empty.
2985 * @param  string|null $scheme  Optional. Scheme to give the home URL context. Accepts
2986 *                              'http', 'https', 'relative', 'rest', or null. Default null.
2987 * @return string Home URL link with optional path appended.
2988 */
2989function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
2990        global $pagenow;
2991
2992        $orig_scheme = $scheme;
2993
2994        if ( empty( $blog_id ) || !is_multisite() ) {
2995                $url = get_option( 'home' );
2996        } else {
2997                switch_to_blog( $blog_id );
2998                $url = get_option( 'home' );
2999                restore_current_blog();
3000        }
3001
3002        if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) ) {
3003                if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $pagenow )
3004                        $scheme = 'https';
3005                else
3006                        $scheme = parse_url( $url, PHP_URL_SCHEME );
3007        }
3008
3009        $url = set_url_scheme( $url, $scheme );
3010
3011        if ( $path && is_string( $path ) )
3012                $url .= '/' . ltrim( $path, '/' );
3013
3014        /**
3015         * Filters the home URL.
3016         *
3017         * @since 3.0.0
3018         *
3019         * @param string      $url         The complete home URL including scheme and path.
3020         * @param string      $path        Path relative to the home URL. Blank string if no path is specified.
3021         * @param string|null $orig_scheme Scheme to give the home URL context. Accepts 'http', 'https',
3022         *                                 'relative', 'rest', or null.
3023         * @param int|null    $blog_id     Site ID, or null for the current site.
3024         */
3025        return apply_filters( 'home_url', $url, $path, $orig_scheme, $blog_id );
3026}
3027
3028/**
3029 * Retrieves the URL for the current site where WordPress application files
3030 * (e.g. wp-blog-header.php or the wp-admin/ folder) are accessible.
3031 *
3032 * Returns the 'site_url' option with the appropriate protocol, 'https' if
3033 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
3034 * overridden.
3035 *
3036 * @since 3.0.0
3037 *
3038 * @param string $path   Optional. Path relative to the site URL. Default empty.
3039 * @param string $scheme Optional. Scheme to give the site URL context. See set_url_scheme().
3040 * @return string Site URL link with optional path appended.
3041 */
3042function site_url( $path = '', $scheme = null ) {
3043        return get_site_url( null, $path, $scheme );
3044}
3045
3046/**
3047 * Retrieves the URL for a given site where WordPress application files
3048 * (e.g. wp-blog-header.php or the wp-admin/ folder) are accessible.
3049 *
3050 * Returns the 'site_url' option with the appropriate protocol, 'https' if
3051 * is_ssl() and 'http' otherwise. If `$scheme` is 'http' or 'https',
3052 * `is_ssl()` is overridden.
3053 *
3054 * @since 3.0.0
3055 *
3056 * @param int    $blog_id Optional. Site ID. Default null (current site).
3057 * @param string $path    Optional. Path relative to the site URL. Default empty.
3058 * @param string $scheme  Optional. Scheme to give the site URL context. Accepts
3059 *                        'http', 'https', 'login', 'login_post', 'admin', or
3060 *                        'relative'. Default null.
3061 * @return string Site URL link with optional path appended.
3062 */
3063function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
3064        if ( empty( $blog_id ) || !is_multisite() ) {
3065                $url = get_option( 'siteurl' );
3066        } else {
3067                switch_to_blog( $blog_id );
3068                $url = get_option( 'siteurl' );
3069                restore_current_blog();
3070        }
3071
3072        $url = set_url_scheme( $url, $scheme );
3073
3074        if ( $path && is_string( $path ) )
3075                $url .= '/' . ltrim( $path, '/' );
3076
3077        /**
3078         * Filters the site URL.
3079         *
3080         * @since 2.7.0
3081         *
3082         * @param string      $url     The complete site URL including scheme and path.
3083         * @param string      $path    Path relative to the site URL. Blank string if no path is specified.
3084         * @param string|null $scheme  Scheme to give the site URL context. Accepts 'http', 'https', 'login',
3085         *                             'login_post', 'admin', 'relative' or null.
3086         * @param int|null    $blog_id Site ID, or null for the current site.
3087         */
3088        return apply_filters( 'site_url', $url, $path, $scheme, $blog_id );
3089}
3090
3091/**
3092 * Retrieves the URL to the admin area for the current site.
3093 *
3094 * @since 2.6.0
3095 *
3096 * @param string $path   Optional path relative to the admin URL.
3097 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl().
3098 *                       'http' or 'https' can be passed to force those schemes.
3099 * @return string Admin URL link with optional path appended.
3100 */
3101function admin_url( $path = '', $scheme = 'admin' ) {
3102        return get_admin_url( null, $path, $scheme );
3103}
3104
3105/**
3106 * Retrieves the URL to the admin area for a given site.
3107 *
3108 * @since 3.0.0
3109 *
3110 * @param int    $blog_id Optional. Site ID. Default null (current site).
3111 * @param string $path    Optional. Path relative to the admin URL. Default empty.
3112 * @param string $scheme  Optional. The scheme to use. Accepts 'http' or 'https',
3113 *                        to force those schemes. Default 'admin', which obeys
3114 *                        force_ssl_admin() and is_ssl().
3115 * @return string Admin URL link with optional path appended.
3116 */
3117function get_admin_url( $blog_id = null, $path = '', $scheme = 'admin' ) {
3118        $url = get_site_url($blog_id, 'wp-admin/', $scheme);
3119
3120        if ( $path && is_string( $path ) )
3121                $url .= ltrim( $path, '/' );
3122
3123        /**
3124         * Filters the admin area URL.
3125         *
3126         * @since 2.8.0
3127         *
3128         * @param string   $url     The complete admin area URL including scheme and path.
3129         * @param string   $path    Path relative to the admin area URL. Blank string if no path is specified.
3130         * @param int|null $blog_id Site ID, or null for the current site.
3131         */
3132        return apply_filters( 'admin_url', $url, $path, $blog_id );
3133}
3134
3135/**
3136 * Retrieves the URL to the includes directory.
3137 *
3138 * @since 2.6.0
3139 *
3140 * @param string $path   Optional. Path relative to the includes URL. Default empty.
3141 * @param string $scheme Optional. Scheme to give the includes URL context. Accepts
3142 *                       'http', 'https', or 'relative'. Default null.
3143 * @return string Includes URL link with optional path appended.
3144 */
3145function includes_url( $path = '', $scheme = null ) {
3146        $url = site_url( '/' . WPINC . '/', $scheme );
3147
3148        if ( $path && is_string( $path ) )
3149                $url .= ltrim($path, '/');
3150
3151        /**
3152         * Filters the URL to the includes directory.
3153         *
3154         * @since 2.8.0
3155         *
3156         * @param string $url  The complete URL to the includes directory including scheme and path.
3157         * @param string $path Path relative to the URL to the wp-includes directory. Blank string
3158         *                     if no path is specified.
3159         */
3160        return apply_filters( 'includes_url', $url, $path );
3161}
3162
3163/**
3164 * Retrieves the URL to the content directory.
3165 *
3166 * @since 2.6.0
3167 *
3168 * @param string $path Optional. Path relative to the content URL. Default empty.
3169 * @return string Content URL link with optional path appended.
3170 */
3171function content_url( $path = '' ) {
3172        $url = set_url_scheme( WP_CONTENT_URL );
3173
3174        if ( $path && is_string( $path ) )
3175                $url .= '/' . ltrim($path, '/');
3176
3177        /**
3178         * Filters the URL to the content directory.
3179         *
3180         * @since 2.8.0
3181         *
3182         * @param string $url  The complete URL to the content directory including scheme and path.
3183         * @param string $path Path relative to the URL to the content directory. Blank string
3184         *                     if no path is specified.
3185         */
3186        return apply_filters( 'content_url', $url, $path);
3187}
3188
3189/**
3190 * Retrieves a URL within the plugins or mu-plugins directory.
3191 *
3192 * Defaults to the plugins directory URL if no arguments are supplied.
3193 *
3194 * @since 2.6.0
3195 *
3196 * @param  string $path   Optional. Extra path appended to the end of the URL, including
3197 *                        the relative directory if $plugin is supplied. Default empty.
3198 * @param  string $plugin Optional. A full path to a file inside a plugin or mu-plugin.
3199 *                        The URL will be relative to its directory. Default empty.
3200 *                        Typically this is done by passing `__FILE__` as the argument.
3201 * @return string Plugins URL link with optional paths appended.
3202 */
3203function plugins_url( $path = '', $plugin = '' ) {
3204
3205        $path = wp_normalize_path( $path );
3206        $plugin = wp_normalize_path( $plugin );
3207        $mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR );
3208
3209        if ( !empty($plugin) && 0 === strpos($plugin, $mu_plugin_dir) )
3210                $url = WPMU_PLUGIN_URL;
3211        else
3212                $url = WP_PLUGIN_URL;
3213
3214
3215        $url = set_url_scheme( $url );
3216
3217        if ( !empty($plugin) && is_string($plugin) ) {
3218                $folder = dirname(plugin_basename($plugin));
3219                if ( '.' != $folder )
3220                        $url .= '/' . ltrim($folder, '/');
3221        }
3222
3223        if ( $path && is_string( $path ) )
3224                $url .= '/' . ltrim($path, '/');
3225
3226        /**
3227         * Filters the URL to the plugins directory.
3228         *
3229         * @since 2.8.0
3230         *
3231         * @param string $url    The complete URL to the plugins directory including scheme and path.
3232         * @param string $path   Path relative to the URL to the plugins directory. Blank string
3233         *                       if no path is specified.
3234         * @param string $plugin The plugin file path to be relative to. Blank string if no plugin
3235         *                       is specified.
3236         */
3237        return apply_filters( 'plugins_url', $url, $path, $plugin );
3238}
3239
3240/**
3241 * Retrieves the site URL for the current network.
3242 *
3243 * Returns the site URL with the appropriate protocol, 'https' if
3244 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
3245 * overridden.
3246 *
3247 * @since 3.0.0
3248 *
3249 * @see set_url_scheme()
3250 *
3251 * @param string $path   Optional. Path relative to the site URL. Default empty.
3252 * @param string $scheme Optional. Scheme to give the site URL context. Accepts
3253 *                       'http', 'https', or 'relative'. Default null.
3254 * @return string Site URL link with optional path appended.
3255 */
3256function network_site_url( $path = '', $scheme = null ) {
3257        if ( ! is_multisite() )
3258                return site_url($path, $scheme);
3259
3260        $current_network = get_network();
3261
3262        if ( 'relative' == $scheme )
3263                $url = $current_network->path;
3264        else
3265                $url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme );
3266
3267        if ( $path && is_string( $path ) )
3268                $url .= ltrim( $path, '/' );
3269
3270        /**
3271         * Filters the network site URL.
3272         *
3273         * @since 3.0.0
3274         *
3275         * @param string      $url    The complete network site URL including scheme and path.
3276         * @param string      $path   Path relative to the network site URL. Blank string if
3277         *                            no path is specified.
3278         * @param string|null $scheme Scheme to give the URL context. Accepts 'http', 'https',
3279         *                            'relative' or null.
3280         */
3281        return apply_filters( 'network_site_url', $url, $path, $scheme );
3282}
3283
3284/**
3285 * Retrieves the home URL for the current network.
3286 *
3287 * Returns the home URL with the appropriate protocol, 'https' is_ssl()
3288 * and 'http' otherwise. If `$scheme` is 'http' or 'https', `is_ssl()` is
3289 * overridden.
3290 *
3291 * @since 3.0.0
3292 *
3293 * @param  string $path   Optional. Path relative to the home URL. Default empty.
3294 * @param  string $scheme Optional. Scheme to give the home URL context. Accepts
3295 *                        'http', 'https', or 'relative'. Default null.
3296 * @return string Home URL link with optional path appended.
3297 */
3298function network_home_url( $path = '', $scheme = null ) {
3299        if ( ! is_multisite() )
3300                return home_url($path, $scheme);
3301
3302        $current_network = get_network();
3303        $orig_scheme = $scheme;
3304
3305        if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) )
3306                $scheme = is_ssl() && ! is_admin() ? 'https' : 'http';
3307
3308        if ( 'relative' == $scheme )
3309                $url = $current_network->path;
3310        else
3311                $url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme );
3312
3313        if ( $path && is_string( $path ) )
3314                $url .= ltrim( $path, '/' );
3315
3316        /**
3317         * Filters the network home URL.
3318         *
3319         * @since 3.0.0
3320         *
3321         * @param string      $url         The complete network home URL including scheme and path.
3322         * @param string      $path        Path relative to the network home URL. Blank string
3323         *                                 if no path is specified.
3324         * @param string|null $orig_scheme Scheme to give the URL context. Accepts 'http', 'https',
3325         *                                 'relative' or null.
3326         */
3327        return apply_filters( 'network_home_url', $url, $path, $orig_scheme);
3328}
3329
3330/**
3331 * Retrieves the URL to the admin area for the network.
3332 *
3333 * @since 3.0.0
3334 *
3335 * @param string $path   Optional path relative to the admin URL. Default empty.
3336 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3337 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3338 * @return string Admin URL link with optional path appended.
3339 */
3340function network_admin_url( $path = '', $scheme = 'admin' ) {
3341        if ( ! is_multisite() )
3342                return admin_url( $path, $scheme );
3343
3344        $url = network_site_url('wp-admin/network/', $scheme);
3345
3346        if ( $path && is_string( $path ) )
3347                $url .= ltrim($path, '/');
3348
3349        /**
3350         * Filters the network admin URL.
3351         *
3352         * @since 3.0.0
3353         *
3354         * @param string $url  The complete network admin URL including scheme and path.
3355         * @param string $path Path relative to the network admin URL. Blank string if
3356         *                     no path is specified.
3357         */
3358        return apply_filters( 'network_admin_url', $url, $path );
3359}
3360
3361/**
3362 * Retrieves the URL to the admin area for the current user.
3363 *
3364 * @since 3.0.0
3365 *
3366 * @param string $path   Optional. Path relative to the admin URL. Default empty.
3367 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3368 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3369 * @return string Admin URL link with optional path appended.
3370 */
3371function user_admin_url( $path = '', $scheme = 'admin' ) {
3372        $url = network_site_url('wp-admin/user/', $scheme);
3373
3374        if ( $path && is_string( $path ) )
3375                $url .= ltrim($path, '/');
3376
3377        /**
3378         * Filters the user admin URL for the current user.
3379         *
3380         * @since 3.1.0
3381         *
3382         * @param string $url  The complete URL including scheme and path.
3383         * @param string $path Path relative to the URL. Blank string if
3384         *                     no path is specified.
3385         */
3386        return apply_filters( 'user_admin_url', $url, $path );
3387}
3388
3389/**
3390 * Retrieves the URL to the admin area for either the current site or the network depending on context.
3391 *
3392 * @since 3.1.0
3393 *
3394 * @param string $path   Optional. Path relative to the admin URL. Default empty.
3395 * @param string $scheme Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3396 *                       and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3397 * @return string Admin URL link with optional path appended.
3398 */
3399function self_admin_url( $path = '', $scheme = 'admin' ) {
3400        if ( is_network_admin() )
3401                return network_admin_url($path, $scheme);
3402        elseif ( is_user_admin() )
3403                return user_admin_url($path, $scheme);
3404        else
3405                return admin_url($path, $scheme);
3406}
3407
3408/**
3409 * Sets the scheme for a URL.
3410 *
3411 * @since 3.4.0
3412 * @since 4.4.0 The 'rest' scheme was added.
3413 *
3414 * @param string      $url    Absolute URL that includes a scheme
3415 * @param string|null $scheme Optional. Scheme to give $url. Currently 'http', 'https', 'login',
3416 *                            'login_post', 'admin', 'relative', 'rest', 'rpc', or null. Default null.
3417 * @return string $url URL with chosen scheme.
3418 */
3419function set_url_scheme( $url, $scheme = null ) {
3420        $orig_scheme = $scheme;
3421
3422        if ( ! $scheme ) {
3423                $scheme = is_ssl() ? 'https' : 'http';
3424        } elseif ( $scheme === 'admin' || $scheme === 'login' || $scheme === 'login_post' || $scheme === 'rpc' ) {
3425                $scheme = is_ssl() || force_ssl_admin() ? 'https' : 'http';
3426        } elseif ( $scheme !== 'http' && $scheme !== 'https' && $scheme !== 'relative' ) {
3427                $scheme = is_ssl() ? 'https' : 'http';
3428        }
3429
3430        $url = trim( $url );
3431        if ( substr( $url, 0, 2 ) === '//' )
3432                $url = 'http:' . $url;
3433
3434        if ( 'relative' == $scheme ) {
3435                $url = ltrim( preg_replace( '#^\w+://[^/]*#', '', $url ) );
3436                if ( $url !== '' && $url[0] === '/' )
3437                        $url = '/' . ltrim($url , "/ \t\n\r\0\x0B" );
3438        } else {
3439                $url = preg_replace( '#^\w+://#', $scheme . '://', $url );
3440        }
3441
3442        /**
3443         * Filters the resulting URL after setting the scheme.
3444         *
3445         * @since 3.4.0
3446         *
3447         * @param string      $url         The complete URL including scheme and path.
3448         * @param string      $scheme      Scheme applied to the URL. One of 'http', 'https', or 'relative'.
3449         * @param string|null $orig_scheme Scheme requested for the URL. One of 'http', 'https', 'login',
3450         *                                 'login_post', 'admin', 'relative', 'rest', 'rpc', or null.
3451         */
3452        return apply_filters( 'set_url_scheme', $url, $scheme, $orig_scheme );
3453}
3454
3455/**
3456 * Retrieves the URL to the user's dashboard.
3457 *
3458 * If a user does not belong to any site, the global user dashboard is used. If the user
3459 * belongs to the current site, the dashboard for the current site is returned. If the user
3460 * cannot edit the current site, the dashboard to the user's primary site is returned.
3461 *
3462 * @since 3.1.0
3463 *
3464 * @param int    $user_id Optional. User ID. Defaults to current user.
3465 * @param string $path    Optional path relative to the dashboard. Use only paths known to
3466 *                        both site and user admins. Default empty.
3467 * @param string $scheme  The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3468 *                        and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3469 * @return string Dashboard URL link with optional path appended.
3470 */
3471function get_dashboard_url( $user_id = 0, $path = '', $scheme = 'admin' ) {
3472        $user_id = $user_id ? (int) $user_id : get_current_user_id();
3473
3474        $blogs = get_blogs_of_user( $user_id );
3475        if ( is_multisite() && ! user_can( $user_id, 'manage_network' ) && empty($blogs) ) {
3476                $url = user_admin_url( $path, $scheme );
3477        } elseif ( ! is_multisite() ) {
3478                $url = admin_url( $path, $scheme );
3479        } else {
3480                $current_blog = get_current_blog_id();
3481                if ( $current_blog  && ( user_can( $user_id, 'manage_network' ) || in_array( $current_blog, array_keys( $blogs ) ) ) ) {
3482                        $url = admin_url( $path, $scheme );
3483                } else {
3484                        $active = get_active_blog_for_user( $user_id );
3485                        if ( $active )
3486                                $url = get_admin_url( $active->blog_id, $path, $scheme );
3487                        else
3488                                $url = user_admin_url( $path, $scheme );
3489                }
3490        }
3491
3492        /**
3493         * Filters the dashboard URL for a user.
3494         *
3495         * @since 3.1.0
3496         *
3497         * @param string $url     The complete URL including scheme and path.
3498         * @param int    $user_id The user ID.
3499         * @param string $path    Path relative to the URL. Blank string if no path is specified.
3500         * @param string $scheme  Scheme to give the URL context. Accepts 'http', 'https', 'login',
3501         *                        'login_post', 'admin', 'relative' or null.
3502         */
3503        return apply_filters( 'user_dashboard_url', $url, $user_id, $path, $scheme);
3504}
3505
3506/**
3507 * Retrieves the URL to the user's profile editor.
3508 *
3509 * @since 3.1.0
3510 *
3511 * @param int    $user_id Optional. User ID. Defaults to current user.
3512 * @param string $scheme  Optional. The scheme to use. Default is 'admin', which obeys force_ssl_admin()
3513 *                        and is_ssl(). 'http' or 'https' can be passed to force those schemes.
3514 * @return string Dashboard URL link with optional path appended.
3515 */
3516function get_edit_profile_url( $user_id = 0, $scheme = 'admin' ) {
3517        $user_id = $user_id ? (int) $user_id : get_current_user_id();
3518
3519        if ( is_user_admin() )
3520                $url = user_admin_url( 'profile.php', $scheme );
3521        elseif ( is_network_admin() )
3522                $url = network_admin_url( 'profile.php', $scheme );
3523        else
3524                $url = get_dashboard_url( $user_id, 'profile.php', $scheme );
3525
3526        /**
3527         * Filters the URL for a user's profile editor.
3528         *
3529         * @since 3.1.0
3530         *
3531         * @param string $url     The complete URL including scheme and path.
3532         * @param int    $user_id The user ID.
3533         * @param string $scheme  Scheme to give the URL context. Accepts 'http', 'https', 'login',
3534         *                        'login_post', 'admin', 'relative' or null.
3535         */
3536        return apply_filters( 'edit_profile_url', $url, $user_id, $scheme);
3537}
3538
3539/**
3540 * Returns the canonical URL for a post.
3541 *
3542 * When the post is the same as the current requested page the function will handle the
3543 * pagination arguments too.
3544 *
3545 * @since 4.6.0
3546 *
3547 * @param int|WP_Post $post Optional. Post ID or object. Default is global `$post`.
3548 * @return string|false The canonical URL, or false if the post does not exist or has not
3549 *                      been published yet.
3550 */
3551function wp_get_canonical_url( $post = null ) {
3552        $post = get_post( $post );
3553
3554        if ( ! $post ) {
3555                return false;
3556        }
3557
3558        if ( 'publish' !== $post->post_status ) {
3559                return false;
3560        }
3561
3562        $canonical_url = get_permalink( $post );
3563
3564        // If a canonical is being generated for the current page, make sure it has pagination if needed.
3565        if ( $post->ID === get_queried_object_id() ) {
3566                $page = get_query_var( 'page', 0 );
3567                if ( $page >= 2 ) {
3568                        if ( '' == get_option( 'permalink_structure' ) ) {
3569                                $canonical_url = add_query_arg( 'page', $page, $canonical_url );
3570                        } else {
3571                                $canonical_url = trailingslashit( $canonical_url ) . user_trailingslashit( $page, 'single_paged' );
3572                        }
3573                }
3574
3575                $cpage = get_query_var( 'cpage', 0 );
3576                if ( $cpage ) {
3577                        $canonical_url = get_comments_pagenum_link( $cpage );
3578                }
3579        }
3580
3581        /**
3582         * Filters the canonical URL for a post.
3583         *
3584         * @since 4.6.0
3585         *
3586         * @param string  $canonical_url The post's canonical URL.
3587         * @param WP_Post $post          Post object.
3588         */
3589        return apply_filters( 'get_canonical_url', $canonical_url, $post );
3590}
3591
3592/**
3593 * Outputs rel=canonical for singular queries.
3594 *
3595 * @since 2.9.0
3596 * @since 4.6.0 Adjusted to use wp_get_canonical_url().
3597 */
3598function rel_canonical() {
3599        if ( ! is_singular() ) {
3600                return;
3601        }
3602
3603        $id = get_queried_object_id();
3604
3605        if ( 0 === $id ) {
3606                return;
3607        }
3608
3609        $url = wp_get_canonical_url( $id );
3610
3611        if ( ! empty( $url ) ) {
3612                echo '<link rel="canonical" href="' . esc_url( $url ) . '" />' . "\n";
3613        }
3614}
3615
3616/**
3617 * Returns a shortlink for a post, page, attachment, or site.
3618 *
3619 * This function exists to provide a shortlink tag that all themes and plugins can target.
3620 * A plugin must hook in to provide the actual shortlinks. Default shortlink support is
3621 * limited to providing ?p= style links for posts. Plugins can short-circuit this function
3622 * via the {@see 'pre_get_shortlink'} filter or filter the output via the {@see 'get_shortlink'}
3623 * filter.
3624 *
3625 * @since 3.0.0.
3626 *
3627 * @param int    $id          Optional. A post or site id. Default is 0, which means the current post or site.
3628 * @param string $context     Optional. Whether the id is a 'site' id, 'post' id, or 'media' id. If 'post',
3629 *                            the post_type of the post is consulted. If 'query', the current query is consulted
3630 *                            to determine the id and context. Default 'post'.
3631 * @param bool   $allow_slugs Optional. Whether to allow post slugs in the shortlink. It is up to the plugin how
3632 *                            and whether to honor this. Default true.
3633 * @return string A shortlink or an empty string if no shortlink exists for the requested resource or if shortlinks
3634 *                are not enabled.
3635 */
3636function wp_get_shortlink( $id = 0, $context = 'post', $allow_slugs = true ) {
3637        /**
3638         * Filters whether to preempt generating a shortlink for the given post.
3639         *
3640         * Passing a truthy value to the filter will effectively short-circuit the
3641         * shortlink-generation process, returning that value instead.
3642         *
3643         * @since 3.0.0
3644         *
3645         * @param bool|string $return      Short-circuit return value. Either false or a URL string.
3646         * @param int         $id          Post ID, or 0 for the current post.
3647         * @param string      $context     The context for the link. One of 'post' or 'query',
3648         * @param bool        $allow_slugs Whether to allow post slugs in the shortlink.
3649         */
3650        $shortlink = apply_filters( 'pre_get_shortlink', false, $id, $context, $allow_slugs );
3651
3652        if ( false !== $shortlink ) {
3653                return $shortlink;
3654        }
3655
3656        $post_id = 0;
3657        if ( 'query' == $context && is_singular() ) {
3658                $post_id = get_queried_object_id();
3659                $post = get_post( $post_id );
3660        } elseif ( 'post' == $context ) {
3661                $post = get_post( $id );
3662                if ( ! empty( $post->ID ) )
3663                        $post_id = $post->ID;
3664        }
3665
3666        $shortlink = '';
3667
3668        // Return p= link for all public post types.
3669        if ( ! empty( $post_id ) ) {
3670                $post_type = get_post_type_object( $post->post_type );
3671
3672                if ( 'page' === $post->post_type && $post->ID == get_option( 'page_on_front' ) && 'page' == get_option( 'show_on_front' ) ) {
3673                        $shortlink = home_url( '/' );
3674                } elseif ( $post_type->public ) {
3675                        $shortlink = home_url( '?p=' . $post_id );
3676                }
3677        }
3678
3679        /**
3680         * Filters the shortlink for a post.
3681         *
3682         * @since 3.0.0
3683         *
3684         * @param string $shortlink   Shortlink URL.
3685         * @param int    $id          Post ID, or 0 for the current post.
3686         * @param string $context     The context for the link. One of 'post' or 'query',
3687         * @param bool   $allow_slugs Whether to allow post slugs in the shortlink. Not used by default.
3688         */
3689        return apply_filters( 'get_shortlink', $shortlink, $id, $context, $allow_slugs );
3690}
3691
3692/**
3693 * Injects rel=shortlink into the head if a shortlink is defined for the current page.
3694 *
3695 * Attached to the {@see 'wp_head'} action.
3696 *
3697 * @since 3.0.0
3698 */
3699function wp_shortlink_wp_head() {
3700        $shortlink = wp_get_shortlink( 0, 'query' );
3701
3702        if ( empty( $shortlink ) )
3703                return;
3704
3705        echo "<link rel='shortlink' href='" . esc_url( $shortlink ) . "' />\n";
3706}
3707
3708/**
3709 * Sends a Link: rel=shortlink header if a shortlink is defined for the current page.
3710 *
3711 * Attached to the {@see 'wp'} action.
3712 *
3713 * @since 3.0.0
3714 */
3715function wp_shortlink_header() {
3716        if ( headers_sent() )
3717                return;
3718
3719        $shortlink = wp_get_shortlink(0, 'query');
3720
3721        if ( empty($shortlink) )
3722                return;
3723
3724        header('Link: <' . $shortlink . '>; rel=shortlink', false);
3725}
3726
3727/**
3728 * Displays the shortlink for a post.
3729 *
3730 * Must be called from inside "The Loop"
3731 *
3732 * Call like the_shortlink( __( 'Shortlinkage FTW' ) )
3733 *
3734 * @since 3.0.0
3735 *
3736 * @param string $text   Optional The link text or HTML to be displayed. Defaults to 'This is the short link.'
3737 * @param string $title  Optional The tooltip for the link. Must be sanitized. Defaults to the sanitized post title.
3738 * @param string $before Optional HTML to display before the link. Default empty.
3739 * @param string $after  Optional HTML to display after the link. Default empty.
3740 */
3741function the_shortlink( $text = '', $title = '', $before = '', $after = '' ) {
3742        $post = get_post();
3743
3744        if ( empty( $text ) )
3745                $text = __('This is the short link.');
3746
3747        if ( empty( $title ) )
3748                $title = the_title_attribute( array( 'echo' => false ) );
3749
3750        $shortlink = wp_get_shortlink( $post->ID );
3751
3752        if ( !empty( $shortlink ) ) {
3753                $link = '<a rel="shortlink" href="' . esc_url( $shortlink ) . '" title="' . $title . '">' . $text . '</a>';
3754
3755                /**
3756                 * Filters the short link anchor tag for a post.
3757                 *
3758                 * @since 3.0.0
3759                 *
3760                 * @param string $link      Shortlink anchor tag.
3761                 * @param string $shortlink Shortlink URL.
3762                 * @param string $text      Shortlink's text.
3763                 * @param string $title     Shortlink's title attribute.
3764                 */
3765                $link = apply_filters( 'the_shortlink', $link, $shortlink, $text, $title );
3766                echo $before, $link, $after;
3767        }
3768}
3769
3770
3771/**
3772 * Retrieves the avatar URL.
3773 *
3774 * @since 4.2.0
3775 *
3776 * @param mixed $id_or_email The Gravatar to retrieve a URL for. Accepts a user_id, gravatar md5 hash,
3777 *                           user email, WP_User object, WP_Post object, or WP_Comment object.
3778 * @param array $args {
3779 *     Optional. Arguments to return instead of the default arguments.
3780 *
3781 *     @type int    $size           Height and width of the avatar in pixels. Default 96.
3782 *     @type string $default        URL for the default image or a default type. Accepts '404' (return
3783 *                                  a 404 instead of a default image), 'retro' (8bit), 'monsterid' (monster),
3784 *                                  'wavatar' (cartoon face), 'indenticon' (the "quilt"), 'mystery', 'mm',
3785 *                                  or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), or
3786 *                                  'gravatar_default' (the Gravatar logo). Default is the value of the
3787 *                                  'avatar_default' option, with a fallback of 'mystery'.
3788 *     @type bool   $force_default  Whether to always show the default image, never the Gravatar. Default false.
3789 *     @type string $rating         What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
3790 *                                  judged in that order. Default is the value of the 'avatar_rating' option.
3791 *     @type string $scheme         URL scheme to use. See set_url_scheme() for accepted values.
3792 *                                  Default null.
3793 *     @type array  $processed_args When the function returns, the value will be the processed/sanitized $args
3794 *                                  plus a "found_avatar" guess. Pass as a reference. Default null.
3795 * }
3796 * @return false|string The URL of the avatar we found, or false if we couldn't find an avatar.
3797 */
3798function get_avatar_url( $id_or_email, $args = null ) {
3799        $args = get_avatar_data( $id_or_email, $args );
3800        return $args['url'];
3801}
3802
3803/**
3804 * Retrieves default data about the avatar.
3805 *
3806 * @since 4.2.0
3807 *
3808 * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
3809 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
3810 * @param array $args {
3811 *     Optional. Arguments to return instead of the default arguments.
3812 *
3813 *     @type int    $size           Height and width of the avatar image file in pixels. Default 96.
3814 *     @type int    $height         Display height of the avatar in pixels. Defaults to $size.
3815 *     @type int    $width          Display width of the avatar in pixels. Defaults to $size.
3816 *     @type string $default        URL for the default image or a default type. Accepts '404' (return
3817 *                                  a 404 instead of a default image), 'retro' (8bit), 'monsterid' (monster),
3818 *                                  'wavatar' (cartoon face), 'indenticon' (the "quilt"), 'mystery', 'mm',
3819 *                                  or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), or
3820 *                                  'gravatar_default' (the Gravatar logo). Default is the value of the
3821 *                                  'avatar_default' option, with a fallback of 'mystery'.
3822 *     @type bool   $force_default  Whether to always show the default image, never the Gravatar. Default false.
3823 *     @type string $rating         What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
3824 *                                  judged in that order. Default is the value of the 'avatar_rating' option.
3825 *     @type string $scheme         URL scheme to use. See set_url_scheme() for accepted values.
3826 *                                  Default null.
3827 *     @type array  $processed_args When the function returns, the value will be the processed/sanitized $args
3828 *                                  plus a "found_avatar" guess. Pass as a reference. Default null.
3829 *     @type string $extra_attr     HTML attributes to insert in the IMG element. Is not sanitized. Default empty.
3830 * }
3831 * @return array $processed_args {
3832 *     Along with the arguments passed in `$args`, this will contain a couple of extra arguments.
3833 *
3834 *     @type bool   $found_avatar True if we were able to find an avatar for this user,
3835 *                                false or not set if we couldn't.
3836 *     @type string $url          The URL of the avatar we found.
3837 * }
3838 */
3839function get_avatar_data( $id_or_email, $args = null ) {
3840        $args = wp_parse_args( $args, array(
3841                'size'           => 96,
3842                'height'         => null,
3843                'width'          => null,
3844                'default'        => get_option( 'avatar_default', 'mystery' ),
3845                'force_default'  => false,
3846                'rating'         => get_option( 'avatar_rating' ),
3847                'scheme'         => null,
3848                'processed_args' => null, // if used, should be a reference
3849                'extra_attr'     => '',
3850        ) );
3851
3852        if ( is_numeric( $args['size'] ) ) {
3853                $args['size'] = absint( $args['size'] );
3854                if ( ! $args['size'] ) {
3855                        $args['size'] = 96;
3856                }
3857        } else {
3858                $args['size'] = 96;
3859        }
3860
3861        if ( is_numeric( $args['height'] ) ) {
3862                $args['height'] = absint( $args['height'] );
3863                if ( ! $args['height'] ) {
3864                        $args['height'] = $args['size'];
3865                }
3866        } else {
3867                $args['height'] = $args['size'];
3868        }
3869
3870        if ( is_numeric( $args['width'] ) ) {
3871                $args['width'] = absint( $args['width'] );
3872                if ( ! $args['width'] ) {
3873                        $args['width'] = $args['size'];
3874                }
3875        } else {
3876                $args['width'] = $args['size'];
3877        }
3878
3879        if ( empty( $args['default'] ) ) {
3880                $args['default'] = get_option( 'avatar_default', 'mystery' );
3881        }
3882
3883        switch ( $args['default'] ) {
3884                case 'mm' :
3885                case 'mystery' :
3886                case 'mysteryman' :
3887                        $args['default'] = 'mm';
3888                        break;
3889                case 'gravatar_default' :
3890                        $args['default'] = false;
3891                        break;
3892        }
3893
3894        $args['force_default'] = (bool) $args['force_default'];
3895
3896        $args['rating'] = strtolower( $args['rating'] );
3897
3898        $args['found_avatar'] = false;
3899
3900        /**
3901         * Filters whether to retrieve the avatar URL early.
3902         *
3903         * Passing a non-null value in the 'url' member of the return array will
3904         * effectively short circuit get_avatar_data(), passing the value through
3905         * the {@see 'get_avatar_data'} filter and returning early.
3906         *
3907         * @since 4.2.0
3908         *
3909         * @param array  $args        Arguments passed to get_avatar_data(), after processing.
3910         * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
3911         *                            user email, WP_User object, WP_Post object, or WP_Comment object.
3912         */
3913        $args = apply_filters( 'pre_get_avatar_data', $args, $id_or_email );
3914
3915        if ( isset( $args['url'] ) && ! is_null( $args['url'] ) ) {
3916                /** This filter is documented in wp-includes/link-template.php */
3917                return apply_filters( 'get_avatar_data', $args, $id_or_email );
3918        }
3919
3920        $email_hash = '';
3921        $user = $email = false;
3922
3923        if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
3924                $id_or_email = get_comment( $id_or_email );
3925        }
3926
3927        // Process the user identifier.
3928        if ( is_numeric( $id_or_email ) ) {
3929                $user = get_user_by( 'id', absint( $id_or_email ) );
3930        } elseif ( is_string( $id_or_email ) ) {
3931                if ( strpos( $id_or_email, '@md5.gravatar.com' ) ) {
3932                        // md5 hash
3933                        list( $email_hash ) = explode( '@', $id_or_email );
3934                } else {
3935                        // email address
3936                        $email = $id_or_email;
3937                }
3938        } elseif ( $id_or_email instanceof WP_User ) {
3939                // User Object
3940                $user = $id_or_email;
3941        } elseif ( $id_or_email instanceof WP_Post ) {
3942                // Post Object
3943                $user = get_user_by( 'id', (int) $id_or_email->post_author );
3944        } elseif ( $id_or_email instanceof WP_Comment ) {
3945                /**
3946                 * Filters the list of allowed comment types for retrieving avatars.
3947                 *
3948                 * @since 3.0.0
3949                 *
3950                 * @param array $types An array of content types. Default only contains 'comment'.
3951                 */
3952                $allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
3953                if ( ! empty( $id_or_email->comment_type ) && ! in_array( $id_or_email->comment_type, (array) $allowed_comment_types ) ) {
3954                        $args['url'] = false;
3955                        /** This filter is documented in wp-includes/link-template.php */
3956                        return apply_filters( 'get_avatar_data', $args, $id_or_email );
3957                }
3958
3959                if ( ! empty( $id_or_email->user_id ) ) {
3960                        $user = get_user_by( 'id', (int) $id_or_email->user_id );
3961                }
3962                if ( ( ! $user || is_wp_error( $user ) ) && ! empty( $id_or_email->comment_author_email ) ) {
3963                        $email = $id_or_email->comment_author_email;
3964                }
3965        }
3966
3967        if ( ! $email_hash ) {
3968                if ( $user ) {
3969                        $email = $user->user_email;
3970                }
3971
3972                if ( $email ) {
3973                        $email_hash = md5( strtolower( trim( $email ) ) );
3974                }
3975        }
3976
3977        if ( $email_hash ) {
3978                $args['found_avatar'] = true;
3979                $gravatar_server = hexdec( $email_hash[0] ) % 3;
3980        } else {
3981                $gravatar_server = rand( 0, 2 );
3982        }
3983
3984        $url_args = array(
3985                's' => $args['size'],
3986                'd' => $args['default'],
3987                'f' => $args['force_default'] ? 'y' : false,
3988                'r' => $args['rating'],
3989        );
3990
3991        if ( is_ssl() ) {
3992                $url = 'https://secure.gravatar.com/avatar/' . $email_hash;
3993        } else {
3994                $url = sprintf( 'http://%d.gravatar.com/avatar/%s', $gravatar_server, $email_hash );
3995        }
3996
3997        $url = add_query_arg(
3998                rawurlencode_deep( array_filter( $url_args ) ),
3999                set_url_scheme( $url, $args['scheme'] )
4000        );
4001
4002        /**
4003         * Filters the avatar URL.
4004         *
4005         * @since 4.2.0
4006         *
4007         * @param string $url         The URL of the avatar.
4008         * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
4009         *                            user email, WP_User object, WP_Post object, or WP_Comment object.
4010         * @param array  $args        Arguments passed to get_avatar_data(), after processing.
4011         */
4012        $args['url'] = apply_filters( 'get_avatar_url', $url, $id_or_email, $args );
4013
4014        /**
4015         * Filters the avatar data.
4016         *
4017         * @since 4.2.0
4018         *
4019         * @param array  $args        Arguments passed to get_avatar_data(), after processing.
4020         * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
4021         *                            user email, WP_User object, WP_Post object, or WP_Comment object.
4022         */
4023        return apply_filters( 'get_avatar_data', $args, $id_or_email );
4024}
4025
4026/**
4027 * Retrieves the URL of a file in the theme.
4028 *
4029 * Searches in the stylesheet directory before the template directory so themes
4030 * which inherit from a parent theme can just override one file.
4031 *
4032 * @since 4.7.0
4033 *
4034 * @param string $file Optional. File to search for in the stylesheet directory.
4035 * @return string The URL of the file.
4036 */
4037function get_theme_file_uri( $file = '' ) {
4038        $file = ltrim( $file, '/' );
4039
4040        if ( empty( $file ) ) {
4041                $url = get_stylesheet_directory_uri();
4042        } elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
4043                $url = get_stylesheet_directory_uri() . '/' . $file;
4044        } else {
4045                $url = get_template_directory_uri() . '/' . $file;
4046        }
4047
4048        /**
4049         * Filters the URL to a file in the theme.
4050         *
4051         * @since 4.7.0
4052         *
4053         * @param string $url  The file URL.
4054         * @param string $file The requested file to search for.
4055         */
4056        return apply_filters( 'theme_file_uri', $url, $file );
4057}
4058
4059/**
4060 * Retrieves the URL of a file in the parent theme.
4061 *
4062 * @since 4.7.0
4063 *
4064 * @param string $file Optional. File to return the URL for in the template directory.
4065 * @return string The URL of the file.
4066 */
4067function get_parent_theme_file_uri( $file = '' ) {
4068        $file = ltrim( $file, '/' );
4069
4070        if ( empty( $file ) ) {
4071                $url = get_template_directory_uri();
4072        } else {
4073                $url = get_template_directory_uri() . '/' . $file;
4074        }
4075
4076        /**
4077         * Filters the URL to a file in the parent theme.
4078         *
4079         * @since 4.7.0
4080         *
4081         * @param string $url  The file URL.
4082         * @param string $file The requested file to search for.
4083         */
4084        return apply_filters( 'parent_theme_file_uri', $url, $file );
4085}
4086
4087/**
4088 * Retrieves the path of a file in the theme.
4089 *
4090 * Searches in the stylesheet directory before the template directory so themes
4091 * which inherit from a parent theme can just override one file.
4092 *
4093 * @since 4.7.0
4094 *
4095 * @param string $file Optional. File to search for in the stylesheet directory.
4096 * @return string The path of the file.
4097 */
4098function get_theme_file_path( $file = '' ) {
4099        $file = ltrim( $file, '/' );
4100
4101        if ( empty( $file ) ) {
4102                $path = get_stylesheet_directory();
4103        } elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
4104                $path = get_stylesheet_directory() . '/' . $file;
4105        } else {
4106                $path = get_template_directory() . '/' . $file;
4107        }
4108
4109        /**
4110         * Filters the path to a file in the theme.
4111         *
4112         * @since 4.7.0
4113         *
4114         * @param string $path The file path.
4115         * @param string $file The requested file to search for.
4116         */
4117        return apply_filters( 'theme_file_path', $path, $file );
4118}
4119
4120/**
4121 * Retrieves the path of a file in the parent theme.
4122 *
4123 * @since 4.7.0
4124 *
4125 * @param string $file Optional. File to return the path for in the template directory.
4126 * @return string The path of the file.
4127 */
4128function get_parent_theme_file_path( $file = '' ) {
4129        $file = ltrim( $file, '/' );
4130
4131        if ( empty( $file ) ) {
4132                $path = get_template_directory();
4133        } else {
4134                $path = get_template_directory() . '/' . $file;
4135        }
4136
4137        /**
4138         * Filters the path to a file in the parent theme.
4139         *
4140         * @since 4.7.0
4141         *
4142         * @param string $path The file path.
4143         * @param string $file The requested file to search for.
4144         */
4145        return apply_filters( 'parent_theme_file_path', $path, $file );
4146}
4147
4148/**
4149 * Require the file of parent theme.
4150 *
4151 * @since 4.9.0
4152 *
4153 * @param string $file Required. File to require.
4154 * @return required the file.
4155 */
4156function wp_require_parent_theme_file( $file = '' ) {
4157        $file = ltrim( $file, '/' );
4158
4159        if ( ! empty( $file ) ) {
4160                $path = get_template_directory() . '/' . $file;
4161        } else {
4162                return;
4163        }
4164
4165        require $path;
4166}