WordPress.org

Make WordPress Core

Ticket #18120: link-template.php

File link-template.php, 72.5 KB (added by javert03, 3 years ago)

includes/link_template.php

Line 
1<?php
2/**
3 * WordPress Link Template Functions
4 *
5 * @package WordPress
6 * @subpackage Template
7 */
8
9/**
10 * Display the permalink for the current post.
11 *
12 * @since 1.2.0
13 * @uses apply_filters() Calls 'the_permalink' filter on the permalink string.
14 */
15function the_permalink() {
16        echo apply_filters('the_permalink', get_permalink());
17}
18
19/**
20 * Retrieve trailing slash string, if blog set for adding trailing slashes.
21 *
22 * Conditionally adds a trailing slash if the permalink structure has a trailing
23 * slash, strips the trailing slash if not. The string is passed through the
24 * 'user_trailingslashit' filter. Will remove trailing slash from string, if
25 * blog is not set to have them.
26 *
27 * @since 2.2.0
28 * @uses $wp_rewrite
29 *
30 * @param string $string URL with or without a trailing slash.
31 * @param string $type_of_url The type of URL being considered (e.g. single, category, etc) for use in the filter.
32 * @return string
33 */
34function user_trailingslashit($string, $type_of_url = '') {
35        global $wp_rewrite;
36        if ( $wp_rewrite->use_trailing_slashes )
37                $string = trailingslashit($string);
38        else
39                $string = untrailingslashit($string);
40
41        // Note that $type_of_url can be one of following:
42        // single, single_trackback, single_feed, single_paged, feed, category, page, year, month, day, paged, post_type_archive
43        $string = apply_filters('user_trailingslashit', $string, $type_of_url);
44        return $string;
45}
46
47/**
48 * Display permalink anchor for current post.
49 *
50 * The permalink mode title will use the post title for the 'a' element 'id'
51 * attribute. The id mode uses 'post-' with the post ID for the 'id' attribute.
52 *
53 * @since 0.71
54 *
55 * @param string $mode Permalink mode can be either 'title', 'id', or default, which is 'id'.
56 */
57function permalink_anchor($mode = 'id') {
58        global $post;
59        switch ( strtolower($mode) ) {
60                case 'title':
61                        $title = sanitize_title($post->post_title) . '-' . $post->ID;
62                        echo '<a id="'.$title.'"></a>';
63                        break;
64                case 'id':
65                default:
66                        echo '<a id="post-' . $post->ID . '"></a>';
67                        break;
68        }
69}
70
71/**
72 * Retrieve full permalink for current post or post ID.
73 *
74 * @since 1.0.0
75 *
76 * @param int $id Optional. Post ID.
77 * @param bool $leavename Optional, defaults to false. Whether to keep post name or page name.
78 * @return string
79 */
80function get_permalink($id = 0, $leavename = false) {
81        $rewritecode = array(
82                '%year%',
83                '%monthnum%',
84                '%day%',
85                '%hour%',
86                '%minute%',
87                '%second%',
88                $leavename? '' : '%postname%',
89                '%post_id%',
90                '%category%',
91                '%author%',
92                $leavename? '' : '%pagename%',
93        );
94
95        if ( is_object($id) && isset($id->filter) && 'sample' == $id->filter ) {
96                $post = $id;
97                $sample = true;
98        } else {
99                $post = &get_post($id);
100                $sample = false;
101        }
102
103        if ( empty($post->ID) )
104                return false;
105
106        if ( $post->post_type == 'page' )
107                return get_page_link($post->ID, $leavename, $sample);
108        elseif ( $post->post_type == 'attachment' )
109                return get_attachment_link($post->ID);
110        elseif ( in_array($post->post_type, get_post_types( array('_builtin' => false) ) ) )
111                return get_post_permalink($post->ID, $leavename, $sample);
112
113        $permalink = get_option('permalink_structure');
114
115        $permalink = apply_filters('pre_post_link', $permalink, $post, $leavename);
116
117        if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {
118                $unixtime = strtotime($post->post_date);
119
120                $category = '';
121                if ( strpos($permalink, '%category%') !== false ) {
122                        $cats = get_the_category($post->ID);
123                        if ( $cats ) {
124                                usort($cats, '_usort_terms_by_ID'); // order by ID
125                                $category = $cats[0]->slug;
126                                if ( $parent = $cats[0]->parent )
127                                        $category = get_category_parents($parent, false, '/', true) . $category;
128                        }
129                        // show default category in permalinks, without
130                        // having to assign it explicitly
131                        if ( empty($category) ) {
132                                $default_category = get_category( get_option( 'default_category' ) );
133                                $category = is_wp_error( $default_category ) ? '' : $default_category->slug;
134                        }
135                }
136
137                $author = '';
138                if ( strpos($permalink, '%author%') !== false ) {
139                        $authordata = get_userdata($post->post_author);
140                        $author = $authordata->user_nicename;
141                }
142
143                $date = explode(" ",date('Y m d H i s', $unixtime));
144                $rewritereplace =
145                array(
146                        $date[0],
147                        $date[1],
148                        $date[2],
149                        $date[3],
150                        $date[4],
151                        $date[5],
152                        $post->post_name,
153                        $post->ID,
154                        $category,
155                        $author,
156                        $post->post_name,
157                );
158                $permalink = home_url( str_replace($rewritecode, $rewritereplace, $permalink) );
159                $permalink = user_trailingslashit($permalink, 'single');
160        } else { // if they're not using the fancy permalink option
161                $permalink = home_url('?p=' . $post->ID);
162        }
163        return apply_filters('post_link', $permalink, $post, $leavename);
164}
165
166/**
167 * Retrieve the permalink for a post with a custom post type.
168 *
169 * @since 3.0.0
170 *
171 * @param int $id Optional. Post ID.
172 * @param bool $leavename Optional, defaults to false. Whether to keep post name.
173 * @param bool $sample Optional, defaults to false. Is it a sample permalink.
174 * @return string
175 */
176function get_post_permalink( $id = 0, $leavename = false, $sample = false ) {
177        global $wp_rewrite;
178
179        $post = &get_post($id);
180
181        if ( is_wp_error( $post ) )
182                return $post;
183
184        $post_link = $wp_rewrite->get_extra_permastruct($post->post_type);
185
186        $slug = $post->post_name;
187
188        $draft_or_pending = isset($post->post_status) && in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
189
190        $post_type = get_post_type_object($post->post_type);
191
192        if ( !empty($post_link) && ( !$draft_or_pending || $sample ) ) {
193                if ( ! $leavename ) {
194                        if ( $post_type->hierarchical )
195                                $slug = get_page_uri($id);
196                        $post_link = str_replace("%$post->post_type%", $slug, $post_link);
197                }
198                $post_link = home_url( user_trailingslashit($post_link) );
199        } else {
200                if ( $post_type->query_var && ( isset($post->post_status) && !$draft_or_pending ) )
201                        $post_link = add_query_arg($post_type->query_var, $slug, '');
202                else
203                        $post_link = add_query_arg(array('post_type' => $post->post_type, 'p' => $post->ID), '');
204                $post_link = home_url($post_link);
205        }
206
207        return apply_filters('post_type_link', $post_link, $post, $leavename, $sample);
208}
209
210/**
211 * Retrieve permalink from post ID.
212 *
213 * @since 1.0.0
214 *
215 * @param int $post_id Optional. Post ID.
216 * @param mixed $deprecated Not used.
217 * @return string
218 */
219function post_permalink( $post_id = 0, $deprecated = '' ) {
220        if ( !empty( $deprecated ) )
221                _deprecated_argument( __FUNCTION__, '1.3' );
222
223        return get_permalink($post_id);
224}
225
226/**
227 * Retrieve the permalink for current page or page ID.
228 *
229 * Respects page_on_front. Use this one.
230 *
231 * @since 1.5.0
232 *
233 * @param int $id Optional. Post ID.
234 * @param bool $leavename Optional, defaults to false. Whether to keep page name.
235 * @param bool $sample Optional, defaults to false. Is it a sample permalink.
236 * @return string
237 */
238function get_page_link( $id = false, $leavename = false, $sample = false ) {
239        global $post;
240
241        $id = (int) $id;
242        if ( !$id )
243                $id = (int) $post->ID;
244
245        if ( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') )
246                $link = home_url('/');
247        else
248                $link = _get_page_link( $id , $leavename, $sample );
249
250        return apply_filters('page_link', $link, $id, $sample);
251}
252
253/**
254 * Retrieve the page permalink.
255 *
256 * Ignores page_on_front. Internal use only.
257 *
258 * @since 2.1.0
259 * @access private
260 *
261 * @param int $id Optional. Post ID.
262 * @param bool $leavename Optional. Leave name.
263 * @param bool $sample Optional. Sample permalink.
264 * @return string
265 */
266function _get_page_link( $id = false, $leavename = false, $sample = false ) {
267        global $post, $wp_rewrite;
268
269        if ( !$id )
270                $id = (int) $post->ID;
271        else
272                $post = &get_post($id);
273
274        $draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
275
276        $link = $wp_rewrite->get_page_permastruct();
277
278        if ( !empty($link) && ( ( isset($post->post_status) && !$draft_or_pending ) || $sample ) ) {
279                if ( ! $leavename ) {
280                        $link = str_replace('%pagename%', get_page_uri($id), $link);
281                }
282
283                $link = home_url($link);
284                $link = user_trailingslashit($link, 'page');
285        } else {
286                $link = home_url("?page_id=$id");
287        }
288
289        return apply_filters( '_get_page_link', $link, $id );
290}
291
292/**
293 * Retrieve permalink for attachment.
294 *
295 * This can be used in the WordPress Loop or outside of it.
296 *
297 * @since 2.0.0
298 *
299 * @param int $id Optional. Post ID.
300 * @return string
301 */
302function get_attachment_link($id = false) {
303        global $post, $wp_rewrite;
304
305        $link = false;
306
307        if ( ! $id)
308                $id = (int) $post->ID;
309
310        $object = get_post($id);
311        if ( $wp_rewrite->using_permalinks() && ($object->post_parent > 0) && ($object->post_parent != $id) ) {
312                $parent = get_post($object->post_parent);
313                if ( 'page' == $parent->post_type )
314                        $parentlink = _get_page_link( $object->post_parent ); // Ignores page_on_front
315                else
316                        $parentlink = get_permalink( $object->post_parent );
317
318                if ( is_numeric($object->post_name) || false !== strpos(get_option('permalink_structure'), '%category%') )
319                        $name = 'attachment/' . $object->post_name; // <permalink>/<int>/ is paged so we use the explicit attachment marker
320                else
321                        $name = $object->post_name;
322
323                if ( strpos($parentlink, '?') === false )
324                        $link = user_trailingslashit( trailingslashit($parentlink) . $name );
325        }
326
327        if ( ! $link )
328                $link = home_url( "/?attachment_id=$id" );
329
330        return apply_filters('attachment_link', $link, $id);
331}
332
333/**
334 * Retrieve the permalink for the year archives.
335 *
336 * @since 1.5.0
337 *
338 * @param int|bool $year False for current year or year for permalink.
339 * @return string
340 */
341function get_year_link($year) {
342        global $wp_rewrite;
343        if ( !$year )
344                $year = gmdate('Y', current_time('timestamp'));
345        $yearlink = $wp_rewrite->get_year_permastruct();
346        if ( !empty($yearlink) ) {
347                $yearlink = str_replace('%year%', $year, $yearlink);
348                return apply_filters('year_link', home_url( user_trailingslashit($yearlink, 'year') ), $year);
349        } else {
350                return apply_filters('year_link', home_url('?m=' . $year), $year);
351        }
352}
353
354/**
355 * Retrieve the permalink for the month archives with year.
356 *
357 * @since 1.0.0
358 *
359 * @param bool|int $year False for current year. Integer of year.
360 * @param bool|int $month False for current month. Integer of month.
361 * @return string
362 */
363function get_month_link($year, $month) {
364        global $wp_rewrite;
365        if ( !$year )
366                $year = gmdate('Y', current_time('timestamp'));
367        if ( !$month )
368                $month = gmdate('m', current_time('timestamp'));
369        $monthlink = $wp_rewrite->get_month_permastruct();
370        if ( !empty($monthlink) ) {
371                $monthlink = str_replace('%year%', $year, $monthlink);
372                $monthlink = str_replace('%monthnum%', zeroise(intval($month), 2), $monthlink);
373                return apply_filters('month_link', home_url( user_trailingslashit($monthlink, 'month') ), $year, $month);
374        } else {
375                return apply_filters('month_link', home_url( '?m=' . $year . zeroise($month, 2) ), $year, $month);
376        }
377}
378
379/**
380 * Retrieve the permalink for the day archives with year and month.
381 *
382 * @since 1.0.0
383 *
384 * @param bool|int $year False for current year. Integer of year.
385 * @param bool|int $month False for current month. Integer of month.
386 * @param bool|int $day False for current day. Integer of day.
387 * @return string
388 */
389function get_day_link($year, $month, $day) {
390        global $wp_rewrite;
391        if ( !$year )
392                $year = gmdate('Y', current_time('timestamp'));
393        if ( !$month )
394                $month = gmdate('m', current_time('timestamp'));
395        if ( !$day )
396                $day = gmdate('j', current_time('timestamp'));
397
398        $daylink = $wp_rewrite->get_day_permastruct();
399        if ( !empty($daylink) ) {
400                $daylink = str_replace('%year%', $year, $daylink);
401                $daylink = str_replace('%monthnum%', zeroise(intval($month), 2), $daylink);
402                $daylink = str_replace('%day%', zeroise(intval($day), 2), $daylink);
403                return apply_filters('day_link', home_url( user_trailingslashit($daylink, 'day') ), $year, $month, $day);
404        } else {
405                return apply_filters('day_link', home_url( '?m=' . $year . zeroise($month, 2) . zeroise($day, 2) ), $year, $month, $day);
406        }
407}
408
409/**
410 * Display the permalink for the feed type.
411 *
412 * @since 3.0.0
413 *
414 * @param string $anchor The link's anchor text.
415 * @param string $feed Optional, defaults to default feed. Feed type.
416 */
417function the_feed_link( $anchor, $feed = '' ) {
418        $link = '<a href="' . esc_url( get_feed_link( $feed ) ) . '">' . $anchor . '</a>';
419        echo apply_filters( 'the_feed_link', $link, $feed );
420}
421
422/**
423 * Retrieve the permalink for the feed type.
424 *
425 * @since 1.5.0
426 *
427 * @param string $feed Optional, defaults to default feed. Feed type.
428 * @return string
429 */
430function get_feed_link($feed = '') {
431        global $wp_rewrite;
432
433        $permalink = $wp_rewrite->get_feed_permastruct();
434        if ( '' != $permalink ) {
435                if ( false !== strpos($feed, 'comments_') ) {
436                        $feed = str_replace('comments_', '', $feed);
437                        $permalink = $wp_rewrite->get_comment_feed_permastruct();
438                }
439
440                if ( get_default_feed() == $feed )
441                        $feed = '';
442
443                $permalink = str_replace('%feed%', $feed, $permalink);
444                $permalink = preg_replace('#/+#', '/', "/$permalink");
445                $output =  home_url( user_trailingslashit($permalink, 'feed') );
446        } else {
447                if ( empty($feed) )
448                        $feed = get_default_feed();
449
450                if ( false !== strpos($feed, 'comments_') )
451                        $feed = str_replace('comments_', 'comments-', $feed);
452
453                $output = home_url("?feed={$feed}");
454        }
455
456        return apply_filters('feed_link', $output, $feed);
457}
458
459/**
460 * Retrieve the permalink for the post comments feed.
461 *
462 * @since 2.2.0
463 *
464 * @param int $post_id Optional. Post ID.
465 * @param string $feed Optional. Feed type.
466 * @return string
467 */
468function get_post_comments_feed_link($post_id = 0, $feed = '') {
469        $post_id = absint( $post_id );
470
471        if ( ! $post_id )
472                $post_id = get_the_ID();
473
474        if ( empty( $feed ) )
475                $feed = get_default_feed();
476
477        if ( '' != get_option('permalink_structure') ) {
478                if ( 'page' == get_option('show_on_front') && $post_id == get_option('page_on_front') )
479                        $url = _get_page_link( $post_id );
480                else
481                        $url = get_permalink($post_id);
482
483                $url = trailingslashit($url) . 'feed';
484                if ( $feed != get_default_feed() )
485                        $url .= "/$feed";
486                $url = user_trailingslashit($url, 'single_feed');
487        } else {
488                $type = get_post_field('post_type', $post_id);
489                if ( 'page' == $type )
490                        $url = home_url("?feed=$feed&amp;page_id=$post_id");
491                else
492                        $url = home_url("?feed=$feed&amp;p=$post_id");
493        }
494
495        return apply_filters('post_comments_feed_link', $url);
496}
497
498/**
499 * Display the comment feed link for a post.
500 *
501 * Prints out the comment feed link for a post. Link text is placed in the
502 * anchor. If no link text is specified, default text is used. If no post ID is
503 * specified, the current post is used.
504 *
505 * @package WordPress
506 * @subpackage Feed
507 * @since 2.5.0
508 *
509 * @param string $link_text Descriptive text.
510 * @param int $post_id Optional post ID.  Default to current post.
511 * @param string $feed Optional. Feed format.
512 * @return string Link to the comment feed for the current post.
513*/
514function post_comments_feed_link( $link_text = '', $post_id = '', $feed = '' ) {
515        $url = get_post_comments_feed_link($post_id, $feed);
516        if ( empty($link_text) )
517                $link_text = __('Comments Feed');
518
519        echo apply_filters( 'post_comments_feed_link_html', "<a href='$url'>$link_text</a>", $post_id, $feed );
520}
521
522/**
523 * Retrieve the feed link for a given author.
524 *
525 * Returns a link to the feed for all posts by a given author. A specific feed
526 * can be requested or left blank to get the default feed.
527 *
528 * @package WordPress
529 * @subpackage Feed
530 * @since 2.5.0
531 *
532 * @param int $author_id ID of an author.
533 * @param string $feed Optional. Feed type.
534 * @return string Link to the feed for the author specified by $author_id.
535*/
536function get_author_feed_link( $author_id, $feed = '' ) {
537        $author_id = (int) $author_id;
538        $permalink_structure = get_option('permalink_structure');
539
540        if ( empty($feed) )
541                $feed = get_default_feed();
542
543        if ( '' == $permalink_structure ) {
544                $link = home_url("?feed=$feed&amp;author=" . $author_id);
545        } else {
546                $link = get_author_posts_url($author_id);
547                if ( $feed == get_default_feed() )
548                        $feed_link = 'feed';
549                else
550                        $feed_link = "feed/$feed";
551
552                $link = trailingslashit($link) . user_trailingslashit($feed_link, 'feed');
553        }
554
555        $link = apply_filters('author_feed_link', $link, $feed);
556
557        return $link;
558}
559
560/**
561 * Retrieve the feed link for a category.
562 *
563 * Returns a link to the feed for all post in a given category. A specific feed
564 * can be requested or left blank to get the default feed.
565 *
566 * @package WordPress
567 * @subpackage Feed
568 * @since 2.5.0
569 *
570 * @param int $cat_id ID of a category.
571 * @param string $feed Optional. Feed type.
572 * @return string Link to the feed for the category specified by $cat_id.
573*/
574function get_category_feed_link($cat_id, $feed = '') {
575        return get_term_feed_link($cat_id, 'category', $feed);
576}
577
578/**
579 * Retrieve the feed link for a taxonomy.
580 *
581 * Returns a link to the feed for all post in a given term. A specific feed
582 * can be requested or left blank to get the default feed.
583 *
584 * @since 3.0
585 *
586 * @param int $term_id ID of a category.
587 * @param string $taxonomy Optional. Taxonomy of $term_id
588 * @param string $feed Optional. Feed type.
589 * @return string Link to the feed for the taxonomy specified by $term_id and $taxonomy.
590*/
591function get_term_feed_link( $term_id, $taxonomy = 'category', $feed = '' ) {
592        global $wp_rewrite;
593
594        $term_id = ( int ) $term_id;
595
596        $term = get_term( $term_id, $taxonomy  );
597
598        if ( empty( $term ) || is_wp_error( $term ) )
599                return false;
600
601        if ( empty( $feed ) )
602                $feed = get_default_feed();
603
604        $permalink_structure = get_option( 'permalink_structure' );
605
606        if ( '' == $permalink_structure ) {
607                if ( 'category' == $taxonomy ) {
608                        $link = home_url("?feed=$feed&amp;cat=$term_id");
609                }
610                elseif ( 'post_tag' == $taxonomy ) {
611                        $link = home_url("?feed=$feed&amp;tag=$term->slug");
612                } else {
613                        $t = get_taxonomy( $taxonomy );
614                        $link = home_url("?feed=$feed&amp;$t->query_var=$term->slug");
615                }
616        } else {
617                $link = get_term_link( $term_id, $term->taxonomy );
618                if ( $feed == get_default_feed() )
619                        $feed_link = 'feed';
620                else
621                        $feed_link = "feed/$feed";
622
623                $link = trailingslashit( $link ) . user_trailingslashit( $feed_link, 'feed' );
624        }
625
626        if ( 'category' == $taxonomy )
627                $link = apply_filters( 'category_feed_link', $link, $feed );
628        elseif ( 'post_tag' == $taxonomy )
629                $link = apply_filters( 'category_feed_link', $link, $feed );
630        else
631                $link = apply_filters( 'taxonomy_feed_link', $link, $feed, $taxonomy );
632
633
634        return $link;
635}
636
637/**
638 * Retrieve permalink for feed of tag.
639 *
640 * @since 2.3.0
641 *
642 * @param int $tag_id Tag ID.
643 * @param string $feed Optional. Feed type.
644 * @return string
645 */
646function get_tag_feed_link($tag_id, $feed = '') {
647        return get_term_feed_link($tag_id, 'post_tag', $feed);
648}
649
650/**
651 * Retrieve edit tag link.
652 *
653 * @since 2.7.0
654 *
655 * @param int $tag_id Tag ID
656 * @param string $taxonomy Taxonomy
657 * @return string
658 */
659function get_edit_tag_link( $tag_id, $taxonomy = 'post_tag' ) {
660        return apply_filters( 'get_edit_tag_link', get_edit_term_link( $tag_id, $taxonomy ) );
661}
662
663/**
664 * Display or retrieve edit tag link with formatting.
665 *
666 * @since 2.7.0
667 *
668 * @param string $link Optional. Anchor text.
669 * @param string $before Optional. Display before edit link.
670 * @param string $after Optional. Display after edit link.
671 * @param int|object $tag Tag object or ID
672 * @return string HTML content.
673 */
674function edit_tag_link( $link = '', $before = '', $after = '', $tag = null ) {
675        $link = edit_term_link( $link, '', '', false, $tag );
676        echo $before . apply_filters( 'edit_tag_link', $link ) . $after;
677}
678
679/**
680 * Retrieve edit term url.
681 *
682 * @since 3.1.0
683 *
684 * @param int $term_id Term ID
685 * @param string $taxonomy Taxonomy
686 * @param string $object_type The object type
687 * @return string
688 */
689function get_edit_term_link( $term_id, $taxonomy, $object_type = '' ) {
690        $tax = get_taxonomy( $taxonomy );
691        if ( !current_user_can( $tax->cap->edit_terms ) )
692                return;
693
694        $term = get_term( $term_id, $taxonomy );
695
696        $args = array(
697                'action' => 'edit',
698                'taxonomy' => $taxonomy,
699                'tag_ID' => $term->term_id,
700        );
701
702        if ( $object_type )
703                $args['post_type'] = $object_type;
704
705        $location = add_query_arg( $args, admin_url( 'edit-tags.php' ) );
706
707        return apply_filters( 'get_edit_term_link', $location, $term_id, $taxonomy, $object_type );
708}
709
710/**
711 * Display or retrieve edit term link with formatting.
712 *
713 * @since 3.1.0
714 *
715 * @param string $link Optional. Anchor text.
716 * @param string $before Optional. Display before edit link.
717 * @param string $after Optional. Display after edit link.
718 * @param object $term Term object
719 * @return string HTML content.
720 */
721function edit_term_link( $link = '', $before = '', $after = '', $term = null, $echo = true ) {
722        if ( is_null( $term ) ) {
723                $term = get_queried_object();
724        }
725
726        $tax = get_taxonomy( $term->taxonomy );
727        if ( !current_user_can($tax->cap->edit_terms) )
728                return;
729
730        if ( empty( $link ) )
731                $link = __('Edit This');
732
733        $link = '<a href="' . get_edit_term_link( $term->term_id, $term->taxonomy ) . '" title="' . $link . '">' . $link . '</a>';
734        $link = $before . apply_filters( 'edit_term_link', $link, $term->term_id ) . $after;
735
736        if ( $echo )
737                echo $link;
738        else
739                return $link;
740}
741
742/**
743* Retrieve permalink for search.
744*
745* @since  3.0.0
746* @param string $query Optional. The query string to use. If empty the current query is used.
747* @return string
748*/
749function get_search_link( $query = '' ) {
750        global $wp_rewrite;
751
752        if ( empty($query) )
753                $search = get_search_query( false );
754        else
755                $search = stripslashes($query);
756
757        $permastruct = $wp_rewrite->get_search_permastruct();
758
759        if ( empty( $permastruct ) ) {
760                $link = home_url('?s=' . urlencode($search) );
761        } else {
762                $search = urlencode($search);
763                $search = str_replace('%2F', '/', $search); // %2F(/) is not valid within a URL, send it unencoded.
764                $link = str_replace( '%search%', $search, $permastruct );
765                $link = home_url( user_trailingslashit( $link, 'search' ) );
766        }
767
768        return apply_filters( 'search_link', $link, $search );
769}
770
771/**
772 * Retrieve the permalink for the feed of the search results.
773 *
774 * @since 2.5.0
775 *
776 * @param string $search_query Optional. Search query.
777 * @param string $feed Optional. Feed type.
778 * @return string
779 */
780function get_search_feed_link($search_query = '', $feed = '') {
781        global $wp_rewrite;
782        $link = get_search_link($search_query);
783
784        if ( empty($feed) )
785                $feed = get_default_feed();
786
787        $permastruct = $wp_rewrite->get_search_permastruct();
788
789        if ( empty($permastruct) ) {
790                $link = add_query_arg('feed', $feed, $link);
791        } else {
792                $link = trailingslashit($link);
793                $link .= "feed/$feed/";
794        }
795
796        $link = apply_filters('search_feed_link', $link, $feed, 'posts');
797
798        return $link;
799}
800
801/**
802 * Retrieve the permalink for the comments feed of the search results.
803 *
804 * @since 2.5.0
805 *
806 * @param string $search_query Optional. Search query.
807 * @param string $feed Optional. Feed type.
808 * @return string
809 */
810function get_search_comments_feed_link($search_query = '', $feed = '') {
811        global $wp_rewrite;
812
813        if ( empty($feed) )
814                $feed = get_default_feed();
815
816        $link = get_search_feed_link($search_query, $feed);
817
818        $permastruct = $wp_rewrite->get_search_permastruct();
819
820        if ( empty($permastruct) )
821                $link = add_query_arg('feed', 'comments-' . $feed, $link);
822        else
823                $link = add_query_arg('withcomments', 1, $link);
824
825        $link = apply_filters('search_feed_link', $link, $feed, 'comments');
826
827        return $link;
828}
829
830/**
831 * Retrieve the permalink for a post type archive.
832 *
833 * @since 3.1.0
834 *
835 * @param string $post_type Post type
836 * @return string
837 */
838function get_post_type_archive_link( $post_type ) {
839        global $wp_rewrite;
840        if ( ! $post_type_obj = get_post_type_object( $post_type ) )
841                return false;
842
843        if ( ! $post_type_obj->has_archive )
844                return false;
845
846        if ( get_option( 'permalink_structure' ) && is_array( $post_type_obj->rewrite ) ) {
847                $struct = ( true === $post_type_obj->has_archive ) ? $post_type_obj->rewrite['slug'] : $post_type_obj->has_archive;
848                if ( $post_type_obj->rewrite['with_front'] )
849                        $struct = $wp_rewrite->front . $struct;
850                else
851                        $struct = $wp_rewrite->root . $struct;
852                $link = home_url( user_trailingslashit( $struct, 'post_type_archive' ) );
853        } else {
854                $link = home_url( '?post_type=' . $post_type );
855        }
856
857        return apply_filters( 'post_type_archive_link', $link, $post_type );
858}
859
860/**
861 * Retrieve the permalink for a post type archive feed.
862 *
863 * @since 3.1.0
864 *
865 * @param string $post_type Post type
866 * @param string $feed Optional. Feed type
867 * @return string
868 */
869function get_post_type_archive_feed_link( $post_type, $feed = '' ) {
870        $default_feed = get_default_feed();
871        if ( empty( $feed ) )
872                $feed = $default_feed;
873
874        if ( ! $link = get_post_type_archive_link( $post_type ) )
875                return false;
876        $post_type_obj = get_post_type_object( $post_type );
877        if ( $post_type_obj->rewrite['feeds'] && get_option( 'permalink_structure' ) ) {
878                $link = trailingslashit($link);
879                $link .= 'feed/';
880                if ( $feed != $default_feed )
881                        $link .= "$feed/";
882        } else {
883                $link = add_query_arg( 'feed', $feed, $link );
884        }
885
886        return apply_filters( 'post_type_archive_feed_link', $link, $feed );
887}
888
889/**
890 * Retrieve edit posts link for post.
891 *
892 * Can be used within the WordPress loop or outside of it. Can be used with
893 * pages, posts, attachments, and revisions.
894 *
895 * @since 2.3.0
896 *
897 * @param int $id Optional. Post ID.
898 * @param string $context Optional, default to display. How to write the '&', defaults to '&amp;'.
899 * @return string
900 */
901function get_edit_post_link( $id = 0, $context = 'display' ) {
902        if ( !$post = &get_post( $id ) )
903                return;
904
905        if ( 'display' == $context )
906                $action = '&amp;action=edit';
907        else
908                $action = '&action=edit';
909
910        $post_type_object = get_post_type_object( $post->post_type );
911        if ( !$post_type_object )
912                return;
913
914        if ( !current_user_can( $post_type_object->cap->edit_post, $post->ID ) )
915                return;
916
917        return apply_filters( 'get_edit_post_link', admin_url( sprintf($post_type_object->_edit_link . $action, $post->ID) ), $post->ID, $context );
918}
919
920/**
921 * Display edit post link for post.
922 *
923 * @since 1.0.0
924 *
925 * @param string $link Optional. Anchor text.
926 * @param string $before Optional. Display before edit link.
927 * @param string $after Optional. Display after edit link.
928 * @param int $id Optional. Post ID.
929 */
930function edit_post_link( $link = null, $before = '', $after = '', $id = 0 ) {
931        if ( !$post = &get_post( $id ) )
932                return;
933
934        if ( !$url = get_edit_post_link( $post->ID ) )
935                return;
936
937        if ( null === $link )
938                $link = __('Edit This');
939
940        $post_type_obj = get_post_type_object( $post->post_type );
941        $link = '<a class="post-edit-link" href="' . $url . '" title="' . esc_attr( $post_type_obj->labels->edit_item ) . '">' . $link . '</a>';
942        echo $before . apply_filters( 'edit_post_link', $link, $post->ID ) . $after;
943}
944
945/**
946 * Retrieve delete posts link for post.
947 *
948 * Can be used within the WordPress loop or outside of it, with any post type.
949 *
950 * @since 2.9.0
951 *
952 * @param int $id Optional. Post ID.
953 * @param string $deprecated Not used.
954 * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
955 * @return string
956 */
957function get_delete_post_link( $id = 0, $deprecated = '', $force_delete = false ) {
958        if ( ! empty( $deprecated ) )
959                _deprecated_argument( __FUNCTION__, '3.0' );
960
961        if ( !$post = &get_post( $id ) )
962                return;
963
964        $post_type_object = get_post_type_object( $post->post_type );
965        if ( !$post_type_object )
966                return;
967
968        if ( !current_user_can( $post_type_object->cap->delete_post, $post->ID ) )
969                return;
970
971        $action = ( $force_delete || !EMPTY_TRASH_DAYS ) ? 'delete' : 'trash';
972
973        $delete_link = add_query_arg( 'action', $action, admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) );
974
975        return apply_filters( 'get_delete_post_link', wp_nonce_url( $delete_link, "$action-{$post->post_type}_{$post->ID}" ), $post->ID, $force_delete );
976}
977
978/**
979 * Retrieve edit comment link.
980 *
981 * @since 2.3.0
982 *
983 * @param int $comment_id Optional. Comment ID.
984 * @return string
985 */
986function get_edit_comment_link( $comment_id = 0 ) {
987        $comment = &get_comment( $comment_id );
988
989        if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
990                return;
991
992        $location = admin_url('comment.php?action=editcomment&amp;c=') . $comment->comment_ID;
993        return apply_filters( 'get_edit_comment_link', $location );
994}
995
996/**
997 * Display or retrieve edit comment link with formatting.
998 *
999 * @since 1.0.0
1000 *
1001 * @param string $link Optional. Anchor text.
1002 * @param string $before Optional. Display before edit link.
1003 * @param string $after Optional. Display after edit link.
1004 * @return string|null HTML content, if $echo is set to false.
1005 */
1006function edit_comment_link( $link = null, $before = '', $after = '' ) {
1007        global $comment;
1008
1009        if ( !current_user_can( 'edit_comment', $comment->comment_ID ) )
1010                return;
1011
1012        if ( null === $link )
1013                $link = __('Edit This');
1014
1015        $link = '<a class="comment-edit-link" href="' . get_edit_comment_link( $comment->comment_ID ) . '" title="' . esc_attr__( 'Edit comment' ) . '">' . $link . '</a>';
1016        echo $before . apply_filters( 'edit_comment_link', $link, $comment->comment_ID ) . $after;
1017}
1018
1019/**
1020 * Display edit bookmark (literally a URL external to blog) link.
1021 *
1022 * @since 2.7.0
1023 *
1024 * @param int $link Optional. Bookmark ID.
1025 * @return string
1026 */
1027function get_edit_bookmark_link( $link = 0 ) {
1028        $link = get_bookmark( $link );
1029
1030        if ( !current_user_can('manage_links') )
1031                return;
1032
1033        $location = admin_url('link.php?action=edit&amp;link_id=') . $link->link_id;
1034        return apply_filters( 'get_edit_bookmark_link', $location, $link->link_id );
1035}
1036
1037/**
1038 * Display edit bookmark (literally a URL external to blog) link anchor content.
1039 *
1040 * @since 2.7.0
1041 *
1042 * @param string $link Optional. Anchor text.
1043 * @param string $before Optional. Display before edit link.
1044 * @param string $after Optional. Display after edit link.
1045 * @param int $bookmark Optional. Bookmark ID.
1046 */
1047function edit_bookmark_link( $link = '', $before = '', $after = '', $bookmark = null ) {
1048        $bookmark = get_bookmark($bookmark);
1049
1050        if ( !current_user_can('manage_links') )
1051                return;
1052
1053        if ( empty($link) )
1054                $link = __('Edit This');
1055
1056        $link = '<a href="' . get_edit_bookmark_link( $bookmark ) . '" title="' . esc_attr__( 'Edit Link' ) . '">' . $link . '</a>';
1057        echo $before . apply_filters( 'edit_bookmark_link', $link, $bookmark->link_id ) . $after;
1058}
1059
1060// Navigation links
1061
1062/**
1063 * Retrieve previous post that is adjacent to current post.
1064 *
1065 * @since 1.5.0
1066 *
1067 * @param bool $in_same_cat Optional. Whether post should be in same category.
1068 * @param string $excluded_categories Optional. Excluded categories IDs.
1069 * @return mixed Post object if successful. Null if global $post is not set. Empty string if no corresponding post exists.
1070 */
1071function get_previous_post($in_same_cat = false, $excluded_categories = '') {
1072        return get_adjacent_post($in_same_cat, $excluded_categories);
1073}
1074
1075/**
1076 * Retrieve next post that is adjacent to current post.
1077 *
1078 * @since 1.5.0
1079 *
1080 * @param bool $in_same_cat Optional. Whether post should be in same category.
1081 * @param string $excluded_categories Optional. Excluded categories IDs.
1082 * @return mixed Post object if successful. Null if global $post is not set. Empty string if no corresponding post exists.
1083 */
1084function get_next_post($in_same_cat = false, $excluded_categories = '') {
1085        return get_adjacent_post($in_same_cat, $excluded_categories, false);
1086}
1087
1088/**
1089 * Retrieve adjacent post.
1090 *
1091 * Can either be next or previous post.
1092 *
1093 * @since 2.5.0
1094 *
1095 * @param bool $in_same_cat Optional. Whether post should be in same category.
1096 * @param string $excluded_categories Optional. Excluded categories IDs.
1097 * @param bool $previous Optional. Whether to retrieve previous post.
1098 * @return mixed Post object if successful. Null if global $post is not set. Empty string if no corresponding post exists.
1099 */
1100function get_adjacent_post($in_same_cat = false, $excluded_categories = '', $previous = true) {
1101        global $post, $wpdb;
1102
1103        if ( empty( $post ) )
1104                return null;
1105
1106        $current_post_date = $post->post_date;
1107
1108        $join = '';
1109        $posts_in_ex_cats_sql = '';
1110        if ( $in_same_cat || !empty($excluded_categories) ) {
1111                $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";
1112
1113                if ( $in_same_cat ) {
1114                        $cat_array = wp_get_object_terms($post->ID, 'category', array('fields' => 'ids'));
1115                        $join .= " AND tt.taxonomy = 'category' AND tt.term_id IN (" . implode(',', $cat_array) . ")";
1116                }
1117
1118                $posts_in_ex_cats_sql = "AND tt.taxonomy = 'category'";
1119                if ( !empty($excluded_categories) ) {
1120                        $excluded_categories = array_map('intval', explode(' and ', $excluded_categories));
1121                        if ( !empty($cat_array) ) {
1122                                $excluded_categories = array_diff($excluded_categories, $cat_array);
1123                                $posts_in_ex_cats_sql = '';
1124                        }
1125
1126                        if ( !empty($excluded_categories) ) {
1127                                $posts_in_ex_cats_sql = " AND tt.taxonomy = 'category' AND tt.term_id NOT IN (" . implode($excluded_categories, ',') . ')';
1128                        }
1129                }
1130        }
1131
1132        $adjacent = $previous ? 'previous' : 'next';
1133        $dateCmp = 'BETWEEN ' . ($previous ? "%s AND '9999-12-31 00:00:00'" : "'1970-01-01 00:00:00' AND %s" ); // javert03 changed from $op = $previous ? '<' : '>';
1134        $order = $previous ? 'DESC' : 'ASC';
1135
1136        $join  = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_cat, $excluded_categories );
1137        $where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare("WHERE p.post_date $dateCmp AND p.post_type = %s AND p.post_status = 'publish' $posts_in_ex_cats_sql", $current_post_date, $post->post_type), $in_same_cat, $excluded_categories ); // javert03 changed "$op %s" to $dateCmp
1138        $sort  = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1" );
1139
1140        $query = "SELECT p.ID, p.post_title, p.post_status, p.post_date FROM $wpdb->posts AS p $join $where $sort"; // javert03 changed from p.*
1141        $query_key = 'adjacent_post_' . md5($query);
1142        $result = wp_cache_get($query_key, 'counts');
1143        if ( false !== $result )
1144                return $result;
1145
1146        $result = $wpdb->get_row("SELECT p.* FROM $wpdb->posts AS p $join $where $sort");
1147        if ( null === $result )
1148                $result = '';
1149
1150        wp_cache_set($query_key, $result, 'counts');
1151        return $result;
1152}
1153
1154/**
1155 * Get adjacent post relational link.
1156 *
1157 * Can either be next or previous post relational link.
1158 *
1159 * @since 2.8.0
1160 *
1161 * @param string $title Optional. Link title format.
1162 * @param bool $in_same_cat Optional. Whether link should be in same category.
1163 * @param string $excluded_categories Optional. Excluded categories IDs.
1164 * @param bool $previous Optional, default is true. Whether display link to previous post.
1165 * @return string
1166 */
1167function get_adjacent_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '', $previous = true) {
1168        if ( $previous && is_attachment() && is_object( $GLOBALS['post'] ) )
1169                $post = & get_post($GLOBALS['post']->post_parent);
1170        else
1171                $post = get_adjacent_post($in_same_cat,$excluded_categories,$previous);
1172
1173        if ( empty($post) )
1174                return;
1175
1176        if ( empty($post->post_title) )
1177                $post->post_title = $previous ? __('Previous Post') : __('Next Post');
1178
1179        $date = mysql2date(get_option('date_format'), $post->post_date);
1180
1181        $title = str_replace('%title', $post->post_title, $title);
1182        $title = str_replace('%date', $date, $title);
1183        $title = apply_filters('the_title', $title, $post->ID);
1184
1185        $link = $previous ? "<link rel='prev' title='" : "<link rel='next' title='";
1186        $link .= esc_attr( $title );
1187        $link .= "' href='" . get_permalink($post) . "' />\n";
1188
1189        $adjacent = $previous ? 'previous' : 'next';
1190        return apply_filters( "{$adjacent}_post_rel_link", $link );
1191}
1192
1193/**
1194 * Display relational links for the posts adjacent to the current post.
1195 *
1196 * @since 2.8.0
1197 *
1198 * @param string $title Optional. Link title format.
1199 * @param bool $in_same_cat Optional. Whether link should be in same category.
1200 * @param string $excluded_categories Optional. Excluded categories IDs.
1201 */
1202function adjacent_posts_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
1203        echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', true);
1204        echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', false);
1205}
1206
1207/**
1208 * Display relational links for the posts adjacent to the current post for single post pages.
1209 *
1210 * This is meant to be attached to actions like 'wp_head'.  Do not call this directly in plugins or theme templates.
1211 * @since 3.0.0
1212 *
1213 */
1214function adjacent_posts_rel_link_wp_head() {
1215        if ( !is_singular() || is_attachment() )
1216                return;
1217        adjacent_posts_rel_link();
1218}
1219
1220/**
1221 * Display relational link for the next post adjacent to the current post.
1222 *
1223 * @since 2.8.0
1224 *
1225 * @param string $title Optional. Link title format.
1226 * @param bool $in_same_cat Optional. Whether link should be in same category.
1227 * @param string $excluded_categories Optional. Excluded categories IDs.
1228 */
1229function next_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
1230        echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', false);
1231}
1232
1233/**
1234 * Display relational link for the previous post adjacent to the current post.
1235 *
1236 * @since 2.8.0
1237 *
1238 * @param string $title Optional. Link title format.
1239 * @param bool $in_same_cat Optional. Whether link should be in same category.
1240 * @param string $excluded_categories Optional. Excluded categories IDs.
1241 */
1242function prev_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
1243        echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', true);
1244}
1245
1246/**
1247 * Retrieve boundary post.
1248 *
1249 * Boundary being either the first or last post by publish date within the constraints specified
1250 * by in same category or excluded categories.
1251 *
1252 * @since 2.8.0
1253 *
1254 * @param bool $in_same_cat Optional. Whether returned post should be in same category.
1255 * @param string $excluded_categories Optional. Excluded categories IDs.
1256 * @param bool $start Optional. Whether to retrieve first or last post.
1257 * @return object
1258 */
1259function get_boundary_post($in_same_cat = false, $excluded_categories = '', $start = true) {
1260        global $post;
1261
1262        if ( empty($post) || !is_single() || is_attachment() )
1263                return null;
1264
1265        $cat_array = array();
1266        $excluded_categories = array();
1267        if ( !empty($in_same_cat) || !empty($excluded_categories) ) {
1268                if ( !empty($in_same_cat) ) {
1269                        $cat_array = wp_get_object_terms($post->ID, 'category', array('fields' => 'ids'));
1270                }
1271
1272                if ( !empty($excluded_categories) ) {
1273                        $excluded_categories = array_map('intval', explode(',', $excluded_categories));
1274
1275                        if ( !empty($cat_array) )
1276                                $excluded_categories = array_diff($excluded_categories, $cat_array);
1277
1278                        $inverse_cats = array();
1279                        foreach ( $excluded_categories as $excluded_category)
1280                                $inverse_cats[] = $excluded_category * -1;
1281                        $excluded_categories = $inverse_cats;
1282                }
1283        }
1284
1285        $categories = implode(',', array_merge($cat_array, $excluded_categories) );
1286
1287        $order = $start ? 'ASC' : 'DESC';
1288
1289        return get_posts( array('numberposts' => 1, 'category' => $categories, 'order' => $order, 'update_post_term_cache' => false, 'update_post_meta_cache' => false) );
1290}
1291
1292/**
1293 * Get boundary post relational link.
1294 *
1295 * Can either be start or end post relational link.
1296 *
1297 * @since 2.8.0
1298 *
1299 * @param string $title Optional. Link title format.
1300 * @param bool $in_same_cat Optional. Whether link should be in same category.
1301 * @param string $excluded_categories Optional. Excluded categories IDs.
1302 * @param bool $start Optional, default is true. Whether display link to first or last post.
1303 * @return string
1304 */
1305function get_boundary_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '', $start = true) {
1306        $posts = get_boundary_post($in_same_cat, $excluded_categories, $start);
1307        // If there is no post stop.
1308        if ( empty($posts) )
1309                return;
1310
1311        // Even though we limited get_posts to return only 1 item it still returns an array of objects.
1312        $post = $posts[0];
1313
1314        if ( empty($post->post_title) )
1315                $post->post_title = $start ? __('First Post') : __('Last Post');
1316
1317        $date = mysql2date(get_option('date_format'), $post->post_date);
1318
1319        $title = str_replace('%title', $post->post_title, $title);
1320        $title = str_replace('%date', $date, $title);
1321        $title = apply_filters('the_title', $title, $post->ID);
1322
1323        $link = $start ? "<link rel='start' title='" : "<link rel='end' title='";
1324        $link .= esc_attr($title);
1325        $link .= "' href='" . get_permalink($post) . "' />\n";
1326
1327        $boundary = $start ? 'start' : 'end';
1328        return apply_filters( "{$boundary}_post_rel_link", $link );
1329}
1330
1331/**
1332 * Display relational link for the first post.
1333 *
1334 * @since 2.8.0
1335 *
1336 * @param string $title Optional. Link title format.
1337 * @param bool $in_same_cat Optional. Whether link should be in same category.
1338 * @param string $excluded_categories Optional. Excluded categories IDs.
1339 */
1340function start_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
1341        echo get_boundary_post_rel_link($title, $in_same_cat, $excluded_categories, true);
1342}
1343
1344/**
1345 * Get site index relational link.
1346 *
1347 * @since 2.8.0
1348 *
1349 * @return string
1350 */
1351function get_index_rel_link() {
1352        $link = "<link rel='index' title='" . esc_attr( get_bloginfo( 'name', 'display' ) ) . "' href='" . esc_url( user_trailingslashit( get_bloginfo( 'url', 'display' ) ) ) . "' />\n";
1353        return apply_filters( "index_rel_link", $link );
1354}
1355
1356/**
1357 * Display relational link for the site index.
1358 *
1359 * @since 2.8.0
1360 */
1361function index_rel_link() {
1362        echo get_index_rel_link();
1363}
1364
1365/**
1366 * Get parent post relational link.
1367 *
1368 * @since 2.8.0
1369 *
1370 * @param string $title Optional. Link title format.
1371 * @return string
1372 */
1373function get_parent_post_rel_link($title = '%title') {
1374        if ( ! empty( $GLOBALS['post'] ) && ! empty( $GLOBALS['post']->post_parent ) )
1375                $post = & get_post($GLOBALS['post']->post_parent);
1376
1377        if ( empty($post) )
1378                return;
1379
1380        $date = mysql2date(get_option('date_format'), $post->post_date);
1381
1382        $title = str_replace('%title', $post->post_title, $title);
1383        $title = str_replace('%date', $date, $title);
1384        $title = apply_filters('the_title', $title, $post->ID);
1385
1386        $link = "<link rel='up' title='";
1387        $link .= esc_attr( $title );
1388        $link .= "' href='" . get_permalink($post) . "' />\n";
1389
1390        return apply_filters( "parent_post_rel_link", $link );
1391}
1392
1393/**
1394 * Display relational link for parent item
1395 *
1396 * @since 2.8.0
1397 */
1398function parent_post_rel_link($title = '%title') {
1399        echo get_parent_post_rel_link($title);
1400}
1401
1402/**
1403 * Display previous post link that is adjacent to the current post.
1404 *
1405 * @since 1.5.0
1406 *
1407 * @param string $format Optional. Link anchor format.
1408 * @param string $link Optional. Link permalink format.
1409 * @param bool $in_same_cat Optional. Whether link should be in same category.
1410 * @param string $excluded_categories Optional. Excluded categories IDs.
1411 */
1412function previous_post_link($format='&laquo; %link', $link='%title', $in_same_cat = false, $excluded_categories = '') {
1413        adjacent_post_link($format, $link, $in_same_cat, $excluded_categories, true);
1414}
1415
1416/**
1417 * Display next post link that is adjacent to the current post.
1418 *
1419 * @since 1.5.0
1420 *
1421 * @param string $format Optional. Link anchor format.
1422 * @param string $link Optional. Link permalink format.
1423 * @param bool $in_same_cat Optional. Whether link should be in same category.
1424 * @param string $excluded_categories Optional. Excluded categories IDs.
1425 */
1426function next_post_link($format='%link &raquo;', $link='%title', $in_same_cat = false, $excluded_categories = '') {
1427        adjacent_post_link($format, $link, $in_same_cat, $excluded_categories, false);
1428}
1429
1430/**
1431 * Display adjacent post link.
1432 *
1433 * Can be either next post link or previous.
1434 *
1435 * @since 2.5.0
1436 *
1437 * @param string $format Link anchor format.
1438 * @param string $link Link permalink format.
1439 * @param bool $in_same_cat Optional. Whether link should be in same category.
1440 * @param string $excluded_categories Optional. Excluded categories IDs.
1441 * @param bool $previous Optional, default is true. Whether display link to previous post.
1442 */
1443function adjacent_post_link($format, $link, $in_same_cat = false, $excluded_categories = '', $previous = true) {
1444        if ( $previous && is_attachment() )
1445                $post = & get_post($GLOBALS['post']->post_parent);
1446        else
1447                $post = get_adjacent_post($in_same_cat, $excluded_categories, $previous);
1448
1449        if ( !$post )
1450                return;
1451
1452        $title = $post->post_title;
1453
1454        if ( empty($post->post_title) )
1455                $title = $previous ? __('Previous Post') : __('Next Post');
1456
1457        $title = apply_filters('the_title', $title, $post->ID);
1458        $date = mysql2date(get_option('date_format'), $post->post_date);
1459        $rel = $previous ? 'prev' : 'next';
1460
1461        $string = '<a href="'.get_permalink($post).'" rel="'.$rel.'">';
1462        $link = str_replace('%title', $title, $link);
1463        $link = str_replace('%date', $date, $link);
1464        $link = $string . $link . '</a>';
1465
1466        $format = str_replace('%link', $link, $format);
1467
1468        $adjacent = $previous ? 'previous' : 'next';
1469        echo apply_filters( "{$adjacent}_post_link", $format, $link );
1470}
1471
1472/**
1473 * Retrieve get links for page numbers.
1474 *
1475 * @since 1.5.0
1476 *
1477 * @param int $pagenum Optional. Page ID.
1478 * @return string
1479 */
1480function get_pagenum_link($pagenum = 1) {
1481        global $wp_rewrite;
1482
1483        $pagenum = (int) $pagenum;
1484
1485        $request = remove_query_arg( 'paged' );
1486
1487        $home_root = parse_url(home_url());
1488        $home_root = ( isset($home_root['path']) ) ? $home_root['path'] : '';
1489        $home_root = preg_quote( trailingslashit( $home_root ), '|' );
1490
1491        $request = preg_replace('|^'. $home_root . '|', '', $request);
1492        $request = preg_replace('|^/+|', '', $request);
1493
1494        if ( !$wp_rewrite->using_permalinks() || is_admin() ) {
1495                $base = trailingslashit( get_bloginfo( 'url' ) );
1496
1497                if ( $pagenum > 1 ) {
1498                        $result = add_query_arg( 'paged', $pagenum, $base . $request );
1499                } else {
1500                        $result = $base . $request;
1501                }
1502        } else {
1503                $qs_regex = '|\?.*?$|';
1504                preg_match( $qs_regex, $request, $qs_match );
1505
1506                if ( !empty( $qs_match[0] ) ) {
1507                        $query_string = $qs_match[0];
1508                        $request = preg_replace( $qs_regex, '', $request );
1509                } else {
1510                        $query_string = '';
1511                }
1512
1513                $request = preg_replace( "|$wp_rewrite->pagination_base/\d+/?$|", '', $request);
1514                $request = preg_replace( '|^index\.php|', '', $request);
1515                $request = ltrim($request, '/');
1516
1517                $base = trailingslashit( get_bloginfo( 'url' ) );
1518
1519                if ( $wp_rewrite->using_index_permalinks() && ( $pagenum > 1 || '' != $request ) )
1520                        $base .= 'index.php/';
1521
1522                if ( $pagenum > 1 ) {
1523                        $request = ( ( !empty( $request ) ) ? trailingslashit( $request ) : $request ) . user_trailingslashit( $wp_rewrite->pagination_base . "/" . $pagenum, 'paged' );
1524                }
1525
1526                $result = $base . $request . $query_string;
1527        }
1528
1529        $result = apply_filters('get_pagenum_link', $result);
1530
1531        return $result;
1532}
1533
1534/**
1535 * Retrieve next posts pages link.
1536 *
1537 * Backported from 2.1.3 to 2.0.10.
1538 *
1539 * @since 2.0.10
1540 *
1541 * @param int $max_page Optional. Max pages.
1542 * @return string
1543 */
1544function get_next_posts_page_link($max_page = 0) {
1545        global $paged;
1546
1547        if ( !is_single() ) {
1548                if ( !$paged )
1549                        $paged = 1;
1550                $nextpage = intval($paged) + 1;
1551                if ( !$max_page || $max_page >= $nextpage )
1552                        return get_pagenum_link($nextpage);
1553        }
1554}
1555
1556/**
1557 * Display or return the next posts pages link.
1558 *
1559 * @since 0.71
1560 *
1561 * @param int $max_page Optional. Max pages.
1562 * @param boolean $echo Optional. Echo or return;
1563 */
1564function next_posts( $max_page = 0, $echo = true ) {
1565        $output = esc_url( get_next_posts_page_link( $max_page ) );
1566
1567        if ( $echo )
1568                echo $output;
1569        else
1570                return $output;
1571}
1572
1573/**
1574 * Return the next posts pages link.
1575 *
1576 * @since 2.7.0
1577 *
1578 * @param string $label Content for link text.
1579 * @param int $max_page Optional. Max pages.
1580 * @return string|null
1581 */
1582function get_next_posts_link( $label = null, $max_page = 0 ) {
1583        global $paged, $wp_query;
1584
1585        if ( !$max_page )
1586                $max_page = $wp_query->max_num_pages;
1587
1588        if ( !$paged )
1589                $paged = 1;
1590
1591        $nextpage = intval($paged) + 1;
1592
1593        if ( null === $label )
1594                $label = __( 'Next Page &raquo;' );
1595
1596        if ( !is_single() && ( $nextpage <= $max_page ) ) {
1597                $attr = apply_filters( 'next_posts_link_attributes', '' );
1598                return '<a href="' . next_posts( $max_page, false ) . "\" $attr>" . preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $label) . '</a>';
1599        }
1600}
1601
1602/**
1603 * Display the next posts pages link.
1604 *
1605 * @since 0.71
1606 * @uses get_next_posts_link()
1607 *
1608 * @param string $label Content for link text.
1609 * @param int $max_page Optional. Max pages.
1610 */
1611function next_posts_link( $label = null, $max_page = 0 ) {
1612        echo get_next_posts_link( $label, $max_page );
1613}
1614
1615/**
1616 * Retrieve previous post pages link.
1617 *
1618 * Will only return string, if not on a single page or post.
1619 *
1620 * Backported to 2.0.10 from 2.1.3.
1621 *
1622 * @since 2.0.10
1623 *
1624 * @return string|null
1625 */
1626function get_previous_posts_page_link() {
1627        global $paged;
1628
1629        if ( !is_single() ) {
1630                $nextpage = intval($paged) - 1;
1631                if ( $nextpage < 1 )
1632                        $nextpage = 1;
1633                return get_pagenum_link($nextpage);
1634        }
1635}
1636
1637/**
1638 * Display or return the previous posts pages link.
1639 *
1640 * @since 0.71
1641 *
1642 * @param boolean $echo Optional. Echo or return;
1643 */
1644function previous_posts( $echo = true ) {
1645        $output = esc_url( get_previous_posts_page_link() );
1646
1647        if ( $echo )
1648                echo $output;
1649        else
1650                return $output;
1651}
1652
1653/**
1654 * Return the previous posts pages link.
1655 *
1656 * @since 2.7.0
1657 *
1658 * @param string $label Optional. Previous page link text.
1659 * @return string|null
1660 */
1661function get_previous_posts_link( $label = null ) {
1662        global $paged;
1663
1664        if ( null === $label )
1665                $label = __( '&laquo; Previous Page' );
1666
1667        if ( !is_single() && $paged > 1 ) {
1668                $attr = apply_filters( 'previous_posts_link_attributes', '' );
1669                return '<a href="' . previous_posts( false ) . "\" $attr>". preg_replace( '/&([^#])(?![a-z]{1,8};)/', '&#038;$1', $label ) .'</a>';
1670        }
1671}
1672
1673/**
1674 * Display the previous posts page link.
1675 *
1676 * @since 0.71
1677 * @uses get_previous_posts_link()
1678 *
1679 * @param string $label Optional. Previous page link text.
1680 */
1681function previous_posts_link( $label = null ) {
1682        echo get_previous_posts_link( $label );
1683}
1684
1685/**
1686 * Return post pages link navigation for previous and next pages.
1687 *
1688 * @since 2.8
1689 *
1690 * @param string|array $args Optional args.
1691 * @return string The posts link navigation.
1692 */
1693function get_posts_nav_link( $args = array() ) {
1694        global $wp_query;
1695
1696        $return = '';
1697
1698        if ( !is_singular() ) {
1699                $defaults = array(
1700                        'sep' => ' &#8212; ',
1701                        'prelabel' => __('&laquo; Previous Page'),
1702                        'nxtlabel' => __('Next Page &raquo;'),
1703                );
1704                $args = wp_parse_args( $args, $defaults );
1705
1706                $max_num_pages = $wp_query->max_num_pages;
1707                $paged = get_query_var('paged');
1708
1709                //only have sep if there's both prev and next results
1710                if ($paged < 2 || $paged >= $max_num_pages) {
1711                        $args['sep'] = '';
1712                }
1713
1714                if ( $max_num_pages > 1 ) {
1715                        $return = get_previous_posts_link($args['prelabel']);
1716                        $return .= preg_replace('/&([^#])(?![a-z]{1,8};)/i', '&#038;$1', $args['sep']);
1717                        $return .= get_next_posts_link($args['nxtlabel']);
1718                }
1719        }
1720        return $return;
1721
1722}
1723
1724/**
1725 * Display post pages link navigation for previous and next pages.
1726 *
1727 * @since 0.71
1728 *
1729 * @param string $sep Optional. Separator for posts navigation links.
1730 * @param string $prelabel Optional. Label for previous pages.
1731 * @param string $nxtlabel Optional Label for next pages.
1732 */
1733function posts_nav_link( $sep = '', $prelabel = '', $nxtlabel = '' ) {
1734        $args = array_filter( compact('sep', 'prelabel', 'nxtlabel') );
1735        echo get_posts_nav_link($args);
1736}
1737
1738/**
1739 * Retrieve page numbers links.
1740 *
1741 * @since 2.7.0
1742 *
1743 * @param int $pagenum Optional. Page number.
1744 * @return string
1745 */
1746function get_comments_pagenum_link( $pagenum = 1, $max_page = 0 ) {
1747        global $post, $wp_rewrite;
1748
1749        $pagenum = (int) $pagenum;
1750
1751        $result = get_permalink( $post->ID );
1752
1753        if ( 'newest' == get_option('default_comments_page') ) {
1754                if ( $pagenum != $max_page ) {
1755                        if ( $wp_rewrite->using_permalinks() )
1756                                $result = user_trailingslashit( trailingslashit($result) . 'comment-page-' . $pagenum, 'commentpaged');
1757                        else
1758                                $result = add_query_arg( 'cpage', $pagenum, $result );
1759                }
1760        } elseif ( $pagenum > 1 ) {
1761                if ( $wp_rewrite->using_permalinks() )
1762                        $result = user_trailingslashit( trailingslashit($result) . 'comment-page-' . $pagenum, 'commentpaged');
1763                else
1764                        $result = add_query_arg( 'cpage', $pagenum, $result );
1765        }
1766
1767        $result .= '#comments';
1768
1769        $result = apply_filters('get_comments_pagenum_link', $result);
1770
1771        return $result;
1772}
1773
1774/**
1775 * Return the link to next comments pages.
1776 *
1777 * @since 2.7.1
1778 *
1779 * @param string $label Optional. Label for link text.
1780 * @param int $max_page Optional. Max page.
1781 * @return string|null
1782 */
1783function get_next_comments_link( $label = '', $max_page = 0 ) {
1784        global $wp_query;
1785
1786        if ( !is_singular() || !get_option('page_comments') )
1787                return;
1788
1789        $page = get_query_var('cpage');
1790
1791        $nextpage = intval($page) + 1;
1792
1793        if ( empty($max_page) )
1794                $max_page = $wp_query->max_num_comment_pages;
1795
1796        if ( empty($max_page) )
1797                $max_page = get_comment_pages_count();
1798
1799        if ( $nextpage > $max_page )
1800                return;
1801
1802        if ( empty($label) )
1803                $label = __('Newer Comments &raquo;');
1804
1805        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>';
1806}
1807
1808/**
1809 * Display the link to next comments pages.
1810 *
1811 * @since 2.7.0
1812 *
1813 * @param string $label Optional. Label for link text.
1814 * @param int $max_page Optional. Max page.
1815 */
1816function next_comments_link( $label = '', $max_page = 0 ) {
1817        echo get_next_comments_link( $label, $max_page );
1818}
1819
1820/**
1821 * Return the previous comments page link.
1822 *
1823 * @since 2.7.1
1824 *
1825 * @param string $label Optional. Label for comments link text.
1826 * @return string|null
1827 */
1828function get_previous_comments_link( $label = '' ) {
1829        if ( !is_singular() || !get_option('page_comments') )
1830                return;
1831
1832        $page = get_query_var('cpage');
1833
1834        if ( intval($page) <= 1 )
1835                return;
1836
1837        $prevpage = intval($page) - 1;
1838
1839        if ( empty($label) )
1840                $label = __('&laquo; Older Comments');
1841
1842        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>';
1843}
1844
1845/**
1846 * Display the previous comments page link.
1847 *
1848 * @since 2.7.0
1849 *
1850 * @param string $label Optional. Label for comments link text.
1851 */
1852function previous_comments_link( $label = '' ) {
1853        echo get_previous_comments_link( $label );
1854}
1855
1856/**
1857 * Create pagination links for the comments on the current post.
1858 *
1859 * @see paginate_links()
1860 * @since 2.7.0
1861 *
1862 * @param string|array $args Optional args. See paginate_links.
1863 * @return string Markup for pagination links.
1864*/
1865function paginate_comments_links($args = array()) {
1866        global $wp_rewrite;
1867
1868        if ( !is_singular() || !get_option('page_comments') )
1869                return;
1870
1871        $page = get_query_var('cpage');
1872        if ( !$page )
1873                $page = 1;
1874        $max_page = get_comment_pages_count();
1875        $defaults = array(
1876                'base' => add_query_arg( 'cpage', '%#%' ),
1877                'format' => '',
1878                'total' => $max_page,
1879                'current' => $page,
1880                'echo' => true,
1881                'add_fragment' => '#comments'
1882        );
1883        if ( $wp_rewrite->using_permalinks() )
1884                $defaults['base'] = user_trailingslashit(trailingslashit(get_permalink()) . 'comment-page-%#%', 'commentpaged');
1885
1886        $args = wp_parse_args( $args, $defaults );
1887        $page_links = paginate_links( $args );
1888
1889        if ( $args['echo'] )
1890                echo $page_links;
1891        else
1892                return $page_links;
1893}
1894
1895/**
1896 * Retrieve shortcut link.
1897 *
1898 * Use this in 'a' element 'href' attribute.
1899 *
1900 * @since 2.6.0
1901 *
1902 * @return string
1903 */
1904function get_shortcut_link() {
1905        $link = "javascript:
1906                        var d=document,
1907                        w=window,
1908                        e=w.getSelection,
1909                        k=d.getSelection,
1910                        x=d.selection,
1911                        s=(e?e():(k)?k():(x?x.createRange().text:0)),
1912                        f='" . admin_url('press-this.php') . "',
1913                        l=d.location,
1914                        e=encodeURIComponent,
1915                        u=f+'?u='+e(l.href)+'&t='+e(d.title)+'&s='+e(s)+'&v=4';
1916                        a=function(){if(!w.open(u,'t','toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570'))l.href=u;};
1917                        if (/Firefox/.test(navigator.userAgent)) setTimeout(a, 0); else a();
1918                        void(0)";
1919
1920        $link = str_replace(array("\r", "\n", "\t"),  '', $link);
1921
1922        return apply_filters('shortcut_link', $link);
1923}
1924
1925/**
1926 * Retrieve the home url for the current site.
1927 *
1928 * Returns the 'home' option with the appropriate protocol,  'https' if
1929 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
1930 * overridden.
1931 *
1932 * @package WordPress
1933 * @since 3.0.0
1934 *
1935 * @uses get_home_url()
1936 *
1937 * @param  string $path   (optional) Path relative to the home url.
1938 * @param  string $scheme (optional) Scheme to give the home url context. Currently 'http','https'
1939 * @return string Home url link with optional path appended.
1940*/
1941function home_url( $path = '', $scheme = null ) {
1942        return get_home_url(null, $path, $scheme);
1943}
1944
1945/**
1946 * Retrieve the home url for a given site.
1947 *
1948 * Returns the 'home' option with the appropriate protocol,  'https' if
1949 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
1950 * overridden.
1951 *
1952 * @package WordPress
1953 * @since 3.0.0
1954 *
1955 * @param  int $blog_id   (optional) Blog ID. Defaults to current blog.
1956 * @param  string $path   (optional) Path relative to the home url.
1957 * @param  string $scheme (optional) Scheme to give the home url context. Currently 'http','https'
1958 * @return string Home url link with optional path appended.
1959*/
1960function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
1961        $orig_scheme = $scheme;
1962
1963        if ( !in_array( $scheme, array( 'http', 'https' ) ) )
1964                $scheme = is_ssl() && !is_admin() ? 'https' : 'http';
1965
1966        if ( empty( $blog_id ) || !is_multisite() )
1967                $url = get_option( 'home' );
1968        else
1969                $url = get_blog_option( $blog_id, 'home' );
1970
1971        if ( 'http' != $scheme )
1972                $url = str_replace( 'http://', "$scheme://", $url );
1973
1974        if ( !empty( $path ) && is_string( $path ) && strpos( $path, '..' ) === false )
1975                $url .= '/' . ltrim( $path, '/' );
1976
1977        return apply_filters( 'home_url', $url, $path, $orig_scheme, $blog_id );
1978}
1979
1980/**
1981 * Retrieve the site url for the current site.
1982 *
1983 * Returns the 'site_url' option with the appropriate protocol,  'https' if
1984 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
1985 * overridden.
1986 *
1987 * @package WordPress
1988 * @since 2.6.0
1989 *
1990 * @uses get_site_url()
1991 *
1992 * @param string $path Optional. Path relative to the site url.
1993 * @param string $scheme Optional. Scheme to give the site url context. Currently 'http','https', 'login', 'login_post', or 'admin'.
1994 * @return string Site url link with optional path appended.
1995*/
1996function site_url( $path = '', $scheme = null ) {
1997        return get_site_url(null, $path, $scheme);
1998}
1999
2000/**
2001 * Retrieve the site url for a given site.
2002 *
2003 * Returns the 'site_url' option with the appropriate protocol,  'https' if
2004 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
2005 * overridden.
2006 *
2007 * @package WordPress
2008 * @since 3.0.0
2009 *
2010 * @param int $blog_id (optional) Blog ID. Defaults to current blog.
2011 * @param string $path Optional. Path relative to the site url.
2012 * @param string $scheme Optional. Scheme to give the site url context. Currently 'http','https', 'login', 'login_post', or 'admin'.
2013 * @return string Site url link with optional path appended.
2014*/
2015function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
2016        // should the list of allowed schemes be maintained elsewhere?
2017        $orig_scheme = $scheme;
2018        if ( !in_array( $scheme, array( 'http', 'https' ) ) ) {
2019                if ( ( 'login_post' == $scheme || 'rpc' == $scheme ) && ( force_ssl_login() || force_ssl_admin() ) )
2020                        $scheme = 'https';
2021                elseif ( ( 'login' == $scheme ) && force_ssl_admin() )
2022                        $scheme = 'https';
2023                elseif ( ( 'admin' == $scheme ) && force_ssl_admin() )
2024                        $scheme = 'https';
2025                else
2026                        $scheme = ( is_ssl() ? 'https' : 'http' );
2027        }
2028
2029        if ( empty( $blog_id ) || !is_multisite() )
2030                $url = get_option( 'siteurl' );
2031        else
2032                $url = get_blog_option( $blog_id, 'siteurl' );
2033
2034        if ( 'http' != $scheme )
2035                $url = str_replace( 'http://', "{$scheme}://", $url );
2036
2037        if ( !empty( $path ) && is_string( $path ) && strpos( $path, '..' ) === false )
2038                $url .= '/' . ltrim( $path, '/' );
2039
2040        return apply_filters( 'site_url', $url, $path, $orig_scheme, $blog_id );
2041}
2042
2043/**
2044 * Retrieve the url to the admin area for the current site.
2045 *
2046 * @package WordPress
2047 * @since 2.6.0
2048 *
2049 * @param string $path Optional path relative to the admin url
2050 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2051 * @return string Admin url link with optional path appended
2052*/
2053function admin_url( $path = '', $scheme = 'admin' ) {
2054        return get_admin_url(null, $path, $scheme);
2055}
2056
2057/**
2058 * Retrieve the url to the admin area for a given site.
2059 *
2060 * @package WordPress
2061 * @since 3.0.0
2062 *
2063 * @param int $blog_id (optional) Blog ID. Defaults to current blog.
2064 * @param string $path Optional path relative to the admin url
2065 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2066 * @return string Admin url link with optional path appended
2067*/
2068function get_admin_url( $blog_id = null, $path = '', $scheme = 'admin' ) {
2069        $url = get_site_url($blog_id, 'wp-admin/', $scheme);
2070
2071        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2072                $url .= ltrim($path, '/');
2073
2074        return apply_filters('admin_url', $url, $path, $blog_id);
2075}
2076
2077/**
2078 * Retrieve the url to the includes directory.
2079 *
2080 * @package WordPress
2081 * @since 2.6.0
2082 *
2083 * @param string $path Optional. Path relative to the includes url.
2084 * @return string Includes url link with optional path appended.
2085*/
2086function includes_url($path = '') {
2087        $url = site_url() . '/' . WPINC . '/';
2088
2089        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2090                $url .= ltrim($path, '/');
2091
2092        return apply_filters('includes_url', $url, $path);
2093}
2094
2095/**
2096 * Retrieve the url to the content directory.
2097 *
2098 * @package WordPress
2099 * @since 2.6.0
2100 *
2101 * @param string $path Optional. Path relative to the content url.
2102 * @return string Content url link with optional path appended.
2103*/
2104function content_url($path = '') {
2105        $url = WP_CONTENT_URL;
2106        if ( 0 === strpos($url, 'http') && is_ssl() )
2107                $url = str_replace( 'http://', 'https://', $url );
2108
2109        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2110                $url .= '/' . ltrim($path, '/');
2111
2112        return apply_filters('content_url', $url, $path);
2113}
2114
2115/**
2116 * Retrieve the url to the plugins directory or to a specific file within that directory.
2117 * You can hardcode the plugin slug in $path or pass __FILE__ as a second argument to get the correct folder name.
2118 *
2119 * @package WordPress
2120 * @since 2.6.0
2121 *
2122 * @param string $path Optional. Path relative to the plugins url.
2123 * @param string $plugin Optional. The plugin file that you want to be relative to - i.e. pass in __FILE__
2124 * @return string Plugins url link with optional path appended.
2125*/
2126function plugins_url($path = '', $plugin = '') {
2127
2128        $mu_plugin_dir = WPMU_PLUGIN_DIR;
2129        foreach ( array('path', 'plugin', 'mu_plugin_dir') as $var ) {
2130                $$var = str_replace('\\' ,'/', $$var); // sanitize for Win32 installs
2131                $$var = preg_replace('|/+|', '/', $$var);
2132        }
2133
2134        if ( !empty($plugin) && 0 === strpos($plugin, $mu_plugin_dir) )
2135                $url = WPMU_PLUGIN_URL;
2136        else
2137                $url = WP_PLUGIN_URL;
2138
2139        if ( 0 === strpos($url, 'http') && is_ssl() )
2140                $url = str_replace( 'http://', 'https://', $url );
2141
2142        if ( !empty($plugin) && is_string($plugin) ) {
2143                $folder = dirname(plugin_basename($plugin));
2144                if ( '.' != $folder )
2145                        $url .= '/' . ltrim($folder, '/');
2146        }
2147
2148        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2149                $url .= '/' . ltrim($path, '/');
2150
2151        return apply_filters('plugins_url', $url, $path, $plugin);
2152}
2153
2154/**
2155 * Retrieve the site url for the current network.
2156 *
2157 * Returns the site url with the appropriate protocol,  'https' if
2158 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
2159 * overridden.
2160 *
2161 * @package WordPress
2162 * @since 3.0.0
2163 *
2164 * @param string $path Optional. Path relative to the site url.
2165 * @param string $scheme Optional. Scheme to give the site url context. Currently 'http','https', 'login', 'login_post', or 'admin'.
2166 * @return string Site url link with optional path appended.
2167*/
2168function network_site_url( $path = '', $scheme = null ) {
2169        global $current_site;
2170
2171        if ( !is_multisite() )
2172                return site_url($path, $scheme);
2173
2174        $orig_scheme = $scheme;
2175        if ( !in_array($scheme, array('http', 'https')) ) {
2176                if ( ( 'login_post' == $scheme || 'rpc' == $scheme ) && ( force_ssl_login() || force_ssl_admin() ) )
2177                        $scheme = 'https';
2178                elseif ( ('login' == $scheme) && ( force_ssl_admin() ) )
2179                        $scheme = 'https';
2180                elseif ( ('admin' == $scheme) && force_ssl_admin() )
2181                        $scheme = 'https';
2182                else
2183                        $scheme = ( is_ssl() ? 'https' : 'http' );
2184        }
2185
2186        $url = $scheme . '://' . $current_site->domain . $current_site->path;
2187
2188        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2189                $url .= ltrim($path, '/');
2190
2191        return apply_filters('network_site_url', $url, $path, $orig_scheme);
2192}
2193
2194/**
2195 * Retrieve the home url for the current network.
2196 *
2197 * Returns the home url with the appropriate protocol,  'https' if
2198 * is_ssl() and 'http' otherwise. If $scheme is 'http' or 'https', is_ssl() is
2199 * overridden.
2200 *
2201 * @package WordPress
2202 * @since 3.0.0
2203 *
2204 * @param  string $path   (optional) Path relative to the home url.
2205 * @param  string $scheme (optional) Scheme to give the home url context. Currently 'http','https'
2206 * @return string Home url link with optional path appended.
2207*/
2208function network_home_url( $path = '', $scheme = null ) {
2209        global $current_site;
2210
2211        if ( !is_multisite() )
2212                return home_url($path, $scheme);
2213
2214        $orig_scheme = $scheme;
2215
2216        if ( !in_array($scheme, array('http', 'https')) )
2217                $scheme = is_ssl() && !is_admin() ? 'https' : 'http';
2218
2219        $url = $scheme . '://' . $current_site->domain . $current_site->path;
2220
2221        if ( !empty( $path ) && is_string( $path ) && strpos( $path, '..' ) === false )
2222                $url .= ltrim( $path, '/' );
2223
2224        return apply_filters( 'network_home_url', $url, $path, $orig_scheme);
2225}
2226
2227/**
2228 * Retrieve the url to the admin area for the network.
2229 *
2230 * @package WordPress
2231 * @since 3.0.0
2232 *
2233 * @param string $path Optional path relative to the admin url
2234 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2235 * @return string Admin url link with optional path appended
2236*/
2237function network_admin_url( $path = '', $scheme = 'admin' ) {
2238        if ( ! is_multisite() )
2239                return admin_url( $path, $scheme );
2240
2241        $url = network_site_url('wp-admin/network/', $scheme);
2242
2243        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2244                $url .= ltrim($path, '/');
2245
2246        return apply_filters('network_admin_url', $url, $path);
2247}
2248
2249/**
2250 * Retrieve the url to the admin area for the current user.
2251 *
2252 * @package WordPress
2253 * @since 3.0.0
2254 *
2255 * @param string $path Optional path relative to the admin url
2256 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2257 * @return string Admin url link with optional path appended
2258*/
2259function user_admin_url( $path = '', $scheme = 'admin' ) {
2260        $url = network_site_url('wp-admin/user/', $scheme);
2261
2262        if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
2263                $url .= ltrim($path, '/');
2264
2265        return apply_filters('user_admin_url', $url, $path);
2266}
2267
2268/**
2269 * Retrieve the url to the admin area for either the current blog or the network depending on context.
2270 *
2271 * @package WordPress
2272 * @since 3.1.0
2273 *
2274 * @param string $path Optional path relative to the admin url
2275 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2276 * @return string Admin url link with optional path appended
2277*/
2278function self_admin_url($path = '', $scheme = 'admin') {
2279        if ( is_network_admin() )
2280                return network_admin_url($path, $scheme);
2281        elseif ( is_user_admin() )
2282                return user_admin_url($path, $scheme);
2283        else
2284                return admin_url($path, $scheme);
2285}
2286
2287/**
2288 * Get the URL to the user's dashboard.
2289 *
2290 * If a user does not belong to any sites, the global user dashboard is used.  If the user belongs to the current site,
2291 * the dashboard for the current site is returned.  If the user cannot edit the current site, the dashboard to the user's
2292 * primary blog is returned.
2293 *
2294 * @since 3.1.0
2295 *
2296 * @param int $user_id User ID
2297 * @param string $path Optional path relative to the dashboard.  Use only paths known to both blog and user admins.
2298 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2299 * @return string Dashboard url link with optional path appended
2300 */
2301function get_dashboard_url( $user_id, $path = '', $scheme = 'admin' ) {
2302        $user_id = (int) $user_id;
2303
2304        $blogs = get_blogs_of_user( $user_id );
2305        if ( ! is_super_admin() && empty($blogs) ) {
2306                $url = user_admin_url( $path, $scheme );
2307        } elseif ( ! is_multisite() ) {
2308                $url = admin_url( $path, $scheme );
2309        } else {
2310                $current_blog = get_current_blog_id();
2311                if ( $current_blog  && ( is_super_admin( $user_id ) || in_array( $current_blog, array_keys( $blogs ) ) ) ) {
2312                        $url = admin_url( $path, $scheme );
2313                } else {
2314                        $active = get_active_blog_for_user( $user_id );
2315                        if ( $active )
2316                                $url = get_admin_url( $active->blog_id, $path, $scheme );
2317                        else
2318                                $url = user_admin_url( $path, $scheme );
2319                }
2320        }
2321
2322        return apply_filters( 'user_dashboard_url', $url, $user_id, $path, $scheme);
2323}
2324
2325/**
2326 * Get the URL to the user's profile editor.
2327 *
2328 * @since 3.1.0
2329 *
2330 * @param int $user User ID
2331 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl(). 'http' or 'https' can be passed to force those schemes.
2332 * @return string Dashboard url link with optional path appended
2333 */
2334function get_edit_profile_url( $user, $scheme = 'admin' ) {
2335        $user = (int) $user;
2336
2337        if ( is_user_admin() )
2338                $url = user_admin_url( 'profile.php', $scheme );
2339        elseif ( is_network_admin() )
2340                $url = network_admin_url( 'profile.php', $scheme );
2341        else
2342                $url = get_dashboard_url( $user, 'profile.php', $scheme );
2343
2344        return apply_filters( 'edit_profile_url', $url, $user, $scheme);
2345}
2346
2347/**
2348 * Output rel=canonical for singular queries
2349 *
2350 * @package WordPress
2351 * @since 2.9.0
2352*/
2353function rel_canonical() {
2354        if ( !is_singular() )
2355                return;
2356
2357        global $wp_the_query;
2358        if ( !$id = $wp_the_query->get_queried_object_id() )
2359                return;
2360
2361        $link = get_permalink( $id );
2362        echo "<link rel='canonical' href='$link' />\n";
2363}
2364
2365/**
2366 * Return a shortlink for a post, page, attachment, or blog.
2367 *
2368 * This function exists to provide a shortlink tag that all themes and plugins can target.  A plugin must hook in to
2369 * provide the actual shortlinks.  Default shortlink support is limited to providing ?p= style links for posts.
2370 * Plugins can short circuit this function via the pre_get_shortlink filter or filter the output
2371 * via the get_shortlink filter.
2372 *
2373 * @since 3.0.0.
2374 *
2375 * @param int $id A post or blog id.  Default is 0, which means the current post or blog.
2376 * @param string $context Whether the id is a 'blog' id, 'post' id, or 'media' id. If 'post', the post_type of the post is consulted. If 'query', the current query is consulted to determine the id and context. Default is 'post'.
2377 * @param bool $allow_slugs Whether to allow post slugs in the shortlink. It is up to the plugin how and whether to honor this.
2378 * @return string A shortlink or an empty string if no shortlink exists for the requested resource or if shortlinks are not enabled.
2379 */
2380function wp_get_shortlink($id = 0, $context = 'post', $allow_slugs = true) {
2381        // Allow plugins to short-circuit this function.
2382        $shortlink = apply_filters('pre_get_shortlink', false, $id, $context, $allow_slugs);
2383        if ( false !== $shortlink )
2384                return $shortlink;
2385
2386        global $wp_query;
2387        $post_id = 0;
2388        if ( 'query' == $context && is_single() ) {
2389                $post_id = $wp_query->get_queried_object_id();
2390        } elseif ( 'post' == $context ) {
2391                $post = get_post($id);
2392                $post_id = $post->ID;
2393        }
2394
2395        $shortlink = '';
2396
2397        // Return p= link for posts.
2398        if ( !empty($post_id) && '' != get_option('permalink_structure') ) {
2399                $post = get_post($post_id);
2400                if ( isset($post->post_type) && 'post' == $post->post_type )
2401                        $shortlink = home_url('?p=' . $post->ID);
2402        }
2403
2404        return apply_filters('get_shortlink', $shortlink, $id, $context, $allow_slugs);
2405}
2406
2407/**
2408 *  Inject rel=sortlink into head if a shortlink is defined for the current page.
2409 *
2410 *  Attached to the wp_head action.
2411 *
2412 * @since 3.0.0
2413 *
2414 * @uses wp_get_shortlink()
2415 */
2416function wp_shortlink_wp_head() {
2417        $shortlink = wp_get_shortlink( 0, 'query' );
2418
2419        if ( empty( $shortlink ) )
2420                return;
2421
2422        echo "<link rel='shortlink' href='" . esc_url( $shortlink ) . "' />\n";
2423}
2424
2425/**
2426 * Send a Link: rel=shortlink header if a shortlink is defined for the current page.
2427 *
2428 * Attached to the wp action.
2429 *
2430 * @since 3.0.0
2431 *
2432 * @uses wp_get_shortlink()
2433 */
2434function wp_shortlink_header() {
2435        if ( headers_sent() )
2436                return;
2437
2438        $shortlink = wp_get_shortlink(0, 'query');
2439
2440        if ( empty($shortlink) )
2441                return;
2442
2443        header('Link: <' . $shortlink . '>; rel=shortlink', false);
2444}
2445
2446/**
2447 * Display the Short Link for a Post
2448 *
2449 * Must be called from inside "The Loop"
2450 *
2451 * Call like the_shortlink(__('Shortlinkage FTW'))
2452 *
2453 * @since 3.0.0
2454 *
2455 * @param string $text Optional The link text or HTML to be displayed.  Defaults to 'This is the short link.'
2456 * @param string $title Optional The tooltip for the link.  Must be sanitized.  Defaults to the sanitized post title.
2457 * @param string $before Optional HTML to display before the link.
2458 * @param string $before Optional HTML to display after the link.
2459 */
2460function the_shortlink( $text = '', $title = '', $before = '', $after = '' ) {
2461        global $post;
2462
2463        if ( empty( $text ) )
2464                $text = __('This is the short link.');
2465
2466        if ( empty( $title ) )
2467                $title = the_title_attribute( array( 'echo' => FALSE ) );
2468
2469        $shortlink = wp_get_shortlink( $post->ID );
2470
2471        if ( !empty( $shortlink ) ) {
2472                $link = '<a rel="shortlink" href="' . esc_url( $shortlink ) . '" title="' . $title . '">' . $text . '</a>';
2473                $link = apply_filters( 'the_shortlink', $link, $shortlink, $text, $title );
2474                echo $before, $link, $after;
2475        }
2476}
2477
2478?>