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