[ Index ]

PHP Cross Reference of BBPress

title

Body

[close]

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

   1  <?php
   2  
   3  /**
   4   * bbPress Reply Functions
   5   *
   6   * @package bbPress
   7   * @subpackage Functions
   8   */
   9  
  10  // Exit if accessed directly
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /** Insert ********************************************************************/
  14  
  15  /**
  16   * A wrapper for wp_insert_post() that also includes the necessary meta values
  17   * for the reply to function properly.
  18   *
  19   * @since 2.0.0 bbPress (r3349)
  20   *
  21   * @param array $reply_data Forum post data
  22   * @param array $reply_meta Forum meta data
  23   */
  24  function bbp_insert_reply( $reply_data = array(), $reply_meta = array() ) {
  25  
  26      // Parse arguments against default values
  27      $reply_data = bbp_parse_args( $reply_data, array(
  28          'post_parent'    => 0, // topic ID
  29          'post_type'      => bbp_get_reply_post_type(),
  30          'post_author'    => bbp_get_current_user_id(),
  31          'post_password'  => '',
  32          'post_content'   => '',
  33          'post_title'     => '',
  34          'menu_order'     => bbp_get_topic_reply_count( $reply_data['post_parent'], true ) + 1,
  35          'comment_status' => 'closed'
  36      ), 'insert_reply' );
  37  
  38      // Possibly override status based on parent topic
  39      if ( ! empty( $reply_data['post_parent'] ) && empty( $reply_data['post_status'] ) ) {
  40          $reply_data['post_status'] = bbp_get_topic_status( $reply_data['post_parent'] );
  41      }
  42  
  43      // Insert reply
  44      $reply_id = wp_insert_post( $reply_data, false );
  45  
  46      // Bail if no reply was added
  47      if ( empty( $reply_id ) ) {
  48          return false;
  49      }
  50  
  51      // Parse arguments against default values
  52      $reply_meta = bbp_parse_args( $reply_meta, array(
  53          'author_ip' => bbp_current_author_ip(),
  54          'forum_id'  => 0,
  55          'topic_id'  => 0,
  56          'reply_to'  => 0
  57      ), 'insert_reply_meta' );
  58  
  59      // Insert reply meta
  60      foreach ( $reply_meta as $meta_key => $meta_value ) {
  61  
  62          // Prefix if not prefixed
  63          if ( '_bbp_' !== substr( $meta_key, 0, 5 ) ) {
  64              $meta_key = '_bbp_' . $meta_key;
  65          }
  66  
  67          // Update the meta
  68          update_post_meta( $reply_id, $meta_key, $meta_value );
  69      }
  70  
  71      // Update the reply and hierarchy
  72      bbp_update_reply( $reply_id, $reply_meta['topic_id'], $reply_meta['forum_id'], array(), $reply_data['post_author'], false, $reply_meta['reply_to'] );
  73  
  74      /**
  75       * Fires after reply has been inserted via `bbp_insert_reply`.
  76       *
  77       * @since 2.6.0 bbPress (r6036)
  78       *
  79       * @param int $reply_id               The reply id.
  80       * @param int $reply_meta['topic_id'] The reply topic meta.
  81       * @param int $reply_meta['forum_id'] The reply forum meta.
  82       */
  83      do_action( 'bbp_insert_reply', (int) $reply_id, (int) $reply_meta['topic_id'], (int) $reply_meta['forum_id'] );
  84  
  85      // Return reply_id
  86      return $reply_id;
  87  }
  88  
  89  /**
  90   * Update counts after a reply is inserted via `bbp_insert_reply`.
  91   *
  92   * @since 2.6.0 bbPress (r6036)
  93   *
  94   * @param int $reply_id The reply id.
  95   * @param int $topic_id The topic id.
  96   * @param int $forum_id The forum id.
  97   *
  98   * @return void
  99   */
 100  function bbp_insert_reply_update_counts( $reply_id = 0, $topic_id = 0, $forum_id = 0 ) {
 101  
 102      // If the reply is public, update the reply counts.
 103      if ( bbp_is_reply_published( $reply_id ) ) {
 104          bbp_increase_topic_reply_count( $topic_id );
 105          bbp_increase_forum_reply_count( $forum_id );
 106  
 107      // If the reply isn't public only update the reply hidden counts.
 108      } else {
 109          bbp_increase_topic_reply_count_hidden( $topic_id );
 110          bbp_increase_forum_reply_count_hidden( $forum_id );
 111      }
 112  }
 113  
 114  /** Post Form Handlers ********************************************************/
 115  
 116  /**
 117   * Handles the front end reply submission
 118   *
 119   * @since 2.0.0 bbPress (r2574)
 120   *
 121   * @param string $action The requested action to compare this function to
 122   *                    id, anonymous data, reply author, edit (false), and
 123   *                    the reply to id
 124   */
 125  function bbp_new_reply_handler( $action = '' ) {
 126  
 127      // Bail if action is not bbp-new-reply
 128      if ( 'bbp-new-reply' !== $action ) {
 129          return;
 130      }
 131  
 132      // Nonce check
 133      if ( ! bbp_verify_nonce_request( 'bbp-new-reply' ) ) {
 134          bbp_add_error( 'bbp_new_reply_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
 135          return;
 136      }
 137  
 138      // Define local variable(s)
 139      $topic_id = $forum_id = $reply_author = $reply_to = 0;
 140      $reply_title = $reply_content = $terms = '';
 141      $anonymous_data = array();
 142  
 143      /** Reply Author **********************************************************/
 144  
 145      // User is anonymous
 146      if ( bbp_is_anonymous() ) {
 147  
 148          // Filter anonymous data (variable is used later)
 149          $anonymous_data = bbp_filter_anonymous_post_data();
 150  
 151          // Anonymous data checks out, so set cookies, etc...
 152          bbp_set_current_anonymous_user_data( $anonymous_data );
 153  
 154      // User is logged in
 155      } else {
 156  
 157          // User cannot create replies
 158          if ( ! current_user_can( 'publish_replies' ) ) {
 159              bbp_add_error( 'bbp_reply_permission', __( '<strong>Error</strong>: You do not have permission to reply.', 'bbpress' ) );
 160          }
 161  
 162          // Reply author is current user
 163          $reply_author = bbp_get_current_user_id();
 164      }
 165  
 166      /** Topic ID **************************************************************/
 167  
 168      // Topic id was not passed
 169      if ( empty( $_POST['bbp_topic_id'] ) ) {
 170          bbp_add_error( 'bbp_reply_topic_id', __( '<strong>Error</strong>: Topic ID is missing.', 'bbpress' ) );
 171  
 172      // Topic id is not a number
 173      } elseif ( ! is_numeric( $_POST['bbp_topic_id'] ) ) {
 174          bbp_add_error( 'bbp_reply_topic_id', __( '<strong>Error</strong>: Topic ID must be a number.', 'bbpress' ) );
 175  
 176      // Topic id might be valid
 177      } else {
 178  
 179          // Get the topic id
 180          $posted_topic_id = intval( $_POST['bbp_topic_id'] );
 181  
 182          // Topic id is a negative number
 183          if ( 0 > $posted_topic_id ) {
 184              bbp_add_error( 'bbp_reply_topic_id', __( '<strong>Error</strong>: Topic ID cannot be a negative number.', 'bbpress' ) );
 185  
 186          // Topic does not exist
 187          } elseif ( ! bbp_get_topic( $posted_topic_id ) ) {
 188              bbp_add_error( 'bbp_reply_topic_id', __( '<strong>Error</strong>: Topic does not exist.', 'bbpress' ) );
 189  
 190          // Use the POST'ed topic id
 191          } else {
 192              $topic_id = $posted_topic_id;
 193          }
 194      }
 195  
 196      /** Forum ID **************************************************************/
 197  
 198      // Try to use the forum id of the topic
 199      if ( ! isset( $_POST['bbp_forum_id'] ) && ! empty( $topic_id ) ) {
 200          $forum_id = bbp_get_topic_forum_id( $topic_id );
 201  
 202      // Error check the POST'ed forum id
 203      } elseif ( isset( $_POST['bbp_forum_id'] ) ) {
 204  
 205          // Empty Forum id was passed
 206          if ( empty( $_POST['bbp_forum_id'] ) ) {
 207              bbp_add_error( 'bbp_reply_forum_id', __( '<strong>Error</strong>: Forum ID is missing.', 'bbpress' ) );
 208  
 209          // Forum id is not a number
 210          } elseif ( ! is_numeric( $_POST['bbp_forum_id'] ) ) {
 211              bbp_add_error( 'bbp_reply_forum_id', __( '<strong>Error</strong>: Forum ID must be a number.', 'bbpress' ) );
 212  
 213          // Forum id might be valid
 214          } else {
 215  
 216              // Get the forum id
 217              $posted_forum_id = intval( $_POST['bbp_forum_id'] );
 218  
 219              // Forum id is empty
 220              if ( 0 === $posted_forum_id ) {
 221                  bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID is missing.', 'bbpress' ) );
 222  
 223              // Forum id is a negative number
 224              } elseif ( 0 > $posted_forum_id ) {
 225                  bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID cannot be a negative number.', 'bbpress' ) );
 226  
 227              // Forum does not exist
 228              } elseif ( ! bbp_get_forum( $posted_forum_id ) ) {
 229                  bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum does not exist.', 'bbpress' ) );
 230  
 231              // Use the POST'ed forum id
 232              } else {
 233                  $forum_id = $posted_forum_id;
 234              }
 235          }
 236      }
 237  
 238      // Forum exists
 239      if ( ! empty( $forum_id ) ) {
 240  
 241          // Forum is a category
 242          if ( bbp_is_forum_category( $forum_id ) ) {
 243              bbp_add_error( 'bbp_new_reply_forum_category', __( '<strong>Error</strong>: This forum is a category. No replies can be created in this forum.', 'bbpress' ) );
 244  
 245          // Forum is not a category
 246          } else {
 247  
 248              // Forum is closed and user cannot access
 249              if ( bbp_is_forum_closed( $forum_id ) && ! current_user_can( 'edit_forum', $forum_id ) ) {
 250                  bbp_add_error( 'bbp_new_reply_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new replies.', 'bbpress' ) );
 251              }
 252  
 253              // Forum is private and user cannot access
 254              if ( bbp_is_forum_private( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
 255                  bbp_add_error( 'bbp_new_reply_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new replies in it.', 'bbpress' ) );
 256  
 257              // Forum is hidden and user cannot access
 258              } elseif ( bbp_is_forum_hidden( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
 259                  bbp_add_error( 'bbp_new_reply_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new replies in it.', 'bbpress' ) );
 260              }
 261          }
 262      }
 263  
 264      /** Unfiltered HTML *******************************************************/
 265  
 266      // Remove kses filters from title and content for capable users and if the nonce is verified
 267      if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_reply'] ) && wp_create_nonce( 'bbp-unfiltered-html-reply_' . $topic_id ) === $_POST['_bbp_unfiltered_html_reply'] ) {
 268          remove_filter( 'bbp_new_reply_pre_title',   'wp_filter_kses'      );
 269          remove_filter( 'bbp_new_reply_pre_content', 'bbp_encode_bad',  10 );
 270          remove_filter( 'bbp_new_reply_pre_content', 'bbp_filter_kses', 30 );
 271      }
 272  
 273      /** Reply Title ***********************************************************/
 274  
 275      if ( ! empty( $_POST['bbp_reply_title'] ) ) {
 276          $reply_title = sanitize_text_field( $_POST['bbp_reply_title'] );
 277      }
 278  
 279      // Filter and sanitize
 280      $reply_title = apply_filters( 'bbp_new_reply_pre_title', $reply_title );
 281  
 282      // Title too long
 283      if ( bbp_is_title_too_long( $reply_title ) ) {
 284          bbp_add_error( 'bbp_reply_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
 285      }
 286  
 287      /** Reply Content *********************************************************/
 288  
 289      if ( ! empty( $_POST['bbp_reply_content'] ) ) {
 290          $reply_content = $_POST['bbp_reply_content'];
 291      }
 292  
 293      // Filter and sanitize
 294      $reply_content = apply_filters( 'bbp_new_reply_pre_content', $reply_content );
 295  
 296      // No reply content
 297      if ( empty( $reply_content ) ) {
 298          bbp_add_error( 'bbp_reply_content', __( '<strong>Error</strong>: Your reply cannot be empty.', 'bbpress' ) );
 299      }
 300  
 301      /** Reply Flooding ********************************************************/
 302  
 303      if ( ! bbp_check_for_flood( $anonymous_data, $reply_author ) ) {
 304          bbp_add_error( 'bbp_reply_flood', __( '<strong>Error</strong>: Slow down; you move too fast.', 'bbpress' ) );
 305      }
 306  
 307      /** Reply Duplicate *******************************************************/
 308  
 309      if ( ! bbp_check_for_duplicate( array( 'post_type' => bbp_get_reply_post_type(), 'post_author' => $reply_author, 'post_content' => $reply_content, 'post_parent' => $topic_id, 'anonymous_data' => $anonymous_data ) ) ) {
 310          bbp_add_error( 'bbp_reply_duplicate', __( '<strong>Error</strong>: Duplicate reply detected; it looks as though you&#8217;ve already said that.', 'bbpress' ) );
 311      }
 312  
 313      /** Reply Bad Words *******************************************************/
 314  
 315      if ( ! bbp_check_for_moderation( $anonymous_data, $reply_author, $reply_title, $reply_content, true ) ) {
 316          bbp_add_error( 'bbp_reply_moderation', __( '<strong>Error</strong>: Your reply cannot be created at this time.', 'bbpress' ) );
 317      }
 318  
 319      /** Reply Status **********************************************************/
 320  
 321      // Maybe put into moderation
 322      if ( bbp_is_topic_pending( $topic_id ) || ! bbp_check_for_moderation( $anonymous_data, $reply_author, $reply_title, $reply_content ) ) {
 323          $reply_status = bbp_get_pending_status_id();
 324  
 325      // Default
 326      } else {
 327          $reply_status = bbp_get_public_status_id();
 328      }
 329  
 330      /** Reply To **************************************************************/
 331  
 332      // Handle Reply To of the reply; $_REQUEST for non-JS submissions
 333      if ( isset( $_REQUEST['bbp_reply_to'] ) ) {
 334          $reply_to = bbp_validate_reply_to( $_REQUEST['bbp_reply_to'] );
 335      }
 336  
 337      /** Topic Closed **********************************************************/
 338  
 339      // If topic is closed, moderators can still reply
 340      if ( bbp_is_topic_closed( $topic_id ) && ! current_user_can( 'moderate', $topic_id ) ) {
 341          bbp_add_error( 'bbp_reply_topic_closed', __( '<strong>Error</strong>: Topic is closed.', 'bbpress' ) );
 342      }
 343  
 344      /** Topic Tags ************************************************************/
 345  
 346      // Either replace terms
 347      if ( bbp_allow_topic_tags() && current_user_can( 'assign_topic_tags', $topic_id ) && ! empty( $_POST['bbp_topic_tags'] ) ) {
 348          $terms = sanitize_text_field( $_POST['bbp_topic_tags'] );
 349  
 350      // ...or remove them.
 351      } elseif ( isset( $_POST['bbp_topic_tags'] ) ) {
 352          $terms = '';
 353  
 354      // Existing terms
 355      } else {
 356          $terms = bbp_get_topic_tag_names( $topic_id );
 357      }
 358  
 359      /** Additional Actions (Before Save) **************************************/
 360  
 361      do_action( 'bbp_new_reply_pre_extras', $topic_id, $forum_id );
 362  
 363      // Bail if errors
 364      if ( bbp_has_errors() ) {
 365          return;
 366      }
 367  
 368      /** No Errors *************************************************************/
 369  
 370      // Add the content of the form to $reply_data as an array
 371      // Just in time manipulation of reply data before being created
 372      $reply_data = apply_filters( 'bbp_new_reply_pre_insert', array(
 373          'post_author'    => $reply_author,
 374          'post_title'     => $reply_title,
 375          'post_content'   => $reply_content,
 376          'post_status'    => $reply_status,
 377          'post_parent'    => $topic_id,
 378          'post_type'      => bbp_get_reply_post_type(),
 379          'comment_status' => 'closed',
 380          'menu_order'     => bbp_get_topic_reply_count( $topic_id, true ) + 1
 381      ) );
 382  
 383      // Insert reply
 384      $reply_id = wp_insert_post( $reply_data, true );
 385  
 386      /** No Errors *************************************************************/
 387  
 388      // Check for missing reply_id or error
 389      if ( ! empty( $reply_id ) && ! is_wp_error( $reply_id ) ) {
 390  
 391          /** Topic Tags ********************************************************/
 392  
 393          // Just in time manipulation of reply terms before being edited
 394          $terms = apply_filters( 'bbp_new_reply_pre_set_terms', $terms, $topic_id, $reply_id );
 395  
 396          // Insert terms
 397          $terms = wp_set_post_terms( $topic_id, $terms, bbp_get_topic_tag_tax_id(), false );
 398  
 399          // Term error
 400          if ( is_wp_error( $terms ) ) {
 401              bbp_add_error( 'bbp_reply_tags', __( '<strong>Error</strong>: There was a problem adding the tags to the topic.', 'bbpress' ) );
 402          }
 403  
 404          /** Trash Check *******************************************************/
 405  
 406          // If this reply starts as trash, add it to pre_trashed_replies
 407          // for the topic, so it is properly restored.
 408          if ( bbp_is_topic_trash( $topic_id ) || ( $reply_data['post_status'] === bbp_get_trash_status_id() ) ) {
 409  
 410              // Trash the reply
 411              wp_trash_post( $reply_id );
 412  
 413              // Only add to pre-trashed array if topic is trashed
 414              if ( bbp_is_topic_trash( $topic_id ) ) {
 415  
 416                  // Get pre_trashed_replies for topic
 417                  $pre_trashed_meta = get_post_meta( $topic_id, '_bbp_pre_trashed_replies', true );
 418  
 419                  // Format the meta value
 420                  $pre_trashed_replies = is_array( $pre_trashed_meta )
 421                      ? array_filter( $pre_trashed_meta )
 422                      : array();
 423  
 424                  // Add this reply to the end of the existing replies
 425                  $pre_trashed_replies[] = $reply_id;
 426  
 427                  // Update the pre_trashed_reply post meta
 428                  update_post_meta( $topic_id, '_bbp_pre_trashed_replies', $pre_trashed_replies );
 429              }
 430  
 431          /** Spam Check ********************************************************/
 432  
 433          // If reply or topic are spam, officially spam this reply
 434          } elseif ( bbp_is_topic_spam( $topic_id ) || ( $reply_data['post_status'] === bbp_get_spam_status_id() ) ) {
 435              add_post_meta( $reply_id, '_bbp_spam_meta_status', bbp_get_public_status_id() );
 436  
 437              // Only add to pre-spammed array if topic is spam
 438              if ( bbp_is_topic_spam( $topic_id ) ) {
 439  
 440                  // Get pre_spammed_replies for topic
 441                  $pre_trashed_meta = get_post_meta( $topic_id, '_bbp_pre_spammed_replies', true );
 442  
 443                  // Format the meta value
 444                  $pre_spammed_replies = is_array( $pre_trashed_meta )
 445                      ? array_filter( $pre_trashed_meta )
 446                      : array();
 447  
 448                  // Add this reply to the end of the existing replies
 449                  $pre_spammed_replies[] = $reply_id;
 450  
 451                  // Update the pre_spammed_replies post meta
 452                  update_post_meta( $topic_id, '_bbp_pre_spammed_replies', $pre_spammed_replies );
 453              }
 454          }
 455  
 456          /** Update counts, etc... *********************************************/
 457  
 458          do_action( 'bbp_new_reply', $reply_id, $topic_id, $forum_id, $anonymous_data, $reply_author, false, $reply_to );
 459  
 460          /** Additional Actions (After Save) ***********************************/
 461  
 462          do_action( 'bbp_new_reply_post_extras', $reply_id );
 463  
 464          /** Redirect **********************************************************/
 465  
 466          // Redirect to
 467          $redirect_to = bbp_get_redirect_to();
 468  
 469          // Get the reply URL
 470          $reply_url = bbp_get_reply_url( $reply_id, $redirect_to );
 471  
 472          // Allow to be filtered
 473          $reply_url = apply_filters( 'bbp_new_reply_redirect_to', $reply_url, $redirect_to, $reply_id );
 474  
 475          /** Successful Save ***************************************************/
 476  
 477          // Redirect back to new reply
 478          bbp_redirect( $reply_url );
 479  
 480      /** Errors ****************************************************************/
 481  
 482      // WP_Error
 483      } elseif ( is_wp_error( $reply_id ) ) {
 484          bbp_add_error( 'bbp_reply_error', sprintf( __( '<strong>Error</strong>: The following problem(s) occurred: %s', 'bbpress' ), $reply_id->get_error_message() ) );
 485  
 486      // Generic error
 487      } else {
 488          bbp_add_error( 'bbp_reply_error', __( '<strong>Error</strong>: The reply was not created.', 'bbpress' ) );
 489      }
 490  }
 491  
 492  /**
 493   * Handles the front end edit reply submission
 494   *
 495   * @param string $action The requested action to compare this function to
 496   *                    id, anonymous data, reply author, bool true (for edit),
 497   *                    and the reply to id
 498   */
 499  function bbp_edit_reply_handler( $action = '' ) {
 500  
 501      // Bail if action is not bbp-edit-reply
 502      if ( 'bbp-edit-reply' !== $action ) {
 503          return;
 504      }
 505  
 506      // Define local variable(s)
 507      $revisions_removed = false;
 508      $reply = $reply_id = $reply_to = $reply_author = $topic_id = $forum_id = 0;
 509      $reply_title = $reply_content = $reply_edit_reason = $terms = '';
 510      $anonymous_data = array();
 511  
 512      /** Reply *****************************************************************/
 513  
 514      // Reply id was not passed
 515      if ( empty( $_POST['bbp_reply_id'] ) ) {
 516          bbp_add_error( 'bbp_edit_reply_id', __( '<strong>Error</strong>: Reply ID not found.', 'bbpress' ) );
 517          return;
 518  
 519      // Reply id was passed
 520      } elseif ( is_numeric( $_POST['bbp_reply_id'] ) ) {
 521          $reply_id = (int) $_POST['bbp_reply_id'];
 522          $reply    = bbp_get_reply( $reply_id );
 523      }
 524  
 525      // Nonce check
 526      if ( ! bbp_verify_nonce_request( 'bbp-edit-reply_' . $reply_id ) ) {
 527          bbp_add_error( 'bbp_edit_reply_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
 528          return;
 529      }
 530  
 531      // Reply does not exist
 532      if ( empty( $reply ) ) {
 533          bbp_add_error( 'bbp_edit_reply_not_found', __( '<strong>Error</strong>: The reply you want to edit was not found.', 'bbpress' ) );
 534          return;
 535  
 536      // Reply exists
 537      } else {
 538  
 539          // Check users ability to create new reply
 540          if ( ! bbp_is_reply_anonymous( $reply_id ) ) {
 541  
 542              // User cannot edit this reply
 543              if ( ! current_user_can( 'edit_reply', $reply_id ) ) {
 544                  bbp_add_error( 'bbp_edit_reply_permission', __( '<strong>Error</strong>: You do not have permission to edit that reply.', 'bbpress' ) );
 545                  return;
 546              }
 547  
 548              // Set reply author
 549              $reply_author = bbp_get_reply_author_id( $reply_id );
 550  
 551          // It is an anonymous post
 552          } else {
 553  
 554              // Filter anonymous data
 555              $anonymous_data = bbp_filter_anonymous_post_data();
 556          }
 557      }
 558  
 559      // Remove kses filters from title and content for capable users and if the nonce is verified
 560      if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_reply'] ) && wp_create_nonce( 'bbp-unfiltered-html-reply_' . $reply_id ) === $_POST['_bbp_unfiltered_html_reply'] ) {
 561          remove_filter( 'bbp_edit_reply_pre_title',   'wp_filter_kses'      );
 562          remove_filter( 'bbp_edit_reply_pre_content', 'bbp_encode_bad',  10 );
 563          remove_filter( 'bbp_edit_reply_pre_content', 'bbp_filter_kses', 30 );
 564      }
 565  
 566      /** Reply Topic ***********************************************************/
 567  
 568      $topic_id = bbp_get_reply_topic_id( $reply_id );
 569  
 570      /** Topic Forum ***********************************************************/
 571  
 572      $forum_id = bbp_get_topic_forum_id( $topic_id );
 573  
 574      // Forum exists
 575      if ( ! empty( $forum_id ) && ( $forum_id !== bbp_get_reply_forum_id( $reply_id ) ) ) {
 576  
 577          // Forum is a category
 578          if ( bbp_is_forum_category( $forum_id ) ) {
 579              bbp_add_error( 'bbp_edit_reply_forum_category', __( '<strong>Error</strong>: This forum is a category. No replies can be created in this forum.', 'bbpress' ) );
 580  
 581          // Forum is not a category
 582          } else {
 583  
 584              // Forum is closed and user cannot access
 585              if ( bbp_is_forum_closed( $forum_id ) && ! current_user_can( 'edit_forum', $forum_id ) ) {
 586                  bbp_add_error( 'bbp_edit_reply_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new replies.', 'bbpress' ) );
 587              }
 588  
 589              // Forum is private and user cannot access
 590              if ( bbp_is_forum_private( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
 591                  bbp_add_error( 'bbp_edit_reply_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new replies in it.', 'bbpress' ) );
 592  
 593              // Forum is hidden and user cannot access
 594              } elseif ( bbp_is_forum_hidden( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
 595                  bbp_add_error( 'bbp_edit_reply_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new replies in it.', 'bbpress' ) );
 596              }
 597          }
 598      }
 599  
 600      /** Reply Title ***********************************************************/
 601  
 602      if ( ! empty( $_POST['bbp_reply_title'] ) ) {
 603          $reply_title = sanitize_text_field( $_POST['bbp_reply_title'] );
 604      }
 605  
 606      // Filter and sanitize
 607      $reply_title = apply_filters( 'bbp_edit_reply_pre_title', $reply_title, $reply_id );
 608  
 609      // Title too long
 610      if ( bbp_is_title_too_long( $reply_title ) ) {
 611          bbp_add_error( 'bbp_reply_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
 612      }
 613  
 614      /** Reply Content *********************************************************/
 615  
 616      if ( ! empty( $_POST['bbp_reply_content'] ) ) {
 617          $reply_content = $_POST['bbp_reply_content'];
 618      }
 619  
 620      // Filter and sanitize
 621      $reply_content = apply_filters( 'bbp_edit_reply_pre_content', $reply_content, $reply_id );
 622  
 623      // No reply content
 624      if ( empty( $reply_content ) ) {
 625          bbp_add_error( 'bbp_edit_reply_content', __( '<strong>Error</strong>: Your reply cannot be empty.', 'bbpress' ) );
 626      }
 627  
 628      /** Reply Bad Words *******************************************************/
 629  
 630      if ( ! bbp_check_for_moderation( $anonymous_data, $reply_author, $reply_title, $reply_content, true ) ) {
 631          bbp_add_error( 'bbp_reply_moderation', __( '<strong>Error</strong>: Your reply cannot be edited at this time.', 'bbpress' ) );
 632      }
 633  
 634      /** Reply Status **********************************************************/
 635  
 636      // Maybe put into moderation
 637      if ( ! bbp_check_for_moderation( $anonymous_data, $reply_author, $reply_title, $reply_content ) ) {
 638  
 639          // Set post status to pending if public
 640          if ( bbp_get_public_status_id() === $reply->post_status ) {
 641              $reply_status = bbp_get_pending_status_id();
 642          }
 643  
 644      // Use existing post_status
 645      } else {
 646          $reply_status = $reply->post_status;
 647      }
 648  
 649      /** Reply To **************************************************************/
 650  
 651      // Handle Reply To of the reply; $_REQUEST for non-JS submissions
 652      if ( isset( $_REQUEST['bbp_reply_to'] ) && current_user_can( 'moderate', $reply_id ) ) {
 653          $reply_to = bbp_validate_reply_to( $_REQUEST['bbp_reply_to'], $reply_id );
 654      } elseif ( bbp_thread_replies() ) {
 655          $reply_to = bbp_get_reply_to( $reply_id );
 656      }
 657  
 658      /** Topic Tags ************************************************************/
 659  
 660      // Either replace terms
 661      if ( bbp_allow_topic_tags() && current_user_can( 'assign_topic_tags', $topic_id ) && ! empty( $_POST['bbp_topic_tags'] ) ) {
 662          $terms = sanitize_text_field( $_POST['bbp_topic_tags'] );
 663  
 664      // ...or remove them.
 665      } elseif ( isset( $_POST['bbp_topic_tags'] ) ) {
 666          $terms = '';
 667  
 668      // Existing terms
 669      } else {
 670          $terms = bbp_get_topic_tag_names( $topic_id );
 671      }
 672  
 673      /** Additional Actions (Before Save) **************************************/
 674  
 675      do_action( 'bbp_edit_reply_pre_extras', $reply_id );
 676  
 677      // Bail if errors
 678      if ( bbp_has_errors() ) {
 679          return;
 680      }
 681  
 682      /** No Errors *************************************************************/
 683  
 684      // Add the content of the form to $reply_data as an array
 685      // Just in time manipulation of reply data before being edited
 686      $reply_data = apply_filters( 'bbp_edit_reply_pre_insert', array(
 687          'ID'           => $reply_id,
 688          'post_title'   => $reply_title,
 689          'post_content' => $reply_content,
 690          'post_status'  => $reply_status,
 691          'post_parent'  => $topic_id,
 692          'post_author'  => $reply_author,
 693          'post_type'    => bbp_get_reply_post_type()
 694      ) );
 695  
 696      // Toggle revisions to avoid duplicates
 697      if ( post_type_supports( bbp_get_reply_post_type(), 'revisions' ) ) {
 698          $revisions_removed = true;
 699          remove_post_type_support( bbp_get_reply_post_type(), 'revisions' );
 700      }
 701  
 702      // Insert reply
 703      $reply_id = wp_update_post( $reply_data );
 704  
 705      // Toggle revisions back on
 706      if ( true === $revisions_removed ) {
 707          $revisions_removed = false;
 708          add_post_type_support( bbp_get_reply_post_type(), 'revisions' );
 709      }
 710  
 711      /** Topic Tags ************************************************************/
 712  
 713      // Just in time manipulation of reply terms before being edited
 714      $terms = apply_filters( 'bbp_edit_reply_pre_set_terms', $terms, $topic_id, $reply_id );
 715  
 716      // Insert terms
 717      $terms = wp_set_post_terms( $topic_id, $terms, bbp_get_topic_tag_tax_id(), false );
 718  
 719      // Term error
 720      if ( is_wp_error( $terms ) ) {
 721          bbp_add_error( 'bbp_reply_tags', __( '<strong>Error</strong>: There was a problem adding the tags to the topic.', 'bbpress' ) );
 722      }
 723  
 724      /** No Errors *************************************************************/
 725  
 726      if ( ! empty( $reply_id ) && ! is_wp_error( $reply_id ) ) {
 727  
 728          // Update counts, etc...
 729          do_action( 'bbp_edit_reply', $reply_id, $topic_id, $forum_id, $anonymous_data, $reply_author, true, $reply_to );
 730  
 731          /** Revisions *********************************************************/
 732  
 733          // Revision Reason
 734          if ( ! empty( $_POST['bbp_reply_edit_reason'] ) ) {
 735              $reply_edit_reason = sanitize_text_field( $_POST['bbp_reply_edit_reason'] );
 736          }
 737  
 738          // Update revision log
 739          if ( ! empty( $_POST['bbp_log_reply_edit'] ) && ( '1' === $_POST['bbp_log_reply_edit'] ) ) {
 740              $revision_id = wp_save_post_revision( $reply_id );
 741              if ( ! empty( $revision_id ) ) {
 742                  bbp_update_reply_revision_log( array(
 743                      'reply_id'    => $reply_id,
 744                      'revision_id' => $revision_id,
 745                      'author_id'   => bbp_get_current_user_id(),
 746                      'reason'      => $reply_edit_reason
 747                  ) );
 748              }
 749          }
 750  
 751          /** Additional Actions (After Save) ***********************************/
 752  
 753          do_action( 'bbp_edit_reply_post_extras', $reply_id );
 754  
 755          /** Redirect **********************************************************/
 756  
 757          // Redirect to
 758          $redirect_to = bbp_get_redirect_to();
 759  
 760          // Get the reply URL
 761          $reply_url = bbp_get_reply_url( $reply_id, $redirect_to );
 762  
 763          // Allow to be filtered
 764          $reply_url = apply_filters( 'bbp_edit_reply_redirect_to', $reply_url, $redirect_to );
 765  
 766          /** Successful Edit ***************************************************/
 767  
 768          // Redirect back to new reply
 769          bbp_redirect( $reply_url );
 770  
 771      /** Errors ****************************************************************/
 772  
 773      } else {
 774          $append_error = ( is_wp_error( $reply_id ) && $reply_id->get_error_message() ) ? $reply_id->get_error_message() . ' ' : '';
 775          bbp_add_error( 'bbp_reply_error', __( '<strong>Error</strong>: The following problem(s) have been found with your reply:' . $append_error . 'Please try again.', 'bbpress' ) );
 776      }
 777  }
 778  
 779  /**
 780   * Handle all the extra meta stuff from posting a new reply or editing a reply
 781   *
 782   * @param int $reply_id Optional. Reply id
 783   * @param int $topic_id Optional. Topic id
 784   * @param int $forum_id Optional. Forum id
 785   * @param array $anonymous_data Optional - if it's an anonymous post. Do not
 786   *                              supply if supplying $author_id. Should be
 787   *                              sanitized (see {@link bbp_filter_anonymous_post_data()}
 788   * @param int $author_id Author id
 789   * @param bool $is_edit Optional. Is the post being edited? Defaults to false.
 790   * @param int $reply_to Optional. Reply to id
 791   */
 792  function bbp_update_reply( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $author_id = 0, $is_edit = false, $reply_to = 0 ) {
 793  
 794      // Validate the ID's passed from 'bbp_new_reply' action
 795      $reply_id = bbp_get_reply_id( $reply_id );
 796      $topic_id = bbp_get_topic_id( $topic_id );
 797      $forum_id = bbp_get_forum_id( $forum_id );
 798      $reply_to = bbp_validate_reply_to( $reply_to, $reply_id );
 799  
 800      // Get the current user ID
 801      $user_id  = bbp_get_current_user_id();
 802  
 803      // Bail if there is no reply
 804      if ( empty( $reply_id ) ) {
 805          return;
 806      }
 807  
 808      // Check author_id, fallback to current user ID
 809      if ( empty( $author_id ) ) {
 810          $author_id = $user_id;
 811      }
 812  
 813      // Check topic_id, fallback to post_parent or meta
 814      if ( empty( $topic_id ) ) {
 815          $topic_id = bbp_get_reply_topic_id( $reply_id );
 816      }
 817  
 818      // Check forum_id, fallback to post_parent or meta
 819      if ( ! empty( $topic_id ) && empty( $forum_id ) ) {
 820          $forum_id = bbp_get_topic_forum_id( $topic_id );
 821      }
 822  
 823      // Update locks
 824      update_post_meta( $reply_id, '_edit_last', $user_id );
 825      delete_post_meta( $reply_id, '_edit_lock' );
 826  
 827      // Forum/Topic meta (early, for use in downstream functions)
 828      bbp_update_reply_forum_id( $reply_id, $forum_id );
 829      bbp_update_reply_topic_id( $reply_id, $topic_id );
 830      bbp_update_reply_to      ( $reply_id, $reply_to );
 831  
 832      // If anonymous post, store name, email, website and ip in post_meta.
 833      if ( ! empty( $anonymous_data ) ) {
 834  
 835          // Update anonymous meta data (not cookies)
 836          bbp_update_anonymous_post_author( $reply_id, $anonymous_data, bbp_get_reply_post_type() );
 837  
 838          // Set transient for throttle check (only on new, not edit)
 839          if ( empty( $is_edit ) ) {
 840              set_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted', time(), HOUR_IN_SECONDS );
 841          }
 842      }
 843  
 844      // Handle Subscription Checkbox
 845      if ( bbp_is_subscriptions_active() && ! empty( $author_id ) && ! empty( $topic_id ) ) {
 846  
 847          // Check if subscribed
 848          $subscribed = bbp_is_user_subscribed( $author_id, $topic_id );
 849  
 850          // Check for action
 851          $subscheck  = ( ! empty( $_POST['bbp_topic_subscription'] ) && ( 'bbp_subscribe' === $_POST['bbp_topic_subscription'] ) )
 852              ? true
 853              : false;
 854  
 855          // Subscribed and unsubscribing
 856          if ( ( true === $subscribed ) && ( false === $subscheck ) ) {
 857              bbp_remove_user_subscription( $author_id, $topic_id );
 858  
 859          // Not subscribed and subscribing
 860          } elseif ( ( false === $subscribed ) && ( true === $subscheck ) ) {
 861              bbp_add_user_subscription( $author_id, $topic_id );
 862          }
 863      }
 864  
 865      // Update associated topic values if this is a new reply
 866      if ( empty( $is_edit ) ) {
 867  
 868          // Update poster activity time
 869          bbp_update_user_last_posted( $author_id );
 870  
 871          // Update poster IP
 872          update_post_meta( $reply_id, '_bbp_author_ip', bbp_current_author_ip(), false );
 873  
 874          // Last active time
 875          $last_active_time = get_post_field( 'post_date', $reply_id );
 876  
 877          // Walk up ancestors and do the dirty work
 878          bbp_update_reply_walker( $reply_id, $last_active_time, $forum_id, $topic_id, false );
 879      }
 880  
 881      // Bump the custom query cache
 882      wp_cache_set( 'last_changed', microtime(), 'bbpress_posts' );
 883  }
 884  
 885  /**
 886   * Walk up the ancestor tree from the current reply, and update all the counts
 887   *
 888   * @since 2.0.0 bbPress (r2884)
 889   *
 890   * @param int $reply_id Optional. Reply id
 891   * @param string $last_active_time Optional. Last active time
 892   * @param int $forum_id Optional. Forum id
 893   * @param int $topic_id Optional. Topic id
 894   * @param bool $refresh If set to true, unsets all the previous parameters.
 895   *                       Defaults to true
 896   */
 897  function bbp_update_reply_walker( $reply_id, $last_active_time = '', $forum_id = 0, $topic_id = 0, $refresh = true ) {
 898  
 899      // Verify the reply ID
 900      $reply_id = bbp_get_reply_id( $reply_id );
 901  
 902      // Reply was passed
 903      if ( ! empty( $reply_id ) ) {
 904  
 905          // Get the topic ID if none was passed
 906          if ( empty( $topic_id ) ) {
 907              $topic_id = bbp_get_reply_topic_id( $reply_id );
 908          }
 909  
 910          // Get the forum ID if none was passed
 911          if ( empty( $forum_id ) ) {
 912              $forum_id = bbp_get_reply_forum_id( $reply_id );
 913          }
 914      }
 915  
 916      // Set the active_id based on topic_id/reply_id
 917      $active_id = empty( $reply_id ) ? $topic_id : $reply_id;
 918  
 919      // Setup ancestors array to walk up
 920      $ancestors = array_values( array_unique( array_merge( array( $topic_id, $forum_id ), (array) get_post_ancestors( $topic_id ) ) ) );
 921  
 922      // If we want a full refresh, unset any of the possibly passed variables
 923      if ( true === $refresh ) {
 924          $forum_id = $topic_id = $reply_id = $active_id = $last_active_time = 0;
 925      }
 926  
 927      // Walk up ancestors
 928      if ( ! empty( $ancestors ) ) {
 929          foreach ( $ancestors as $ancestor ) {
 930  
 931              // Reply meta relating to most recent reply
 932              if ( bbp_is_reply( $ancestor ) ) {
 933                  // @todo - hierarchical replies
 934  
 935              // Topic meta relating to most recent reply
 936              } elseif ( bbp_is_topic( $ancestor ) ) {
 937  
 938                  // Only update if reply is published
 939                  if ( ! bbp_is_reply_pending( $reply_id ) ) {
 940  
 941                      // Last reply and active ID's
 942                      bbp_update_topic_last_reply_id ( $ancestor, $reply_id  );
 943                      bbp_update_topic_last_active_id( $ancestor, $active_id );
 944  
 945                      // Get the last active time if none was passed
 946                      $topic_last_active_time = $last_active_time;
 947                      if ( empty( $last_active_time ) ) {
 948                          $topic_last_active_time = get_post_field( 'post_date', bbp_get_topic_last_active_id( $ancestor ) );
 949                      }
 950  
 951                      bbp_update_topic_last_active_time( $ancestor, $topic_last_active_time );
 952                  }
 953  
 954                  // Only update reply count if we've deleted a reply
 955                  if ( in_array( current_filter(), array( 'bbp_deleted_reply', 'save_post' ), true ) ) {
 956                      bbp_update_topic_reply_count(        $ancestor );
 957                      bbp_update_topic_reply_count_hidden( $ancestor );
 958                      bbp_update_topic_voice_count(        $ancestor );
 959                  }
 960  
 961              // Forum meta relating to most recent topic
 962              } elseif ( bbp_is_forum( $ancestor ) ) {
 963  
 964                  // Only update if reply is published
 965                  if ( ! bbp_is_reply_pending( $reply_id ) && ! bbp_is_topic_pending( $topic_id ) ) {
 966  
 967                      // Last topic and reply ID's
 968                      bbp_update_forum_last_topic_id( $ancestor, $topic_id );
 969                      bbp_update_forum_last_reply_id( $ancestor, $reply_id );
 970  
 971                      // Last Active
 972                      bbp_update_forum_last_active_id( $ancestor, $active_id );
 973  
 974                      // Get the last active time if none was passed
 975                      $forum_last_active_time = $last_active_time;
 976                      if ( empty( $last_active_time ) ) {
 977                          $forum_last_active_time = get_post_field( 'post_date', bbp_get_forum_last_active_id( $ancestor ) );
 978                      }
 979  
 980                      bbp_update_forum_last_active_time( $ancestor, $forum_last_active_time );
 981                  }
 982  
 983                  // Only update reply count if we've deleted a reply
 984                  if ( in_array( current_filter(), array( 'bbp_deleted_reply', 'save_post' ), true ) ) {
 985                      bbp_update_forum_reply_count( $ancestor );
 986                  }
 987              }
 988          }
 989      }
 990  }
 991  
 992  /** Reply Updaters ************************************************************/
 993  
 994  /**
 995   * Update the reply with its forum id it is in
 996   *
 997   * @since 2.0.0 bbPress (r2855)
 998   *
 999   * @param int $reply_id Optional. Reply id to update
1000   * @param int $forum_id Optional. Forum id
1001   * @return bool The forum id of the reply
1002   */
1003  function bbp_update_reply_forum_id( $reply_id = 0, $forum_id = 0 ) {
1004  
1005      // Validation
1006      $reply_id = bbp_get_reply_id( $reply_id );
1007      $forum_id = bbp_get_forum_id( $forum_id );
1008  
1009      // If no forum_id was passed, walk up ancestors and look for forum type
1010      if ( empty( $forum_id ) ) {
1011  
1012          // Get ancestors
1013          $ancestors = get_post_ancestors( $reply_id );
1014  
1015          // Loop through ancestors
1016          if ( ! empty( $ancestors ) ) {
1017              foreach ( $ancestors as $ancestor ) {
1018  
1019                  // Get first parent that is a forum
1020                  if ( get_post_field( 'post_type', $ancestor ) === bbp_get_forum_post_type() ) {
1021                      $forum_id = $ancestor;
1022  
1023                      // Found a forum, so exit the loop and continue
1024                      continue;
1025                  }
1026              }
1027          }
1028      }
1029  
1030      // Update the forum ID
1031      $retval = bbp_update_forum_id( $reply_id, $forum_id );
1032  
1033      // Filter & return
1034      return (int) apply_filters( 'bbp_update_reply_forum_id', $retval, $reply_id, $forum_id );
1035  }
1036  
1037  /**
1038   * Update the reply with its topic id it is in
1039   *
1040   * @since 2.0.0 bbPress (r2855)
1041   *
1042   * @param int $reply_id Optional. Reply id to update
1043   * @param int $topic_id Optional. Topic id
1044   * @return bool The topic id of the reply
1045   */
1046  function bbp_update_reply_topic_id( $reply_id = 0, $topic_id = 0 ) {
1047  
1048      // Validation
1049      $reply_id = bbp_get_reply_id( $reply_id );
1050      $topic_id = bbp_get_topic_id( $topic_id );
1051  
1052      // If no topic_id was passed, walk up ancestors and look for topic type
1053      if ( empty( $topic_id ) ) {
1054  
1055          // Get ancestors
1056          $ancestors = (array) get_post_ancestors( $reply_id );
1057  
1058          // Loop through ancestors
1059          if ( ! empty( $ancestors ) ) {
1060              foreach ( $ancestors as $ancestor ) {
1061  
1062                  // Get first parent that is a topic
1063                  if ( get_post_field( 'post_type', $ancestor ) === bbp_get_topic_post_type() ) {
1064                      $topic_id = $ancestor;
1065  
1066                      // Found a topic, so exit the loop and continue
1067                      continue;
1068                  }
1069              }
1070          }
1071      }
1072  
1073      // Update the topic ID
1074      $retval = bbp_update_topic_id( $reply_id, $topic_id );
1075  
1076      // Filter & return
1077      return (int) apply_filters( 'bbp_update_reply_topic_id', $retval, $reply_id, $topic_id );
1078  }
1079  
1080  /*
1081   * Update the meta data with its parent reply-to id, of a reply
1082   *
1083   * @since 2.4.0 bbPress (r4944)
1084   *
1085   * @param int $reply_id Reply id to update
1086   * @param int $reply_to Optional. Reply to id
1087   * @return bool The parent reply id of the reply
1088   */
1089  function bbp_update_reply_to( $reply_id = 0, $reply_to = 0 ) {
1090  
1091      // Validation
1092      $reply_id = bbp_get_reply_id( $reply_id );
1093      $reply_to = bbp_validate_reply_to( $reply_to, $reply_id );
1094  
1095      // Update or delete the `reply_to` postmeta
1096      if ( ! empty( $reply_id ) ) {
1097  
1098          // Update the reply to
1099          if ( ! empty( $reply_to ) ) {
1100              $reply_to = bbp_update_reply_to_id( $reply_id, $reply_to );
1101  
1102          // Delete the reply to
1103          } else {
1104              delete_post_meta( $reply_id, '_bbp_reply_to' );
1105          }
1106      }
1107  
1108      // Filter & return
1109      return (int) apply_filters( 'bbp_update_reply_to', $reply_to, $reply_id );
1110  }
1111  
1112  /**
1113   * Get all ancestors to a reply
1114   *
1115   * Because settings can be changed, this function does not care if hierarchical
1116   * replies are active or to what depth.
1117   *
1118   * @since 2.6.0 bbPress (r5390)
1119   *
1120   * @param int $reply_id
1121   * @return array
1122   */
1123  function bbp_get_reply_ancestors( $reply_id = 0 ) {
1124  
1125      // Validation
1126      $reply_id  = bbp_get_reply_id( $reply_id );
1127      $ancestors = array();
1128  
1129      // Reply id is valid
1130      if ( ! empty( $reply_id ) ) {
1131  
1132          // Try to get reply parent
1133          $reply_to = bbp_get_reply_to( $reply_id );
1134  
1135          // Reply has a hierarchical parent
1136          if ( ! empty( $reply_to ) ) {
1137  
1138              // Setup the current ID and current post as an ancestor
1139              $id        = $reply_to;
1140              $ancestors = array( $reply_to );
1141  
1142              // Get parent reply
1143              while ( $ancestor = bbp_get_reply( $id ) ) {
1144  
1145                  // Does parent have a parent?
1146                  $grampy_id = bbp_get_reply_to( $ancestor->ID );
1147  
1148                  // Loop detection: If the ancestor has been seen before, break.
1149                  if ( empty( $ancestor->post_parent ) || ( $grampy_id === $reply_id ) || in_array( $grampy_id, $ancestors, true ) ) {
1150                      break;
1151                  }
1152  
1153                  $id = $ancestors[] = $grampy_id;
1154              }
1155          }
1156      }
1157  
1158      // Filter & return
1159      return (array) apply_filters( 'bbp_get_reply_ancestors', $ancestors, $reply_id );
1160  }
1161  
1162  /**
1163   * Update the revision log of the reply
1164   *
1165   * @since 2.0.0 bbPress (r2782)
1166   *
1167   * @param array $args Supports these args:
1168   *  - reply_id: reply id
1169   *  - author_id: Author id
1170   *  - reason: Reason for editing
1171   *  - revision_id: Revision id
1172   * @return mixed False on failure, true on success
1173   */
1174  function bbp_update_reply_revision_log( $args = array() ) {
1175  
1176      // Parse arguments against default values
1177      $r = bbp_parse_args( $args, array(
1178          'reason'      => '',
1179          'reply_id'    => 0,
1180          'author_id'   => 0,
1181          'revision_id' => 0
1182      ), 'update_reply_revision_log' );
1183  
1184      // Populate the variables
1185      $r['reason']      = bbp_format_revision_reason( $r['reason'] );
1186      $r['reply_id']    = bbp_get_reply_id( $r['reply_id'] );
1187      $r['author_id']   = bbp_get_user_id ( $r['author_id'], false, true );
1188      $r['revision_id'] = (int) $r['revision_id'];
1189  
1190      // Get the logs and append the new one to those
1191      $revision_log                      = bbp_get_reply_raw_revision_log( $r['reply_id'] );
1192      $revision_log[ $r['revision_id'] ] = array( 'author' => $r['author_id'], 'reason' => $r['reason'] );
1193  
1194      // Finally, update
1195      update_post_meta( $r['reply_id'], '_bbp_revision_log', $revision_log );
1196  
1197      // Filter & return
1198      return apply_filters( 'bbp_update_reply_revision_log', $revision_log, $r['reply_id'] );
1199  }
1200  
1201  /**
1202   * Move reply handler
1203   *
1204   * Handles the front end move reply submission
1205   *
1206   * @since 2.3.0 bbPress (r4521)
1207   *
1208   * @param string $action The requested action to compare this function to
1209   */
1210  function bbp_move_reply_handler( $action = '' ) {
1211  
1212      // Bail if action is not 'bbp-move-reply'
1213      if ( 'bbp-move-reply' !== $action ) {
1214          return;
1215      }
1216  
1217      // Prevent debug notices
1218      $move_reply_id = $destination_topic_id = 0;
1219      $destination_topic_title = '';
1220      $destination_topic = $move_reply = $source_topic = '';
1221  
1222      /** Move Reply ***********************************************************/
1223  
1224      if ( empty( $_POST['bbp_reply_id'] ) ) {
1225          bbp_add_error( 'bbp_move_reply_reply_id', __( '<strong>Error</strong>: A reply ID is required.', 'bbpress' ) );
1226      } else {
1227          $move_reply_id = (int) $_POST['bbp_reply_id'];
1228      }
1229  
1230      $move_reply = bbp_get_reply( $move_reply_id );
1231  
1232      // Reply exists
1233      if ( empty( $move_reply ) ) {
1234          bbp_add_error( 'bbp_mover_reply_r_not_found', __( '<strong>Error</strong>: The reply you want to move was not found.', 'bbpress' ) );
1235      }
1236  
1237      /** Topic to Move From ***************************************************/
1238  
1239      // Get the current topic a reply is in
1240      $source_topic = bbp_get_topic( $move_reply->post_parent );
1241  
1242      // No topic
1243      if ( empty( $source_topic ) ) {
1244          bbp_add_error( 'bbp_move_reply_source_not_found', __( '<strong>Error</strong>: The topic you want to move from was not found.', 'bbpress' ) );
1245      }
1246  
1247      // Nonce check failed
1248      if ( ! bbp_verify_nonce_request( 'bbp-move-reply_' . $move_reply->ID ) ) {
1249          bbp_add_error( 'bbp_move_reply_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1250          return;
1251      }
1252  
1253      // Use cannot edit topic
1254      if ( ! current_user_can( 'edit_topic', $source_topic->ID ) ) {
1255          bbp_add_error( 'bbp_move_reply_source_permission', __( '<strong>Error</strong>: You do not have permission to edit the source topic.', 'bbpress' ) );
1256      }
1257  
1258      // How to move
1259      if ( ! empty( $_POST['bbp_reply_move_option'] ) ) {
1260          $move_option = (string) trim( $_POST['bbp_reply_move_option'] );
1261      }
1262  
1263      // Invalid move option
1264      if ( empty( $move_option ) || ! in_array( $move_option, array( 'existing', 'topic' ), true ) ) {
1265          bbp_add_error( 'bbp_move_reply_option', __( '<strong>Error</strong>: You need to choose a valid move option.', 'bbpress' ) );
1266  
1267      // Valid move option
1268      } else {
1269  
1270          // What kind of move
1271          switch ( $move_option ) {
1272  
1273              // Into an existing topic
1274              case 'existing' :
1275  
1276                  // Get destination topic id
1277                  if ( empty( $_POST['bbp_destination_topic'] ) ) {
1278                      bbp_add_error( 'bbp_move_reply_destination_id', __( '<strong>Error</strong>: A topic ID is required.', 'bbpress' ) );
1279                  } else {
1280                      $destination_topic_id = (int) $_POST['bbp_destination_topic'];
1281                  }
1282  
1283                  // Get the destination topic
1284                  $destination_topic = bbp_get_topic( $destination_topic_id );
1285  
1286                  // No destination topic
1287                  if ( empty( $destination_topic ) ) {
1288                      bbp_add_error( 'bbp_move_reply_destination_not_found', __( '<strong>Error</strong>: The topic you want to move to was not found.', 'bbpress' ) );
1289                  }
1290  
1291                  // User cannot edit the destination topic
1292                  if ( ! current_user_can( 'edit_topic', $destination_topic->ID ) ) {
1293                      bbp_add_error( 'bbp_move_reply_destination_permission', __( '<strong>Error</strong>: You do not have permission to edit the destination topic.', 'bbpress' ) );
1294                  }
1295  
1296                  // Bump the reply position
1297                  $reply_position = bbp_get_topic_reply_count( $destination_topic->ID, true ) + 1;
1298  
1299                  // Update the reply
1300                  wp_update_post( array(
1301                      'ID'          => $move_reply->ID,
1302                      'post_title'  => '',
1303                      'post_name'   => false, // will be automatically generated
1304                      'post_parent' => $destination_topic->ID,
1305                      'menu_order'  => $reply_position,
1306                      'guid'        => ''
1307                  ) );
1308  
1309                  // Adjust reply meta values
1310                  bbp_update_reply_topic_id( $move_reply->ID, $destination_topic->ID );
1311                  bbp_update_reply_forum_id( $move_reply->ID, bbp_get_topic_forum_id( $destination_topic->ID ) );
1312  
1313                  break;
1314  
1315              // Move reply to a new topic
1316              case 'topic' :
1317              default :
1318  
1319                  // User needs to be able to publish topics
1320                  if ( current_user_can( 'publish_topics' ) ) {
1321  
1322                      // Use the new title that was passed
1323                      if ( ! empty( $_POST['bbp_reply_move_destination_title'] ) ) {
1324                          $destination_topic_title = sanitize_text_field( $_POST['bbp_reply_move_destination_title'] );
1325  
1326                      // Use the source topic title
1327                      } else {
1328                          $destination_topic_title = $source_topic->post_title;
1329                      }
1330  
1331                      // Update the topic
1332                      $destination_topic_id = wp_update_post( array(
1333                          'ID'          => $move_reply->ID,
1334                          'post_title'  => $destination_topic_title,
1335                          'post_name'   => false,
1336                          'post_type'   => bbp_get_topic_post_type(),
1337                          'post_parent' => $source_topic->post_parent,
1338                          'guid'        => ''
1339                      ) );
1340                      $destination_topic = bbp_get_topic( $destination_topic_id );
1341  
1342                      // Make sure the new topic knows its a topic
1343                      bbp_update_topic_topic_id( $move_reply->ID );
1344  
1345                      // Shouldn't happen
1346                      if ( false === $destination_topic_id || is_wp_error( $destination_topic_id ) || empty( $destination_topic ) ) {
1347                          bbp_add_error( 'bbp_move_reply_destination_reply', __( '<strong>Error</strong>: There was a problem converting the reply into the topic. Please try again.', 'bbpress' ) );
1348                      }
1349  
1350                  // User cannot publish posts
1351                  } else {
1352                      bbp_add_error( 'bbp_move_reply_destination_permission', __( '<strong>Error</strong>: You do not have permission to create new topics. The reply could not be converted into a topic.', 'bbpress' ) );
1353                  }
1354  
1355                  break;
1356          }
1357      }
1358  
1359      // Bail if there are errors
1360      if ( bbp_has_errors() ) {
1361          return;
1362      }
1363  
1364      /** No Errors - Clean Up **************************************************/
1365  
1366      // Update counts, etc...
1367      do_action( 'bbp_pre_move_reply', $move_reply->ID, $source_topic->ID, $destination_topic->ID );
1368  
1369      /** Date Check ************************************************************/
1370  
1371      // Check if the destination topic is older than the move reply
1372      if ( strtotime( $move_reply->post_date ) < strtotime( $destination_topic->post_date ) ) {
1373  
1374          // Set destination topic post_date to 1 second before from reply
1375          $destination_post_date = date( 'Y-m-d H:i:s', strtotime( $move_reply->post_date ) - 1 );
1376  
1377          // Update destination topic
1378          wp_update_post( array(
1379              'ID'            => $destination_topic_id,
1380              'post_date'     => $destination_post_date,
1381              'post_date_gmt' => get_gmt_from_date( $destination_post_date )
1382          ) );
1383      }
1384  
1385      // Set the last reply ID and freshness to the move_reply
1386      $last_reply_id = $move_reply->ID;
1387      $freshness     = $move_reply->post_date;
1388  
1389      // Get the reply to
1390      $parent = bbp_get_reply_to( $move_reply->ID );
1391  
1392      // Fix orphaned children
1393      $children = get_posts( array(
1394          'post_type'  => bbp_get_reply_post_type(),
1395          'meta_key'   => '_bbp_reply_to',
1396          'meta_type'  => 'NUMERIC',
1397          'meta_value' => $move_reply->ID,
1398      ) );
1399      foreach ( $children as $child ) {
1400          bbp_update_reply_to( $child->ID, $parent );
1401      }
1402  
1403      // Remove reply_to from moved reply
1404      delete_post_meta( $move_reply->ID, '_bbp_reply_to' );
1405  
1406      // It is a new topic and we need to set some default metas to make
1407      // the topic display in bbp_has_topics() list
1408      if ( 'topic' === $move_option ) {
1409          bbp_update_topic_last_reply_id   ( $destination_topic->ID, $last_reply_id );
1410          bbp_update_topic_last_active_id  ( $destination_topic->ID, $last_reply_id );
1411          bbp_update_topic_last_active_time( $destination_topic->ID, $freshness     );
1412  
1413      // Otherwise update the existing destination topic
1414      } else {
1415          bbp_update_topic_last_reply_id   ( $destination_topic->ID );
1416          bbp_update_topic_last_active_id  ( $destination_topic->ID );
1417          bbp_update_topic_last_active_time( $destination_topic->ID );
1418      }
1419  
1420      // Update source topic ID last active
1421      bbp_update_topic_last_reply_id   ( $source_topic->ID );
1422      bbp_update_topic_last_active_id  ( $source_topic->ID );
1423      bbp_update_topic_last_active_time( $source_topic->ID );
1424  
1425      /** Successful Move ******************************************************/
1426  
1427      // Update counts, etc...
1428      do_action( 'bbp_post_move_reply', $move_reply->ID, $source_topic->ID, $destination_topic->ID );
1429  
1430      // Redirect back to the topic
1431      bbp_redirect( bbp_get_topic_permalink( $destination_topic->ID ) );
1432  }
1433  
1434  /**
1435   * Fix counts on reply move
1436   *
1437   * When a reply is moved, update the counts of source and destination topic
1438   * and their forums.
1439   *
1440   * @since 2.3.0 bbPress (r4521)
1441   *
1442   * @param int $move_reply_id Move reply id
1443   * @param int $source_topic_id Source topic id
1444   * @param int $destination_topic_id Destination topic id
1445   */
1446  function bbp_move_reply_count( $move_reply_id, $source_topic_id, $destination_topic_id ) {
1447  
1448      // Forum topic counts
1449      bbp_update_forum_topic_count( bbp_get_topic_forum_id( $destination_topic_id ) );
1450  
1451      // Forum reply counts
1452      bbp_update_forum_reply_count( bbp_get_topic_forum_id( $destination_topic_id ) );
1453  
1454      // Topic reply counts
1455      bbp_update_topic_reply_count( $source_topic_id      );
1456      bbp_update_topic_reply_count( $destination_topic_id );
1457  
1458      // Topic hidden reply counts
1459      bbp_update_topic_reply_count_hidden( $source_topic_id      );
1460      bbp_update_topic_reply_count_hidden( $destination_topic_id );
1461  
1462      // Topic voice counts
1463      bbp_update_topic_voice_count( $source_topic_id      );
1464      bbp_update_topic_voice_count( $destination_topic_id );
1465  
1466      do_action( 'bbp_move_reply_count', $move_reply_id, $source_topic_id, $destination_topic_id );
1467  }
1468  
1469  /** Reply Actions *************************************************************/
1470  
1471  /**
1472   * Handles the front end spamming/unspamming and trashing/untrashing/deleting of
1473   * replies
1474   *
1475   * @since 2.0.0 bbPress (r2740)
1476   *
1477   * @param string $action The requested action to compare this function to
1478   */
1479  function bbp_toggle_reply_handler( $action = '' ) {
1480  
1481      // Bail if required GET actions aren't passed
1482      if ( empty( $_GET['reply_id'] ) ) {
1483          return;
1484      }
1485  
1486      // What's the reply id?
1487      $reply_id = bbp_get_reply_id( (int) $_GET['reply_id'] );
1488  
1489      // Get possible reply-handler toggles
1490      $toggles = bbp_get_reply_toggles( $reply_id );
1491  
1492      // Bail if action isn't meant for this function
1493      if ( ! in_array( $action, $toggles, true ) ) {
1494          return;
1495      }
1496  
1497      // Make sure reply exists
1498      $reply = bbp_get_reply( $reply_id );
1499      if ( empty( $reply ) ) {
1500          bbp_add_error( 'bbp_toggle_reply_missing', __( '<strong>Error</strong>: This reply could not be found or no longer exists.', 'bbpress' ) );
1501          return;
1502      }
1503  
1504      // What is the user doing here?
1505      if ( ! current_user_can( 'edit_reply', $reply_id ) || ( 'bbp_toggle_reply_trash' === $action && ! current_user_can( 'delete_reply', $reply_id ) ) ) {
1506          bbp_add_error( 'bbp_toggle_reply_permission', __( '<strong>Error</strong>: You do not have permission to do that.', 'bbpress' ) );
1507          return;
1508      }
1509  
1510      // Sub-action?
1511      $sub_action = ! empty( $_GET['sub_action'] )
1512          ? sanitize_key( $_GET['sub_action'] )
1513          : false;
1514  
1515      // Preliminary array
1516      $post_data = array( 'ID' => $reply_id );
1517  
1518      // Do the reply toggling
1519      $retval = bbp_toggle_reply( array(
1520          'id'         => $reply_id,
1521          'action'     => $action,
1522          'sub_action' => $sub_action,
1523          'data'       => $post_data
1524      ) );
1525  
1526      // Do additional reply toggle actions
1527      do_action( 'bbp_toggle_reply_handler', $retval['status'], $post_data, $action );
1528  
1529      // Redirect back to reply
1530      if ( ( false !== $retval['status'] ) && ! is_wp_error( $retval['status'] ) ) {
1531          bbp_redirect( $retval['redirect_to'] );
1532  
1533      // Handle errors
1534      } else {
1535          bbp_add_error( 'bbp_toggle_reply', $retval['message'] );
1536      }
1537  }
1538  
1539  /**
1540   * Do the actual reply toggling
1541   *
1542   * This function is used by `bbp_toggle_reply_handler()` to do the actual heavy
1543   * lifting when it comes to toggling replies. It only really makes sense to call
1544   * within that context, so if you need to call this function directly, make sure
1545   * you're also doing what the handler does too.
1546   *
1547   * @since 2.6.0 bbPress (r6133)
1548   * @access private
1549   *
1550   * @param array $args
1551   */
1552  function bbp_toggle_reply( $args = array() ) {
1553  
1554      // Parse the arguments
1555      $r = bbp_parse_args( $args, array(
1556          'id'         => 0,
1557          'action'     => '',
1558          'sub_action' => '',
1559          'data'       => array()
1560      ) );
1561  
1562      // Build the nonce suffix
1563      $nonce_suffix = bbp_get_reply_post_type() . '_' . (int) $r['id'];
1564  
1565      // Default return values
1566      $retval = array(
1567          'status'      => 0,
1568          'message'     => '',
1569          'redirect_to' => bbp_get_reply_url( $r['id'], bbp_get_redirect_to() ),
1570          'view_all'    => false
1571      );
1572  
1573      // What action are we trying to perform?
1574      switch ( $r['action'] ) {
1575  
1576          // Toggle approve
1577          case 'bbp_toggle_reply_approve' :
1578              check_ajax_referer( "approve-{$nonce_suffix}" );
1579  
1580              $is_approve         = bbp_is_reply_pending( $r['id'] );
1581              $retval['status']   = $is_approve ? bbp_approve_reply( $r['id'] ) : bbp_unapprove_reply( $r['id'] );
1582              $retval['message']  = $is_approve ? __( '<strong>Error</strong>: There was a problem approving the reply.', 'bbpress' ) : __( '<strong>Error</strong>: There was a problem unapproving the reply.', 'bbpress' );
1583              $retval['view_all'] = ! $is_approve;
1584  
1585              break;
1586  
1587          // Toggle spam
1588          case 'bbp_toggle_reply_spam' :
1589              check_ajax_referer( "spam-{$nonce_suffix}" );
1590  
1591              $is_spam            = bbp_is_reply_spam( $r['id'] );
1592              $retval['status']   = $is_spam ? bbp_unspam_reply( $r['id'] ) : bbp_spam_reply( $r['id'] );
1593              $retval['message']  = $is_spam ? __( '<strong>Error</strong>: There was a problem unmarking the reply as spam.', 'bbpress' ) : __( '<strong>Error</strong>: There was a problem marking the reply as spam.', 'bbpress' );
1594              $retval['view_all'] = ! $is_spam;
1595  
1596              break;
1597  
1598          // Toggle trash
1599          case 'bbp_toggle_reply_trash' :
1600  
1601              // Which subaction?
1602              switch ( $r['sub_action'] ) {
1603                  case 'trash':
1604                      check_ajax_referer( "trash-{$nonce_suffix}" );
1605  
1606                      $retval['view_all'] = true;
1607                      $retval['status']   = wp_trash_post( $r['id'] );
1608                      $retval['message']  = __( '<strong>Error</strong>: There was a problem trashing the reply.', 'bbpress' );
1609  
1610                      break;
1611  
1612                  case 'untrash':
1613                      check_ajax_referer( "untrash-{$nonce_suffix}" );
1614  
1615                      $retval['status']  = wp_untrash_post( $r['id'] );
1616                      $retval['message'] = __( '<strong>Error</strong>: There was a problem untrashing the reply.', 'bbpress' );
1617  
1618                      break;
1619  
1620                  case 'delete':
1621                      check_ajax_referer( "delete-{$nonce_suffix}" );
1622  
1623                      $retval['status']  = wp_delete_post( $r['id'] );
1624                      $retval['message'] = __( '<strong>Error</strong>: There was a problem deleting the reply.', 'bbpress' );
1625  
1626                      break;
1627              }
1628  
1629              break;
1630      }
1631  
1632      // Add view all if needed
1633      if ( ! empty( $retval['view_all'] ) ) {
1634          $retval['redirect_to'] = bbp_add_view_all( $retval['redirect_to'], true );
1635      }
1636  
1637      // Filter & return
1638      return apply_filters( 'bbp_toggle_reply', $retval, $r, $args );
1639  }
1640  
1641  /** Helpers *******************************************************************/
1642  
1643  /**
1644   * Return an associative array of available reply statuses
1645   *
1646   * @since 2.6.0 bbPress (r5399)
1647   *
1648   * @param int $reply_id   Optional. Reply id.
1649   *
1650   * @return array
1651   */
1652  function bbp_get_reply_statuses( $reply_id = 0 ) {
1653  
1654      // Filter & return
1655      return (array) apply_filters( 'bbp_get_reply_statuses', array(
1656          bbp_get_public_status_id()  => _x( 'Publish', 'Publish the reply',     'bbpress' ),
1657          bbp_get_spam_status_id()    => _x( 'Spam',    'Spam the reply',        'bbpress' ),
1658          bbp_get_trash_status_id()   => _x( 'Trash',   'Trash the reply',       'bbpress' ),
1659          bbp_get_pending_status_id() => _x( 'Pending', 'Mark reply as pending', 'bbpress' )
1660      ), $reply_id );
1661  }
1662  
1663  /**
1664   * Return array of available reply toggle actions
1665   *
1666   * @since 2.6.0 bbPress (r6133)
1667   *
1668   * @param int $reply_id   Optional. Reply id.
1669   *
1670   * @return array
1671   */
1672  function bbp_get_reply_toggles( $reply_id = 0 ) {
1673  
1674      // Filter & return
1675      return (array) apply_filters( 'bbp_get_toggle_reply_actions', array(
1676          'bbp_toggle_reply_spam',
1677          'bbp_toggle_reply_trash',
1678          'bbp_toggle_reply_approve'
1679      ), $reply_id );
1680  }
1681  
1682  /**
1683   * Return array of public reply statuses.
1684   *
1685   * @since 2.6.0 bbPress (r6705)
1686   *
1687   * @return array
1688   */
1689  function bbp_get_public_reply_statuses() {
1690      $statuses = array(
1691          bbp_get_public_status_id()
1692      );
1693  
1694      // Filter & return
1695      return (array) apply_filters( 'bbp_get_public_reply_statuses', $statuses );
1696  }
1697  
1698  /**
1699   * Return array of non-public reply statuses.
1700   *
1701   * @since 2.6.0 bbPress (r6791)
1702   *
1703   * @return array
1704   */
1705  function bbp_get_non_public_reply_statuses() {
1706      $statuses = array(
1707          bbp_get_trash_status_id(),
1708          bbp_get_spam_status_id(),
1709          bbp_get_pending_status_id()
1710      );
1711  
1712      // Filter & return
1713      return (array) apply_filters( 'bbp_get_non_public_reply_statuses', $statuses );
1714  }
1715  
1716  /** Reply Actions *************************************************************/
1717  
1718  /**
1719   * Marks a reply as spam
1720   *
1721   * @since 2.0.0 bbPress (r2740)
1722   *
1723   * @param int $reply_id Reply id
1724   * @return mixed False or {@link WP_Error} on failure, reply id on success
1725   */
1726  function bbp_spam_reply( $reply_id = 0 ) {
1727  
1728      // Get reply
1729      $reply = bbp_get_reply( $reply_id );
1730      if ( empty( $reply ) ) {
1731          return $reply;
1732      }
1733  
1734      // Bail if already spam
1735      if ( bbp_get_spam_status_id() === $reply->post_status ) {
1736          return false;
1737      }
1738  
1739      // Execute pre spam code
1740      do_action( 'bbp_spam_reply', $reply_id );
1741  
1742      // Add the original post status as post meta for future restoration
1743      add_post_meta( $reply_id, '_bbp_spam_meta_status', $reply->post_status );
1744  
1745      // Set post status to spam
1746      $reply->post_status = bbp_get_spam_status_id();
1747  
1748      // No revisions
1749      remove_action( 'pre_post_update', 'wp_save_post_revision' );
1750  
1751      // Update the reply
1752      $reply_id = wp_update_post( $reply );
1753  
1754      // Execute post spam code
1755      do_action( 'bbp_spammed_reply', $reply_id );
1756  
1757      // Return reply_id
1758      return $reply_id;
1759  }
1760  
1761  /**
1762   * Unspams a reply
1763   *
1764   * @since 2.0.0 bbPress (r2740)
1765   *
1766   * @param int $reply_id Reply id
1767   * @return mixed False or {@link WP_Error} on failure, reply id on success
1768   */
1769  function bbp_unspam_reply( $reply_id = 0 ) {
1770  
1771      // Get reply
1772      $reply = bbp_get_reply( $reply_id );
1773      if ( empty( $reply ) ) {
1774          return $reply;
1775      }
1776  
1777      // Bail if already not spam
1778      if ( bbp_get_spam_status_id() !== $reply->post_status ) {
1779          return false;
1780      }
1781  
1782      // Execute pre unspam code
1783      do_action( 'bbp_unspam_reply', $reply_id );
1784  
1785      // Get pre spam status
1786      $reply->post_status = get_post_meta( $reply_id, '_bbp_spam_meta_status', true );
1787  
1788      // If no previous status, default to publish
1789      if ( empty( $reply->post_status ) ) {
1790          $reply->post_status = bbp_get_public_status_id();
1791      }
1792  
1793      // Delete pre spam meta
1794      delete_post_meta( $reply_id, '_bbp_spam_meta_status' );
1795  
1796      // No revisions
1797      remove_action( 'pre_post_update', 'wp_save_post_revision' );
1798  
1799      // Update the reply
1800      $reply_id = wp_update_post( $reply );
1801  
1802      // Execute post unspam code
1803      do_action( 'bbp_unspammed_reply', $reply_id );
1804  
1805      // Return reply_id
1806      return $reply_id;
1807  }
1808  
1809  /**
1810   * Approves a reply
1811   *
1812   * @since 2.6.0 bbPress (r5506)
1813   *
1814   * @param int $reply_id Reply id
1815   * @return mixed False or {@link WP_Error} on failure, reply id on success
1816   */
1817  function bbp_approve_reply( $reply_id = 0 ) {
1818  
1819      // Get reply
1820      $reply = bbp_get_reply( $reply_id );
1821      if ( empty( $reply ) ) {
1822          return $reply;
1823      }
1824  
1825      // Get new status
1826      $status = bbp_get_public_status_id();
1827  
1828      // Bail if already approved
1829      if ( $status === $reply->post_status ) {
1830          return false;
1831      }
1832  
1833      // Execute pre pending code
1834      do_action( 'bbp_approve_reply', $reply_id );
1835  
1836      // Set publish status
1837      $reply->post_status = $status;
1838  
1839      // Set post date GMT - prevents post_date override in wp_update_post()
1840      $reply->post_date_gmt = get_gmt_from_date( $reply->post_date );
1841  
1842      // No revisions
1843      remove_action( 'pre_post_update', 'wp_save_post_revision' );
1844  
1845      // Update reply
1846      $reply_id = wp_update_post( $reply );
1847  
1848      // Execute post pending code
1849      do_action( 'bbp_approved_reply', $reply_id );
1850  
1851      // Return reply_id
1852      return $reply_id;
1853  }
1854  
1855  /**
1856   * Unapproves a reply
1857   *
1858   * @since 2.6.0 bbPress (r5506)
1859   *
1860   * @param int $reply_id Reply id
1861   * @return mixed False or {@link WP_Error} on failure, reply id on success
1862   */
1863  function bbp_unapprove_reply( $reply_id = 0 ) {
1864  
1865      // Get reply
1866      $reply = bbp_get_reply( $reply_id );
1867      if ( empty( $reply ) ) {
1868          return $reply;
1869      }
1870  
1871      // Get new status
1872      $status = bbp_get_pending_status_id();
1873  
1874      // Bail if already pending
1875      if ( $status === $reply->post_status ) {
1876          return false;
1877      }
1878  
1879      // Execute pre open code
1880      do_action( 'bbp_unapprove_reply', $reply_id );
1881  
1882      // Set pending status
1883      $reply->post_status = $status;
1884  
1885      // No revisions
1886      remove_action( 'pre_post_update', 'wp_save_post_revision' );
1887  
1888      // Update reply
1889      $reply_id = wp_update_post( $reply );
1890  
1891      // Execute post open code
1892      do_action( 'bbp_unapproved_reply', $reply_id );
1893  
1894      // Return reply_id
1895      return $reply_id;
1896  }
1897  
1898  /** Before Delete/Trash/Untrash ***********************************************/
1899  
1900  /**
1901   * Called before deleting a reply
1902   */
1903  function bbp_delete_reply( $reply_id = 0 ) {
1904      $reply_id = bbp_get_reply_id( $reply_id );
1905  
1906      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1907          return false;
1908      }
1909  
1910      do_action( 'bbp_delete_reply', $reply_id );
1911  }
1912  
1913  /**
1914   * Called before trashing a reply
1915   */
1916  function bbp_trash_reply( $reply_id = 0 ) {
1917      $reply_id = bbp_get_reply_id( $reply_id );
1918  
1919      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1920          return false;
1921      }
1922  
1923      do_action( 'bbp_trash_reply', $reply_id );
1924  }
1925  
1926  /**
1927   * Called before untrashing (restoring) a reply
1928   */
1929  function bbp_untrash_reply( $reply_id = 0 ) {
1930      $reply_id = bbp_get_reply_id( $reply_id );
1931  
1932      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1933          return false;
1934      }
1935  
1936      do_action( 'bbp_untrash_reply', $reply_id );
1937  }
1938  
1939  /** After Delete/Trash/Untrash ************************************************/
1940  
1941  /**
1942   * Called after deleting a reply
1943   *
1944   * @since 2.0.0 bbPress (r2993)
1945   */
1946  function bbp_deleted_reply( $reply_id = 0 ) {
1947      $reply_id = bbp_get_reply_id( $reply_id );
1948  
1949      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1950          return false;
1951      }
1952  
1953      do_action( 'bbp_deleted_reply', $reply_id );
1954  }
1955  
1956  /**
1957   * Called after trashing a reply
1958   *
1959   * @since 2.0.0 bbPress (r2993)
1960   */
1961  function bbp_trashed_reply( $reply_id = 0 ) {
1962      $reply_id = bbp_get_reply_id( $reply_id );
1963  
1964      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1965          return false;
1966      }
1967  
1968      do_action( 'bbp_trashed_reply', $reply_id );
1969  }
1970  
1971  /**
1972   * Called after untrashing (restoring) a reply
1973   *
1974   * @since 2.0.0 bbPress (r2993)
1975   */
1976  function bbp_untrashed_reply( $reply_id = 0 ) {
1977      $reply_id = bbp_get_reply_id( $reply_id );
1978  
1979      if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
1980          return false;
1981      }
1982  
1983      do_action( 'bbp_untrashed_reply', $reply_id );
1984  }
1985  
1986  /** Settings ******************************************************************/
1987  
1988  /**
1989   * Return the replies per page setting
1990   *
1991   * @since 2.0.0 bbPress (r3540)
1992   *
1993   * @param int $default Default replies per page (15)
1994   * @return int
1995   */
1996  function bbp_get_replies_per_page( $default = 15 ) {
1997  
1998      // Get database option and cast as integer
1999      $retval = get_option( '_bbp_replies_per_page', $default );
2000  
2001      // If return val is empty, set it to default
2002      if ( empty( $retval ) ) {
2003          $retval = $default;
2004      }
2005  
2006      // Filter & return
2007      return (int) apply_filters( 'bbp_get_replies_per_page', $retval, $default );
2008  }
2009  
2010  /**
2011   * Return the replies per RSS page setting
2012   *
2013   * @since 2.0.0 bbPress (r3540)
2014   *
2015   * @param int $default Default replies per page (25)
2016   * @return int
2017   */
2018  function bbp_get_replies_per_rss_page( $default = 25 ) {
2019  
2020      // Get database option and cast as integer
2021      $retval = get_option( '_bbp_replies_per_rss_page', $default );
2022  
2023      // If return val is empty, set it to default
2024      if ( empty( $retval ) ) {
2025          $retval = $default;
2026      }
2027  
2028      // Filter & return
2029      return (int) apply_filters( 'bbp_get_replies_per_rss_page', $retval, $default );
2030  }
2031  
2032  /** Autoembed *****************************************************************/
2033  
2034  /**
2035   * Check if autoembeds are enabled and hook them in if so
2036   *
2037   * @since 2.1.0 bbPress (r3752)
2038   *
2039   * @global WP_Embed $wp_embed
2040   */
2041  function bbp_reply_content_autoembed() {
2042      global $wp_embed;
2043  
2044      if ( bbp_use_autoembed() && is_a( $wp_embed, 'WP_Embed' ) ) {
2045          add_filter( 'bbp_get_reply_content', array( $wp_embed, 'autoembed' ), 2 );
2046      }
2047  }
2048  
2049  /** Filters *******************************************************************/
2050  
2051  /**
2052   * Used by bbp_has_replies() to add the lead topic post to the posts loop
2053   *
2054   * This function filters the 'post_where' of the WP_Query, and changes the query
2055   * to include both the topic AND its children in the same loop.
2056   *
2057   * @since 2.1.0 bbPress (r4058)
2058   *
2059   * @param string $where
2060   * @return string
2061   */
2062  function _bbp_has_replies_where( $where = '', $query = false ) {
2063  
2064      /** Bail ******************************************************************/
2065  
2066      // Bail if the sky is falling
2067      if ( empty( $where ) || empty( $query ) ) {
2068          return $where;
2069      }
2070  
2071      // Bail if no post_parent to replace
2072      if ( ! is_numeric( $query->get( 'post_parent' ) ) ) {
2073          return $where;
2074      }
2075  
2076      // Bail if not a topic and reply query
2077      if ( array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) !== $query->get( 'post_type' ) ) {
2078          return $where;
2079      }
2080  
2081      // Bail if including specific post ID's
2082      if ( $query->get( 'post__in' ) ) {
2083          return $where;
2084      }
2085  
2086      /** Proceed ***************************************************************/
2087  
2088      // Table name for posts
2089      $table_name = bbp_db()->prefix . 'posts';
2090  
2091      // Get the topic ID from the post_parent, set in bbp_has_replies()
2092      $topic_id   = bbp_get_topic_id( $query->get( 'post_parent' ) );
2093  
2094      // The texts to search for
2095      $search     = array(
2096          "FROM {$table_name} ",
2097          "WHERE 1=1  AND {$table_name}.post_parent = {$topic_id}",
2098          ") AND {$table_name}.post_parent = {$topic_id}"
2099      );
2100  
2101      // The texts to replace them with
2102      $replace     = array(
2103          $search[0] . 'FORCE INDEX (PRIMARY, post_parent) ',
2104          "WHERE 1=1 AND ({$table_name}.ID = {$topic_id} OR {$table_name}.post_parent = {$topic_id})",
2105          ") AND ({$table_name}.ID = {$topic_id} OR {$table_name}.post_parent = {$topic_id})"
2106      );
2107  
2108      // Try to replace the search text with the replacement
2109      $new_where = str_replace( $search, $replace, $where );
2110      if ( ! empty( $new_where ) ) {
2111          $where = $new_where;
2112      }
2113  
2114      return $where;
2115  }
2116  
2117  /** Feeds *********************************************************************/
2118  
2119  /**
2120   * Output an RSS2 feed of replies, based on the query passed.
2121   *
2122   * @since 2.0.0 bbPress (r3171)
2123   *
2124   * @param array $replies_query
2125   */
2126  function bbp_display_replies_feed_rss2( $replies_query = array() ) {
2127  
2128      // User cannot access forum this topic is in
2129      if ( bbp_is_single_topic() && ! bbp_user_can_view_forum( array( 'forum_id' => bbp_get_topic_forum_id() ) ) ) {
2130          return;
2131      }
2132  
2133      // Adjust the title based on context
2134      if ( bbp_is_single_topic() ) {
2135          $title = get_wp_title_rss();
2136      } elseif ( ! bbp_show_lead_topic() ) {
2137          $title = get_bloginfo_rss( 'name' ) . ' &#187; ' .  __( 'All Posts',   'bbpress' );
2138      } else {
2139          $title = get_bloginfo_rss( 'name' ) . ' &#187; ' .  __( 'All Replies', 'bbpress' );
2140      }
2141  
2142      $title = apply_filters( 'wp_title_rss', $title );
2143  
2144      // Display the feed
2145      header( 'Content-Type: ' . feed_content_type( 'rss2' ) . '; charset=' . get_option( 'blog_charset' ), true );
2146      header( 'Status: 200 OK' );
2147      echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>'; ?>
2148  
2149      <rss version="2.0"
2150          xmlns:content="http://purl.org/rss/1.0/modules/content/"
2151          xmlns:wfw="http://wellformedweb.org/CommentAPI/"
2152          xmlns:dc="http://purl.org/dc/elements/1.1/"
2153          xmlns:atom="http://www.w3.org/2005/Atom"
2154  
2155          <?php do_action( 'bbp_feed' ); ?>
2156      >
2157  
2158      <channel>
2159  
2160          <title><?php echo $title; // Already escaped ?></title>
2161          <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
2162          <link><?php self_link(); ?></link>
2163          <description><?php //?></description><?php // phpcs:ignore ?>
2164          <lastBuildDate><?php echo date( 'r' ); ?></lastBuildDate>
2165          <generator><?php echo esc_url_raw( 'https://bbpress.org/?v=' . convert_chars( bbp_get_version() ) ); ?></generator>
2166          <language><?php bloginfo_rss( 'language' ); ?></language>
2167  
2168          <?php do_action( 'bbp_feed_head' ); ?>
2169  
2170          <?php if ( bbp_is_single_topic() ) : ?>
2171              <?php if ( bbp_user_can_view_forum( array( 'forum_id' => bbp_get_topic_forum_id() ) ) ) : ?>
2172                  <?php if ( bbp_show_lead_topic() ) : ?>
2173  
2174                      <item>
2175                          <guid><?php bbp_topic_permalink(); ?></guid>
2176                          <title><![CDATA[<?php bbp_topic_title(); ?>]]></title>
2177                          <link><?php bbp_topic_permalink(); ?></link>
2178                          <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ); ?></pubDate>
2179                          <dc:creator><?php the_author(); ?></dc:creator>
2180  
2181                          <description>
2182                              <![CDATA[
2183                              <p><?php printf( __( 'Replies: %s', 'bbpress' ), bbp_get_topic_reply_count() ); ?></p>
2184                              <?php bbp_topic_content(); ?>
2185                              ]]>
2186                          </description>
2187  
2188                          <?php rss_enclosure(); ?>
2189  
2190                          <?php do_action( 'bbp_feed_item' ); ?>
2191  
2192                      </item>
2193  
2194                  <?php endif; ?>
2195              <?php endif; ?>
2196          <?php endif; ?>
2197  
2198          <?php if ( bbp_has_replies( $replies_query ) ) : ?>
2199              <?php while ( bbp_replies() ) : bbp_the_reply(); ?>
2200  
2201                  <item>
2202                      <guid><?php bbp_reply_url(); ?></guid>
2203                      <title><![CDATA[<?php bbp_reply_title(); ?>]]></title>
2204                      <link><?php bbp_reply_url(); ?></link>
2205                      <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ); ?></pubDate>
2206                      <dc:creator><?php the_author(); ?></dc:creator>
2207  
2208                      <description>
2209                          <![CDATA[
2210                          <?php bbp_reply_content(); ?>
2211                          ]]>
2212                      </description>
2213  
2214                      <?php rss_enclosure(); ?>
2215  
2216                      <?php do_action( 'bbp_feed_item' ); ?>
2217  
2218                  </item>
2219  
2220              <?php endwhile; ?>
2221          <?php endif; ?>
2222  
2223          <?php do_action( 'bbp_feed_footer' ); ?>
2224  
2225      </channel>
2226      </rss>
2227  
2228  <?php
2229  
2230      // We're done here
2231      exit();
2232  }
2233  
2234  /** Permissions ***************************************************************/
2235  
2236  /**
2237   * Redirect if unauthorized user is attempting to edit a reply
2238   *
2239   * @since 2.1.0 bbPress (r3605)
2240   */
2241  function bbp_check_reply_edit() {
2242  
2243      // Bail if not editing a topic
2244      if ( ! bbp_is_reply_edit() ) {
2245          return;
2246      }
2247  
2248      // User cannot edit topic, so redirect back to reply
2249      if ( ! current_user_can( 'edit_reply', bbp_get_reply_id() ) ) {
2250          bbp_redirect( bbp_get_reply_url() );
2251      }
2252  }
2253  
2254  /** Reply Position ************************************************************/
2255  
2256  /**
2257   * Update the position of the reply.
2258   *
2259   * The reply position is stored in the menu_order column of the posts table.
2260   * This is done to prevent using a meta_query to retrieve posts in the proper
2261   * freshness order. By updating the menu_order accordingly, we're able to
2262   * leverage core WordPress query ordering much more effectively.
2263   *
2264   * @since 2.1.0 bbPress (r3933)
2265   *
2266   * @param int $reply_id
2267   * @param int $reply_position
2268   *
2269   * @return mixed
2270   */
2271  function bbp_update_reply_position( $reply_id = 0, $reply_position = false ) {
2272  
2273      // Bail if reply_id is empty
2274      $reply_id = bbp_get_reply_id( $reply_id );
2275      if ( empty( $reply_id ) ) {
2276          return false;
2277      }
2278  
2279      // Prepare the reply position
2280      $reply_position = is_numeric( $reply_position )
2281          ? (int) $reply_position
2282          : bbp_get_reply_position_raw( $reply_id, bbp_get_reply_topic_id( $reply_id ) );
2283  
2284      // Get the current reply position
2285      $current_position = get_post_field( 'menu_order', $reply_id );
2286  
2287      // Bail if no change
2288      if ( $reply_position === $current_position ) {
2289          return false;
2290      }
2291  
2292      // Filters not removed
2293      $removed = false;
2294  
2295      // Toggle revisions off as we are not altering content
2296      if ( has_filter( 'clean_post_cache', 'bbp_clean_post_cache' ) ) {
2297          $removed = true;
2298          remove_filter( 'clean_post_cache', 'bbp_clean_post_cache', 10, 2 );
2299      }
2300  
2301      // Update the replies' 'menu_order' with the reply position
2302      $bbp_db = bbp_db();
2303      $bbp_db->update( $bbp_db->posts, array( 'menu_order' => $reply_position ), array( 'ID' => $reply_id ) );
2304      clean_post_cache( $reply_id );
2305  
2306      // Toggle revisions back on
2307      if ( true === $removed ) {
2308          $removed = false;
2309          add_filter( 'clean_post_cache', 'bbp_clean_post_cache', 10, 2 );
2310      }
2311  
2312      return (int) $reply_position;
2313  }
2314  
2315  /**
2316   * Get the position of a reply by querying the DB directly for the replies
2317   * of a given topic.
2318   *
2319   * @since 2.1.0 bbPress (r3933)
2320   *
2321   * @param int $reply_id
2322   * @param int $topic_id
2323   */
2324  function bbp_get_reply_position_raw( $reply_id = 0, $topic_id = 0 ) {
2325  
2326      // Get required data
2327      $reply_position = 0;
2328      $reply_id       = bbp_get_reply_id( $reply_id );
2329      $topic_id       = ! empty( $topic_id )
2330          ? bbp_get_topic_id( $topic_id )
2331          : bbp_get_reply_topic_id( $reply_id );
2332  
2333      // If reply is actually the first post in a topic, return 0
2334      if ( $reply_id !== $topic_id ) {
2335  
2336          // Make sure the topic has replies before running another query
2337          $reply_count = bbp_get_topic_reply_count( $topic_id, false );
2338          if ( ! empty( $reply_count ) ) {
2339  
2340              // Get reply id's
2341              $topic_replies = bbp_get_all_child_ids( $topic_id, bbp_get_reply_post_type() );
2342              if ( ! empty( $topic_replies ) ) {
2343  
2344                  // Reverse replies array and search for current reply position
2345                  $topic_replies  = array_reverse( $topic_replies );
2346                  $reply_position = array_search( (string) $reply_id, $topic_replies );
2347  
2348                  // Bump the position to compensate for the lead topic post
2349                  $reply_position++;
2350              }
2351          }
2352      }
2353  
2354      return (int) $reply_position;
2355  }
2356  
2357  /** Hierarchical Replies ******************************************************/
2358  
2359  /**
2360   * Are replies threaded?
2361   *
2362   * @since 2.4.0 bbPress (r4944)
2363   * @since 2.6.0 bbPress (r6245) Always false on user profile reply pages
2364   *
2365   * @param bool $default Optional. Default value true
2366   *
2367   * @return bool Are replies threaded?
2368   */
2369  function bbp_thread_replies() {
2370      $depth = bbp_thread_replies_depth();
2371      $allow = bbp_allow_threaded_replies();
2372  
2373      // Never thread replies on user profile pages. It looks weird, and we know
2374      // it is undesirable for the majority of installations.
2375      if ( bbp_is_single_user_replies() ) {
2376          $retval = false;
2377      } else {
2378          $retval = (bool) ( ( $depth >= 2 ) && ( true === $allow ) );
2379      }
2380  
2381      // Filter & return
2382      return (bool) apply_filters( 'bbp_thread_replies', $retval, $depth, $allow );
2383  }
2384  
2385  /**
2386   * List threaded replies
2387   *
2388   * @since 2.4.0 bbPress (r4944)
2389   */
2390  function bbp_list_replies( $args = array() ) {
2391  
2392      // Get bbPress
2393      $bbp = bbpress();
2394  
2395      // Reset the reply depth
2396      $bbp->reply_query->reply_depth = 0;
2397  
2398      // In reply loop
2399      $bbp->reply_query->in_the_loop = true;
2400  
2401      // Parse arguments
2402      $r = bbp_parse_args( $args, array(
2403          'walker'       => new BBP_Walker_Reply(),
2404          'max_depth'    => bbp_thread_replies_depth(),
2405          'style'        => 'ul',
2406          'callback'     => null,
2407          'end_callback' => null,
2408          'page'         => 1,
2409          'per_page'     => -1
2410      ), 'list_replies' );
2411  
2412      // Get replies to loop through in $_replies
2413      echo '<ul>' . $r['walker']->paged_walk( $bbp->reply_query->posts, $r['max_depth'], $r['page'], $r['per_page'], $r ) . '</ul>';
2414  
2415      $bbp->max_num_pages            = $r['walker']->max_pages;
2416      $bbp->reply_query->in_the_loop = false;
2417  }
2418  
2419  /**
2420   * Validate a `reply_to` field for hierarchical replies
2421   *
2422   * Checks for 2 scenarios:
2423   * -- The reply to ID is actually a reply
2424   * -- The reply to ID does not match the current reply
2425   *
2426   * @see https://bbpress.trac.wordpress.org/ticket/2588
2427   * @see https://bbpress.trac.wordpress.org/ticket/2586
2428   *
2429   * @since 2.5.4 bbPress (r5377)
2430   *
2431   * @param int $reply_to
2432   * @param int $reply_id
2433   *
2434   * @return int $reply_to
2435   */
2436  function bbp_validate_reply_to( $reply_to = 0, $reply_id = 0 ) {
2437  
2438      // The parent reply must actually be a reply
2439      if ( ! bbp_is_reply( $reply_to ) ) {
2440          $reply_to = 0;
2441      }
2442  
2443      // The parent reply cannot be itself
2444      if ( $reply_id === $reply_to ) {
2445          $reply_to = 0;
2446      }
2447  
2448      return (int) $reply_to;
2449  }


Generated: Sat Jan 16 01:01:25 2021 Cross-referenced by PHPXref 0.7.1