[ Index ]

PHP Cross Reference of BBPress

title

Body

[close]

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

   1  <?php
   2  
   3  /**
   4   * bbPress Common Functions
   5   *
   6   * Common functions are ones that are used by more than one component, like
   7   * forums, topics, replies, users, topic tags, etc...
   8   *
   9   * @package bbPress
  10   * @subpackage Functions
  11   */
  12  
  13  // Exit if accessed directly
  14  defined( 'ABSPATH' ) || exit;
  15  
  16  /**
  17   * Return array of bbPress registered post types
  18   *
  19   * @since 2.6.0 bbPress (r6813)
  20   *
  21   * @param array $args Array of arguments to pass into `get_post_types()`
  22   *
  23   * @return array
  24   */
  25  function bbp_get_post_types( $args = array() ) {
  26  
  27      // Parse args
  28      $r = bbp_parse_args( $args, array(
  29          'source' => 'bbpress'
  30      ), 'get_post_types' );
  31  
  32      // Return post types
  33      return get_post_types( $r );
  34  }
  35  
  36  /** URLs **********************************************************************/
  37  
  38  /**
  39   * Return the unescaped redirect_to request value
  40   *
  41   * @bbPress (r4655)
  42   *
  43   * @return string The URL to redirect to, if set
  44   */
  45  function bbp_get_redirect_to() {
  46  
  47      // Check 'redirect_to' request parameter
  48      $retval = ! empty( $_REQUEST['redirect_to'] )
  49          ? $_REQUEST['redirect_to']
  50          : '';
  51  
  52      // Filter & return
  53      return apply_filters( 'bbp_get_redirect_to', $retval );
  54  }
  55  
  56  /**
  57   * Append 'view=all' to query string if it's already there from referer
  58   *
  59   * @since 2.0.0 bbPress (r3325)
  60   *
  61   * @param string $original_link Original Link to be modified
  62   * @param bool $force Override bbp_get_view_all() check
  63   * @return string The link with 'view=all' appended if necessary
  64   */
  65  function bbp_add_view_all( $original_link = '', $force = false ) {
  66  
  67      // Are we appending the view=all vars?
  68      $link = ( bbp_get_view_all() || ! empty( $force ) )
  69          ? add_query_arg( array( 'view' => 'all' ), $original_link )
  70          : $original_link;
  71  
  72      // Filter & return
  73      return apply_filters( 'bbp_add_view_all', $link, $original_link );
  74  }
  75  
  76  /**
  77   * Remove 'view=all' from query string
  78   *
  79   * @since 2.0.0 bbPress (r3325)
  80   *
  81   * @param string $original_link Original Link to be modified
  82   * @return string The link with 'view=all' appended if necessary
  83   */
  84  function bbp_remove_view_all( $original_link = '' ) {
  85  
  86      // Remove `view' argument
  87      $link = remove_query_arg( 'view', $original_link );
  88  
  89      // Filter & return
  90      return apply_filters( 'bbp_remove_view_all', $link, $original_link );
  91  }
  92  
  93  /**
  94   * If current user can and is viewing all topics/replies
  95   *
  96   * @since 2.0.0 bbPress (r3325)
  97   *
  98   * @param string $cap Capability used to ensure user can view all
  99   *
 100   * @return bool Whether current user can and is viewing all
 101   */
 102  function bbp_get_view_all( $cap = 'moderate' ) {
 103      $retval = ( ( ! empty( $_GET['view'] ) && ( 'all' === $_GET['view'] ) && current_user_can( $cap ) ) );
 104  
 105      // Filter & return
 106      return (bool) apply_filters( 'bbp_get_view_all', (bool) $retval, $cap );
 107  }
 108  
 109  /**
 110   * Assist pagination by returning correct page number
 111   *
 112   * @since 2.0.0 bbPress (r2628)
 113   *
 114   * @return int Current page number
 115   */
 116  function bbp_get_paged() {
 117      $wp_query = bbp_get_wp_query();
 118  
 119      // Check the query var
 120      if ( get_query_var( 'paged' ) ) {
 121          $paged = get_query_var( 'paged' );
 122  
 123      // Check query paged
 124      } elseif ( ! empty( $wp_query->query['paged'] ) ) {
 125          $paged = $wp_query->query['paged'];
 126      }
 127  
 128      // Paged found
 129      if ( ! empty( $paged ) ) {
 130          return (int) $paged;
 131      }
 132  
 133      // Default to first page
 134      return 1;
 135  }
 136  
 137  /** Misc **********************************************************************/
 138  
 139  /**
 140   * Return the unique non-empty values of an array.
 141   *
 142   * @since 2.6.0 bbPress (r6481)
 143   *
 144   * @param array $array Array to get values of
 145   *
 146   * @return array
 147   */
 148  function bbp_get_unique_array_values( $array = array() ) {
 149      return array_unique( array_filter( array_values( $array ) ) );
 150  }
 151  
 152  /**
 153   * Fix post author id on post save
 154   *
 155   * When a logged in user changes the status of an anonymous reply or topic, or
 156   * edits it, the post_author field is set to the logged in user's id. This
 157   * function fixes that.
 158   *
 159   * @since 2.0.0 bbPress (r2734)
 160   *
 161   * @param array $data Post data
 162   * @param array $postarr Original post array (includes post id)
 163   * @return array Data
 164   */
 165  function bbp_fix_post_author( $data = array(), $postarr = array() ) {
 166  
 167      // Post is not being updated or the post_author is already 0, return
 168      if ( empty( $postarr['ID'] ) || empty( $data['post_author'] ) ) {
 169          return $data;
 170      }
 171  
 172      // Post is not a topic or reply, return
 173      if ( ! in_array( $data['post_type'], array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ), true ) ) {
 174          return $data;
 175      }
 176  
 177      // Is the post by an anonymous user?
 178      if ( ( bbp_get_topic_post_type() === $data['post_type'] && ! bbp_is_topic_anonymous( $postarr['ID'] ) ) ||
 179           ( bbp_get_reply_post_type() === $data['post_type'] && ! bbp_is_reply_anonymous( $postarr['ID'] ) ) ) {
 180          return $data;
 181      }
 182  
 183      // The post is being updated. It is a topic or a reply and is written by an anonymous user.
 184      // Set the post_author back to 0
 185      $data['post_author'] = 0;
 186  
 187      return $data;
 188  }
 189  
 190  /**
 191   * Use the previous status when restoring a topic or reply.
 192   *
 193   * Fixes an issue since WordPress 5.6.0. See
 194   * {@link https://bbpress.trac.wordpress.org/ticket/3433}.
 195   *
 196   * @since 2.6.10 bbPress (r7233)
 197   *
 198   * @param string $new_status      New status to use when untrashing. Default: 'draft'
 199   * @param int    $post_id         Post ID
 200   * @param string $previous_status Previous post status from '_wp_trash_meta_status' meta key. Default: 'pending'
 201   */
 202  function bbp_fix_untrash_post_status( $new_status = 'draft', $post_id = 0, $previous_status = 'pending' ) {
 203  
 204      // Bail if not Topic or Reply
 205      if ( ! bbp_is_topic( $post_id ) && ! bbp_is_reply( $post_id ) ) {
 206          return $new_status;
 207      }
 208  
 209      // Prefer the previous status, falling back to the new status
 210      $retval = ! empty( $previous_status )
 211          ? $previous_status
 212          : $new_status;
 213  
 214      return $retval;
 215  }
 216  
 217  /**
 218   * Check a date against the length of time something can be edited.
 219   *
 220   * It is recommended to leave $utc set to true and to work with UTC/GMT dates.
 221   * Turning this off will use the WordPress offset which is likely undesirable.
 222   *
 223   * @since 2.0.0 bbPress (r3133)
 224   * @since 2.6.0 bbPress (r6868) Inverted some logic and added unit tests
 225   *
 226   * @param string  $datetime Gets run through strtotime()
 227   * @param boolean $utc      Default true. Is the timestamp in UTC?
 228   *
 229   * @return bool True by default, if date is past, or editing is disabled.
 230   */
 231  function bbp_past_edit_lock( $datetime = '', $utc = true ) {
 232  
 233      // Default value
 234      $retval = true;
 235  
 236      // Check if date and editing is allowed
 237      if ( bbp_allow_content_edit() ) {
 238  
 239          // Get number of minutes to allow editing for
 240          $minutes = bbp_get_edit_lock();
 241  
 242          // 0 minutes means forever, so can never be past edit-lock time
 243          if ( 0 === $minutes ) {
 244              $retval = false;
 245  
 246          // Checking against a specific datetime
 247          } elseif ( ! empty( $datetime ) ) {
 248  
 249              // Period of time
 250              $lockable = "+{$minutes} minutes";
 251              if ( true === $utc ) {
 252                  $lockable .= ' UTC';
 253              }
 254  
 255              // Now
 256              $cur_time  = current_time( 'timestamp', $utc );
 257  
 258              // Get the duration in seconds
 259              $duration  = strtotime( $lockable ) - $cur_time;
 260  
 261              // Diff the times down to seconds
 262              $lock_time = strtotime( $lockable, $cur_time );
 263              $past_time = strtotime( $datetime, $cur_time );
 264              $diff_time = ( $lock_time - $past_time ) - $duration;
 265  
 266              // Check if less than lock time
 267              if ( $diff_time < $duration ) {
 268                  $retval = false;
 269              }
 270          }
 271      }
 272  
 273      // Filter & return
 274      return (bool) apply_filters( 'bbp_past_edit_lock', $retval, $datetime, $utc );
 275  }
 276  
 277  /**
 278   * Get number of days something should remain trashed for before it is cleaned
 279   * up by WordPress Cron. If set to 0, items will skip trash and be deleted
 280   * immediately.
 281   *
 282   * @since 2.6.0 bbPress (r6424)
 283   *
 284   * @param string $context Provide context for additional filtering
 285   * @return int Number of days items remain in trash
 286   */
 287  function bbp_get_trash_days( $context = 'forum' ) {
 288  
 289      // Sanitize the context
 290      $context = sanitize_key( $context );
 291  
 292      // Check the WordPress constant
 293      $days    = defined( 'EMPTY_TRASH_DAYS' )
 294          ? (int) EMPTY_TRASH_DAYS
 295          : 30;
 296  
 297      // Filter & return
 298      return (int) apply_filters( 'bbp_get_trash_days', $days, $context );
 299  }
 300  
 301  /** Statistics ****************************************************************/
 302  
 303  /**
 304   * Get the forum statistics
 305   *
 306   * @since 2.0.0 bbPress (r2769)
 307   * @since 2.6.0 bbPress (r6055)  Added:
 308   *                               `count_pending_topics`
 309   *                               `count_pending_replies`
 310   * @since 2.6.10 bbPress (r7235) Renamed:
 311   *                                `count_trashed_topics`  to `count_trash_topics`
 312   *                                `count_trashed_replies` to `count_trash_replies`
 313   *                                `count_spammed_topics`  to `count_spam_topics`
 314   *                                `count_spammed_replies` to `count_spam_replies`
 315   *                               Added:
 316   *                                `count_hidden_topics`
 317   *                                `count_hidden_replies`
 318   *
 319   * @param array $args Optional. The function supports these arguments (all
 320   *                    default to true):
 321   *
 322   *  - count_users:           Count users?
 323   *  - count_forums:          Count forums?
 324   *  - count_topics:          Count topics? If set to false, private, spam and
 325   *                           trash topics are also not counted.
 326   *  - count_pending_topics:  Count pending topics? (only counted if the current
 327   *                           user has edit_others_topics cap)
 328   *  - count_private_topics:  Count private topics? (only counted if the current
 329   *                           user has read_private_topics cap)
 330   *  - count_hidden_topics:   Count hidden topics? (only counted if the current
 331   *                           user has read_hidden_topics cap)
 332   *  - count_spam_topics:     Count spam topics? (only counted if the current
 333   *                           user has edit_others_topics cap)
 334   *  - count_trash_topics:    Count trash topics? (only counted if the current
 335   *                           user has view_trash cap)
 336   *  - count_replies:         Count replies? If set to false, private, spam and
 337   *                           trash replies are also not counted.
 338   *  - count_pending_replies: Count pending replies? (only counted if the current
 339   *                           user has edit_others_replies cap)
 340   *  - count_private_replies: Count private replies? (only counted if the current
 341   *                           user has read_private_replies cap)
 342   *  - count_hidden_replies:  Count hidden replies? (only counted if the current
 343   *                           user has read_hidden_replies cap)
 344   *  - count_spam_replies:    Count spam replies? (only counted if the current
 345   *                           user has edit_others_replies cap)
 346   *  - count_trash_replies:   Count trash replies? (only counted if the current
 347   *                           user has view_trash cap)
 348   *  - count_tags:            Count tags? If set to false, empty tags are also
 349   *                           not counted
 350   *  - count_empty_tags:      Count empty tags?
 351   *
 352   * @return array Array of statistics
 353   */
 354  function bbp_get_statistics( $args = array() ) {
 355  
 356      // Parse arguments against default values
 357      $r = bbp_parse_args( $args, array(
 358  
 359          // Users
 360          'count_users'           => true,
 361  
 362          // Forums
 363          'count_forums'          => true,
 364  
 365          // Topics
 366          'count_topics'          => true,
 367          'count_pending_topics'  => true,
 368          'count_private_topics'  => true,
 369          'count_spam_topics'     => true,
 370          'count_trash_topics'    => true,
 371          'count_hidden_topics'   => true,
 372  
 373          // Replies
 374          'count_replies'         => true,
 375          'count_pending_replies' => true,
 376          'count_private_replies' => true,
 377          'count_spam_replies'    => true,
 378          'count_trash_replies'   => true,
 379          'count_hidden_replies'  => true,
 380  
 381          // Topic tags
 382          'count_tags'            => true,
 383          'count_empty_tags'      => true
 384  
 385      ), 'get_statistics' );
 386  
 387      // Defaults
 388      $topic_count     = $topic_count_hidden    = 0;
 389      $reply_count     = $reply_count_hidden    = 0;
 390      $topic_tag_count = $empty_topic_tag_count = 0;
 391      $hidden_topic_title = $hidden_reply_title = '';
 392  
 393      // Post statuses
 394      $publish = bbp_get_public_status_id();
 395      $closed  = bbp_get_closed_status_id();
 396      $pending = bbp_get_pending_status_id();
 397      $private = bbp_get_private_status_id();
 398      $hidden  = bbp_get_hidden_status_id();
 399      $spam    = bbp_get_spam_status_id();
 400      $trash   = bbp_get_trash_status_id();
 401  
 402      // Users
 403      $user_count = ! empty( $r['count_users'] )
 404          ? bbp_get_total_users()
 405          : 0;
 406  
 407      // Forums
 408      $forum_count = ! empty( $r['count_forums'] )
 409          ? wp_count_posts( bbp_get_forum_post_type() )->{$publish}
 410          : 0;
 411  
 412      // Default capabilities
 413      $caps = array(
 414          'view_trash'           => false,
 415          'read_private_topics'  => false,
 416          'edit_others_topics'   => false,
 417          'read_private_replies' => false,
 418          'edit_others_replies'  => false,
 419          'edit_topic_tags'      => false
 420      );
 421  
 422      // Get capabilities
 423      foreach ( $caps as $key => $cap ) {
 424          $caps[ $key ] = current_user_can( $cap );
 425      }
 426  
 427      // Topics
 428      if ( ! empty( $r['count_topics'] ) ) {
 429  
 430          // Count all topics
 431          $all_topics  = wp_count_posts( bbp_get_topic_post_type() );
 432  
 433          // Published (publish + closed)
 434          $topic_count = $all_topics->{$publish} + $all_topics->{$closed};
 435  
 436          // Declare empty arrays
 437          $topics = $topic_titles = array_fill_keys( bbp_get_non_public_topic_statuses(), '' );
 438  
 439          // Pending
 440          if ( ! empty( $r['count_pending_topics'] ) && ! empty( $caps['edit_others_topics'] ) ) {
 441              $topics[ $pending ]       = bbp_number_not_negative( $all_topics->{$pending} );
 442              $topic_titles[ $pending ] = sprintf( esc_html__( 'Pending: %s', 'bbpress' ), bbp_number_format_i18n( $topics[ $pending ] ) );
 443          }
 444  
 445          // Private
 446          if ( ! empty( $r['count_private_topics'] ) && ! empty( $caps['read_private_topics'] ) ) {
 447              $topics[ $private ]       = bbp_number_not_negative( $all_topics->{$private} );
 448              $topic_titles[ $private ] = sprintf( esc_html__( 'Private: %s', 'bbpress' ), bbp_number_format_i18n( $topics[ $private ] ) );
 449          }
 450  
 451          // Hidden
 452          if ( ! empty( $r['count_hidden_topics'] ) && ! empty( $caps['read_hidden_topics'] ) ) {
 453              $topics[ $hidden ]       = bbp_number_not_negative( $all_topics->{$hidden} );
 454              $topic_titles[ $hidden ] = sprintf( esc_html__( 'Hidden: %s', 'bbpress' ), bbp_number_format_i18n( $topics[ $hidden ] ) );
 455          }
 456  
 457          // Spam
 458          if ( ! empty( $r['count_spam_topics'] ) && ! empty( $caps['edit_others_topics'] ) ) {
 459              $topics[ $spam ]       = bbp_number_not_negative( $all_topics->{$spam} );
 460              $topic_titles[ $spam ] = sprintf( esc_html__( 'Spammed: %s', 'bbpress' ), bbp_number_format_i18n( $topics[ $spam ] ) );
 461          }
 462  
 463          // Trash
 464          if ( ! empty( $r['count_trash_topics'] ) && ! empty( $caps['view_trash'] ) ) {
 465              $topics[ $trash ]       = bbp_number_not_negative( $all_topics->{$trash} );
 466              $topic_titles[ $trash ] = sprintf( esc_html__( 'Trashed: %s', 'bbpress' ), bbp_number_format_i18n( $topics[ $trash ] ) );
 467          }
 468  
 469          // Total hidden (pending, private, hidden, spam, trash)
 470          $topic_count_hidden = array_sum( array_filter( $topics ) );
 471  
 472          // Compile the hidden topic title
 473          $hidden_topic_title = implode( ' | ', array_filter( $topic_titles ) );
 474      }
 475  
 476      // Replies
 477      if ( ! empty( $r['count_replies'] ) ) {
 478  
 479          // Count all replies
 480          $all_replies = wp_count_posts( bbp_get_reply_post_type() );
 481  
 482          // Published
 483          $reply_count = $all_replies->{$publish};
 484  
 485          // Declare empty arrays
 486          $topics = $topic_titles = array_fill_keys( bbp_get_non_public_reply_statuses(), '' );
 487  
 488          // Pending
 489          if ( ! empty( $r['count_pending_replies'] ) && ! empty( $caps['edit_others_replies'] ) ) {
 490              $replies[ $pending ]      = bbp_number_not_negative( $all_replies->{$pending} );
 491              $reply_titles[ $pending ] = sprintf( esc_html__( 'Pending: %s', 'bbpress' ), bbp_number_format_i18n( $replies[ $pending ] ) );
 492          }
 493  
 494          // Private
 495          if ( ! empty( $r['count_private_replies'] ) && ! empty( $caps['read_private_replies'] ) ) {
 496              $replies[ $private ]      = bbp_number_not_negative( $all_replies->{$private} );
 497              $reply_titles[ $private ] = sprintf( esc_html__( 'Private: %s', 'bbpress' ), bbp_number_format_i18n( $replies[ $private ] ) );
 498          }
 499  
 500          // Hidden
 501          if ( ! empty( $r['count_hidden_replies'] ) && ! empty( $caps['read_hidden_replies'] ) ) {
 502              $replies[ $hidden ]      = bbp_number_not_negative( $all_replies->{$hidden} );
 503              $reply_titles[ $hidden ] = sprintf( esc_html__( 'Hidden: %s', 'bbpress' ), bbp_number_format_i18n( $replies[ $hidden ] ) );
 504          }
 505  
 506          // Spam
 507          if ( ! empty( $r['count_spam_replies'] ) && ! empty( $caps['edit_others_replies'] ) ) {
 508              $replies[ $spam ]      = bbp_number_not_negative( $all_replies->{$spam} );
 509              $reply_titles[ $spam ] = sprintf( esc_html__( 'Spammed: %s', 'bbpress' ), bbp_number_format_i18n( $replies[ $spam ] ) );
 510          }
 511  
 512          // Trash
 513          if ( ! empty( $r['count_trash_replies'] ) && ! empty( $caps['view_trash'] ) ) {
 514              $replies[ $trash ]      = bbp_number_not_negative( $all_replies->{$trash} );
 515              $reply_titles[ $trash ] = sprintf( esc_html__( 'Trashed: %s', 'bbpress' ), bbp_number_format_i18n( $replies[ $trash ] ) );
 516          }
 517  
 518          // Total hidden (pending, private, hidden, spam, trash)
 519          $reply_count_hidden = array_sum( $replies );
 520  
 521          // Compile the hidden replies title
 522          $hidden_reply_title = implode( ' | ', $reply_titles );
 523      }
 524  
 525      // Topic Tags
 526      if ( ! empty( $r['count_tags'] ) && bbp_allow_topic_tags() ) {
 527  
 528          // Get the topic-tag taxonomy ID
 529          $tt_id = bbp_get_topic_tag_tax_id();
 530  
 531          // Get the count
 532          $topic_tag_count = wp_count_terms( $tt_id, array( 'hide_empty' => true ) );
 533  
 534          // Empty tags
 535          if ( ! empty( $r['count_empty_tags'] ) && ! empty( 'edit_topic_tags' ) ) {
 536              $empty_topic_tag_count = wp_count_terms( $tt_id ) - $topic_tag_count;
 537          }
 538      }
 539  
 540      // Tally the tallies
 541      $counts = array_map( 'absint', compact(
 542          'user_count',
 543          'forum_count',
 544          'topic_count',
 545          'topic_count_hidden',
 546          'reply_count',
 547          'reply_count_hidden',
 548          'topic_tag_count',
 549          'empty_topic_tag_count'
 550      ) );
 551  
 552      // Define return value
 553      $statistics = array();
 554  
 555      // Loop through and store the integer and i18n formatted counts
 556      foreach ( $counts as $key => $count ) {
 557          $not_negative               = bbp_number_not_negative( $count );
 558          $statistics[ $key ]         = bbp_number_format_i18n( $not_negative );
 559          $statistics[ "{$key}_int" ] = $not_negative;
 560      }
 561  
 562      // Add the hidden (topic/reply) count title attribute strings
 563      $statistics['hidden_topic_title'] = $hidden_topic_title;
 564      $statistics['hidden_reply_title'] = $hidden_reply_title;
 565  
 566      // Filter & return
 567      return (array) apply_filters( 'bbp_get_statistics', $statistics, $r, $args );
 568  }
 569  
 570  /** New/edit topic/reply helpers **********************************************/
 571  
 572  /**
 573   * Filter anonymous post data
 574   *
 575   * We use REMOTE_ADDR here directly. If you are behind a proxy, you should
 576   * ensure that it is properly set, such as in wp-config.php, for your
 577   * environment. See {@link https://core.trac.wordpress.org/ticket/9235}
 578   *
 579   * Note that bbp_pre_anonymous_filters() is responsible for sanitizing each
 580   * of the filtered core anonymous values here.
 581   *
 582   * If there are any errors, those are directly added to {@link bbPress:errors}
 583   *
 584   * @since 2.0.0 bbPress (r2734)
 585   *
 586   * @param array $args Optional. If no args are there, then $_POST values are
 587   * @return bool|array False on errors, values in an array on success
 588   */
 589  function bbp_filter_anonymous_post_data( $args = array() ) {
 590  
 591      // Parse arguments against default values
 592      $r = bbp_parse_args( $args, array(
 593          'bbp_anonymous_name'    => ! empty( $_POST['bbp_anonymous_name']    ) ? $_POST['bbp_anonymous_name']    : false,
 594          'bbp_anonymous_email'   => ! empty( $_POST['bbp_anonymous_email']   ) ? $_POST['bbp_anonymous_email']   : false,
 595          'bbp_anonymous_website' => ! empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false,
 596      ), 'filter_anonymous_post_data' );
 597  
 598      // Strip invalid characters
 599      $r = bbp_sanitize_anonymous_post_author( $r );
 600  
 601      // Filter name
 602      $r['bbp_anonymous_name'] = apply_filters( 'bbp_pre_anonymous_post_author_name', $r['bbp_anonymous_name'] );
 603      if ( empty( $r['bbp_anonymous_name'] ) ) {
 604          bbp_add_error( 'bbp_anonymous_name',  __( '<strong>Error</strong>: Invalid author name.', 'bbpress' ) );
 605      }
 606  
 607      // Filter email address
 608      $r['bbp_anonymous_email'] = apply_filters( 'bbp_pre_anonymous_post_author_email', $r['bbp_anonymous_email'] );
 609      if ( empty( $r['bbp_anonymous_email'] ) ) {
 610          bbp_add_error( 'bbp_anonymous_email', __( '<strong>Error</strong>: Invalid email address.', 'bbpress' ) );
 611      }
 612  
 613      // Website is optional (can be empty)
 614      $r['bbp_anonymous_website'] = apply_filters( 'bbp_pre_anonymous_post_author_website', $r['bbp_anonymous_website'] );
 615  
 616      // Filter & return
 617      return (array) apply_filters( 'bbp_filter_anonymous_post_data', $r, $args );
 618  }
 619  
 620  /**
 621   * Sanitize an array of anonymous post author data
 622   *
 623   * @since 2.6.0 bbPress (r6400)
 624   *
 625   * @param array $anonymous_data
 626   * @return array
 627   */
 628  function bbp_sanitize_anonymous_post_author( $anonymous_data = array() ) {
 629  
 630      // Make sure anonymous data is an array
 631      if ( ! is_array( $anonymous_data ) ) {
 632          $anonymous_data = array();
 633      }
 634  
 635      // Map meta data to comment fields (as guides for stripping invalid text)
 636      $fields = array(
 637          'bbp_anonymous_name'    => 'comment_author',
 638          'bbp_anonymous_email'   => 'comment_author_email',
 639          'bbp_anonymous_website' => 'comment_author_url'
 640      );
 641  
 642      // Setup a new return array
 643      $r = $anonymous_data;
 644  
 645      // Get the database
 646      $bbp_db = bbp_db();
 647  
 648      // Strip invalid text from fields
 649      foreach ( $fields as $bbp_field => $comment_field ) {
 650          if ( ! empty( $r[ $bbp_field ] ) ) {
 651              $r[ $bbp_field ] = $bbp_db->strip_invalid_text_for_column( $bbp_db->comments, $comment_field, $r[ $bbp_field ] );
 652          }
 653      }
 654  
 655      // Filter & return
 656      return (array) apply_filters( 'bbp_sanitize_anonymous_post_author', $r, $anonymous_data );
 657  }
 658  
 659  /**
 660   * Update the relevant meta-data for an anonymous post author
 661   *
 662   * @since 2.6.0 bbPress (r6400)
 663   *
 664   * @param int    $post_id
 665   * @param array  $anonymous_data
 666   * @param string $post_type
 667   */
 668  function bbp_update_anonymous_post_author( $post_id = 0, $anonymous_data = array(), $post_type = '' ) {
 669  
 670      // Maybe look for anonymous
 671      if ( empty( $anonymous_data ) ) {
 672          $anonymous_data = bbp_filter_anonymous_post_data();
 673      }
 674  
 675      // Sanitize parameters
 676      $post_id   = (int) $post_id;
 677      $post_type = sanitize_key( $post_type );
 678  
 679      // Bail if missing required data
 680      if ( empty( $post_id ) || empty( $post_type ) || empty( $anonymous_data ) ) {
 681          return;
 682      }
 683  
 684      // Parse arguments against default values
 685      $r = bbp_parse_args( $anonymous_data, array(
 686          'bbp_anonymous_name'    => '',
 687          'bbp_anonymous_email'   => '',
 688          'bbp_anonymous_website' => '',
 689      ), "update_{$post_type}" );
 690  
 691      // Update all anonymous metas
 692      foreach ( $r as $anon_key => $anon_value ) {
 693  
 694          // Update, or delete if empty
 695          ! empty( $anon_value )
 696              ? update_post_meta( $post_id, '_' . $anon_key, (string) $anon_value, false )
 697              : delete_post_meta( $post_id, '_' . $anon_key );
 698      }
 699  }
 700  
 701  /**
 702   * Check for duplicate topics/replies
 703   *
 704   * Check to make sure that a user is not making a duplicate post
 705   *
 706   * @since 2.0.0 bbPress (r2763)
 707   *
 708   * @param array $post_data Contains information about the comment
 709   * @return bool True if it is not a duplicate, false if it is
 710   */
 711  function bbp_check_for_duplicate( $post_data = array() ) {
 712  
 713      // Parse arguments against default values
 714      $r = bbp_parse_args( $post_data, array(
 715          'post_author'    => 0,
 716          'post_type'      => array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ),
 717          'post_parent'    => 0,
 718          'post_content'   => '',
 719          'post_status'    => bbp_get_trash_status_id(),
 720          'anonymous_data' => array()
 721      ), 'check_for_duplicate' );
 722  
 723      // No duplicate checks for those who can throttle
 724      if ( user_can( (int) $r['post_author'], 'throttle' ) ) {
 725          return true;
 726      }
 727  
 728      // Get the DB
 729      $bbp_db = bbp_db();
 730  
 731      // Default clauses
 732      $join = $where = '';
 733  
 734      // Check for anonymous post
 735      if ( empty( $r['post_author'] ) && ( ! empty( $r['anonymous_data'] ) && ! empty( $r['anonymous_data']['bbp_anonymous_email'] ) ) ) {
 736  
 737          // Sanitize the email address for querying
 738          $email = sanitize_email( $r['anonymous_data']['bbp_anonymous_email'] );
 739  
 740          // Only proceed
 741          if ( ! empty( $email ) && is_email( $email ) ) {
 742  
 743              // Get the meta SQL
 744              $clauses = get_meta_sql( array( array(
 745                  'key'   => '_bbp_anonymous_email',
 746                  'value' => $email,
 747              ) ), 'post', $bbp_db->posts, 'ID' );
 748  
 749              // Set clauses
 750              $join  = $clauses['join'];
 751  
 752              // "'", "%", "$" and are valid characters in email addresses
 753              $where = $bbp_db->remove_placeholder_escape( $clauses['where'] );
 754          }
 755      }
 756  
 757      // Unslash $r to pass through DB->prepare()
 758      //
 759      // @see: https://bbpress.trac.wordpress.org/ticket/2185/
 760      // @see: https://core.trac.wordpress.org/changeset/23973/
 761      $r = wp_unslash( $r );
 762  
 763      // Prepare duplicate check query
 764      $query  = "SELECT ID FROM {$bbp_db->posts} {$join}";
 765      $query .= $bbp_db->prepare('WHERE post_type = %s AND post_status != %s AND post_author = %d AND post_content = %s', $r['post_type'], $r['post_status'], $r['post_author'], $r['post_content'] );
 766      $query .= ! empty( $r['post_parent'] )
 767          ? $bbp_db->prepare( ' AND post_parent = %d', $r['post_parent'] )
 768          : '';
 769      $query .= $where;
 770      $query .= ' LIMIT 1';
 771      $dupe   = apply_filters( 'bbp_check_for_duplicate_query', $query, $r );
 772  
 773      // Dupe found
 774      if ( $bbp_db->get_var( $dupe ) ) {
 775          do_action( 'bbp_check_for_duplicate_trigger', $post_data );
 776          return false;
 777      }
 778  
 779      // Dupe not found
 780      return true;
 781  }
 782  
 783  /**
 784   * Check for flooding
 785   *
 786   * Check to make sure that a user is not making too many posts in a short amount
 787   * of time.
 788   *
 789   * @since 2.0.0 bbPress (r2734)
 790   *
 791   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
 792   *                              supply if supplying $author_id. Should be
 793   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
 794   * @param int $author_id Optional. Supply if it's a post by a logged in user.
 795   *                        Do not supply if supplying $anonymous_data.
 796   * @return bool True if there is no flooding, false if there is
 797   */
 798  function bbp_check_for_flood( $anonymous_data = array(), $author_id = 0 ) {
 799  
 800      // Allow for flood check to be skipped
 801      if ( apply_filters( 'bbp_bypass_check_for_flood', false, $anonymous_data, $author_id ) ) {
 802          return true;
 803      }
 804  
 805      // Option disabled. No flood checks.
 806      $throttle_time = get_option( '_bbp_throttle_time' );
 807      if ( empty( $throttle_time ) || ! bbp_allow_content_throttle() ) {
 808          return true;
 809      }
 810  
 811      // User is anonymous, so check a transient based on the IP
 812      if ( ! empty( $anonymous_data ) ) {
 813          $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' );
 814  
 815          if ( ! empty( $last_posted ) && ( time() < ( $last_posted + $throttle_time ) ) ) {
 816              return false;
 817          }
 818  
 819      // User is logged in, so check their last posted time
 820      } elseif ( ! empty( $author_id ) ) {
 821          $author_id   = (int) $author_id;
 822          $last_posted = bbp_get_user_last_posted( $author_id );
 823  
 824          if ( ! empty( $last_posted ) && ( time() < ( $last_posted + $throttle_time ) ) && ! user_can( $author_id, 'throttle' ) ) {
 825              return false;
 826          }
 827      } else {
 828          return false;
 829      }
 830  
 831      return true;
 832  }
 833  
 834  /**
 835   * Checks topics and replies against the discussion moderation of blocked keys
 836   *
 837   * @since 2.1.0 bbPress (r3581)
 838   *
 839   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
 840   *                              supply if supplying $author_id. Should be
 841   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
 842   * @param int $author_id Topic or reply author ID
 843   * @param string $title The title of the content
 844   * @param string $content The content being posted
 845   * @param mixed  $strict  False for moderation_keys. True for blacklist_keys.
 846   *                        String for custom keys.
 847   * @return bool True if test is passed, false if fail
 848   */
 849  function bbp_check_for_moderation( $anonymous_data = array(), $author_id = 0, $title = '', $content = '', $strict = false ) {
 850  
 851      // Custom moderation option key
 852      if ( is_string( $strict ) ) {
 853          $strict = sanitize_key( $strict );
 854  
 855          // Use custom key
 856          if ( ! empty( $strict ) ) {
 857              $hook_name   = $strict;
 858              $option_name = "{$strict}_keys";
 859  
 860          // Key was invalid, so default to moderation keys
 861          } else {
 862              $strict = false;
 863          }
 864      }
 865  
 866      // Strict mode uses WordPress "blacklist" settings
 867      if ( true === $strict ) {
 868          $hook_name   = 'blacklist';
 869          $option_name = 'blacklist_keys';
 870  
 871      // Non-strict uses WordPress "moderation" settings
 872      } elseif ( false === $strict ) {
 873          $hook_name   = 'moderation';
 874          $option_name = 'moderation_keys';
 875      }
 876  
 877      // Allow for moderation check to be skipped
 878      if ( apply_filters( "bbp_bypass_check_for_{$hook_name}", false, $anonymous_data, $author_id, $title, $content, $strict ) ) {
 879          return true;
 880      }
 881  
 882      // Maybe perform some author-specific capability checks
 883      if ( ! empty( $author_id ) ) {
 884  
 885          // Bail if user is a keymaster
 886          if ( bbp_is_user_keymaster( $author_id ) ) {
 887              return true;
 888  
 889          // Bail if user can moderate
 890          // https://bbpress.trac.wordpress.org/ticket/2726
 891          } elseif ( ( false === $strict ) && user_can( $author_id, 'moderate' ) ) {
 892              return true;
 893          }
 894      }
 895  
 896      // Define local variable(s)
 897      $_post     = array();
 898      $match_out = '';
 899  
 900      /** Max Links *************************************************************/
 901  
 902      // Only check max_links when not being strict
 903      if ( false === $strict ) {
 904          $max_links = get_option( 'comment_max_links' );
 905          if ( ! empty( $max_links ) ) {
 906  
 907              // How many links?
 908              $num_links = preg_match_all( '/(http|ftp|https):\/\//i', $content, $match_out );
 909  
 910              // Allow for bumping the max to include the user's URL
 911              if ( ! empty( $_post['url'] ) ) {
 912                  $num_links = apply_filters( 'comment_max_links_url', $num_links, $_post['url'], $content );
 913              }
 914  
 915              // Das ist zu viele links!
 916              if ( $num_links >= $max_links ) {
 917                  return false;
 918              }
 919          }
 920      }
 921  
 922      /** Moderation ************************************************************/
 923  
 924      /**
 925       * Filters the bbPress moderation keys.
 926       *
 927       * @since 2.6.0 bbPress (r6050)
 928       *
 929       * @param string $moderation List of moderation keys. One per new line.
 930       */
 931      $moderation = apply_filters( "bbp_{$hook_name}_keys", trim( get_option( $option_name ) ) );
 932  
 933      // Bail if no words to look for
 934      if ( empty( $moderation ) ) {
 935          return true;
 936      }
 937  
 938      /** User Data *************************************************************/
 939  
 940      // Map anonymous user data
 941      if ( ! empty( $anonymous_data ) ) {
 942          $_post['author'] = $anonymous_data['bbp_anonymous_name'];
 943          $_post['email']  = $anonymous_data['bbp_anonymous_email'];
 944          $_post['url']    = $anonymous_data['bbp_anonymous_website'];
 945  
 946      // Map current user data
 947      } elseif ( ! empty( $author_id ) ) {
 948  
 949          // Get author data
 950          $user = get_userdata( $author_id );
 951  
 952          // If data exists, map it
 953          if ( ! empty( $user ) ) {
 954              $_post['author'] = $user->display_name;
 955              $_post['email']  = $user->user_email;
 956              $_post['url']    = $user->user_url;
 957          }
 958      }
 959  
 960      // Current user IP and user agent
 961      $_post['user_ip'] = bbp_current_author_ip();
 962      $_post['user_ua'] = bbp_current_author_ua();
 963  
 964      // Post title and content
 965      $_post['title']   = $title;
 966      $_post['content'] = $content;
 967  
 968      // Ensure HTML tags are not being used to bypass the moderation list.
 969      $_post['comment_without_html'] = wp_strip_all_tags( $content );
 970  
 971      /** Words *****************************************************************/
 972  
 973      // Get words separated by new lines
 974      $words = explode( "\n", $moderation );
 975  
 976      // Loop through words
 977      foreach ( (array) $words as $word ) {
 978  
 979          // Trim the whitespace from the word
 980          $word = trim( $word );
 981  
 982          // Skip empty lines
 983          if ( empty( $word ) ) {
 984              continue;
 985          }
 986  
 987          // Do some escaping magic so that '#' chars in the
 988          // spam words don't break things:
 989          $word    = preg_quote( $word, '#' );
 990          $pattern = "#{$word}#i";
 991  
 992          // Loop through post data
 993          foreach ( $_post as $post_data ) {
 994  
 995              // Check each user data for current word
 996              if ( preg_match( $pattern, $post_data ) ) {
 997  
 998                  // Post does not pass
 999                  return false;
1000              }
1001          }
1002      }
1003  
1004      // Check passed successfully
1005      return true;
1006  }
1007  
1008  /**
1009   * Deprecated. Use bbp_check_for_moderation() with strict flag set.
1010   *
1011   * @since 2.0.0 bbPress (r3446)
1012   * @since 2.6.0 bbPress (r6854)
1013   * @deprecated 2.6.0 Use bbp_check_for_moderation() with strict flag set
1014   */
1015  function bbp_check_for_blacklist( $anonymous_data = array(), $author_id = 0, $title = '', $content = '' ) {
1016      return bbp_check_for_moderation( $anonymous_data, $author_id, $title, $content, true );
1017  }
1018  
1019  /** Subscriptions *************************************************************/
1020  
1021  /**
1022   * Get the "Do Not Reply" email address to use when sending subscription emails.
1023   *
1024   * We make some educated guesses here based on the home URL. Filters are
1025   * available to customize this address further. In the future, we may consider
1026   * using `admin_email` instead, though this is not normally publicized.
1027   *
1028   * We use `$_SERVER['SERVER_NAME']` here to mimic similar functionality in
1029   * WordPress core. Previously, we used `get_home_url()` to use already validated
1030   * user input, but it was causing issues in some installations.
1031   *
1032   * @since 2.6.0 bbPress (r5409)
1033   *
1034   * @see  wp_mail
1035   * @see  wp_notify_postauthor
1036   * @link https://bbpress.trac.wordpress.org/ticket/2618
1037   *
1038   * @return string
1039   */
1040  function bbp_get_do_not_reply_address() {
1041      $sitename = strtolower( $_SERVER['SERVER_NAME'] );
1042      if ( substr( $sitename, 0, 4 ) === 'www.' ) {
1043          $sitename = substr( $sitename, 4 );
1044      }
1045  
1046      // Filter & return
1047      return apply_filters( 'bbp_get_do_not_reply_address', 'noreply@' . $sitename );
1048  }
1049  
1050  /**
1051   * Sends notification emails for new replies to subscribed topics
1052   *
1053   * Gets new post ID and check if there are subscribed users to that topic, and
1054   * if there are, send notifications
1055   *
1056   * Note: in bbPress 2.6, we've moved away from 1 email per subscriber to 1 email
1057   * with everyone BCC'd. This may have negative repercussions for email services
1058   * that limit the number of addresses in a BCC field (often to around 500.) In
1059   * those cases, we recommend unhooking this function and creating your own
1060   * custom email script.
1061   *
1062   * @since 2.6.0 bbPress (r5413)
1063   *
1064   * @param int $reply_id ID of the newly made reply
1065   * @param int $topic_id ID of the topic of the reply
1066   * @param int $forum_id ID of the forum of the reply
1067   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
1068   *                              supply if supplying $author_id. Should be
1069   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
1070   * @param int $reply_author ID of the topic author ID
1071   * @return bool True on success, false on failure
1072   */
1073  function bbp_notify_topic_subscribers( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $reply_author = 0 ) {
1074  
1075      // Bail if subscriptions are turned off
1076      if ( ! bbp_is_subscriptions_active() ) {
1077          return false;
1078      }
1079  
1080      // Bail if importing
1081      if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
1082          return false;
1083      }
1084  
1085      /** Validation ************************************************************/
1086  
1087      $reply_id = bbp_get_reply_id( $reply_id );
1088      $topic_id = bbp_get_topic_id( $topic_id );
1089      $forum_id = bbp_get_forum_id( $forum_id );
1090  
1091      /** Topic *****************************************************************/
1092  
1093      // Bail if topic is not public (includes closed)
1094      if ( ! bbp_is_topic_public( $topic_id ) ) {
1095          return false;
1096      }
1097  
1098      /** Reply *****************************************************************/
1099  
1100      // Bail if reply is not published
1101      if ( ! bbp_is_reply_published( $reply_id ) ) {
1102          return false;
1103      }
1104  
1105      // Poster name
1106      $reply_author_name = bbp_get_reply_author_display_name( $reply_id );
1107  
1108      /** Users *****************************************************************/
1109  
1110      // Get topic subscribers and bail if empty
1111      $user_ids = bbp_get_subscribers( $topic_id );
1112  
1113      // Remove the reply author from the list.
1114      $reply_author_key = array_search( (int) $reply_author, $user_ids, true );
1115      if ( false !== $reply_author_key ) {
1116          unset( $user_ids[ $reply_author_key ] );
1117      }
1118  
1119      // Dedicated filter to manipulate user ID's to send emails to
1120      $user_ids = (array) apply_filters( 'bbp_topic_subscription_user_ids', $user_ids, $reply_id, $topic_id );
1121  
1122      // Bail of the reply author was the only one subscribed.
1123      if ( empty( $user_ids ) ) {
1124          return false;
1125      }
1126  
1127      // Get email addresses, bail if empty
1128      $email_addresses = bbp_get_email_addresses_from_user_ids( $user_ids );
1129      if ( empty( $email_addresses ) ) {
1130          return false;
1131      }
1132  
1133      /** Mail ******************************************************************/
1134  
1135      // Remove filters from reply content and topic title to prevent content
1136      // from being encoded with HTML entities, wrapped in paragraph tags, etc...
1137      bbp_remove_all_filters( 'bbp_get_reply_content' );
1138      bbp_remove_all_filters( 'bbp_get_topic_title'   );
1139      bbp_remove_all_filters( 'the_title'             );
1140  
1141      // Strip tags from text and setup mail data
1142      $forum_title       = wp_specialchars_decode( strip_tags( bbp_get_forum_title( $forum_id ) ), ENT_QUOTES );
1143      $topic_title       = wp_specialchars_decode( strip_tags( bbp_get_topic_title( $topic_id ) ), ENT_QUOTES );
1144      $reply_author_name = wp_specialchars_decode( strip_tags( $reply_author_name ), ENT_QUOTES );
1145      $reply_content     = wp_specialchars_decode( strip_tags( bbp_get_reply_content( $reply_id ) ), ENT_QUOTES );
1146      $reply_url         = bbp_get_reply_url( $reply_id );
1147  
1148      // For plugins to filter messages per reply/topic/user
1149      $message = sprintf( esc_html__( '%1$s wrote:
1150  
1151  %2$s
1152  
1153  Post Link: %3$s
1154  
1155  -----------
1156  
1157  You are receiving this email because you subscribed to a forum topic.
1158  
1159  Login and visit the topic to unsubscribe from these emails.', 'bbpress' ),
1160  
1161          $reply_author_name,
1162          $reply_content,
1163          $reply_url
1164      );
1165  
1166      $message = apply_filters( 'bbp_subscription_mail_message', $message, $reply_id, $topic_id );
1167      if ( empty( $message ) ) {
1168          return;
1169      }
1170  
1171      // For plugins to filter titles per reply/topic/user
1172      $subject = apply_filters( 'bbp_subscription_mail_title', '[' . $forum_title . '] ' . $topic_title, $reply_id, $topic_id );
1173      if ( empty( $subject ) ) {
1174          return;
1175      }
1176  
1177      /** Headers ***************************************************************/
1178  
1179      // Default bbPress X-header
1180      $headers    = array( bbp_get_email_header() );
1181  
1182      // Get the noreply@ address
1183      $no_reply   = bbp_get_do_not_reply_address();
1184  
1185      // Setup "From" email address
1186      $from_email = apply_filters( 'bbp_subscription_from_email', $no_reply );
1187  
1188      // Setup the From header
1189      $headers[]  = 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>';
1190  
1191      // Loop through addresses
1192      foreach ( (array) $email_addresses as $address ) {
1193          $headers[] = 'Bcc: ' . $address;
1194      }
1195  
1196      /** Send it ***************************************************************/
1197  
1198      // Custom headers
1199      $headers  = apply_filters( 'bbp_subscription_mail_headers', $headers  );
1200      $to_email = apply_filters( 'bbp_subscription_to_email',     $no_reply );
1201  
1202      // Before
1203      do_action( 'bbp_pre_notify_subscribers', $reply_id, $topic_id, $user_ids );
1204  
1205      // Send notification email
1206      wp_mail( $to_email, $subject, $message, $headers );
1207  
1208      // After
1209      do_action( 'bbp_post_notify_subscribers', $reply_id, $topic_id, $user_ids );
1210  
1211      // Restore previously removed filters
1212      bbp_restore_all_filters( 'bbp_get_topic_content' );
1213      bbp_restore_all_filters( 'bbp_get_topic_title'   );
1214      bbp_restore_all_filters( 'the_title'             );
1215  
1216      return true;
1217  }
1218  
1219  /**
1220   * Sends notification emails for new topics to subscribed forums
1221   *
1222   * Gets new post ID and check if there are subscribed users to that forum, and
1223   * if there are, send notifications
1224   *
1225   * Note: in bbPress 2.6, we've moved away from 1 email per subscriber to 1 email
1226   * with everyone BCC'd. This may have negative repercussions for email services
1227   * that limit the number of addresses in a BCC field (often to around 500.) In
1228   * those cases, we recommend unhooking this function and creating your own
1229   * custom email script.
1230   *
1231   * @since 2.5.0 bbPress (r5156)
1232   *
1233   * @param int $topic_id ID of the newly made reply
1234   * @param int $forum_id ID of the forum for the topic
1235   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
1236   *                              supply if supplying $author_id. Should be
1237   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
1238   * @param int $topic_author ID of the topic author ID
1239   * @return bool True on success, false on failure
1240   */
1241  function bbp_notify_forum_subscribers( $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $topic_author = 0 ) {
1242  
1243      // Bail if subscriptions are turned off
1244      if ( ! bbp_is_subscriptions_active() ) {
1245          return false;
1246      }
1247  
1248      // Bail if importing
1249      if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
1250          return false;
1251      }
1252  
1253      /** Validation ************************************************************/
1254  
1255      $topic_id = bbp_get_topic_id( $topic_id );
1256      $forum_id = bbp_get_forum_id( $forum_id );
1257  
1258      /**
1259       * Necessary for backwards compatibility
1260       *
1261       * @see https://bbpress.trac.wordpress.org/ticket/2620
1262       */
1263      $user_id  = 0;
1264  
1265      /** Topic *****************************************************************/
1266  
1267      // Bail if topic is not public (includes closed)
1268      if ( ! bbp_is_topic_public( $topic_id ) ) {
1269          return false;
1270      }
1271  
1272      // Poster name
1273      $topic_author_name = bbp_get_topic_author_display_name( $topic_id );
1274  
1275      /** Users *****************************************************************/
1276  
1277      // Get topic subscribers and bail if empty
1278      $user_ids = bbp_get_subscribers( $forum_id );
1279  
1280      // Remove the topic author from the list.
1281      $topic_author_key = array_search( (int) $topic_author, $user_ids, true );
1282      if ( false !== $topic_author_key ) {
1283          unset( $user_ids[ $topic_author_key ] );
1284      }
1285  
1286      // Dedicated filter to manipulate user ID's to send emails to
1287      $user_ids = (array) apply_filters( 'bbp_forum_subscription_user_ids', $user_ids, $topic_id, $forum_id );
1288  
1289      // Bail of the reply author was the only one subscribed.
1290      if ( empty( $user_ids ) ) {
1291          return false;
1292      }
1293  
1294      // Get email addresses, bail if empty
1295      $email_addresses = bbp_get_email_addresses_from_user_ids( $user_ids );
1296      if ( empty( $email_addresses ) ) {
1297          return false;
1298      }
1299  
1300      /** Mail ******************************************************************/
1301  
1302      // Remove filters from reply content and topic title to prevent content
1303      // from being encoded with HTML entities, wrapped in paragraph tags, etc...
1304      bbp_remove_all_filters( 'bbp_get_topic_content' );
1305      bbp_remove_all_filters( 'bbp_get_topic_title'   );
1306      bbp_remove_all_filters( 'the_title'             );
1307  
1308      // Strip tags from text and setup mail data
1309      $forum_title       = wp_specialchars_decode( strip_tags( bbp_get_forum_title( $forum_id ) ), ENT_QUOTES );
1310      $topic_title       = wp_specialchars_decode( strip_tags( bbp_get_topic_title( $topic_id ) ), ENT_QUOTES );
1311      $topic_author_name = wp_specialchars_decode( strip_tags( $topic_author_name ), ENT_QUOTES );
1312      $topic_content     = wp_specialchars_decode( strip_tags( bbp_get_topic_content( $topic_id ) ), ENT_QUOTES );
1313      $topic_url         = get_permalink( $topic_id );
1314  
1315      // For plugins to filter messages per reply/topic/user
1316      $message = sprintf( esc_html__( '%1$s wrote:
1317  
1318  %2$s
1319  
1320  Topic Link: %3$s
1321  
1322  -----------
1323  
1324  You are receiving this email because you subscribed to a forum.
1325  
1326  Login and visit the topic to unsubscribe from these emails.', 'bbpress' ),
1327  
1328          $topic_author_name,
1329          $topic_content,
1330          $topic_url
1331      );
1332  
1333      $message = apply_filters( 'bbp_forum_subscription_mail_message', $message, $topic_id, $forum_id, $user_id );
1334      if ( empty( $message ) ) {
1335          return;
1336      }
1337  
1338      // For plugins to filter titles per reply/topic/user
1339      $subject = apply_filters( 'bbp_forum_subscription_mail_title', '[' . $forum_title . '] ' . $topic_title, $topic_id, $forum_id, $user_id );
1340      if ( empty( $subject ) ) {
1341          return;
1342      }
1343  
1344      /** Headers ***************************************************************/
1345  
1346      // Default bbPress X-header
1347      $headers    = array( bbp_get_email_header() );
1348  
1349      // Get the noreply@ address
1350      $no_reply   = bbp_get_do_not_reply_address();
1351  
1352      // Setup "From" email address
1353      $from_email = apply_filters( 'bbp_subscription_from_email', $no_reply );
1354  
1355      // Setup the From header
1356      $headers[] = 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>';
1357  
1358      // Loop through addresses
1359      foreach ( (array) $email_addresses as $address ) {
1360          $headers[] = 'Bcc: ' . $address;
1361      }
1362  
1363      /** Send it ***************************************************************/
1364  
1365      // Custom headers
1366      $headers  = apply_filters( 'bbp_subscription_mail_headers', $headers  );
1367      $to_email = apply_filters( 'bbp_subscription_to_email',     $no_reply );
1368  
1369      // Before
1370      do_action( 'bbp_pre_notify_forum_subscribers', $topic_id, $forum_id, $user_ids );
1371  
1372      // Send notification email
1373      wp_mail( $to_email, $subject, $message, $headers );
1374  
1375      // After
1376      do_action( 'bbp_post_notify_forum_subscribers', $topic_id, $forum_id, $user_ids );
1377  
1378      // Restore previously removed filters
1379      bbp_restore_all_filters( 'bbp_get_topic_content' );
1380      bbp_restore_all_filters( 'bbp_get_topic_title'   );
1381      bbp_restore_all_filters( 'the_title'             );
1382  
1383      return true;
1384  }
1385  
1386  /**
1387   * Sends notification emails for new replies to subscribed topics
1388   *
1389   * This function is deprecated. Please use: bbp_notify_topic_subscribers()
1390   *
1391   * @since 2.0.0 bbPress (r2668)
1392   *
1393   * @deprecated 2.6.0 bbPress (r5412)
1394   *
1395   * @param int $reply_id ID of the newly made reply
1396   * @param int $topic_id ID of the topic of the reply
1397   * @param int $forum_id ID of the forum of the reply
1398   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
1399   *                              supply if supplying $author_id. Should be
1400   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
1401   * @param int $reply_author ID of the topic author ID
1402   *
1403   * @return bool True on success, false on failure
1404   */
1405  function bbp_notify_subscribers( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $reply_author = 0 ) {
1406      return bbp_notify_topic_subscribers( $reply_id, $topic_id, $forum_id, $anonymous_data, $reply_author );
1407  }
1408  
1409  /**
1410   * Return an array of user email addresses from an array of user IDs
1411   *
1412   * @since 2.6.0 bbPress (r6722)
1413   *
1414   * @param array $user_ids
1415   * @return array
1416   */
1417  function bbp_get_email_addresses_from_user_ids( $user_ids = array() ) {
1418  
1419      // Default return value
1420      $retval = array();
1421  
1422      // Maximum number of users to get per database query
1423      $limit = apply_filters( 'bbp_get_users_chunk_limit', 100 );
1424  
1425      // Only do the work if there are user IDs to query for
1426      if ( ! empty( $user_ids ) ) {
1427  
1428          // Get total number of sets
1429          $steps = ceil( count( $user_ids ) / $limit );
1430          $range = array_map( 'intval', range( 1, $steps ) );
1431  
1432          // Loop through users
1433          foreach ( $range as $loop ) {
1434  
1435              // Initial loop has no offset
1436              $offset = $limit * ( $loop - 1 );
1437  
1438              // Calculate user IDs to include
1439              $loop_ids = array_slice( $user_ids, $offset, $limit );
1440  
1441              // Skip if something went wrong
1442              if ( empty( $loop_ids ) ) {
1443                  continue;
1444              }
1445  
1446              // Call get_users() in a way that users are cached
1447              $loop_users = get_users( array(
1448                  'blog_id' => 0,
1449                  'fields'  => 'all_with_meta',
1450                  'include' => $loop_ids
1451              ) );
1452  
1453              // Pluck emails from users
1454              $loop_emails = wp_list_pluck( $loop_users, 'user_email' );
1455  
1456              // Clean-up memory, for big user sets
1457              unset( $loop_users );
1458  
1459              // Merge users into return value
1460              if ( ! empty( $loop_emails ) ) {
1461                  $retval = array_merge( $retval, $loop_emails );
1462              }
1463          }
1464  
1465          // No duplicates
1466          $retval = bbp_get_unique_array_values( $retval );
1467      }
1468  
1469      // Filter & return
1470      return apply_filters( 'bbp_get_email_addresses_from_user_ids', $retval, $user_ids, $limit );
1471  }
1472  
1473  /**
1474   * Automatically splits bbPress emails with many Bcc recipients into chunks.
1475   *
1476   * This middleware is useful because topics and forums with many subscribers
1477   * run into problems with Bcc limits, and many hosting companies & third-party
1478   * services limit the size of a Bcc audience to prevent spamming.
1479   *
1480   * The default "chunk" size is 40 users per iteration, and can be filtered if
1481   * desired. A future version of bbPress will introduce a setting to more easily
1482   * tune this.
1483   *
1484   * @since 2.6.0 bbPress (r6918)
1485   *
1486   * @param array $args Original arguments passed to wp_mail().
1487   * @return array
1488   */
1489  function bbp_chunk_emails( $args = array() ) {
1490  
1491      // Get the maximum number of Bcc's per chunk
1492      $max_num = apply_filters( 'bbp_get_bcc_chunk_limit', 40, $args );
1493  
1494      // Look for "bcc: " in a case-insensitive way, and split into 2 sets
1495      $match       = '/^bcc: (\w+)/i';
1496      $old_headers = preg_grep( $match, $args['headers'], PREG_GREP_INVERT );
1497      $bcc_headers = preg_grep( $match, $args['headers'] );
1498  
1499      // Bail if less than $max_num recipients
1500      if ( empty( $bcc_headers ) || ( count( $bcc_headers ) < $max_num ) ) {
1501          return $args;
1502      }
1503  
1504      // Reindex the headers arrays
1505      $old_headers = array_values( $old_headers );
1506      $bcc_headers = array_values( $bcc_headers );
1507  
1508      // Break the Bcc emails into chunks
1509      foreach ( array_chunk( $bcc_headers, $max_num ) as $i => $chunk ) {
1510  
1511          // Skip the first chunk (it will get used in the original wp_mail() call)
1512          if ( 0 === $i ) {
1513              $first_chunk = $chunk;
1514              continue;
1515          }
1516  
1517          // Send out the chunk
1518          $chunk_headers = array_merge( $old_headers, $chunk );
1519  
1520          // Recursion alert, but should be OK!
1521          wp_mail(
1522              $args['to'],
1523              $args['subject'],
1524              $args['message'],
1525              $chunk_headers,
1526              $args['attachments']
1527          );
1528      }
1529  
1530      // Set headers to old headers + the $first_chunk of Bcc's
1531      $args['headers'] = array_merge( $old_headers, $first_chunk );
1532  
1533      // Return the reduced args, with the first chunk of Bcc's
1534      return $args;
1535  }
1536  
1537  /**
1538   * Return the string used for the bbPress specific X-header.
1539   *
1540   * @since 2.6.0 bbPress (r6919)
1541   *
1542   * @return string
1543   */
1544  function bbp_get_email_header() {
1545      return apply_filters( 'bbp_get_email_header', 'X-bbPress: ' . bbp_get_version() );
1546  }
1547  
1548  /** Login *********************************************************************/
1549  
1550  /**
1551   * Return a clean and reliable logout URL
1552   *
1553   * This function is used to filter `logout_url`. If no $redirect_to value is
1554   * passed, it will default to the request uri, then the forum root.
1555   *
1556   * See: `wp_logout_url()`
1557   *
1558   * @since 2.1.0 bbPress (2815)
1559   *
1560   * @param string $url URL used to log out
1561   * @param string $redirect_to Where to redirect to?
1562   *
1563   * @return string The url
1564   */
1565  function bbp_logout_url( $url = '', $redirect_to = '' ) {
1566  
1567      // If there is no redirect in the URL, let's add one...
1568      if ( ! strstr( $url, 'redirect_to' ) ) {
1569  
1570          // Get the forum root, to maybe use as a default
1571          $forum_root = bbp_get_root_url();
1572  
1573          // No redirect passed, so check referer and fallback to request uri
1574          if ( empty( $redirect_to ) ) {
1575  
1576              // Check for a valid referer
1577              $redirect_to = wp_get_referer();
1578  
1579              // Fallback to request uri if invalid referer
1580              if ( false === $redirect_to ) {
1581                  $redirect_to = bbp_get_url_scheme() . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
1582              }
1583          }
1584  
1585          // Filter the $redirect_to destination
1586          $filtered  = apply_filters( 'bbp_logout_url_redirect_to', $redirect_to );
1587  
1588          // Validate $redirect_to, default to root
1589          $validated = wp_validate_redirect( $filtered, $forum_root );
1590  
1591          // Assemble $redirect_to and add it (encoded) to full $url
1592          $appended  = add_query_arg( array( 'loggedout'   => 'true'   ), $validated );
1593          $encoded   = urlencode( $appended );
1594          $url       = add_query_arg( array( 'redirect_to' => $encoded ), $url       );
1595      }
1596  
1597      // Filter & return
1598      return apply_filters( 'bbp_logout_url', $url, $redirect_to );
1599  }
1600  
1601  /** Queries *******************************************************************/
1602  
1603  /**
1604   * Merge user defined arguments into defaults array.
1605   *
1606   * This function is used throughout bbPress to allow for either a string or array
1607   * to be merged into another array. It is identical to wp_parse_args() except
1608   * it allows for arguments to be passively or aggressively filtered using the
1609   * optional $filter_key parameter.
1610   *
1611   * @since 2.1.0 bbPress (r3839)
1612   *
1613   * @param string|array $args Value to merge with $defaults
1614   * @param array $defaults Array that serves as the defaults.
1615   * @param string $filter_key String to key the filters from
1616   * @return array Merged user defined values with defaults.
1617   */
1618  function bbp_parse_args( $args, $defaults = array(), $filter_key = '' ) {
1619  
1620      // Setup a temporary array from $args
1621      if ( is_object( $args ) ) {
1622          $r = get_object_vars( $args );
1623      } elseif ( is_array( $args ) ) {
1624          $r =& $args;
1625      } else {
1626          wp_parse_str( $args, $r );
1627      }
1628  
1629      // Passively filter the args before the parse
1630      if ( ! empty( $filter_key ) ) {
1631          $r = apply_filters( "bbp_before_{$filter_key}_parse_args", $r, $args, $defaults );
1632      }
1633  
1634      // Parse
1635      if ( is_array( $defaults ) && ! empty( $defaults ) ) {
1636          $r = array_merge( $defaults, $r );
1637      }
1638  
1639      // Aggressively filter the args after the parse
1640      if ( ! empty( $filter_key ) ) {
1641          $r = apply_filters( "bbp_after_{$filter_key}_parse_args", $r, $args, $defaults );
1642      }
1643  
1644      // Return the parsed results
1645      return $r;
1646  }
1647  
1648  /**
1649   * Adds ability to include or exclude specific post_parent ID's
1650   *
1651   * @since 2.0.0 bbPress (r2996)
1652   *
1653   * @deprecated 2.5.8 bbPress (r5814)
1654   *
1655   * @global WP $wp
1656   * @param string $where
1657   * @param WP_Query $object
1658   * @return string
1659   */
1660  function bbp_query_post_parent__in( $where, $object = '' ) {
1661      global $wp;
1662  
1663      // Noop if WP core supports this already
1664      if ( in_array( 'post_parent__in', $wp->private_query_vars, true ) ) {
1665          return $where;
1666      }
1667  
1668      // Bail if no object passed
1669      if ( empty( $object ) ) {
1670          return $where;
1671      }
1672  
1673      // Only 1 post_parent so return $where
1674      if ( is_numeric( $object->query_vars['post_parent'] ) ) {
1675          return $where;
1676      }
1677  
1678      // Get the DB
1679      $bbp_db = bbp_db();
1680  
1681      // Including specific post_parent's
1682      if ( ! empty( $object->query_vars['post_parent__in'] ) ) {
1683          $ids    = implode( ',', wp_parse_id_list( $object->query_vars['post_parent__in'] ) );
1684          $where .= " AND {$bbp_db->posts}.post_parent IN ($ids)";
1685  
1686      // Excluding specific post_parent's
1687      } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) {
1688          $ids    = implode( ',', wp_parse_id_list( $object->query_vars['post_parent__not_in'] ) );
1689          $where .= " AND {$bbp_db->posts}.post_parent NOT IN ($ids)";
1690      }
1691  
1692      // Return possibly modified $where
1693      return $where;
1694  }
1695  
1696  /**
1697   * Query the DB and get the last public post_id that has parent_id as post_parent
1698   *
1699   * @since 2.0.0 bbPress (r2868)
1700   * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
1701   *
1702   * @param int    $parent_id Parent id.
1703   * @param string $post_type Post type. Defaults to 'post'.
1704   * @return int The last active post_id
1705   */
1706  function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) {
1707  
1708      // Bail if nothing passed
1709      if ( empty( $parent_id ) ) {
1710          return false;
1711      }
1712  
1713      // Which statuses
1714      switch ( $post_type ) {
1715  
1716          // Forum
1717          case bbp_get_forum_post_type() :
1718              $post_status = bbp_get_public_forum_statuses();
1719              break;
1720  
1721          // Topic
1722          case bbp_get_topic_post_type() :
1723              $post_status = bbp_get_public_topic_statuses();
1724              break;
1725  
1726          // Reply
1727          case bbp_get_reply_post_type() :
1728          default :
1729              $post_status = bbp_get_public_reply_statuses();
1730              break;
1731      }
1732  
1733      $query = new WP_Query( array(
1734          'fields'         => 'ids',
1735          'post_parent'    => $parent_id,
1736          'post_status'    => $post_status,
1737          'post_type'      => $post_type,
1738          'posts_per_page' => 1,
1739          'orderby'        => array(
1740              'post_date' => 'DESC',
1741              'ID'        => 'DESC'
1742          ),
1743  
1744          // Performance
1745          'suppress_filters'       => true,
1746          'update_post_term_cache' => false,
1747          'update_post_meta_cache' => false,
1748          'ignore_sticky_posts'    => true,
1749          'no_found_rows'          => true
1750      ) );
1751      $child_id = array_shift( $query->posts );
1752      unset( $query );
1753  
1754      // Filter & return
1755      return (int) apply_filters( 'bbp_get_public_child_last_id', $child_id, $parent_id, $post_type );
1756  }
1757  
1758  /**
1759   * Query the database for child counts, grouped by type & status
1760   *
1761   * @since 2.6.0 bbPress (r6826)
1762   *
1763   * @param int $parent_id
1764   */
1765  function bbp_get_child_counts( $parent_id = 0 ) {
1766  
1767      // Create cache key
1768      $parent_id    = absint( $parent_id );
1769      $key          = md5( serialize( array( 'parent_id' => $parent_id, 'post_type' => bbp_get_post_types() ) ) );
1770      $last_changed = wp_cache_get_last_changed( 'bbpress_posts' );
1771      $cache_key    = "bbp_child_counts:{$key}:{$last_changed}";
1772  
1773      // Check for cache and set if needed
1774      $retval = wp_cache_get( $cache_key, 'bbpress_posts' );
1775      if ( false === $retval ) {
1776  
1777          // Setup the DB & query
1778          $bbp_db = bbp_db();
1779          $sql    = "SELECT
1780                          p.post_type AS type,
1781                          p.post_status AS status,
1782                          COUNT( * ) AS count
1783                      FROM {$bbp_db->posts} AS p
1784                          LEFT JOIN {$bbp_db->postmeta} AS pm
1785                              ON p.ID = pm.post_id
1786                              AND pm.meta_key = %s
1787                      WHERE pm.meta_value = %s
1788                      GROUP BY p.post_status, p.post_type";
1789  
1790          // Get prepare vars
1791          $post_type = get_post_type( $parent_id );
1792          $meta_key  = "_bbp_{$post_type}_id";
1793  
1794          // Prepare & get results
1795          $query     = $bbp_db->prepare( $sql, $meta_key, $parent_id );
1796          $results   = $bbp_db->get_results( $query, ARRAY_A );
1797  
1798          // Setup return value
1799          $retval    = wp_list_pluck( $results, 'type', 'type' );
1800          $statuses  = get_post_stati();
1801  
1802          // Loop through results
1803          foreach ( $results as $row ) {
1804  
1805              // Setup empties
1806              if ( ! is_array( $retval[ $row['type'] ] ) ) {
1807                  $retval[ $row['type'] ] = array_fill_keys( $statuses, 0 );
1808              }
1809  
1810              // Set statuses
1811              $retval[ $row['type'] ][ $row['status'] ] = bbp_number_not_negative( $row['count'] );
1812          }
1813  
1814          // Always cache the results
1815          wp_cache_set( $cache_key, $retval, 'bbpress_posts' );
1816      }
1817  
1818      // Make sure results are INTs
1819      return (array) apply_filters( 'bbp_get_child_counts', $retval, $parent_id );
1820  }
1821  
1822  /**
1823   * Filter a list of child counts, from `bbp_get_child_counts()`
1824   *
1825   * @since 2.6.0 bbPress (r6826)
1826   *
1827   * @param int    $parent_id  ID of post to get child counts from
1828   * @param array  $types      Optional. An array of post types to filter by
1829   * @param array  $statuses   Optional. An array of post statuses to filter by
1830   *
1831   * @return array A list of objects or object fields.
1832   */
1833  function bbp_filter_child_counts_list( $parent_id = 0, $types = array( 'post' ), $statuses = array() ) {
1834  
1835      // Setup local vars
1836      $retval   = array();
1837      $types    = array_flip( (array) $types    );
1838      $statuses = array_flip( (array) $statuses );
1839      $counts   = bbp_get_child_counts( $parent_id );
1840  
1841      // Loop through counts by type
1842      foreach ( $counts as $type => $type_counts ) {
1843  
1844          // Skip if not this type
1845          if ( ! isset( $types[ $type ] ) ) {
1846              continue;
1847          }
1848  
1849          // Maybe filter statuses
1850          if ( ! empty( $statuses ) ) {
1851              $type_counts = array_intersect_key( $type_counts, $statuses );
1852          }
1853  
1854          // Add type counts to return array
1855          $retval[ $type ] = $type_counts;
1856      }
1857  
1858      // Filter & return
1859      return (array) apply_filters( 'bbp_filter_child_counts_list', $retval, $parent_id, $types, $statuses );
1860  }
1861  
1862  /**
1863   * Query the DB and get a count of public children
1864   *
1865   * @since 2.0.0 bbPress (r2868)
1866   * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
1867   *
1868   * @param int    $parent_id Parent id.
1869   * @param string $post_type Post type. Defaults to 'post'.
1870   * @return int The number of children
1871   */
1872  function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) {
1873  
1874      // Bail if nothing passed
1875      if ( empty( $post_type ) ) {
1876          return false;
1877      }
1878  
1879      // Which statuses
1880      switch ( $post_type ) {
1881  
1882          // Forum
1883          case bbp_get_forum_post_type() :
1884              $post_status = bbp_get_public_forum_statuses();
1885              break;
1886  
1887          // Topic
1888          case bbp_get_topic_post_type() :
1889              $post_status = bbp_get_public_topic_statuses();
1890              break;
1891  
1892          // Reply
1893          case bbp_get_reply_post_type() :
1894          default :
1895              $post_status = bbp_get_public_reply_statuses();
1896              break;
1897      }
1898  
1899      // Get counts
1900      $counts      = bbp_filter_child_counts_list( $parent_id, $post_type, $post_status );
1901      $child_count = isset( $counts[ $post_type ] )
1902          ? bbp_number_not_negative( array_sum( array_values( $counts[ $post_type ] ) ) )
1903          : 0;
1904  
1905      // Filter & return
1906      return (int) apply_filters( 'bbp_get_public_child_count', $child_count, $parent_id, $post_type );
1907  }
1908  /**
1909   * Query the DB and get a count of public children
1910   *
1911   * @since 2.0.0 bbPress (r2868)
1912   * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
1913   *
1914   * @param int    $parent_id Parent id.
1915   * @param string $post_type Post type. Defaults to 'post'.
1916   * @return int The number of children
1917   */
1918  function bbp_get_non_public_child_count( $parent_id = 0, $post_type = 'post' ) {
1919  
1920      // Bail if nothing passed
1921      if ( empty( $parent_id ) || empty( $post_type ) ) {
1922          return false;
1923      }
1924  
1925      // Which statuses
1926      switch ( $post_type ) {
1927  
1928          // Forum
1929          case bbp_get_forum_post_type() :
1930              $post_status = bbp_get_non_public_forum_statuses();
1931              break;
1932  
1933          // Topic
1934          case bbp_get_topic_post_type() :
1935              $post_status = bbp_get_non_public_topic_statuses();
1936              break;
1937  
1938          // Reply
1939          case bbp_get_reply_post_type() :
1940              $post_status = bbp_get_non_public_reply_statuses();
1941              break;
1942  
1943          // Any
1944          default :
1945              $post_status = bbp_get_public_status_id();
1946              break;
1947      }
1948  
1949      // Get counts
1950      $counts      = bbp_filter_child_counts_list( $parent_id, $post_type, $post_status );
1951      $child_count = isset( $counts[ $post_type ] )
1952          ? bbp_number_not_negative( array_sum( array_values( $counts[ $post_type ] ) ) )
1953          : 0;
1954  
1955      // Filter & return
1956      return (int) apply_filters( 'bbp_get_non_public_child_count', $child_count, $parent_id, $post_type );
1957  }
1958  
1959  /**
1960   * Query the DB and get the child id's of public children
1961   *
1962   * @since 2.0.0 bbPress (r2868)
1963   * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
1964   *
1965   * @param int    $parent_id Parent id.
1966   * @param string $post_type Post type. Defaults to 'post'.
1967   *
1968   * @return array The array of children
1969   */
1970  function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) {
1971  
1972      // Bail if nothing passed
1973      if ( empty( $parent_id ) || empty( $post_type ) ) {
1974          return array();
1975      }
1976  
1977      // Which statuses
1978      switch ( $post_type ) {
1979  
1980          // Forum
1981          case bbp_get_forum_post_type() :
1982              $post_status = bbp_get_public_forum_statuses();
1983              break;
1984  
1985          // Topic
1986          case bbp_get_topic_post_type() :
1987              $post_status = bbp_get_public_topic_statuses();
1988              break;
1989  
1990          // Reply
1991          case bbp_get_reply_post_type() :
1992          default :
1993              $post_status = bbp_get_public_reply_statuses();
1994              break;
1995      }
1996  
1997      $query = new WP_Query( array(
1998          'fields'         => 'ids',
1999          'post_parent'    => $parent_id,
2000          'post_status'    => $post_status,
2001          'post_type'      => $post_type,
2002          'posts_per_page' => -1,
2003          'orderby'        => array(
2004              'post_date' => 'DESC',
2005              'ID'        => 'DESC'
2006          ),
2007  
2008          // Performance
2009          'nopaging'               => true,
2010          'suppress_filters'       => true,
2011          'update_post_term_cache' => false,
2012          'update_post_meta_cache' => false,
2013          'ignore_sticky_posts'    => true,
2014          'no_found_rows'          => true
2015      ) );
2016  
2017      $child_ids = ! empty( $query->posts )
2018          ? $query->posts
2019          : array();
2020  
2021      unset( $query );
2022  
2023      // Filter & return
2024      return (array) apply_filters( 'bbp_get_public_child_ids', $child_ids, $parent_id, $post_type );
2025  }
2026  
2027  /**
2028   * Query the DB and get the child id's of all children
2029   *
2030   * @since 2.0.0 bbPress (r3325)
2031   *
2032   * @param int $parent_id  Parent id
2033   * @param string $post_type Post type. Defaults to 'post'
2034   *
2035   * @return array The array of children
2036   */
2037  function bbp_get_all_child_ids( $parent_id = 0, $post_type = 'post' ) {
2038  
2039      // Bail if nothing passed
2040      if ( empty( $parent_id ) || empty( $post_type ) ) {
2041          return array();
2042      }
2043  
2044      // Make cache key
2045      $not_in = array( 'draft', 'future' );
2046      $key    = md5( serialize( array(
2047          'parent_id'   => $parent_id,
2048          'post_type'   => $post_type,
2049          'post_status' => $not_in
2050      ) ) );
2051  
2052      // Check last changed
2053      $last_changed = wp_cache_get_last_changed( 'bbpress_posts' );
2054      $cache_key    = "bbp_child_ids:{$key}:{$last_changed}";
2055  
2056      // Check for cache and set if needed
2057      $child_ids = wp_cache_get( $cache_key, 'bbpress_posts' );
2058  
2059      // Not already cached
2060      if ( false === $child_ids ) {
2061  
2062          // Join post statuses to specifically exclude together
2063          $post_status = "'" . implode( "', '", $not_in ) . "'";
2064          $bbp_db      = bbp_db();
2065  
2066          // Note that we can't use WP_Query here thanks to post_status assumptions
2067          $query       = $bbp_db->prepare( "SELECT ID FROM {$bbp_db->posts} WHERE post_parent = %d AND post_status NOT IN ( {$post_status} ) AND post_type = %s ORDER BY ID DESC", $parent_id, $post_type );
2068          $child_ids   = (array) $bbp_db->get_col( $query );
2069  
2070          // Always cache the results
2071          wp_cache_set( $cache_key, $child_ids, 'bbpress_posts' );
2072      }
2073  
2074      // Make sure results are INTs
2075      $child_ids = wp_parse_id_list( $child_ids );
2076  
2077      // Filter & return
2078      return (array) apply_filters( 'bbp_get_all_child_ids', $child_ids, $parent_id, $post_type );
2079  }
2080  
2081  /**
2082   * Prime familial post caches.
2083   *
2084   * This function uses _prime_post_caches() to prepare the object cache for
2085   * imminent requests to post objects that aren't naturally cached by the primary
2086   * WP_Query calls themselves. Post author caches are also primed.
2087   *
2088   * This is triggered when a `update_post_family_cache` argument is set to true.
2089   *
2090   * Also see: bbp_update_post_author_caches()
2091   *
2092   * @since 2.6.0 bbPress (r6699)
2093   *
2094   * @param array $objects Array of objects, fresh from a query
2095   *
2096   * @return bool True if some IDs were cached
2097   */
2098  function bbp_update_post_family_caches( $objects = array() ) {
2099  
2100      // Bail if no posts
2101      if ( empty( $objects ) ) {
2102          return false;
2103      }
2104  
2105      // Default value
2106      $post_ids = array();
2107  
2108      // Filter the types of IDs to prime
2109      $ids = apply_filters( 'bbp_update_post_family_caches', array(
2110          '_bbp_last_active_id',
2111          '_bbp_last_reply_id',
2112          '_bbp_last_topic_id',
2113          '_bbp_reply_to'
2114      ), $objects );
2115  
2116      // Get the last active IDs
2117      foreach ( $objects as $object ) {
2118          $object = get_post( $object );
2119  
2120          // Skip if post ID is empty.
2121          if ( empty( $object->ID ) ) {
2122              continue;
2123          }
2124  
2125          // Meta IDs
2126          foreach ( $ids as $key ) {
2127              $post_ids[] = get_post_meta( $object->ID, $key, true );
2128          }
2129  
2130          // This post ID is already cached, but the post author may not be
2131          $post_ids[] = $object->ID;
2132      }
2133  
2134      // Unique, non-zero values
2135      $post_ids = bbp_get_unique_array_values( $post_ids );
2136  
2137      // Bail if no IDs to prime
2138      if ( empty( $post_ids ) ) {
2139          return false;
2140      }
2141  
2142      // Prime post caches
2143      _prime_post_caches( $post_ids, true, true );
2144  
2145      // Prime post author caches
2146      bbp_update_post_author_caches( $post_ids );
2147  
2148      // Return
2149      return true;
2150  }
2151  
2152  /**
2153   * Prime post author caches.
2154   *
2155   * This function uses cache_users() to prepare the object cache for
2156   * imminent requests to user objects that aren't naturally cached by the primary
2157   * WP_Query calls themselves.
2158   *
2159   * This is triggered when a `update_post_author_cache` argument is set to true.
2160   *
2161   * @since 2.6.0 bbPress (r6699)
2162   *
2163   * @param array $objects Array of objects, fresh from a query
2164   *
2165   * @return bool True if some IDs were cached
2166   */
2167  function bbp_update_post_author_caches( $objects = array() ) {
2168  
2169      // Bail if no posts
2170      if ( empty( $objects ) ) {
2171          return false;
2172      }
2173  
2174      // Default value
2175      $user_ids = array();
2176  
2177      // Get the user IDs (could use wp_list_pluck() if this is ever a bottleneck)
2178      foreach ( $objects as $object ) {
2179          $object = get_post( $object );
2180  
2181          // Skip if post does not have an author ID.
2182          if ( empty( $object->post_author ) ) {
2183              continue;
2184          }
2185  
2186          // If post exists, add post author to the array.
2187          $user_ids[] = (int) $object->post_author;
2188      }
2189  
2190      // Unique, non-zero values
2191      $user_ids = bbp_get_unique_array_values( $user_ids );
2192  
2193      // Bail if no IDs to prime
2194      if ( empty( $user_ids ) ) {
2195          return false;
2196      }
2197  
2198      // Try to prime user caches
2199      cache_users( $user_ids );
2200  
2201      // Return
2202      return true;
2203  }
2204  
2205  /** Globals *******************************************************************/
2206  
2207  /**
2208   * Get the unfiltered value of a global $post's key
2209   *
2210   * Used most frequently when editing a forum/topic/reply
2211   *
2212   * @since 2.1.0 bbPress (r3694)
2213   *
2214   * @param string $field Name of the key
2215   * @param string $context How to sanitize - raw|edit|db|display|attribute|js
2216   * @return string Field value
2217   */
2218  function bbp_get_global_post_field( $field = 'ID', $context = 'edit' ) {
2219  
2220      // Get the post, and maybe get a field from it
2221      $post   = get_post();
2222      $retval = isset( $post->{$field} )
2223          ? sanitize_post_field( $field, $post->{$field}, $post->ID, $context )
2224          : '';
2225  
2226      // Filter & return
2227      return apply_filters( 'bbp_get_global_post_field', $retval, $post, $field, $context );
2228  }
2229  
2230  /** Nonces ********************************************************************/
2231  
2232  /**
2233   * Makes sure the user requested an action from another page on this site.
2234   *
2235   * To avoid security exploits within the theme.
2236   *
2237   * @since 2.1.0 bbPress (r4022)
2238   *
2239   * @param string $action Action nonce
2240   * @param string $query_arg where to look for nonce in $_REQUEST
2241   */
2242  function bbp_verify_nonce_request( $action = '', $query_arg = '_wpnonce' ) {
2243  
2244      /** Home URL **************************************************************/
2245  
2246      // Parse home_url() into pieces to remove query-strings, strange characters,
2247      // and other funny things that plugins might to do to it.
2248      $parsed_home = parse_url( home_url( '/', ( is_ssl() ? 'https' : 'http' ) ) );
2249  
2250      // Maybe include the port, if it's included
2251      if ( isset( $parsed_home['port'] ) ) {
2252          $parsed_host = $parsed_home['host'] . ':' . $parsed_home['port'];
2253      } else {
2254          $parsed_host = $parsed_home['host'];
2255      }
2256  
2257      // Set the home URL for use in comparisons
2258      $home_url = trim( strtolower( $parsed_home['scheme'] . '://' . $parsed_host . $parsed_home['path'] ), '/' );
2259  
2260      /** Requested URL *********************************************************/
2261  
2262      // Maybe include the port, if it's included in home_url()
2263      if ( isset( $parsed_home['port'] ) && false === strpos( $_SERVER['HTTP_HOST'], ':' ) ) {
2264          $request_host = $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'];
2265      } else {
2266          $request_host = $_SERVER['HTTP_HOST'];
2267      }
2268  
2269      // Build the currently requested URL
2270      $scheme        = bbp_get_url_scheme();
2271      $requested_url = strtolower( $scheme . $request_host . $_SERVER['REQUEST_URI'] );
2272  
2273      /** Look for match ********************************************************/
2274  
2275      /**
2276       * Filters the requested URL being nonce-verified.
2277       *
2278       * Useful for configurations like reverse proxying.
2279       *
2280       * @since 2.2.0 bbPress (r4361)
2281       *
2282       * @param string $requested_url The requested URL.
2283       */
2284      $matched_url = apply_filters( 'bbp_verify_nonce_request_url', $requested_url );
2285  
2286      // Check the nonce
2287      $result = isset( $_REQUEST[ $query_arg ] )
2288          ? wp_verify_nonce( $_REQUEST[ $query_arg ], $action )
2289          : false;
2290  
2291      // Nonce check failed
2292      if ( empty( $result ) || empty( $action ) || ( strpos( $matched_url, $home_url ) !== 0 ) ) {
2293          $result = false;
2294      }
2295  
2296      /**
2297       * Fires at the end of the nonce verification check.
2298       *
2299       * @since 2.1.0 bbPress (r4023)
2300       *
2301       * @param string $action Action nonce.
2302       * @param bool   $result Boolean result of nonce verification.
2303       */
2304      do_action( 'bbp_verify_nonce_request', $action, $result );
2305  
2306      return $result;
2307  }
2308  
2309  /** Feeds *********************************************************************/
2310  
2311  /**
2312   * This function is hooked into the WordPress 'request' action and is
2313   * responsible for sniffing out the query vars and serving up RSS2 feeds if
2314   * the stars align and the user has requested a feed of any bbPress type.
2315   *
2316   * @since 2.0.0 bbPress (r3171)
2317   *
2318   * @param array $query_vars
2319   * @return array
2320   */
2321  function bbp_request_feed_trap( $query_vars = array() ) {
2322  
2323      // Looking at a feed
2324      if ( isset( $query_vars['feed'] ) ) {
2325  
2326          // Forum/Topic/Reply Feed
2327          if ( isset( $query_vars['post_type'] ) ) {
2328  
2329              // Matched post type
2330              $post_type = false;
2331  
2332              // Post types to check
2333              $post_types = array(
2334                  bbp_get_forum_post_type(),
2335                  bbp_get_topic_post_type(),
2336                  bbp_get_reply_post_type()
2337              );
2338  
2339              // Cast query vars as array outside of foreach loop
2340              $qv_array = (array) $query_vars['post_type'];
2341  
2342              // Check if this query is for a bbPress post type
2343              foreach ( $post_types as $bbp_pt ) {
2344                  if ( in_array( $bbp_pt, $qv_array, true ) ) {
2345                      $post_type = $bbp_pt;
2346                      break;
2347                  }
2348              }
2349  
2350              // Looking at a bbPress post type
2351              if ( ! empty( $post_type ) ) {
2352  
2353                  // Supported select query vars
2354                  $select_query_vars = array(
2355                      'p'        => false,
2356                      'name'     => false,
2357                      $post_type => false,
2358                  );
2359  
2360                  // Setup matched variables to select
2361                  foreach ( $query_vars as $key => $value ) {
2362                      if ( isset( $select_query_vars[ $key ] ) ) {
2363                          $select_query_vars[ $key ] = $value;
2364                      }
2365                  }
2366  
2367                  // Remove any empties
2368                  $select_query_vars = array_filter( $select_query_vars );
2369  
2370                  // What bbPress post type are we looking for feeds on?
2371                  switch ( $post_type ) {
2372  
2373                      // Forum
2374                      case bbp_get_forum_post_type() :
2375  
2376                          // Define local variable(s)
2377                          $meta_query = array();
2378  
2379                          // Single forum
2380                          if ( ! empty( $select_query_vars ) ) {
2381  
2382                              // Load up our own query
2383                              query_posts( array_merge( array(
2384                                  'post_type' => bbp_get_forum_post_type(),
2385                                  'feed'      => true
2386                              ), $select_query_vars ) );
2387  
2388                              // Restrict to specific forum ID
2389                              $meta_query = array( array(
2390                                  'key'     => '_bbp_forum_id',
2391                                  'value'   => bbp_get_forum_id(),
2392                                  'type'    => 'NUMERIC',
2393                                  'compare' => '='
2394                              ) );
2395                          }
2396  
2397                          // Only forum replies
2398                          if ( ! empty( $_GET['type'] ) && ( bbp_get_reply_post_type() === $_GET['type'] ) ) {
2399  
2400                              // The query
2401                              $the_query = array(
2402                                  'author'         => 0,
2403                                  'feed'           => true,
2404                                  'post_type'      => bbp_get_reply_post_type(),
2405                                  'post_parent'    => 'any',
2406                                  'post_status'    => bbp_get_public_reply_statuses(),
2407                                  'posts_per_page' => bbp_get_replies_per_rss_page(),
2408                                  'order'          => 'DESC',
2409                                  'meta_query'     => $meta_query
2410                              );
2411  
2412                              // Output the feed
2413                              bbp_display_replies_feed_rss2( $the_query );
2414  
2415                          // Only forum topics
2416                          } elseif ( ! empty( $_GET['type'] ) && ( bbp_get_topic_post_type() === $_GET['type'] ) ) {
2417  
2418                              // The query
2419                              $the_query = array(
2420                                  'author'         => 0,
2421                                  'feed'           => true,
2422                                  'post_type'      => bbp_get_topic_post_type(),
2423                                  'post_parent'    => bbp_get_forum_id(),
2424                                  'post_status'    => bbp_get_public_topic_statuses(),
2425                                  'posts_per_page' => bbp_get_topics_per_rss_page(),
2426                                  'order'          => 'DESC'
2427                              );
2428  
2429                              // Output the feed
2430                              bbp_display_topics_feed_rss2( $the_query );
2431  
2432                          // All forum topics and replies
2433                          } else {
2434  
2435                              // Exclude private/hidden forums if not looking at single
2436                              if ( empty( $select_query_vars ) ) {
2437                                  $meta_query = array( bbp_exclude_forum_ids( 'meta_query' ) );
2438                              }
2439  
2440                              // The query
2441                              $the_query = array(
2442                                  'author'         => 0,
2443                                  'feed'           => true,
2444                                  'post_type'      => array( bbp_get_reply_post_type(), bbp_get_topic_post_type() ),
2445                                  'post_parent'    => 'any',
2446                                  'post_status'    => bbp_get_public_topic_statuses(),
2447                                  'posts_per_page' => bbp_get_replies_per_rss_page(),
2448                                  'order'          => 'DESC',
2449                                  'meta_query'     => $meta_query
2450                              );
2451  
2452                              // Output the feed
2453                              bbp_display_replies_feed_rss2( $the_query );
2454                          }
2455  
2456                          break;
2457  
2458                      // Topic feed - Show replies
2459                      case bbp_get_topic_post_type() :
2460  
2461                          // Single topic
2462                          if ( ! empty( $select_query_vars ) ) {
2463  
2464                              // Load up our own query
2465                              query_posts( array_merge( array(
2466                                  'post_type' => bbp_get_topic_post_type(),
2467                                  'feed'      => true
2468                              ), $select_query_vars ) );
2469  
2470                              // Output the feed
2471                              bbp_display_replies_feed_rss2( array( 'feed' => true ) );
2472  
2473                          // All topics
2474                          } else {
2475  
2476                              // The query
2477                              $the_query = array(
2478                                  'author'         => 0,
2479                                  'feed'           => true,
2480                                  'post_parent'    => 'any',
2481                                  'posts_per_page' => bbp_get_topics_per_rss_page(),
2482                                  'show_stickies'  => false
2483                              );
2484  
2485                              // Output the feed
2486                              bbp_display_topics_feed_rss2( $the_query );
2487                          }
2488  
2489                          break;
2490  
2491                      // Replies
2492                      case bbp_get_reply_post_type() :
2493  
2494                          // The query
2495                          $the_query = array(
2496                              'posts_per_page' => bbp_get_replies_per_rss_page(),
2497                              'meta_query'     => array( array() ),
2498                              'feed'           => true
2499                          );
2500  
2501                          // All replies
2502                          if ( empty( $select_query_vars ) ) {
2503                              bbp_display_replies_feed_rss2( $the_query );
2504                          }
2505  
2506                          break;
2507                  }
2508              }
2509  
2510          // Single Topic Vview
2511          } elseif ( isset( $query_vars[ bbp_get_view_rewrite_id() ] ) ) {
2512  
2513              // Get the view
2514              $view = $query_vars[ bbp_get_view_rewrite_id() ];
2515  
2516              // We have a view to display a feed
2517              if ( ! empty( $view ) ) {
2518  
2519                  // Get the view query
2520                  $the_query = bbp_get_view_query_args( $view );
2521  
2522                  // Output the feed
2523                  bbp_display_topics_feed_rss2( $the_query );
2524              }
2525          }
2526  
2527          // @todo User profile feeds
2528      }
2529  
2530      // No feed so continue on
2531      return $query_vars;
2532  }
2533  
2534  /** Templates ******************************************************************/
2535  
2536  /**
2537   * Used to guess if page exists at requested path
2538   *
2539   * @since 2.0.0 bbPress (r3304)
2540   *
2541   * @param string $path
2542   * @return mixed False if no page, Page object if true
2543   */
2544  function bbp_get_page_by_path( $path = '' ) {
2545  
2546      // Default to false
2547      $retval = false;
2548  
2549      // Path is not empty
2550      if ( ! empty( $path ) ) {
2551  
2552          // Pretty permalinks are on so path might exist
2553          if ( get_option( 'permalink_structure' ) ) {
2554              $retval = get_page_by_path( $path );
2555          }
2556      }
2557  
2558      // Filter & return
2559      return apply_filters( 'bbp_get_page_by_path', $retval, $path );
2560  }
2561  
2562  /**
2563   * Sets the 404 status.
2564   *
2565   * Used primarily with topics/replies inside hidden forums.
2566   *
2567   * @since 2.0.0 bbPress (r3051)
2568   * @since 2.6.0 bbPress (r6583) Use status_header() & nocache_headers()
2569   *
2570   * @param WP_Query $query  The query being checked
2571   *
2572   * @return bool Always returns true
2573   */
2574  function bbp_set_404( $query = null ) {
2575  
2576      // Global fallback
2577      if ( empty( $query ) ) {
2578          $query = bbp_get_wp_query();
2579      }
2580  
2581      // Setup environment
2582      $query->set_404();
2583  
2584      // Setup request
2585      status_header( 404 );
2586      nocache_headers();
2587  }
2588  
2589  /**
2590   * Sets the 200 status header.
2591   *
2592   * @since 2.6.0 bbPress (r6583)
2593   */
2594  function bbp_set_200() {
2595      status_header( 200 );
2596  }
2597  
2598  /**
2599   * Maybe handle the default 404 handling for some bbPress conditions
2600   *
2601   * Some conditions (like private/hidden forums and edits) have their own checks
2602   * on `bbp_template_redirect` and are not currently 404s.
2603   *
2604   * @since 2.6.0 bbPress (r6555)
2605   *
2606   * @param bool $override Whether to override the default handler
2607   * @param WP_Query $wp_query The posts query being referenced
2608   *
2609   * @return bool False to leave alone, true to override
2610   */
2611  function bbp_pre_handle_404( $override = false, $wp_query = false ) {
2612  
2613      // Handle a bbPress 404 condition
2614      if ( isset( $wp_query->bbp_is_404 ) ) {
2615  
2616          // Either force a 404 when 200, or a 200 when 404
2617          $override = ( true === $wp_query->bbp_is_404 )
2618              ? bbp_set_404( $wp_query )
2619              : bbp_set_200();
2620      }
2621  
2622      // Return, maybe overridden
2623      return $override;
2624  }
2625  
2626  /**
2627   * Maybe pre-assign the posts that are returned from a WP_Query.
2628   *
2629   * This effectively short-circuits the default query for posts, which is
2630   * currently only used to avoid calling the main query when it's not necessary.
2631   *
2632   * @since 2.6.0 bbPress (r6580)
2633   *
2634   * @param mixed $posts Default null. Array of posts (possibly empty)
2635   * @param WP_Query $wp_query
2636   *
2637   * @return mixed Null if no override. Array if overridden.
2638   */
2639  function bbp_posts_pre_query( $posts = null, $wp_query = false ) {
2640  
2641      // Custom 404 handler is set, so set posts to empty array to avoid 2 queries
2642      if ( ! empty( $wp_query->bbp_is_404 ) ) {
2643          $posts = array();
2644      }
2645  
2646      // Return, maybe overridden
2647      return $posts;
2648  }
2649  
2650  /**
2651   * Get scheme for a URL based on is_ssl() results.
2652   *
2653   * @since 2.6.0 bbPress (r6759)
2654   *
2655   * @return string https:// if is_ssl(), otherwise http://
2656   */
2657  function bbp_get_url_scheme() {
2658      return is_ssl()
2659          ? 'https://'
2660          : 'http://';
2661  }
2662  
2663  /** Titles ********************************************************************/
2664  
2665  /**
2666   * Is a title longer that the maximum title length?
2667   *
2668   * Uses mb_strlen() in `8bit` mode to treat strings as raw. This matches the
2669   * behavior present in Comments, PHPMailer, RandomCompat, and others.
2670   *
2671   * @since 2.6.0 bbPress (r6783)
2672   *
2673   * @param string $title
2674   * @return bool
2675   */
2676  function bbp_is_title_too_long( $title = '' ) {
2677      $max    = bbp_get_title_max_length();
2678      $len    = mb_strlen( $title, '8bit' );
2679      $result = ( $len > $max );
2680  
2681      // Filter & return
2682      return (bool) apply_filters( 'bbp_is_title_too_long', $result, $title, $max, $len );
2683  }


Generated: Tue Mar 19 01:01:02 2024 Cross-referenced by PHPXref 0.7.1