[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> link-template.php (source)

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


Generated: Thu Apr 21 03:55:47 2016 Hosted by follow the white rabbit.