[ Index ]

PHP Cross Reference of BBPress

title

Body

[close]

/src/includes/common/ -> formatting.php (source)

   1  <?php
   2  
   3  /**
   4   * bbPress Formatting
   5   *
   6   * @package bbPress
   7   * @subpackage Formatting
   8   */
   9  
  10  // Exit if accessed directly
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /** Kses **********************************************************************/
  14  
  15  /**
  16   * Custom allowed tags for forum topics and replies
  17   *
  18   * Allows all users to post links, quotes, code, formatting, lists, and images
  19   *
  20   * @since 2.3.0 bbPress (r4603)
  21   *
  22   * @return array Associative array of allowed tags and attributes
  23   */
  24  function bbp_kses_allowed_tags() {
  25  
  26      // Filter & return
  27      return (array) apply_filters( 'bbp_kses_allowed_tags', array(
  28  
  29          // Links
  30          'a' => array(
  31              'href'     => true,
  32              'title'    => true,
  33              'rel'      => true,
  34              'target'   => true
  35          ),
  36  
  37          // Quotes
  38          'blockquote'   => array(
  39              'cite'     => true
  40          ),
  41  
  42          // Code
  43          'code'         => array(),
  44          'pre'          => array(
  45              'class'    => true
  46          ),
  47  
  48          // Formatting
  49          'em'           => array(),
  50          'strong'       => array(),
  51          'del'          => array(
  52              'datetime' => true,
  53              'cite'     => true
  54          ),
  55          'ins' => array(
  56              'datetime' => true,
  57              'cite'     => true
  58          ),
  59  
  60          // Lists
  61          'ul'           => array(),
  62          'ol'           => array(
  63              'start'    => true,
  64          ),
  65          'li'           => array(),
  66  
  67          // Images
  68          'img'          => array(
  69              'src'      => true,
  70              'border'   => true,
  71              'alt'      => true,
  72              'height'   => true,
  73              'width'    => true,
  74          )
  75      ) );
  76  }
  77  
  78  /**
  79   * Custom kses filter for forum topics and replies, for filtering incoming data
  80   *
  81   * @since 2.3.0 bbPress (r4603)
  82   *
  83   * @param string $data Content to filter, expected to be escaped with slashes
  84   * @return string Filtered content
  85   */
  86  function bbp_filter_kses( $data = '' ) {
  87      return wp_slash( wp_kses( wp_unslash( $data ), bbp_kses_allowed_tags() ) );
  88  }
  89  
  90  /**
  91   * Custom kses filter for forum topics and replies, for raw data
  92   *
  93   * @since 2.3.0 bbPress (r4603)
  94   *
  95   * @param string $data Content to filter, expected to not be escaped
  96   * @return string Filtered content
  97   */
  98  function bbp_kses_data( $data = '' ) {
  99      return wp_kses( $data, bbp_kses_allowed_tags() );
 100  }
 101  
 102  /** Formatting ****************************************************************/
 103  
 104  /**
 105   * Filter the topic or reply content and output code and pre tags
 106   *
 107   * @since 2.3.0 bbPress (r4641)
 108   *
 109   * @param string $content Topic and reply content
 110   * @return string Partially encoded content
 111   */
 112  function bbp_code_trick( $content = '' ) {
 113      $content = str_replace( array( "\r\n", "\r" ), "\n", $content );
 114      $content = preg_replace_callback('|(`)(.*?)`|',      'bbp_encode_callback', $content );
 115      $content = preg_replace_callback( "!(^|\n)`(.*?)`!s", 'bbp_encode_callback', $content );
 116  
 117      return $content;
 118  }
 119  
 120  /**
 121   * When editing a topic or reply, reverse the code trick so the textarea
 122   * contains the correct editable content.
 123   *
 124   * @since 2.3.0 bbPress (r4641)
 125   *
 126   * @param string $content Topic and reply content
 127   * @return string Partially encoded content
 128   */
 129  function bbp_code_trick_reverse( $content = '' ) {
 130  
 131      // Setup variables
 132      $openers = array( '<p>', '<br />' );
 133      $content = preg_replace_callback( '!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s', 'bbp_decode_callback', $content );
 134  
 135      // Do the do
 136      $content = str_replace( $openers,       '',       $content );
 137      $content = str_replace( '</p>',         "\n",     $content );
 138      $content = str_replace( '<coded_br />', '<br />', $content );
 139      $content = str_replace( '<coded_p>',    '<p>',    $content );
 140      $content = str_replace( '</coded_p>',   '</p>',   $content );
 141  
 142      return $content;
 143  }
 144  
 145  /**
 146   * Filter the content and encode any bad HTML tags
 147   *
 148   * @since 2.3.0 bbPress (r4641)
 149   *
 150   * @param string $content Topic and reply content
 151   * @return string Partially encoded content
 152   */
 153  function bbp_encode_bad( $content = '' ) {
 154  
 155      // Setup variables
 156      $content = _wp_specialchars( $content, ENT_NOQUOTES );
 157      $content = preg_split( '@(`[^`]*`)@m', $content, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE );
 158      $allowed = bbp_kses_allowed_tags();
 159      $empty   = array(
 160          'br'    => true,
 161          'hr'    => true,
 162          'img'   => true,
 163          'input' => true,
 164          'param' => true,
 165          'area'  => true,
 166          'col'   => true,
 167          'embed' => true
 168      );
 169  
 170      // Loop through allowed tags and compare for empty and normal tags
 171      foreach ( $allowed as $tag => $args ) {
 172          $preg = $args ? "{$tag}(?:\s.*?)?" : $tag;
 173  
 174          // Which walker to use based on the tag and arguments
 175          if ( isset( $empty[ $tag ] ) ) {
 176              array_walk( $content, 'bbp_encode_empty_callback',  $preg );
 177          } else {
 178              array_walk( $content, 'bbp_encode_normal_callback', $preg );
 179          }
 180      }
 181  
 182      // Return the joined content array
 183      return implode( '', $content );
 184  }
 185  
 186  /** Code Callbacks ************************************************************/
 187  
 188  /**
 189   * Callback to encode the tags in topic or reply content
 190   *
 191   * @since 2.3.0 bbPress (r4641)
 192   *
 193   * @param array $matches
 194   * @return string
 195   */
 196  function bbp_encode_callback( $matches = array() ) {
 197  
 198      // Trim inline code, not pre blocks (to prevent removing indentation)
 199      if ( '`' === $matches[1] ) {
 200          $content = trim( $matches[2] );
 201      } else {
 202          $content = $matches[2];
 203      }
 204  
 205      // Do some replacing
 206      $content = htmlspecialchars( $content, ENT_QUOTES );
 207      $content = str_replace( array( "\r\n", "\r" ), "\n", $content );
 208      $content = preg_replace( "|\n\n\n+|", "\n\n", $content );
 209      $content = str_replace( '&amp;amp;', '&amp;', $content );
 210      $content = str_replace( '&amp;lt;',  '&lt;',  $content );
 211      $content = str_replace( '&amp;gt;',  '&gt;',  $content );
 212  
 213      // Wrap in code tags
 214      $content = '<code>' . $content . '</code>';
 215  
 216      // Wrap blocks in pre tags
 217      if ('`' !== $matches[1] ) {
 218          $content = "\n<pre>" . $content . "</pre>\n";
 219      }
 220  
 221      return $content;
 222  }
 223  
 224  /**
 225   * Callback to decode the tags in topic or reply content
 226   *
 227   * @since 2.3.0 bbPress (r4641)
 228   *
 229   * @param array $matches
 230   * @todo Experiment with _wp_specialchars()
 231   * @return string
 232   */
 233  function bbp_decode_callback( $matches = array() ) {
 234  
 235      // Setup variables
 236      $trans_table = array_flip( get_html_translation_table( HTML_ENTITIES ) );
 237      $amps        = array( '&#38;', '&#038;', '&amp;' );
 238      $single      = array( '&#39;', '&#039;'          );
 239      $content     = $matches[2];
 240      $content     = strtr( $content, $trans_table );
 241  
 242      // Do the do
 243      $content = str_replace( '<br />', '<coded_br />', $content );
 244      $content = str_replace( '<p>',    '<coded_p>',    $content );
 245      $content = str_replace( '</p>',   '</coded_p>',   $content );
 246      $content = str_replace( $amps,    '&',            $content );
 247      $content = str_replace( $single,  "'",            $content );
 248  
 249      // Return content wrapped in code tags
 250      return '`' . $content . '`';
 251  }
 252  
 253  /**
 254   * Callback to replace empty HTML tags in a content string
 255   *
 256   * @since 2.3.0 bbPress (r4641)
 257   *
 258   * @internal Used by bbp_encode_bad()
 259   * @param string $content
 260   * @param string $key Not used
 261   * @param string $preg
 262   */
 263  function bbp_encode_empty_callback( &$content = '', $key = '', $preg = '' ) {
 264      if ( strpos( $content, '`' ) !== 0 ) {
 265          $content = preg_replace( "|&lt;({$preg})\s*?/*?&gt;|i", '<$1 />', $content );
 266      }
 267  }
 268  
 269  /**
 270   * Callback to replace normal HTML tags in a content string
 271   *
 272   * @since 2.3.0 bbPress (r4641)
 273   *
 274   * @internal Used by bbp_encode_bad()
 275   *
 276   * @param string $content
 277   * @param string $key
 278   * @param string $preg
 279   */
 280  function bbp_encode_normal_callback( &$content = '', $key = '', $preg = '') {
 281      if ( strpos( $content, '`' ) !== 0 ) {
 282          $content = preg_replace( "|&lt;(/?{$preg})&gt;|i", '<$1>', $content );
 283      }
 284  }
 285  
 286  /** No Follow *****************************************************************/
 287  
 288  /**
 289   * Catches links so rel=nofollow can be added (on output, not save)
 290   *
 291   * @since 2.3.0 bbPress (r4865)
 292   *
 293   * @param string $text Post text
 294   * @return string $text Text with rel=nofollow added to any links
 295   */
 296  function bbp_rel_nofollow( $text = '' ) {
 297      return preg_replace_callback( '|<a (.+?)>|i', 'bbp_rel_nofollow_callback', $text );
 298  }
 299  
 300  /**
 301   * Adds rel=nofollow to a link
 302   *
 303   * @since 2.3.0 bbPress (r4865)
 304   *
 305   * @param array $matches
 306   * @return string $text Link with rel=nofollow added
 307   */
 308  function bbp_rel_nofollow_callback( $matches = array() ) {
 309      $text     = $matches[1];
 310      $atts     = shortcode_parse_atts( $matches[1] );
 311      $rel      = 'nofollow';
 312      $home_url = home_url();
 313  
 314      // Bail on links that match the current domain
 315      if ( preg_match( '%href=["\'](' . preg_quote( set_url_scheme( $home_url, 'http'  ) ) . ')%i', $text ) ||
 316           preg_match( '%href=["\'](' . preg_quote( set_url_scheme( $home_url, 'https' ) ) . ')%i', $text )
 317      ) {
 318          return "<a {$text}>";
 319      }
 320  
 321      // Avoid collisions with existing "rel" attribute
 322      if ( ! empty( $atts['rel'] ) ) {
 323          $parts = array_map( 'trim', explode( ' ', $atts['rel'] ) );
 324          if ( false === array_search( 'nofollow', $parts ) ) {
 325              $parts[] = 'nofollow';
 326          }
 327  
 328          $rel = implode( ' ', $parts );
 329          unset( $atts['rel'] );
 330  
 331          $html = '';
 332          foreach ( $atts as $name => $value ) {
 333              $html .= "{$name}=\"{$value}\" ";
 334          }
 335  
 336          $text = trim( $html );
 337      }
 338  
 339      return "<a {$text} rel=\"{$rel}\">";
 340  }
 341  
 342  /** Make Clickable ************************************************************/
 343  
 344  /**
 345   * Convert plaintext URI to HTML links.
 346   *
 347   * Converts URI, www and ftp, and email addresses. Finishes by fixing links
 348   * within links.
 349   *
 350   * This custom version of WordPress's make_clickable() skips links inside of
 351   * pre and code tags.
 352   *
 353   * @since 2.4.0 bbPress (r4941)
 354   *
 355   * @param string $text Content to convert URIs.
 356   * @return string Content with converted URIs.
 357   */
 358  function bbp_make_clickable( $text = '' ) {
 359      $r               = '';
 360      $textarr         = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
 361      $nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code>
 362  
 363      foreach ( $textarr as $piece ) {
 364  
 365          if ( preg_match( '|^<code[\s>]|i', $piece ) || preg_match( '|^<pre[\s>]|i', $piece ) || preg_match( '|^<script[\s>]|i', $piece ) || preg_match( '|^<style[\s>]|i', $piece ) ) {
 366              $nested_code_pre++;
 367          } elseif ( $nested_code_pre && ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) || '</script>' === strtolower( $piece ) || '</style>' === strtolower( $piece ) ) ) {
 368              $nested_code_pre--;
 369          }
 370  
 371          if ( $nested_code_pre || empty( $piece ) || ( $piece[0] === '<' && ! preg_match( '|^<\s*[\w]{1,20}+://|', $piece ) ) ) {
 372              $r .= $piece;
 373              continue;
 374          }
 375  
 376          // Long strings might contain expensive edge cases ...
 377          if ( 10000 < strlen( $piece ) ) {
 378              // ... break it up
 379              foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
 380                  if ( 2101 < strlen( $chunk ) ) {
 381                      $r .= $chunk; // Too big, no whitespace: bail.
 382                  } else {
 383                      $r .= bbp_make_clickable( $chunk );
 384                  }
 385              }
 386          } else {
 387              $ret = " {$piece} "; // Pad with whitespace to simplify the regexes
 388              $ret = apply_filters( 'bbp_make_clickable', $ret, $text );
 389              $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
 390              $r .= $ret;
 391          }
 392      }
 393  
 394      // Cleanup of accidental links within links
 395      return preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a>([^<]*)</a>#i', '$1$3$4</a>', $r );
 396  }
 397  
 398  /**
 399   * Make URLs clickable in content areas
 400   *
 401   * @since 2.6.0 bbPress (r6014)
 402   *
 403   * @param  string $text
 404   * @return string
 405   */
 406  function bbp_make_urls_clickable( $text = '' ) {
 407      $url_clickable = '~
 408          ([\\s(<.,;:!?])                                # 1: Leading whitespace, or punctuation
 409          (                                              # 2: URL
 410              [\\w]{1,20}+://                            # Scheme and hier-part prefix
 411              (?=\S{1,2000}\s)                           # Limit to URLs less than about 2000 characters long
 412              [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+     # Non-punctuation URL character
 413              (?:                                        # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
 414                  [\'.,;:!?)]                            # Punctuation URL character
 415                  [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
 416              )*
 417          )
 418          (\)?)                                          # 3: Trailing closing parenthesis (for parethesis balancing post processing)
 419      ~xS';
 420  
 421      // The regex is a non-anchored pattern and does not have a single fixed starting character.
 422      // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
 423      return preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $text );
 424  }
 425  
 426  /**
 427   * Make FTP clickable in content areas
 428   *
 429   * @since 2.6.0 bbPress (r6014)
 430   *
 431   * @see make_clickable()
 432   *
 433   * @param  string $text
 434   * @return string
 435   */
 436  function bbp_make_ftps_clickable( $text = '' ) {
 437      return preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $text );
 438  }
 439  
 440  /**
 441   * Make emails clickable in content areas
 442   *
 443   * @since 2.6.0 bbPress (r6014)
 444   *
 445   * @see make_clickable()
 446   *
 447   * @param  string $text
 448   * @return string
 449   */
 450  function bbp_make_emails_clickable( $text = '' ) {
 451      return preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $text );
 452  }
 453  
 454  /**
 455   * Make mentions clickable in content areas
 456   *
 457   * @since 2.6.0 bbPress (r6014)
 458   *
 459   * @see make_clickable()
 460   *
 461   * @param  string $text
 462   * @return string
 463   */
 464  function bbp_make_mentions_clickable( $text = '' ) {
 465      return preg_replace_callback( '#([\s>])@([0-9a-zA-Z-_]+)#i', 'bbp_make_mentions_clickable_callback', $text );
 466  }
 467  
 468  /**
 469   * Callback to convert mention matches to HTML A tag.
 470   *
 471   * @since 2.6.0 bbPress (r6014)
 472   *
 473   * @param array $matches Regular expression matches in the current text blob.
 474   *
 475   * @return string Original text if no user exists, or link to user profile.
 476   */
 477  function bbp_make_mentions_clickable_callback( $matches = array() ) {
 478  
 479      // Bail if the match is empty malformed
 480      if ( empty( $matches[2] ) || ! is_string( $matches[2] ) ) {
 481          return $matches[0];
 482      }
 483  
 484      // Get user; bail if not found
 485      $user = get_user_by( 'slug', $matches[2] );
 486      if ( empty( $user ) || bbp_is_user_inactive( $user->ID ) ) {
 487          return $matches[0];
 488      }
 489  
 490      // Default anchor classes
 491      $classes = array(
 492          'bbp-user-mention',
 493          'bbp-user-id-' . absint( $user->ID )
 494      );
 495  
 496      // Filter classes
 497      $classes = (array) apply_filters( 'bbp_make_mentions_clickable_classes', $classes, $user );
 498  
 499      // Escape & implode if not empty, otherwise an empty string
 500      $class_str = ! empty( $classes )
 501          ? implode( ' ', array_map( 'sanitize_html_class', $classes ) )
 502          : '';
 503  
 504      // Setup as a variable to avoid a potentially empty class attribute
 505      $class = ! empty( $class_str )
 506          ? ' class="' . esc_attr( $class_str ) . '"'
 507          : '';
 508  
 509      // Create the link to the user's profile
 510      $html   = '<a href="%1$s"' . $class . '>%2$s</a>';
 511      $url    = bbp_get_user_profile_url( $user->ID );
 512      $anchor = sprintf( $html, esc_url( $url ), esc_html( $matches[0] ) );
 513  
 514      // Prevent this link from being followed by bots
 515      $link   = bbp_rel_nofollow( $anchor );
 516  
 517      // Concatenate the matches into the return value
 518      $retval = $matches[1] . $link;
 519  
 520      // Return the link
 521      return $retval;
 522  }
 523  
 524  /** Numbers *******************************************************************/
 525  
 526  /**
 527   * Never let a numeric value be less than zero.
 528   *
 529   * @since 2.6.0 bbPress (r6300)
 530   *
 531   * @param int $number
 532   */
 533  function bbp_number_not_negative( $number = 0 ) {
 534  
 535      // Protect against formatted strings
 536      if ( is_string( $number ) ) {
 537          $number = strip_tags( $number );                    // No HTML
 538          $number = preg_replace( '/[^0-9-]/', '', $number ); // No number-format
 539  
 540      // Protect against objects, arrays, scalars, etc...
 541      } elseif ( ! is_numeric( $number ) ) {
 542          $number = 0;
 543      }
 544  
 545      // Make the number an integer
 546      $int = intval( $number );
 547  
 548      // Pick the maximum value, never less than zero
 549      $not_less_than_zero = max( 0, $int );
 550  
 551      // Filter & return
 552      return (int) apply_filters( 'bbp_number_not_negative', $not_less_than_zero, $int, $number );
 553  }
 554  
 555  /**
 556   * A bbPress specific method of formatting numeric values
 557   *
 558   * @since 2.0.0 bbPress (r2486)
 559   *
 560   * @param string $number Number to format
 561   * @param string $decimals Optional. Display decimals
 562   *
 563   * @return string Formatted string
 564   */
 565  function bbp_number_format( $number = 0, $decimals = false, $dec_point = '.', $thousands_sep = ',' ) {
 566  
 567      // If empty, set $number to (int) 0
 568      if ( ! is_numeric( $number ) ) {
 569          $number = 0;
 570      }
 571  
 572      // Filter & return
 573      return apply_filters( 'bbp_number_format', number_format( $number, $decimals, $dec_point, $thousands_sep ), $number, $decimals, $dec_point, $thousands_sep );
 574  }
 575  
 576  /**
 577   * A bbPress specific method of formatting numeric values
 578   *
 579   * @since 2.1.0 bbPress (r3857)
 580   *
 581   * @param string $number Number to format
 582   * @param string $decimals Optional. Display decimals
 583   *
 584   * @return string Formatted string
 585   */
 586  function bbp_number_format_i18n( $number = 0, $decimals = false ) {
 587  
 588      // If empty, set $number to (int) 0
 589      if ( ! is_numeric( $number ) ) {
 590          $number = 0;
 591      }
 592  
 593      // Filter & return
 594      return apply_filters( 'bbp_number_format_i18n', number_format_i18n( $number, $decimals ), $number, $decimals );
 595  }
 596  
 597  /** Dates *********************************************************************/
 598  
 599  /**
 600   * Convert time supplied from database query into specified date format.
 601   *
 602   * @since 2.0.0 bbPress (r2544)
 603   *
 604   * @param string $time Time to convert
 605   * @param string $d Optional. Default is 'U'. Either 'G', 'U', or php date
 606   *                             format
 607   * @param bool $translate Optional. Default is false. Whether to translate the
 608   *
 609   * @return string Returns timestamp
 610   */
 611  function bbp_convert_date( $time, $d = 'U', $translate = false ) {
 612      $new_time = mysql2date( $d, $time, $translate );
 613  
 614      // Filter & return
 615      return apply_filters( 'bbp_convert_date', $new_time, $d, $translate, $time );
 616  }
 617  
 618  /**
 619   * Output formatted time to display human readable time difference.
 620   *
 621   * @since 2.0.0 bbPress (r2544)
 622   *
 623   * @param string $older_date Unix timestamp from which the difference begins.
 624   * @param string $newer_date Optional. Unix timestamp from which the
 625   *                            difference ends. False for current time.
 626   * @param int $gmt Optional. Whether to use GMT timezone. Default is false.
 627   */
 628  function bbp_time_since( $older_date, $newer_date = false, $gmt = false ) {
 629      echo bbp_get_time_since( $older_date, $newer_date, $gmt );
 630  }
 631      /**
 632       * Return formatted time to display human readable time difference.
 633       *
 634       * @since 2.0.0 bbPress (r2544)
 635       *
 636       * @param string $older_date Unix timestamp from which the difference begins.
 637       * @param string $newer_date Optional. Unix timestamp from which the
 638       *                            difference ends. False for current time.
 639       * @param int $gmt Optional. Whether to use GMT timezone. Default is false.
 640       *
 641       * @return string Formatted time
 642       */
 643  	function bbp_get_time_since( $older_date, $newer_date = false, $gmt = false ) {
 644  
 645          // Setup the strings
 646          $unknown_text   = apply_filters( 'bbp_core_time_since_unknown_text',   esc_html__( 'sometime',  'bbpress' ) );
 647          $right_now_text = apply_filters( 'bbp_core_time_since_right_now_text', esc_html__( 'right now', 'bbpress' ) );
 648          $ago_text       = apply_filters( 'bbp_core_time_since_ago_text',       esc_html__( '%s ago',    'bbpress' ) );
 649  
 650          // array of time period chunks
 651          $chunks = array(
 652              array( YEAR_IN_SECONDS,   _n_noop( '%s year',   '%s years',   'bbpress' ) ),
 653              array( MONTH_IN_SECONDS,  _n_noop( '%s month',  '%s months',  'bbpress' ) ),
 654              array( WEEK_IN_SECONDS,   _n_noop( '%s week',   '%s weeks',   'bbpress' ) ),
 655              array( DAY_IN_SECONDS,    _n_noop( '%s day',    '%s days',    'bbpress' ) ),
 656              array( HOUR_IN_SECONDS,   _n_noop( '%s hour',   '%s hours',   'bbpress' ) ),
 657              array( MINUTE_IN_SECONDS, _n_noop( '%s minute', '%s minutes', 'bbpress' ) ),
 658              array( 1,                 _n_noop( '%s second', '%s seconds', 'bbpress' ) ),
 659          );
 660  
 661          // Attempt to parse non-numeric older date
 662          if ( ! empty( $older_date ) && ! is_numeric( $older_date ) ) {
 663              $time_chunks = explode( ':', str_replace( ' ', ':', $older_date ) );
 664              $date_chunks = explode( '-', str_replace( ' ', '-', $older_date ) );
 665              $older_date  = gmmktime( (int) $time_chunks[1], (int) $time_chunks[2], (int) $time_chunks[3], (int) $date_chunks[1], (int) $date_chunks[2], (int) $date_chunks[0] );
 666          }
 667  
 668          // Attempt to parse non-numeric newer date
 669          if ( ! empty( $newer_date ) && ! is_numeric( $newer_date ) ) {
 670              $time_chunks = explode( ':', str_replace( ' ', ':', $newer_date ) );
 671              $date_chunks = explode( '-', str_replace( ' ', '-', $newer_date ) );
 672              $newer_date  = gmmktime( (int) $time_chunks[1], (int) $time_chunks[2], (int) $time_chunks[3], (int) $date_chunks[1], (int) $date_chunks[2], (int) $date_chunks[0] );
 673          }
 674  
 675          // Set newer date to current time
 676          if ( empty( $newer_date ) ) {
 677              $newer_date = strtotime( current_time( 'mysql', $gmt ) );
 678          }
 679  
 680          // Cast both dates to ints to avoid notices & errors with invalid values
 681          $newer_date = intval( $newer_date );
 682          $older_date = intval( $older_date );
 683  
 684          // Difference in seconds
 685          $since = intval( $newer_date - $older_date );
 686  
 687          // Something went wrong with date calculation and we ended up with a negative date.
 688          if ( 0 > $since ) {
 689              $output = $unknown_text;
 690  
 691          // We only want to output two chunks of time here, eg:
 692          //     x years, xx months
 693          //     x days, xx hours
 694          // so there's only two bits of calculation below:
 695          } else {
 696  
 697              // Default count values
 698              $count  = 0;
 699              $count2 = 0;
 700  
 701              // Step one: the first chunk
 702              for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) {
 703                  $seconds = $chunks[ $i ][0];
 704  
 705                  // Finding the biggest chunk (if the chunk fits, break)
 706                  $count = floor( $since / $seconds );
 707                  if ( 0 != $count ) {
 708                      break;
 709                  }
 710              }
 711  
 712              // If $i iterates all the way to $j, then the event happened 0 seconds ago
 713              if ( ! isset( $chunks[ $i ] ) ) {
 714                  $output = $right_now_text;
 715  
 716              } else {
 717  
 718                  // Set output var
 719                  $output = sprintf( translate_nooped_plural( $chunks[ $i ][1], $count, 'bbpress' ), bbp_number_format_i18n( $count ) );
 720  
 721                  // Step two: the second chunk
 722                  if ( $i + 2 < $j ) {
 723                      $seconds2 = $chunks[ $i + 1 ][0];
 724                      $count2   = floor( ( $since - ( $seconds * $count ) ) / $seconds2 );
 725  
 726                      // Add to output var
 727                      if ( 0 != $count2 ) {
 728                          $output .= _x( ',', 'Separator in time since', 'bbpress' ) . ' ';
 729                          $output .= sprintf( translate_nooped_plural( $chunks[ $i + 1 ][1], $count2, 'bbpress' ), bbp_number_format_i18n( $count2 ) );
 730                      }
 731                  }
 732  
 733                  // Empty counts, so fallback to right now
 734                  if ( empty( $count ) && empty( $count2 ) ) {
 735                      $output = $right_now_text;
 736                  }
 737              }
 738          }
 739  
 740          // Append 'ago' to the end of time-since if not 'right now'
 741          if ( $output != $right_now_text ) {
 742              $output = sprintf( $ago_text, $output );
 743          }
 744  
 745          // Filter & return
 746          return apply_filters( 'bbp_get_time_since', $output, $older_date, $newer_date );
 747      }
 748  
 749  /** Revisions *****************************************************************/
 750  
 751  /**
 752   * Formats the reason for editing the topic/reply.
 753   *
 754   * Does these things:
 755   *  - Trimming
 756   *  - Removing periods from the end of the string
 757   *  - Trimming again
 758   *
 759   * @since 2.0.0 bbPress (r2782)
 760   *
 761   * @param string $reason Optional. User submitted reason for editing.
 762   * @return string Status of topic
 763   */
 764  function bbp_format_revision_reason( $reason = '' ) {
 765      $reason = (string) $reason;
 766  
 767      // Bail if reason is empty
 768      if ( empty( $reason ) ) {
 769          return $reason;
 770      }
 771  
 772      // Trimming
 773      $reason = trim( $reason );
 774  
 775      // We add our own full stop.
 776      while ( substr( $reason, -1 ) === '.' ) {
 777          $reason = substr( $reason, 0, -1 );
 778      }
 779  
 780      // Trim again
 781      $reason = trim( $reason );
 782  
 783      return $reason;
 784  }


Generated: Sat Apr 20 01:00:52 2024 Cross-referenced by PHPXref 0.7.1