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


Generated: Sat Apr 27 01:00:49 2024 Cross-referenced by PHPXref 0.7.1