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


Generated: Thu Sep 19 01:01:28 2019 Cross-referenced by PHPXref 0.7.1