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