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


Generated: Sun Oct 25 01:01:30 2020 Cross-referenced by PHPXref 0.7.1