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