PluginProbe
bbPress / 2.6.19
bbPress v2.6.19
2.6.19 2.6.18 2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 All 74 releases
bbpress / includes / replies / functions.php

functions.php in bbPress 2.6.19, at includes/replies/functions.php

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