PluginProbe
bbPress / 2.6.17
bbPress v2.6.17
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 2.2 2.2.1 All 72 releases
bbpress / includes / replies / functions.php

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

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