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 / forums / functions.php

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

3,570 lines 106.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Forum 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 forum to function properly.
18 *
19 * @since 2.0.0 bbPress (r3349)
20 *
21 * @param array $forum_data Forum post data
22 * @param array $forum_meta Forum meta data
23 */
24 function bbp_insert_forum( $forum_data = array(), $forum_meta = array() ) {
25
26 // Forum
27 $forum_data = bbp_parse_args(
28 $forum_data,
29 array(
30 'post_parent' => 0, // forum ID
31 'post_status' => bbp_get_public_status_id(),
32 'post_type' => bbp_get_forum_post_type(),
33 'post_author' => bbp_get_current_user_id(),
34 'post_password' => '',
35 'post_content' => '',
36 'post_title' => '',
37 'menu_order' => 0,
38 'comment_status' => 'closed'
39 ),
40 'insert_forum'
41 );
42
43 // Insert forum
44 $forum_id = wp_insert_post( $forum_data, false );
45
46 // Bail if no forum was added
47 if ( empty( $forum_id ) ) {
48 return false;
49 }
50
51 // Forum meta
52 $forum_meta = bbp_parse_args(
53 $forum_meta,
54 array(
55 'forum_type' => 'forum',
56 'status' => 'open',
57 'reply_count' => 0,
58 'topic_count' => 0,
59 'topic_count_hidden' => 0,
60 'total_reply_count' => 0,
61 'total_topic_count' => 0,
62 'last_topic_id' => 0,
63 'last_reply_id' => 0,
64 'last_active_id' => 0,
65 'last_active_time' => 0,
66 'forum_subforum_count' => 0,
67 ),
68 'insert_forum_meta'
69 );
70
71 // Insert forum meta
72 foreach ( $forum_meta as $meta_key => $meta_value ) {
73
74 // Prefix if not prefixed
75 if ( '_bbp_' !== substr( $meta_key, 0, 5 ) ) {
76 $meta_key = '_bbp_' . $meta_key;
77 }
78
79 // Update the meta
80 update_post_meta( $forum_id, $meta_key, $meta_value );
81 }
82
83 // Update the forum and hierarchy
84 bbp_update_forum(
85 array(
86 'forum_id' => $forum_id,
87 'post_parent' => $forum_data['post_parent']
88 )
89 );
90
91 // Maybe make private
92 if ( bbp_is_forum_private( $forum_id, false ) ) {
93 bbp_privatize_forum( $forum_id );
94
95 // Maybe make hidden
96 } elseif ( bbp_is_forum_hidden( $forum_id, false ) ) {
97 bbp_hide_forum( $forum_id );
98
99 // Publicize
100 } else {
101 bbp_publicize_forum( $forum_id );
102 }
103
104 /**
105 * Fires after forum has been inserted via `bbp_insert_forum`.
106 *
107 * @since 2.6.0 bbPress (r6036)
108 *
109 * @param int $forum_id The forum id.
110 */
111 do_action( 'bbp_insert_forum', (int) $forum_id );
112
113 // Bump the last changed cache
114 wp_cache_set( 'last_changed', microtime(), 'bbpress_posts' );
115
116 // Return forum_id
117 return $forum_id;
118 }
119
120 /** Post Form Handlers ********************************************************/
121
122 /**
123 * Handles the front end forum submission
124 *
125 * @param string $action The requested action to compare this function to
126 */
127 function bbp_new_forum_handler( $action = '' ) {
128
129 // Bail if action is not bbp-new-forum
130 if ( 'bbp-new-forum' !== $action ) {
131 return;
132 }
133
134 // Nonce check
135 if ( ! bbp_verify_nonce_request( 'bbp-new-forum' ) ) {
136 bbp_add_error( 'bbp_new_forum_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
137 return;
138 }
139
140 // Define local variable(s)
141 $view_all = false;
142 $forum_parent_id = $forum_author = 0;
143 $forum_title = $forum_content = '';
144 $anonymous_data = array();
145
146 /** Forum Author **********************************************************/
147
148 // User cannot create forums
149 if ( ! current_user_can( 'publish_forums' ) ) {
150 bbp_add_error( 'bbp_forum_permission', __( '<strong>Error</strong>: You do not have permission to create new forums.', 'bbpress' ) );
151 return;
152 }
153
154 // Forum author is current user
155 $forum_author = bbp_get_current_user_id();
156
157 // Remove kses filters from title and content for capable users and if the nonce is verified
158 if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_forum'] ) && wp_create_nonce( 'bbp-unfiltered-html-forum_new' ) === $_POST['_bbp_unfiltered_html_forum'] ) {
159 remove_filter( 'bbp_new_forum_pre_title', 'wp_filter_kses' );
160 remove_filter( 'bbp_new_forum_pre_content', 'bbp_encode_bad', 10 );
161 remove_filter( 'bbp_new_forum_pre_content', 'bbp_filter_kses', 30 );
162 }
163
164 /** Forum Title ***********************************************************/
165
166 if ( ! empty( $_POST['bbp_forum_title'] ) ) {
167 $forum_title = sanitize_text_field( $_POST['bbp_forum_title'] );
168 }
169
170 // Filter and sanitize
171 $forum_title = apply_filters( 'bbp_new_forum_pre_title', $forum_title );
172
173 // No forum title
174 if ( empty( $forum_title ) ) {
175 bbp_add_error( 'bbp_forum_title', __( '<strong>Error</strong>: Your forum needs a title.', 'bbpress' ) );
176 }
177
178 // Title too long
179 if ( bbp_is_title_too_long( $forum_title ) ) {
180 bbp_add_error( 'bbp_forum_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
181 }
182
183 /** Forum Content *********************************************************/
184
185 if ( ! empty( $_POST['bbp_forum_content'] ) ) {
186 $forum_content = $_POST['bbp_forum_content'];
187 }
188
189 // Filter and sanitize
190 $forum_content = apply_filters( 'bbp_new_forum_pre_content', $forum_content );
191
192 // No forum content
193 if ( empty( $forum_content ) ) {
194 bbp_add_error( 'bbp_forum_content', __( '<strong>Error</strong>: Your forum description cannot be empty.', 'bbpress' ) );
195 }
196
197 /** Forum Parent **********************************************************/
198
199 // Forum parent was passed (the norm)
200 if ( ! empty( $_POST['bbp_forum_parent_id'] ) ) {
201 $forum_parent_id = bbp_get_forum_id( $_POST['bbp_forum_parent_id'] );
202 }
203
204 // Filter and sanitize
205 $forum_parent_id = apply_filters( 'bbp_new_forum_pre_parent_id', $forum_parent_id );
206
207 // No forum parent was passed (should never happen)
208 if ( empty( $forum_parent_id ) ) {
209 bbp_add_error( 'bbp_new_forum_missing_parent', __( '<strong>Error</strong>: Your forum must have a parent.', 'bbpress' ) );
210
211 // Forum exists
212 } elseif ( ! empty( $forum_parent_id ) ) {
213
214 // Forum is a category
215 if ( bbp_is_forum_category( $forum_parent_id ) ) {
216 bbp_add_error( 'bbp_new_forum_forum_category', __( '<strong>Error</strong>: This forum is a category. No forums can be created in this forum.', 'bbpress' ) );
217 }
218
219 // Forum is closed and user cannot access
220 if ( bbp_is_forum_closed( $forum_parent_id ) && ! current_user_can( 'edit_forum', $forum_parent_id ) ) {
221 bbp_add_error( 'bbp_new_forum_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new forums.', 'bbpress' ) );
222 }
223
224 // Forum is private and user cannot access
225 if ( bbp_is_forum_private( $forum_parent_id ) && ! current_user_can( 'read_forum', $forum_parent_id ) ) {
226 bbp_add_error( 'bbp_new_forum_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new forums in it.', 'bbpress' ) );
227 }
228
229 // Forum is hidden and user cannot access
230 if ( bbp_is_forum_hidden( $forum_parent_id ) && ! current_user_can( 'read_forum', $forum_parent_id ) ) {
231 bbp_add_error( 'bbp_new_forum_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new forums in it.', 'bbpress' ) );
232 }
233 }
234
235 /** Forum Flooding ********************************************************/
236
237 if ( ! bbp_check_for_flood( $anonymous_data, $forum_author ) ) {
238 bbp_add_error( 'bbp_forum_flood', __( '<strong>Error</strong>: Slow down; you move too fast.', 'bbpress' ) );
239 }
240
241 /** Forum Duplicate *******************************************************/
242
243 $dupe_args = array(
244 'post_type' => bbp_get_forum_post_type(),
245 'post_author' => $forum_author,
246 'post_content' => $forum_content,
247 'post_parent' => $forum_parent_id,
248 'anonymous_data' => $anonymous_data
249 );
250
251 if ( ! bbp_check_for_duplicate( $dupe_args ) ) {
252 bbp_add_error( 'bbp_forum_duplicate', __( '<strong>Error</strong>: This forum already exists.', 'bbpress' ) );
253 }
254
255 /** Forum Bad Words *******************************************************/
256
257 if ( ! bbp_check_for_moderation( $anonymous_data, $forum_author, $forum_title, $forum_content, true ) ) {
258 bbp_add_error( 'bbp_forum_moderation', __( '<strong>Error</strong>: Your forum cannot be created at this time.', 'bbpress' ) );
259 }
260
261 /** Forum Moderation ******************************************************/
262
263 // Default to published
264 $forum_status = bbp_get_public_status_id();
265
266 // Maybe force into pending
267 if ( ! bbp_check_for_moderation( $anonymous_data, $forum_author, $forum_title, $forum_content ) ) {
268 $forum_status = bbp_get_pending_status_id();
269 }
270
271 /** Additional Actions (Before Save) **************************************/
272
273 do_action( 'bbp_new_forum_pre_extras', $forum_parent_id );
274
275 // Bail if errors
276 if ( bbp_has_errors() ) {
277 return;
278 }
279
280 /** No Errors *************************************************************/
281
282 // Add the content of the form to $forum_data as an array
283 // Just in time manipulation of forum data before being created
284 $forum_data = apply_filters(
285 'bbp_new_forum_pre_insert',
286 array(
287 'post_author' => $forum_author,
288 'post_title' => $forum_title,
289 'post_content' => $forum_content,
290 'post_parent' => $forum_parent_id,
291 'post_status' => $forum_status,
292 'post_type' => bbp_get_forum_post_type(),
293 'comment_status' => 'closed'
294 )
295 );
296
297 // Insert forum
298 $forum_id = wp_insert_post( $forum_data, true );
299
300 /** No Errors *************************************************************/
301
302 if ( ! empty( $forum_id ) && ! is_wp_error( $forum_id ) ) {
303
304 /** Trash Check *******************************************************/
305
306 // If the forum is trash, or the forum_status is switched to
307 // trash, trash it properly
308 if ( ( get_post_field( 'post_status', $forum_id ) === bbp_get_trash_status_id() ) || ( bbp_get_trash_status_id() === $forum_data['post_status'] ) ) {
309
310 // Trash the reply
311 wp_trash_post( $forum_id );
312
313 // Force view=all
314 $view_all = true;
315 }
316
317 /** Spam Check ********************************************************/
318
319 // If reply or forum are spam, officially spam this reply
320 if ( bbp_get_spam_status_id() === $forum_data['post_status'] ) {
321 add_post_meta( $forum_id, '_bbp_spam_meta_status', bbp_get_public_status_id() );
322
323 // Force view=all
324 $view_all = true;
325 }
326
327 /** Update counts, etc... *********************************************/
328
329 do_action(
330 'bbp_new_forum',
331 array(
332 'forum_id' => $forum_id,
333 'post_parent' => $forum_data['post_parent'],
334 'forum_author' => $forum_data['post_author'],
335 'last_topic_id' => 0,
336 'last_reply_id' => 0,
337 'last_active_id' => 0,
338 'last_active_time' => 0,
339 'last_active_status' => bbp_get_public_status_id()
340 )
341 );
342
343 /** Additional Actions (After Save) ***********************************/
344
345 do_action( 'bbp_new_forum_post_extras', $forum_id );
346
347 /** Redirect **********************************************************/
348
349 // Redirect to
350 $redirect_to = bbp_get_redirect_to();
351
352 // Get the forum URL
353 $redirect_url = bbp_get_forum_permalink( $forum_id, $redirect_to );
354
355 // Add view all?
356 if ( bbp_get_view_all() || ! empty( $view_all ) ) {
357
358 // User can moderate, so redirect to forum with view all set
359 if ( current_user_can( 'moderate', $forum_id ) ) {
360 $redirect_url = bbp_add_view_all( $redirect_url );
361
362 // User cannot moderate, so redirect to forum
363 } else {
364 $redirect_url = bbp_get_forum_permalink( $forum_id );
365 }
366 }
367
368 // Allow to be filtered
369 $redirect_url = apply_filters( 'bbp_new_forum_redirect_to', $redirect_url, $redirect_to );
370
371 /** Successful Save ***************************************************/
372
373 // Redirect back to new forum
374 bbp_redirect( $redirect_url );
375
376 /** Errors ****************************************************************/
377
378 // WP_Error
379
380 } elseif ( is_wp_error( $forum_id ) ) {
381 bbp_add_error(
382 'bbp_forum_error',
383 sprintf( /* translators: %s: Error message(s) */ __( '<strong>Error</strong>: The following problem(s) occurred: %s', 'bbpress' ),
384 $forum_id->get_error_message()
385 )
386 );
387
388 // Generic error
389 } else {
390 bbp_add_error( 'bbp_forum_error', __( '<strong>Error</strong>: The forum was not created.', 'bbpress' ) );
391 }
392 }
393
394 /**
395 * Handles the front end edit forum submission
396 *
397 * @param string $action The requested action to compare this function to
398 */
399 function bbp_edit_forum_handler( $action = '' ) {
400
401 // Bail if action is not bbp-edit-forum
402 if ( 'bbp-edit-forum' !== $action ) {
403 return;
404 }
405
406 // Define local variable(s)
407 $anonymous_data = array();
408 $forum = $forum_id = $forum_author = $forum_parent_id = 0;
409 $forum_title = $forum_content = $forum_edit_reason = '';
410
411 /** Forum *****************************************************************/
412
413 // Forum id was not passed
414 if ( empty( $_POST['bbp_forum_id'] ) ) {
415 bbp_add_error( 'bbp_edit_forum_id', __( '<strong>Error</strong>: Forum ID not found.', 'bbpress' ) );
416 return;
417
418 // Forum id was passed
419 } elseif ( is_numeric( $_POST['bbp_forum_id'] ) ) {
420 $forum_id = (int) $_POST['bbp_forum_id'];
421 $forum = bbp_get_forum( $forum_id );
422 }
423
424 // Nonce check
425 if ( ! bbp_verify_nonce_request( 'bbp-edit-forum_' . $forum_id ) ) {
426 bbp_add_error( 'bbp_edit_forum_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
427 return;
428
429 // Forum does not exist
430 } elseif ( empty( $forum ) ) {
431 bbp_add_error( 'bbp_edit_forum_not_found', __( '<strong>Error</strong>: The forum you want to edit was not found.', 'bbpress' ) );
432 return;
433
434 // User cannot edit this forum
435 } elseif ( ! current_user_can( 'edit_forum', $forum_id ) ) {
436 bbp_add_error( 'bbp_edit_forum_permission', __( '<strong>Error</strong>: You do not have permission to edit that forum.', 'bbpress' ) );
437 return;
438 }
439
440 // Remove kses filters from title and content for capable users and if the nonce is verified
441 if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_forum'] ) && ( wp_create_nonce( 'bbp-unfiltered-html-forum_' . $forum_id ) === $_POST['_bbp_unfiltered_html_forum'] ) ) {
442 remove_filter( 'bbp_edit_forum_pre_title', 'wp_filter_kses' );
443 remove_filter( 'bbp_edit_forum_pre_content', 'bbp_encode_bad', 10 );
444 remove_filter( 'bbp_edit_forum_pre_content', 'bbp_filter_kses', 30 );
445 }
446
447 // Get forum author
448 $forum_author = bbp_get_forum_author_id( $forum_id );
449
450 /** Forum Parent ***********************************************************/
451
452 // Current forum this forum is in
453 $current_parent_forum_id = bbp_get_forum_parent_id( $forum_id );
454
455 // Only users who can assign forum moderators can change forum structure
456 if ( current_user_can( 'assign_moderators' ) ) {
457 $forum_parent_id = ! empty( $_POST['bbp_forum_parent_id'] )
458 ? bbp_get_forum_id( $_POST['bbp_forum_parent_id'] )
459 : 0;
460 } else {
461 $forum_parent_id = $current_parent_forum_id;
462 }
463
464 // Forum exists
465 if ( ! empty( $forum_parent_id ) && ( $forum_parent_id !== $current_parent_forum_id ) ) {
466
467 // Forum is closed and user cannot access
468 if ( bbp_is_forum_closed( $forum_parent_id ) && ! current_user_can( 'edit_forum', $forum_parent_id ) ) {
469 bbp_add_error( 'bbp_edit_forum_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new forums.', 'bbpress' ) );
470 }
471
472 // Forum is private and user cannot access
473 if ( bbp_is_forum_private( $forum_parent_id ) && ! current_user_can( 'read_forum', $forum_parent_id ) ) {
474 bbp_add_error( 'bbp_edit_forum_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new forums in it.', 'bbpress' ) );
475 }
476
477 // Forum is hidden and user cannot access
478 if ( bbp_is_forum_hidden( $forum_parent_id ) && ! current_user_can( 'read_forum', $forum_parent_id ) ) {
479 bbp_add_error( 'bbp_edit_forum_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new forums in it.', 'bbpress' ) );
480 }
481 }
482
483 /** Forum Title ***********************************************************/
484
485 if ( ! empty( $_POST['bbp_forum_title'] ) ) {
486 $forum_title = sanitize_text_field( $_POST['bbp_forum_title'] );
487 }
488
489 // Filter and sanitize
490 $forum_title = apply_filters( 'bbp_edit_forum_pre_title', $forum_title, $forum_id );
491
492 // No forum title
493 if ( empty( $forum_title ) ) {
494 bbp_add_error( 'bbp_edit_forum_title', __( '<strong>Error</strong>: Your forum needs a title.', 'bbpress' ) );
495 }
496
497 // Title too long
498 if ( bbp_is_title_too_long( $forum_title ) ) {
499 bbp_add_error( 'bbp_forum_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
500 }
501
502 /** Forum Content *********************************************************/
503
504 if ( ! empty( $_POST['bbp_forum_content'] ) ) {
505 $forum_content = $_POST['bbp_forum_content'];
506 }
507
508 // Filter and sanitize
509 $forum_content = apply_filters( 'bbp_edit_forum_pre_content', $forum_content, $forum_id );
510
511 // No forum content
512 if ( empty( $forum_content ) ) {
513 bbp_add_error( 'bbp_edit_forum_content', __( '<strong>Error</strong>: Your forum description cannot be empty.', 'bbpress' ) );
514 }
515
516 /** Forum Bad Words *******************************************************/
517
518 if ( ! bbp_check_for_moderation( $anonymous_data, bbp_get_forum_author_id( $forum_id ), $forum_title, $forum_content, true ) ) {
519 bbp_add_error( 'bbp_forum_moderation', __( '<strong>Error</strong>: Your forum cannot be edited at this time.', 'bbpress' ) );
520 }
521
522 /** Forum Moderation ******************************************************/
523
524 // Use existing post_status
525 $forum_status = $forum->post_status;
526
527 // Maybe force into pending
528 if ( ! bbp_check_for_moderation( $anonymous_data, bbp_get_forum_author_id( $forum_id ), $forum_title, $forum_content ) ) {
529 $forum_status = bbp_get_pending_status_id();
530 }
531
532 /** Additional Actions (Before Save) **************************************/
533
534 do_action( 'bbp_edit_forum_pre_extras', $forum_id );
535
536 // Bail if errors
537 if ( bbp_has_errors() ) {
538 return;
539 }
540
541 /** No Errors *************************************************************/
542
543 // Add the content of the form to $forum_data as an array
544 // Just in time manipulation of forum data before being edited
545 $forum_data = apply_filters(
546 'bbp_edit_forum_pre_insert',
547 array(
548 'ID' => $forum_id,
549 'post_title' => $forum_title,
550 'post_content' => $forum_content,
551 'post_status' => $forum_status,
552 'post_parent' => $forum_parent_id,
553 'post_author' => $forum_author
554 )
555 );
556
557 // Insert forum
558 $forum_id = wp_update_post( $forum_data );
559
560 /** No Errors *************************************************************/
561
562 if ( ! empty( $forum_id ) && ! is_wp_error( $forum_id ) ) {
563
564 // Update counts, etc...
565 do_action(
566 'bbp_edit_forum',
567 array(
568 'forum_id' => $forum_id,
569 'post_parent' => $forum_data['post_parent'],
570 'forum_author' => $forum_data['post_author'],
571 'last_topic_id' => 0,
572 'last_reply_id' => 0,
573 'last_active_id' => 0,
574 'last_active_time' => 0,
575 'last_active_status' => bbp_get_public_status_id()
576 )
577 );
578
579 /** Revisions *********************************************************/
580
581 // Update locks
582 update_post_meta( $forum_id, '_edit_last', bbp_get_current_user_id() );
583 delete_post_meta( $forum_id, '_edit_lock' );
584
585 /**
586 * @todo omitted for now
587 // Revision Reason
588 if ( ! empty( $_POST['bbp_forum_edit_reason'] ) )
589 $forum_edit_reason = sanitize_text_field( $_POST['bbp_forum_edit_reason'] );
590
591 // Update revision log
592 if ( ! empty( $_POST['bbp_log_forum_edit'] ) && ( "1" === $_POST['bbp_log_forum_edit'] ) && ( $revision_id = wp_save_post_revision( $forum_id ) ) ) {
593 bbp_update_forum_revision_log( array(
594 'forum_id' => $forum_id,
595 'revision_id' => $revision_id,
596 'author_id' => bbp_get_current_user_id(),
597 'reason' => $forum_edit_reason
598 ) );
599 }
600
601 // If the new forum parent id is not equal to the old forum parent
602 // id, run the bbp_move_forum action and pass the forum's parent id
603 // as the first argument and new forum parent id as the second.
604 // @todo implement
605 if ( $forum_id !== $forum->post_parent ) {
606 bbp_move_forum_handler( $forum_parent_id, $forum->post_parent, $forum_id );
607 }
608
609 */
610
611 /** Additional Actions (After Save) ***********************************/
612
613 do_action( 'bbp_edit_forum_post_extras', $forum_id );
614
615 /** Redirect **********************************************************/
616
617 // Redirect to
618 $redirect_to = bbp_get_redirect_to();
619
620 // View all?
621 $view_all = bbp_get_view_all();
622
623 // Get the forum URL
624 $forum_url = bbp_get_forum_permalink( $forum_id, $redirect_to );
625
626 // Add view all?
627 if ( ! empty( $view_all ) ) {
628 $forum_url = bbp_add_view_all( $forum_url );
629 }
630
631 // Allow to be filtered
632 $forum_url = apply_filters( 'bbp_edit_forum_redirect_to', $forum_url, $view_all, $redirect_to );
633
634 /** Successful Edit ***************************************************/
635
636 // Redirect back to new forum
637 bbp_redirect( $forum_url );
638
639 /** Errors ****************************************************************/
640
641 } else {
642 $append_error = ( is_wp_error( $forum_id ) && $forum_id->get_error_message() ) ? $forum_id->get_error_message() . ' ' : '';
643 bbp_add_error(
644 'bbp_forum_error',
645 sprintf(
646 /* translators: %s: Error message */
647 __( '<strong>Error</strong>: The following problem(s) have been found with your forum: %sPlease try again.', 'bbpress' ),
648 $append_error
649 )
650 );
651 }
652 }
653
654 /**
655 * Filter forum data submitted through the WordPress administration area.
656 *
657 * @since 2.6.17 bbPress (r7490)
658 *
659 * @param array $data Sanitized post data.
660 * @param array $postarr Raw post data.
661 * @return array Filtered post data.
662 */
663 function bbp_filter_admin_forum_post_data( $data = array(), $postarr = array() ) {
664
665 // Bail unless an existing forum is being updated in wp-admin
666 if ( ! is_admin() || empty( $postarr['ID'] ) || empty( $data['post_type'] ) || ( bbp_get_forum_post_type() !== $data['post_type'] ) ) {
667 return $data;
668 }
669
670 $forum_id = bbp_get_forum_id( $postarr['ID'] );
671 $forum = bbp_get_forum( $forum_id );
672
673 if ( empty( $forum ) ) {
674 return $data;
675 }
676
677 // Preserve structure unless the user can assign forum moderators
678 if ( ! current_user_can( 'assign_moderators' ) ) {
679 $data['post_parent'] = $forum->post_parent;
680 $data['menu_order'] = $forum->menu_order;
681 }
682
683 // Preserve visibility unless the user can manage forum attributes
684 if ( ! current_user_can( 'manage_forum_attributes', $forum_id ) ) {
685 $data['post_status'] = $forum->post_status;
686 }
687
688 return $data;
689 }
690
691 /**
692 * Handle the saving of core forum metadata (Status, Visibility, and Type)
693 *
694 * @since 2.1.0 bbPress (r3679)
695 *
696 * @param int $forum_id
697 * @return If forum ID is empty
698 */
699 function bbp_save_forum_extras( $forum_id = 0 ) {
700
701 // Validate the forum ID
702 $forum_id = bbp_get_forum_id( $forum_id );
703
704 // Bail if forum ID is empty
705 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
706 return;
707 }
708
709 /** Forum Status **********************************************************/
710
711 if ( current_user_can( 'manage_forum_attributes', $forum_id ) && ! empty( $_POST['bbp_forum_status'] ) && in_array( $_POST['bbp_forum_status'], array( 'open', 'closed' ), true ) ) {
712 if ( 'closed' === $_POST['bbp_forum_status'] && ! bbp_is_forum_closed( $forum_id, false ) ) {
713 bbp_close_forum( $forum_id );
714 } elseif ( 'open' === $_POST['bbp_forum_status'] && bbp_is_forum_open( $forum_id, false ) ) {
715 bbp_open_forum( $forum_id );
716 } elseif ( 'open' === $_POST['bbp_forum_status'] && bbp_is_forum_closed( $forum_id, false ) ) {
717 bbp_open_forum( $forum_id );
718 }
719 }
720
721 /** Forum Type ************************************************************/
722
723 if ( current_user_can( 'manage_forum_attributes', $forum_id ) && ! empty( $_POST['bbp_forum_type'] ) && in_array( $_POST['bbp_forum_type'], array( 'forum', 'category' ), true ) ) {
724 if ( 'category' === $_POST['bbp_forum_type'] && ! bbp_is_forum_category( $forum_id ) ) {
725 bbp_categorize_forum( $forum_id );
726 } elseif ( 'forum' === $_POST['bbp_forum_type'] && ! bbp_is_forum_category( $forum_id ) ) {
727 bbp_normalize_forum( $forum_id );
728 } elseif ( 'forum' === $_POST['bbp_forum_type'] && bbp_is_forum_category( $forum_id ) ) {
729 bbp_normalize_forum( $forum_id );
730 }
731 }
732
733 /** Forum Visibility ******************************************************/
734
735 if ( current_user_can( 'manage_forum_attributes', $forum_id ) && ! empty( $_POST['bbp_forum_visibility'] ) && in_array( $_POST['bbp_forum_visibility'], array_keys( bbp_get_forum_visibilities() ), true ) ) {
736
737 // Get forums current visibility
738 $old_visibility = bbp_get_forum_visibility( $forum_id );
739
740 // Sanitize the new visibility
741 $new_visibility = sanitize_key( $_POST['bbp_forum_visibility'] );
742
743 // What is the new forum visibility setting?
744 switch ( $new_visibility ) {
745
746 // Hidden
747 case bbp_get_hidden_status_id() :
748 bbp_hide_forum( $forum_id, $old_visibility );
749 break;
750
751 // Private
752 case bbp_get_private_status_id() :
753 bbp_privatize_forum( $forum_id, $old_visibility );
754 break;
755
756 // Publish (default)
757 case bbp_get_public_status_id() :
758 default :
759 bbp_publicize_forum( $forum_id, $old_visibility );
760 break;
761 }
762
763 /**
764 * Allow custom forum visibility save actions
765 *
766 * @since 2.6.0 bbPress (r5856)
767 *
768 * @param int $forum_id The forum ID
769 * @param string $old_visibility The current forum visibility
770 * @param string $new_visibility The new forum visibility
771 */
772 do_action( 'bbp_update_forum_visibility', $forum_id, $old_visibility, $new_visibility );
773 }
774
775 /** Forum Moderators ******************************************************/
776
777 // Either replace terms
778 if ( bbp_allow_forum_mods() && current_user_can( 'assign_moderators' ) ) {
779 if ( ! empty( $_POST['bbp_moderators'] ) ) {
780
781 // Escape tag input
782 $users = sanitize_text_field( $_POST['bbp_moderators'] );
783 $user_ids = bbp_get_user_ids_from_nicenames( $users );
784
785 // Update forum moderators
786 if ( ! empty( $user_ids ) ) {
787
788 // Remove all moderators
789 bbp_remove_moderator( $forum_id, null );
790
791 // Add moderators
792 foreach ( $user_ids as $user_id ) {
793 bbp_add_moderator( $forum_id, $user_id );
794 }
795 }
796
797 // ...or remove them.
798 } elseif ( isset( $_POST['bbp_moderators'] ) ) {
799 bbp_remove_moderator( $forum_id, null );
800 }
801 }
802 }
803
804 /** Forum Open/Close **********************************************************/
805
806 /**
807 * Closes a forum
808 *
809 * @since 2.0.0 bbPress (r2746)
810 *
811 * @param int $forum_id forum id
812 * @return mixed False or {@link WP_Error} on failure, forum id on success
813 */
814 function bbp_close_forum( $forum_id = 0 ) {
815
816 $forum_id = bbp_get_forum_id( $forum_id );
817
818 do_action( 'bbp_close_forum', $forum_id );
819
820 update_post_meta( $forum_id, '_bbp_status', 'closed' );
821
822 do_action( 'bbp_closed_forum', $forum_id );
823
824 return $forum_id;
825 }
826
827 /**
828 * Opens a forum
829 *
830 * @since 2.0.0 bbPress (r2746)
831 *
832 * @param int $forum_id forum id
833 * @return mixed False or {@link WP_Error} on failure, forum id on success
834 */
835 function bbp_open_forum( $forum_id = 0 ) {
836
837 $forum_id = bbp_get_forum_id( $forum_id );
838
839 do_action( 'bbp_open_forum', $forum_id );
840
841 update_post_meta( $forum_id, '_bbp_status', 'open' );
842
843 do_action( 'bbp_opened_forum', $forum_id );
844
845 return $forum_id;
846 }
847
848 /** Forum Type ****************************************************************/
849
850 /**
851 * Make the forum a category
852 *
853 * @since 2.0.0 bbPress (r2746)
854 *
855 * @param int $forum_id Optional. Forum id
856 * @return bool False on failure, true on success
857 */
858 function bbp_categorize_forum( $forum_id = 0 ) {
859
860 $forum_id = bbp_get_forum_id( $forum_id );
861
862 do_action( 'bbp_categorize_forum', $forum_id );
863
864 update_post_meta( $forum_id, '_bbp_forum_type', 'category' );
865
866 do_action( 'bbp_categorized_forum', $forum_id );
867
868 return $forum_id;
869 }
870
871 /**
872 * Remove the category status from a forum
873 *
874 * @since 2.0.0 bbPress (r2746)
875 *
876 * @param int $forum_id Optional. Forum id
877 * @return bool False on failure, true on success
878 */
879 function bbp_normalize_forum( $forum_id = 0 ) {
880
881 $forum_id = bbp_get_forum_id( $forum_id );
882
883 do_action( 'bbp_normalize_forum', $forum_id );
884
885 update_post_meta( $forum_id, '_bbp_forum_type', 'forum' );
886
887 do_action( 'bbp_normalized_forum', $forum_id );
888
889 return $forum_id;
890 }
891
892 /** Forum Visibility **********************************************************/
893
894 /**
895 * Update the cached forum visibility options.
896 *
897 * This function only synchronizes the private and hidden forum ID options. It
898 * does not update the forum post status or fire any status transition actions.
899 *
900 * @since 2.6.19 bbPress
901 *
902 * @param int $forum_id Optional. Forum ID.
903 * @param string $visibility Optional. Forum visibility.
904 * @return int|false Forum ID on success, false on failure.
905 */
906 function bbp_update_forum_visibility_options( $forum_id = 0, $visibility = '' ) {
907
908 $forum_id = bbp_get_forum_id( $forum_id );
909 $allowed = array(
910 bbp_get_public_status_id(),
911 bbp_get_private_status_id(),
912 bbp_get_hidden_status_id()
913 );
914
915 // Bail if the forum or visibility is invalid
916 if ( empty( $forum_id ) || ! in_array( $visibility, $allowed, true ) ) {
917 return false;
918 }
919
920 // Remove the forum from both visibility options
921 $private = bbp_get_unique_array_values( wp_parse_id_list( get_option( '_bbp_private_forums', array() ) ) );
922 $hidden = bbp_get_unique_array_values( wp_parse_id_list( get_option( '_bbp_hidden_forums', array() ) ) );
923 $old_private = $private;
924 $old_hidden = $hidden;
925
926 $private = array_diff( $private, array( $forum_id ) );
927 $hidden = array_diff( $hidden, array( $forum_id ) );
928
929 // Add the forum to its current visibility option
930 if ( bbp_get_private_status_id() === $visibility ) {
931 $private[] = $forum_id;
932 } elseif ( bbp_get_hidden_status_id() === $visibility ) {
933 $hidden[] = $forum_id;
934 }
935
936 // Update only the visibility options that changed
937 $private = bbp_get_unique_array_values( $private );
938 $hidden = bbp_get_unique_array_values( $hidden );
939
940 if ( $private !== $old_private ) {
941 update_option( '_bbp_private_forums', $private );
942 }
943
944 if ( $hidden !== $old_hidden ) {
945 update_option( '_bbp_hidden_forums', $hidden );
946 }
947
948 return $forum_id;
949 }
950
951 /**
952 * Synchronize forum visibility options after a post status transition.
953 *
954 * @since 2.6.19 bbPress
955 *
956 * @param string $new_status New post status.
957 * @param string $old_status Old post status.
958 * @param WP_Post $post Post object.
959 */
960 function bbp_update_forum_visibility_on_transition_post_status( $new_status = '', $old_status = '', $post = false ) {
961
962 // Bail if the status did not change or this is not a forum
963 if ( ( $new_status === $old_status ) || ! ( $post instanceof WP_Post ) || ( bbp_get_forum_post_type() !== $post->post_type ) ) {
964 return;
965 }
966
967 bbp_update_forum_visibility_options( $post->ID, $new_status );
968 }
969
970 /**
971 * Mark the forum as public
972 *
973 * @since 2.0.0 bbPress (r2746)
974 *
975 * @param int $forum_id Optional. Forum id
976 * @return bool False on failure, true on success
977 */
978 function bbp_publicize_forum( $forum_id = 0, $current_visibility = '' ) {
979
980 $forum_id = bbp_get_forum_id( $forum_id );
981
982 do_action( 'bbp_publicize_forum', $forum_id );
983
984 // Update forum visibility options
985 bbp_update_forum_visibility_options( $forum_id, bbp_get_public_status_id() );
986
987 // Only run queries if visibility is changing
988 if ( bbp_get_public_status_id() !== $current_visibility ) {
989 $bbp_db = bbp_db();
990 $bbp_db->update( $bbp_db->posts, array( 'post_status' => bbp_get_public_status_id() ), array( 'ID' => $forum_id ) );
991 wp_transition_post_status( bbp_get_public_status_id(), $current_visibility, get_post( $forum_id ) );
992 clean_post_cache( $forum_id );
993 }
994
995 do_action( 'bbp_publicized_forum', $forum_id );
996
997 return $forum_id;
998 }
999
1000 /**
1001 * Mark the forum as private
1002 *
1003 * @since 2.0.0 bbPress (r2746)
1004 *
1005 * @param int $forum_id Optional. Forum id
1006 * @return bool False on failure, true on success
1007 */
1008 function bbp_privatize_forum( $forum_id = 0, $current_visibility = '' ) {
1009
1010 $forum_id = bbp_get_forum_id( $forum_id );
1011
1012 do_action( 'bbp_privatize_forum', $forum_id );
1013
1014 // Update forum visibility options
1015 bbp_update_forum_visibility_options( $forum_id, bbp_get_private_status_id() );
1016
1017 // Only run queries if visibility is changing
1018 if ( bbp_get_private_status_id() !== $current_visibility ) {
1019
1020 // Update forums visibility setting
1021 $bbp_db = bbp_db();
1022 $bbp_db->update( $bbp_db->posts, array( 'post_status' => bbp_get_private_status_id() ), array( 'ID' => $forum_id ) );
1023 wp_transition_post_status( bbp_get_private_status_id(), $current_visibility, get_post( $forum_id ) );
1024 clean_post_cache( $forum_id );
1025 }
1026
1027 do_action( 'bbp_privatized_forum', $forum_id );
1028
1029 return $forum_id;
1030 }
1031
1032 /**
1033 * Mark the forum as hidden
1034 *
1035 * @since 2.0.0 bbPress (r2996)
1036 *
1037 * @param int $forum_id Optional. Forum id
1038 * @return bool False on failure, true on success
1039 */
1040 function bbp_hide_forum( $forum_id = 0, $current_visibility = '' ) {
1041
1042 $forum_id = bbp_get_forum_id( $forum_id );
1043
1044 do_action( 'bbp_hide_forum', $forum_id );
1045
1046 // Update forum visibility options
1047 bbp_update_forum_visibility_options( $forum_id, bbp_get_hidden_status_id() );
1048
1049 // Only run queries if visibility is changing
1050 if ( bbp_get_hidden_status_id() !== $current_visibility ) {
1051
1052 // Update forums visibility setting
1053 $bbp_db = bbp_db();
1054 $bbp_db->update( $bbp_db->posts, array( 'post_status' => bbp_get_hidden_status_id() ), array( 'ID' => $forum_id ) );
1055 wp_transition_post_status( bbp_get_hidden_status_id(), $current_visibility, get_post( $forum_id ) );
1056 clean_post_cache( $forum_id );
1057 }
1058
1059 do_action( 'bbp_hid_forum', $forum_id );
1060
1061 return $forum_id;
1062 }
1063
1064 /**
1065 * Recaches the private and hidden forums
1066 *
1067 * @since 2.4.0 bbPress (r5017)
1068 *
1069 * @return array An array of the status code and the message
1070 */
1071 function bbp_repair_forum_visibility() {
1072
1073 // First, delete everything.
1074 delete_option( '_bbp_private_forums' );
1075 delete_option( '_bbp_hidden_forums' );
1076
1077 /**
1078 * Don't search for both private/hidden statuses. Since 'pre_get_posts' is an
1079 * action, it's not removed by suppress_filters. We need to make sure that
1080 * we're only searching for the supplied post_status.
1081 *
1082 * @see https://bbpress.trac.wordpress.org/ticket/2512
1083 */
1084 remove_action( 'pre_get_posts', 'bbp_pre_get_posts_normalize_forum_visibility', 4 );
1085
1086 // Query for private forums
1087 $private_forums = new WP_Query(
1088 array(
1089 'fields' => 'ids',
1090 'post_type' => bbp_get_forum_post_type(),
1091 'post_status' => bbp_get_private_status_id(),
1092 'posts_per_page' => -1,
1093
1094 // Performance
1095 'nopaging' => true,
1096 'suppress_filters' => true,
1097 'update_post_term_cache' => false,
1098 'update_post_meta_cache' => false,
1099 'ignore_sticky_posts' => true,
1100 'no_found_rows' => true
1101 )
1102 );
1103
1104 // Query for hidden forums
1105 $hidden_forums = new WP_Query(
1106 array(
1107 'fields' => 'ids',
1108 'post_type' => bbp_get_forum_post_type(),
1109 'post_status' => bbp_get_hidden_status_id(),
1110 'posts_per_page' => -1,
1111
1112 // Performance
1113 'nopaging' => true,
1114 'suppress_filters' => true,
1115 'update_post_term_cache' => false,
1116 'update_post_meta_cache' => false,
1117 'ignore_sticky_posts' => true,
1118 'no_found_rows' => true
1119 )
1120 );
1121
1122 // Enable forum visibilty normalization
1123 add_action( 'pre_get_posts', 'bbp_pre_get_posts_normalize_forum_visibility', 4 );
1124
1125 // Reset the $post global
1126 wp_reset_postdata();
1127
1128 // Private
1129 if ( ! is_wp_error( $private_forums ) ) {
1130 update_option( '_bbp_private_forums', $private_forums->posts );
1131 }
1132
1133 // Hidden forums
1134 if ( ! is_wp_error( $hidden_forums ) ) {
1135 update_option( '_bbp_hidden_forums', $hidden_forums->posts );
1136 }
1137
1138 // Complete results
1139 return true;
1140 }
1141
1142 /** Subscriptions *************************************************************/
1143
1144 /**
1145 * Remove a deleted forum from all user subscriptions
1146 *
1147 * @since 2.5.0 bbPress (r5156)
1148 *
1149 * @param int $forum_id Get the forum ID to remove
1150 */
1151 function bbp_remove_forum_from_all_subscriptions( $forum_id = 0 ) {
1152
1153 // Subscriptions are not active
1154 if ( ! bbp_is_subscriptions_active() ) {
1155 return;
1156 }
1157
1158 // Bail if no forum
1159 $forum_id = bbp_get_forum_id( $forum_id );
1160 if ( empty( $forum_id ) ) {
1161 return;
1162 }
1163
1164 // Remove forum from all subscriptions
1165 return bbp_remove_object_from_all_users( $forum_id, '_bbp_subscription', 'post' );
1166 }
1167
1168 /** Count Bumpers *************************************************************/
1169
1170 /**
1171 * Bump the total topic count of a forum
1172 *
1173 * @since 2.1.0 bbPress (r3826)
1174 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
1175 *
1176 * @param int $forum_id Optional. Forum id.
1177 * @param int $difference Optional. Default 1
1178 * @param bool $update_ancestors Optional. Default true
1179 *
1180 * @return int Forum topic count
1181 */
1182 function bbp_bump_forum_topic_count( $forum_id = 0, $difference = 1, $update_ancestors = true ) {
1183
1184 // Bail if no bump
1185 if ( empty( $difference ) ) {
1186 return false;
1187 }
1188
1189 // Get some counts
1190 $forum_id = bbp_get_forum_id( $forum_id );
1191 $topic_count = bbp_get_forum_topic_count( $forum_id, false, true );
1192 $total_topic_count = bbp_get_forum_topic_count( $forum_id, true, true );
1193 $difference = (int) $difference;
1194
1195 // Update this forum id
1196 bbp_bump_count_meta( 'post', $forum_id, '_bbp_topic_count', $difference, $topic_count );
1197 bbp_bump_count_meta( 'post', $forum_id, '_bbp_total_topic_count', $difference, $total_topic_count );
1198
1199 // Check for ancestors
1200 if ( true === $update_ancestors ) {
1201
1202 // Get post ancestors
1203 $forum = get_post( $forum_id );
1204 $ancestors = get_post_ancestors( $forum );
1205
1206 // If has ancestors, loop through them...
1207 if ( ! empty( $ancestors ) ) {
1208 foreach ( (array) $ancestors as $parent_forum_id ) {
1209
1210 // Only update topic count when an ancestor is not a category.
1211 if ( ! bbp_is_forum_category( $parent_forum_id ) ) {
1212
1213 $parent_topic_count = bbp_get_forum_topic_count( $parent_forum_id, false, true );
1214 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_topic_count', $difference, $parent_topic_count );
1215 }
1216
1217 // Update the total topic count.
1218 $parent_total_topic_count = bbp_get_forum_topic_count( $parent_forum_id, true, true );
1219 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_total_topic_count', $difference, $parent_total_topic_count );
1220 }
1221 }
1222 }
1223
1224 $forum_topic_count = bbp_number_not_negative( $total_topic_count + $difference );
1225
1226 // Filter & return
1227 return (int) apply_filters( 'bbp_bump_forum_topic_count', $forum_topic_count, $forum_id, $difference, $update_ancestors );
1228 }
1229
1230 /**
1231 * Increase the total topic count of a forum by one.
1232 *
1233 * @since 2.6.0 bbPress (r6036)
1234 *
1235 * @param int $forum_id The forum id.
1236 * @return void
1237 */
1238 function bbp_increase_forum_topic_count( $forum_id = 0 ) {
1239
1240 // Bail early if no id is passed.
1241 if ( empty( $forum_id ) ) {
1242 return;
1243 }
1244
1245 // If it's a topic, get the forum id.
1246 if ( bbp_is_topic( $forum_id ) ) {
1247 $topic_id = $forum_id;
1248 $forum_id = bbp_get_topic_forum_id( $topic_id );
1249
1250 // Update inverse based on item status
1251 if ( ! bbp_is_topic_public( $topic_id ) ) {
1252 bbp_increase_forum_topic_count_hidden( $forum_id );
1253 return;
1254 }
1255 }
1256
1257 // Bump up
1258 bbp_bump_forum_topic_count( $forum_id );
1259 }
1260
1261 /**
1262 * Decrease the total topic count of a forum by one.
1263 *
1264 * @since 2.6.0 bbPress (r6036)
1265 *
1266 * @param int $forum_id The forum id.
1267 *
1268 * @return void
1269 */
1270 function bbp_decrease_forum_topic_count( $forum_id = 0 ) {
1271
1272 // Bail early if no id is passed.
1273 if ( empty( $forum_id ) ) {
1274 return;
1275 }
1276
1277 // If it's a topic, get the forum id.
1278 if ( bbp_is_topic( $forum_id ) ) {
1279 $topic_id = $forum_id;
1280 $forum_id = bbp_get_topic_forum_id( $topic_id );
1281
1282 // Update inverse based on item status
1283 if ( ! bbp_is_topic_public( $topic_id ) ) {
1284 bbp_decrease_forum_topic_count_hidden( $forum_id );
1285 return;
1286 }
1287 }
1288
1289 // Bump down
1290 bbp_bump_forum_topic_count( $forum_id, -1 );
1291 }
1292
1293 /**
1294 * Bump the total topic count of a forum
1295 *
1296 * @since 2.1.0 bbPress (r3826)
1297 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
1298 *
1299 * @param int $forum_id Optional. Forum id.
1300 * @param int $difference Optional. Default 1
1301 * @param bool $update_ancestors Optional. Default true
1302 *
1303 * @return int Forum topic count
1304 */
1305 function bbp_bump_forum_topic_count_hidden( $forum_id = 0, $difference = 1, $update_ancestors = true ) {
1306
1307 // Bail if no bump
1308 if ( empty( $difference ) ) {
1309 return false;
1310 }
1311
1312 // Get some counts
1313 $forum_id = bbp_get_forum_id( $forum_id );
1314 $reply_count = bbp_get_forum_topic_count_hidden( $forum_id, false, true );
1315 $total_topic_count = bbp_get_forum_topic_count_hidden( $forum_id, true, true );
1316 $difference = (int) $difference;
1317
1318 // Update this forum id
1319 bbp_bump_count_meta( 'post', $forum_id, '_bbp_topic_count_hidden', $difference, $reply_count );
1320 bbp_bump_count_meta( 'post', $forum_id, '_bbp_total_topic_count_hidden', $difference, $total_topic_count );
1321
1322 // Check for ancestors
1323 if ( true === $update_ancestors ) {
1324
1325 // Get post ancestors
1326 $forum = get_post( $forum_id );
1327 $ancestors = get_post_ancestors( $forum );
1328
1329 // If has ancestors, loop through them...
1330 if ( ! empty( $ancestors ) ) {
1331 foreach ( (array) $ancestors as $parent_forum_id ) {
1332
1333 // Only update topic count when an ancestor is not a category.
1334 if ( ! bbp_is_forum_category( $parent_forum_id ) ) {
1335
1336 $parent_topic_count = bbp_get_forum_topic_count_hidden( $parent_forum_id, false, true );
1337 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_topic_count_hidden', $difference, $parent_topic_count );
1338 }
1339
1340 // Update the total topic count.
1341 $parent_total_topic_count = bbp_get_forum_topic_count_hidden( $parent_forum_id, true, true );
1342 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_total_topic_count_hidden', $difference, $parent_total_topic_count );
1343 }
1344 }
1345 }
1346
1347 $forum_topic_count = bbp_number_not_negative( $total_topic_count + $difference );
1348
1349 // Filter & return
1350 return (int) apply_filters( 'bbp_bump_forum_topic_count_hidden', $forum_topic_count, $forum_id, $difference, $update_ancestors );
1351 }
1352
1353 /**
1354 * Increase the total hidden topic count of a forum by one.
1355 *
1356 * @since 2.6.0 bbPress (r6036)
1357 *
1358 * @param int $forum_id The forum id.
1359 *
1360 * @return void
1361 */
1362 function bbp_increase_forum_topic_count_hidden( $forum_id = 0 ) {
1363
1364 // Bail early if no id is passed.
1365 if ( empty( $forum_id ) ) {
1366 return;
1367 }
1368
1369 // If it's a topic, get the forum id.
1370 if ( bbp_is_topic( $forum_id ) ) {
1371 $topic_id = $forum_id;
1372 $forum_id = bbp_get_topic_forum_id( $topic_id );
1373
1374 // Update inverse based on item status
1375 if ( bbp_is_topic_public( $topic_id ) ) {
1376 bbp_increase_forum_topic_count( $forum_id );
1377 return;
1378 }
1379 }
1380
1381 // Bump up
1382 bbp_bump_forum_topic_count_hidden( $forum_id );
1383 }
1384
1385 /**
1386 * Decrease the total hidden topic count of a forum by one.
1387 *
1388 * @since 2.6.0 bbPress (r6036)
1389 *
1390 * @param int $forum_id The forum id.
1391 *
1392 * @return void
1393 */
1394 function bbp_decrease_forum_topic_count_hidden( $forum_id = 0 ) {
1395
1396 // Bail early if no id is passed.
1397 if ( empty( $forum_id ) ) {
1398 return;
1399 }
1400
1401 // If it's a topic, get the forum id.
1402 if ( bbp_is_topic( $forum_id ) ) {
1403 $topic_id = $forum_id;
1404 $forum_id = bbp_get_topic_forum_id( $topic_id );
1405
1406 // Update inverse based on item status
1407 if ( bbp_is_topic_public( $topic_id ) ) {
1408 bbp_decrease_forum_topic_count( $forum_id );
1409 return;
1410 }
1411 }
1412
1413 // Bump down
1414 bbp_bump_forum_topic_count_hidden( $forum_id, -1 );
1415 }
1416
1417 /**
1418 * Bump the total topic count of a forum
1419 *
1420 * @since 2.1.0 bbPress (r3826)
1421 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
1422 *
1423 * @param int $forum_id Optional. Forum id.
1424 * @param int $difference Optional. Default 1
1425 * @param bool $update_ancestors Optional. Default true
1426 *
1427 * @return int Forum topic count
1428 */
1429 function bbp_bump_forum_reply_count( $forum_id = 0, $difference = 1, $update_ancestors = true ) {
1430
1431 // Bail if no bump
1432 if ( empty( $difference ) ) {
1433 return false;
1434 }
1435
1436 // Get some counts
1437 $forum_id = bbp_get_forum_id( $forum_id );
1438 $reply_count = bbp_get_forum_reply_count( $forum_id, false, true );
1439 $total_reply_count = bbp_get_forum_reply_count( $forum_id, true, true );
1440 $difference = (int) $difference;
1441
1442 // Update this forum id
1443 bbp_bump_count_meta( 'post', $forum_id, '_bbp_reply_count', $difference, $reply_count );
1444 bbp_bump_count_meta( 'post', $forum_id, '_bbp_total_reply_count', $difference, $total_reply_count );
1445
1446 // Check for ancestors
1447 if ( true === $update_ancestors ) {
1448
1449 // Get post ancestors
1450 $forum = get_post( $forum_id );
1451 $ancestors = get_post_ancestors( $forum );
1452
1453 // If has ancestors, loop through them...
1454 if ( ! empty( $ancestors ) ) {
1455 foreach ( (array) $ancestors as $parent_forum_id ) {
1456
1457 // Only update reply count when an ancestor is not a category.
1458 if ( ! bbp_is_forum_category( $parent_forum_id ) ) {
1459
1460 $parent_reply_count = bbp_get_forum_reply_count( $parent_forum_id, false, true );
1461 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_reply_count', $difference, $parent_reply_count );
1462 }
1463
1464 // Update the total reply count.
1465 $parent_total_reply_count = bbp_get_forum_reply_count( $parent_forum_id, true, true );
1466 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_total_reply_count', $difference, $parent_total_reply_count );
1467 }
1468 }
1469 }
1470
1471 $forum_reply_count = bbp_number_not_negative( $total_reply_count + $difference );
1472
1473 // Filter & return
1474 return (int) apply_filters( 'bbp_bump_forum_reply_count', $forum_reply_count, $forum_id, $difference, $update_ancestors );
1475 }
1476
1477 /**
1478 * Bump the total topic count of a forum
1479 *
1480 * @since 2.6.0 bbPress (r6923)
1481 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
1482 *
1483 * @param int $forum_id Optional. Forum id.
1484 * @param int $difference Optional. Default 1
1485 * @param bool $update_ancestors Optional. Default true
1486 *
1487 * @return int Forum topic count
1488 */
1489 function bbp_bump_forum_reply_count_hidden( $forum_id = 0, $difference = 1, $update_ancestors = true ) {
1490
1491 // Bail if no bump
1492 if ( empty( $difference ) ) {
1493 return false;
1494 }
1495
1496 // Get some counts
1497 $forum_id = bbp_get_forum_id( $forum_id );
1498 $reply_count = bbp_get_forum_reply_count_hidden( $forum_id, false, true );
1499 $total_reply_count = bbp_get_forum_reply_count_hidden( $forum_id, true, true );
1500 $difference = (int) $difference;
1501
1502 // Update this forum id
1503 bbp_bump_count_meta( 'post', $forum_id, '_bbp_reply_count_hidden', $difference, $reply_count );
1504 bbp_bump_count_meta( 'post', $forum_id, '_bbp_total_reply_count_hidden', $difference, $total_reply_count );
1505
1506 // Check for ancestors
1507 if ( true === $update_ancestors ) {
1508
1509 // Get post ancestors
1510 $forum = get_post( $forum_id );
1511 $ancestors = get_post_ancestors( $forum );
1512
1513 // If has ancestors, loop through them...
1514 if ( ! empty( $ancestors ) ) {
1515 foreach ( (array) $ancestors as $parent_forum_id ) {
1516
1517 // Only update reply count when an ancestor is not a category.
1518 if ( ! bbp_is_forum_category( $parent_forum_id ) ) {
1519
1520 $parent_reply_count = bbp_get_forum_reply_count_hidden( $parent_forum_id, false, true );
1521 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_reply_count_hidden', $difference, $parent_reply_count );
1522 }
1523
1524 // Update the total reply count.
1525 $parent_total_reply_count = bbp_get_forum_reply_count_hidden( $parent_forum_id, true, true );
1526 bbp_bump_count_meta( 'post', $parent_forum_id, '_bbp_total_reply_count_hidden', $difference, $parent_total_reply_count );
1527 }
1528 }
1529 }
1530
1531 $forum_reply_count = bbp_number_not_negative( $total_reply_count + $difference );
1532
1533 // Filter & return
1534 return (int) apply_filters( 'bbp_bump_forum_reply_count_hidden', $forum_reply_count, $forum_id, $difference, $update_ancestors );
1535 }
1536
1537 /**
1538 * Bump one total count through a forum's ancestors.
1539 *
1540 * This is used after recounting a forum that may be nested as a subforum. The
1541 * starting forum's total has already been updated, so only its parent forums
1542 * receive the difference between the old and new totals. The supplied metadata
1543 * key identifies which total topic or reply count is propagated.
1544 *
1545 * Forum hierarchy is stored in `post_parent`, making get_post_ancestors() the
1546 * canonical, cache-aware way to walk from a subforum toward its root forum.
1547 * Only parent forums are traversed: topics and replies are never ancestors in
1548 * a valid forum hierarchy, even when the metadata key stores their totals. The
1549 * walk stops if a malformed parent relationship leaves the forum post type.
1550 *
1551 * @since 2.6.17 bbPress (r7468)
1552 *
1553 * @param int $forum_id Starting forum ID. Its own count is not changed.
1554 * @param string $meta_key Topic or reply total count metadata key.
1555 * @param int $difference Amount to add to the stored value.
1556 * @return bool True when all ancestor counts were updated, false otherwise.
1557 */
1558 function bbp_bump_forum_ancestor_count( $forum_id = 0, $meta_key = '', $difference = 0 ) {
1559 $forum_id = bbp_get_forum_id( $forum_id );
1560 $difference = (int) $difference;
1561
1562 // Bail if nothing can change
1563 if ( empty( $forum_id ) || empty( $meta_key ) || empty( $difference ) ) {
1564 return false;
1565 }
1566
1567 $updated = true;
1568 $ancestor_ids = get_post_ancestors( $forum_id );
1569
1570 // Return if this forum has no ancestors
1571 if ( empty( $ancestor_ids ) ) {
1572 return $updated;
1573 }
1574
1575 // Update only total counts on ancestor forums
1576 foreach ( $ancestor_ids as $ancestor_id ) {
1577
1578 // Stop if malformed data leaves the subforum hierarchy
1579 if ( ! bbp_is_forum( $ancestor_id ) ) {
1580 break;
1581 }
1582
1583 $count = (int) get_post_meta( $ancestor_id, $meta_key, true );
1584
1585 if ( ! bbp_bump_count_meta( 'post', $ancestor_id, $meta_key, $difference, $count ) ) {
1586 $updated = false;
1587 }
1588 }
1589
1590 return $updated;
1591 }
1592
1593 /**
1594 * Increase the total reply count of a forum by one.
1595 *
1596 * @since 2.6.0 bbPress (r6036)
1597 *
1598 * @param int $forum_id The forum id.
1599 *
1600 * @return void
1601 */
1602 function bbp_increase_forum_reply_count( $forum_id = 0 ) {
1603
1604 // Bail early if no id is passed.
1605 if ( empty( $forum_id ) ) {
1606 return;
1607 }
1608
1609 // If it's a reply, get the forum id.
1610 if ( bbp_is_reply( $forum_id ) ) {
1611 $reply_id = $forum_id;
1612 $forum_id = bbp_get_reply_forum_id( $reply_id );
1613
1614 // Update inverse based on item status
1615 if ( ! bbp_is_reply_public( $reply_id ) ) {
1616 bbp_increase_forum_reply_count_hidden( $forum_id );
1617 return;
1618 }
1619 }
1620
1621 // Bump up
1622 bbp_bump_forum_reply_count( $forum_id );
1623 }
1624
1625 /**
1626 * Decrease the total reply count of a forum by one.
1627 *
1628 * @since 2.6.0 bbPress (r6036)
1629 *
1630 * @param int $forum_id The forum id.
1631 *
1632 * @return void
1633 */
1634 function bbp_decrease_forum_reply_count( $forum_id = 0 ) {
1635
1636 // Bail early if no id is passed.
1637 if ( empty( $forum_id ) ) {
1638 return;
1639 }
1640
1641 // If it's a reply, get the forum id.
1642 if ( bbp_is_reply( $forum_id ) ) {
1643 $reply_id = $forum_id;
1644 $forum_id = bbp_get_reply_forum_id( $reply_id );
1645
1646 // Update inverse based on item status
1647 if ( ! bbp_is_reply_public( $reply_id ) ) {
1648 bbp_decrease_forum_reply_count_hidden( $forum_id );
1649 return;
1650 }
1651 }
1652
1653 // Bump down
1654 bbp_bump_forum_reply_count( $forum_id, -1 );
1655 }
1656
1657 /**
1658 * Increase the total hidden reply count of a forum by one.
1659 *
1660 * @since 2.6.0 bbPress (r6923)
1661 *
1662 * @param int $forum_id The forum id.
1663 *
1664 * @return void
1665 */
1666 function bbp_increase_forum_reply_count_hidden( $forum_id = 0 ) {
1667
1668 // Bail early if no id is passed.
1669 if ( empty( $forum_id ) ) {
1670 return;
1671 }
1672
1673 // If it's a reply, get the forum id.
1674 if ( bbp_is_reply( $forum_id ) ) {
1675 $reply_id = $forum_id;
1676 $forum_id = bbp_get_reply_forum_id( $reply_id );
1677
1678 // Update inverse based on item status
1679 if ( bbp_is_reply_public( $reply_id ) ) {
1680 bbp_increase_forum_reply_count( $forum_id );
1681 return;
1682 }
1683 }
1684
1685 // Bump up
1686 bbp_bump_forum_reply_count_hidden( $forum_id );
1687 }
1688
1689 /**
1690 * Decrease the total hidden reply count of a forum by one.
1691 *
1692 * @since 2.6.0 bbPress (r6923)
1693 *
1694 * @param int $forum_id The forum id.
1695 *
1696 * @return void
1697 */
1698 function bbp_decrease_forum_reply_count_hidden( $forum_id = 0 ) {
1699
1700 // Bail early if no id is passed.
1701 if ( empty( $forum_id ) ) {
1702 return;
1703 }
1704
1705 // If it's a reply, get the forum id.
1706 if ( bbp_is_reply( $forum_id ) ) {
1707 $reply_id = $forum_id;
1708 $forum_id = bbp_get_reply_forum_id( $reply_id );
1709
1710 // Update inverse based on item status
1711 if ( bbp_is_reply_public( $reply_id ) ) {
1712 bbp_decrease_forum_reply_count( $forum_id );
1713 return;
1714 }
1715 }
1716
1717 // Bump down
1718 bbp_bump_forum_reply_count_hidden( $forum_id, -1 );
1719 }
1720
1721 /**
1722 * Update forum reply counts when a topic is approved or unapproved.
1723 *
1724 * @since 2.6.0 bbPress (r6036)
1725 *
1726 * @param int $topic_id The topic id.
1727 *
1728 * @return void
1729 */
1730 function bbp_approved_unapproved_topic_update_forum_reply_count( $topic_id = 0 ) {
1731
1732 // Bail early if we don't have a topic id.
1733 if ( empty( $topic_id ) ) {
1734 return;
1735 }
1736
1737 // Get the topic's replies.
1738 $count = bbp_get_public_child_count( $topic_id, bbp_get_reply_post_type() );
1739
1740 // If we're unapproving, set count to negative.
1741 if ( 'bbp_unapproved_topic' === current_filter() ) {
1742 $count = -$count;
1743 }
1744
1745 // Bump up or down
1746 bbp_bump_forum_reply_count( bbp_get_topic_forum_id( $topic_id ), $count );
1747 }
1748
1749 /** Forum Updaters ************************************************************/
1750
1751 /**
1752 * Update the forum last topic id
1753 *
1754 * @since 2.0.0 bbPress (r2627)
1755 *
1756 * @param int $forum_id Optional. Forum id.
1757 * @param int $topic_id Optional. Topic id.
1758 * @return int Id of the forums most recent topic
1759 */
1760 function bbp_update_forum_last_topic_id( $forum_id = 0, $topic_id = 0 ) {
1761 $forum_id = bbp_get_forum_id( $forum_id );
1762
1763 // Define local variable(s)
1764 $children_last_topic = 0;
1765
1766 // Do some calculation if not manually set
1767 if ( empty( $topic_id ) ) {
1768
1769 // Loop through children and add together forum reply counts
1770 $children = bbp_forum_query_subforum_ids( $forum_id );
1771 if ( ! empty( $children ) ) {
1772 foreach ( $children as $child ) {
1773 $children_last_topic = bbp_update_forum_last_topic_id( $child ); // Recursive
1774 }
1775 }
1776
1777 // Setup recent topic query vars
1778 $post_vars = array(
1779 'post_parent' => $forum_id,
1780 'post_type' => bbp_get_topic_post_type(),
1781 'meta_key' => '_bbp_last_active_time',
1782 'meta_type' => 'DATETIME',
1783 'orderby' => 'meta_value',
1784 'numberposts' => 1
1785 );
1786
1787 // Get the most recent topic in this forum_id
1788 $recent_topic = get_posts( $post_vars );
1789 if ( ! empty( $recent_topic ) ) {
1790 $topic_id = $recent_topic[0]->ID;
1791 }
1792 }
1793
1794 // Cast as integer in case of empty or string
1795 $topic_id = (int) $topic_id;
1796 $children_last_topic = (int) $children_last_topic;
1797
1798 // If child forums have higher id, use that instead
1799 if ( ! empty( $children ) && ( $children_last_topic > $topic_id ) ) {
1800 $topic_id = $children_last_topic;
1801 }
1802
1803 // Update the last public topic ID
1804 update_post_meta( $forum_id, '_bbp_last_topic_id', $topic_id );
1805
1806 // Filter & return
1807 return (int) apply_filters( 'bbp_update_forum_last_topic_id', $topic_id, $forum_id );
1808 }
1809
1810 /**
1811 * Update the forum last reply id
1812 *
1813 * @since 2.0.0 bbPress (r2627)
1814 *
1815 * @param int $forum_id Optional. Forum id.
1816 * @param int $reply_id Optional. Reply id.
1817 * @return int Id of the forums most recent reply
1818 */
1819 function bbp_update_forum_last_reply_id( $forum_id = 0, $reply_id = 0 ) {
1820 $forum_id = bbp_get_forum_id( $forum_id );
1821
1822 // Define local variable(s)
1823 $children_last_reply = 0;
1824
1825 // Do some calculation if not manually set
1826 if ( empty( $reply_id ) ) {
1827
1828 // Loop through children and get the most recent reply id
1829 $children = bbp_forum_query_subforum_ids( $forum_id );
1830 if ( ! empty( $children ) ) {
1831 foreach ( $children as $child ) {
1832 $children_last_reply = bbp_update_forum_last_reply_id( $child ); // Recursive
1833 }
1834 }
1835
1836 // If this forum has topics...
1837 $topic_ids = bbp_forum_query_topic_ids( $forum_id );
1838 if ( ! empty( $topic_ids ) ) {
1839
1840 // ...get the most recent reply from those topics...
1841 $reply_id = bbp_forum_query_last_reply_id( $forum_id, $topic_ids );
1842
1843 // ...and compare it to the most recent topic id...
1844 $reply_id = ( $reply_id > max( $topic_ids ) )
1845 ? $reply_id
1846 : max( $topic_ids );
1847 }
1848 }
1849
1850 // Cast as integer in case of empty or string
1851 $reply_id = (int) $reply_id;
1852 $children_last_reply = (int) $children_last_reply;
1853
1854 // If child forums have higher ID, check for newer reply id
1855 if ( ! empty( $children ) && ( $children_last_reply > $reply_id ) ) {
1856 $reply_id = $children_last_reply;
1857 }
1858
1859 // Update the last public reply ID
1860 update_post_meta( $forum_id, '_bbp_last_reply_id', $reply_id );
1861
1862 // Filter & return
1863 return (int) apply_filters( 'bbp_update_forum_last_reply_id', $reply_id, $forum_id );
1864 }
1865
1866 /**
1867 * Update the forum last active post id
1868 *
1869 * @since 2.0.0 bbPress (r2870)
1870 *
1871 * @param int $forum_id Optional. Forum id.
1872 * @param int $active_id Optional. Active post id.
1873 * @return int Id of the forums last active post
1874 */
1875 function bbp_update_forum_last_active_id( $forum_id = 0, $active_id = 0 ) {
1876
1877 $forum_id = bbp_get_forum_id( $forum_id );
1878
1879 // Define local variable(s)
1880 $children_last_active = 0;
1881
1882 // Do some calculation if not manually set
1883 if ( empty( $active_id ) ) {
1884
1885 // Loop through children and get the last active ID
1886 $children = bbp_forum_query_subforum_ids( $forum_id );
1887 if ( ! empty( $children ) ) {
1888 foreach ( $children as $child ) {
1889 $children_last_active = bbp_update_forum_last_active_id( $child, $active_id );
1890 }
1891 }
1892
1893 // Get topic IDs and only accept larger IDs
1894 $topic_ids = bbp_forum_query_topic_ids( $forum_id );
1895 if ( ! empty( $topic_ids ) ) {
1896
1897 // Make sure ID is larger
1898 $active_id = bbp_forum_query_last_reply_id( $forum_id, $topic_ids );
1899 $active_id = $active_id > max( $topic_ids )
1900 ? $active_id
1901 : max( $topic_ids );
1902
1903 // Forum has no topics
1904 } else {
1905 $active_id = 0;
1906 }
1907 }
1908
1909 // Cast as integer in case of empty or string
1910 $active_id = (int) $active_id;
1911 $children_last_active = (int) $children_last_active;
1912
1913 // If child forums have higher ID, use that instead
1914 if ( ! empty( $children ) && ( $children_last_active > $active_id ) ) {
1915 $active_id = $children_last_active;
1916 }
1917
1918 update_post_meta( $forum_id, '_bbp_last_active_id', $active_id );
1919
1920 // Filter & return
1921 return (int) apply_filters( 'bbp_update_forum_last_active_id', $active_id, $forum_id );
1922 }
1923
1924 /**
1925 * Update the forums last active date/time (aka freshness)
1926 *
1927 * @since 2.0.0 bbPress (r2870)
1928 *
1929 * @param int $forum_id Optional. Topic id.
1930 * @param string $new_time Optional. New time in mysql format.
1931 *
1932 * @return string MySQL timestamp of last active topic or reply
1933 */
1934 function bbp_update_forum_last_active_time( $forum_id = 0, $new_time = '' ) {
1935 $forum_id = bbp_get_forum_id( $forum_id );
1936
1937 // Check time and use current if empty
1938 if ( empty( $new_time ) ) {
1939 $new_time = get_post_field( 'post_date', bbp_get_forum_last_active_id( $forum_id ) );
1940 }
1941
1942 // Update only if there is a time
1943 if ( ! empty( $new_time ) ) {
1944 update_post_meta( $forum_id, '_bbp_last_active_time', $new_time );
1945 }
1946
1947 // Filter & return
1948 return apply_filters( 'bbp_update_forum_last_active', $new_time, $forum_id );
1949 }
1950
1951 /**
1952 * Update the forum sub-forum count
1953 *
1954 * @since 2.0.0 bbPress (r2627)
1955 * @since 2.6.17 bbPress (r7454) Count supported forum visibilities from the post hierarchy.
1956 *
1957 * @param int $forum_id Optional. Forum ID.
1958 * @param int|bool $subforums Optional. Number of subforums, or false to query.
1959 * @return int|false Number of subforums, or false on query failure.
1960 */
1961 function bbp_update_forum_subforum_count( $forum_id = 0, $subforums = false ) {
1962 $forum_id = bbp_get_forum_id( $forum_id );
1963
1964 // Maybe query for counts
1965 if ( ! is_int( $subforums ) ) {
1966 $bbp_db = bbp_db();
1967 $post_type = bbp_get_forum_post_type();
1968 $statuses = bbp_get_countable_forum_statuses();
1969
1970 if ( ! empty( $statuses ) ) {
1971 $placeholders = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) );
1972 $sql = "SELECT COUNT(*) FROM {$bbp_db->posts} WHERE post_parent = %d AND post_type = %s AND post_status IN ({$placeholders})";
1973 $query = $bbp_db->prepare( $sql, array_merge( array( $forum_id, $post_type ), $statuses ) );
1974 $subforums = $bbp_db->get_var( $query );
1975
1976 // Bail if the count query failed
1977 if ( ! empty( $bbp_db->last_error ) ) {
1978 return false;
1979 }
1980
1981 $subforums = bbp_number_not_negative( $subforums );
1982 } else {
1983 $subforums = 0;
1984 }
1985 }
1986
1987 update_post_meta( $forum_id, '_bbp_forum_subforum_count', $subforums );
1988
1989 // Filter & return
1990 return (int) apply_filters( 'bbp_update_forum_subforum_count', $subforums, $forum_id );
1991 }
1992
1993 /**
1994 * Synchronize child-forum metadata after WordPress reparents deleted children.
1995 *
1996 * @since 2.6.17 bbPress (r7454)
1997 *
1998 * @param int $forum_id Deleted forum ID.
1999 * @param WP_Post $forum Deleted forum post object.
2000 */
2001 function bbp_reparent_forum_subforums( $forum_id = 0, $forum = false ) {
2002 $bbp_db = bbp_db();
2003 $post_type = bbp_get_forum_post_type();
2004 $meta_key = '_bbp_forum_id';
2005 $sql = "SELECT posts.ID
2006 FROM {$bbp_db->posts} AS posts
2007 INNER JOIN {$bbp_db->postmeta} AS postmeta
2008 ON posts.ID = postmeta.post_id
2009 AND postmeta.meta_key = %s
2010 WHERE posts.post_type = %s
2011 AND posts.post_parent = %d
2012 AND postmeta.meta_value = %d";
2013 $query = $bbp_db->prepare( $sql, $meta_key, $post_type, $forum->post_parent, $forum_id );
2014
2015 foreach ( wp_parse_id_list( $bbp_db->get_col( $query ) ) as $subforum_id ) {
2016 bbp_update_forum_id( $subforum_id, $forum->post_parent );
2017 }
2018 }
2019
2020 /**
2021 * Update a parent forum's subforum count after a child is permanently deleted.
2022 *
2023 * @since 2.6.17 bbPress (r7454)
2024 *
2025 * @param int $forum_id Forum ID.
2026 * @param WP_Post|bool $forum Optional. Forum post object.
2027 * @return int|false Updated subforum count, or false if there is no parent.
2028 */
2029 function bbp_update_parent_forum_subforum_count( $forum_id = 0, $forum = false ) {
2030 $forum = ( $forum instanceof WP_Post )
2031 ? $forum
2032 : get_post( $forum_id );
2033
2034 // Bail if the forum has no parent
2035 if ( empty( $forum ) || empty( $forum->post_parent ) ) {
2036 return false;
2037 }
2038
2039 return bbp_update_forum_subforum_count( $forum->post_parent );
2040 }
2041
2042 /**
2043 * Update a parent forum's subforum count after a child changes count status.
2044 *
2045 * @since 2.6.17 bbPress (r7454)
2046 *
2047 * @param string $new_status New post status.
2048 * @param string $old_status Old post status.
2049 * @param WP_Post $forum Forum post object.
2050 * @return int|false Updated subforum count, or false when no update is needed.
2051 */
2052 function bbp_update_forum_subforum_count_on_transition_post_status( $new_status = '', $old_status = '', $forum = false ) {
2053
2054 // Bail if this is not a child forum
2055 if ( ( bbp_get_forum_post_type() !== $forum->post_type ) || empty( $forum->post_parent ) ) {
2056 return false;
2057 }
2058
2059 $statuses = bbp_get_countable_forum_statuses();
2060 $was_counted = in_array( $old_status, $statuses, true );
2061 $is_counted = in_array( $new_status, $statuses, true );
2062
2063 // Bail if subforum count membership did not change
2064 if ( $was_counted === $is_counted ) {
2065 return false;
2066 }
2067
2068 return bbp_update_forum_subforum_count( $forum->post_parent );
2069 }
2070
2071 /**
2072 * Update subforum counts after a forum changes parent or post type.
2073 *
2074 * @since 2.6.17 bbPress (r7454)
2075 *
2076 * @param int $forum_id Forum ID.
2077 * @param WP_Post $forum_after Forum object following the update.
2078 * @param WP_Post $forum_before Forum object before the update.
2079 */
2080 function bbp_update_forum_subforum_counts_on_post_updated( $forum_id = 0, $forum_after = false, $forum_before = false ) {
2081 $post_type = bbp_get_forum_post_type();
2082 $parent_moved = ( $forum_after->post_parent !== $forum_before->post_parent );
2083
2084 // Bail if forum hierarchy membership did not change
2085 if ( ! $parent_moved && ( $forum_after->post_type === $forum_before->post_type ) ) {
2086 return false;
2087 }
2088
2089 // Synchronize parent metadata when the updated post remains a forum
2090 if ( ( $post_type === $forum_after->post_type ) && ( $parent_moved || ( $post_type !== $forum_before->post_type ) ) ) {
2091 bbp_update_forum_id( $forum_id, $forum_after->post_parent );
2092 }
2093
2094 $statuses = bbp_get_countable_forum_statuses();
2095 $was_counted = ( $post_type === $forum_before->post_type ) && in_array( $forum_before->post_status, $statuses, true );
2096 $is_counted = ( $post_type === $forum_after->post_type ) && in_array( $forum_after->post_status, $statuses, true );
2097
2098 // Include the previous parent when it formerly contained this subforum
2099 $parent_ids = $was_counted
2100 ? array( $forum_before->post_parent )
2101 : array();
2102
2103 // Include the new parent when it now contains this subforum
2104 if ( $is_counted ) {
2105 $parent_ids[] = $forum_after->post_parent;
2106 }
2107
2108 // Recount each affected parent once
2109 foreach ( array_filter( array_unique( $parent_ids ) ) as $parent_id ) {
2110 bbp_update_forum_subforum_count( $parent_id );
2111 }
2112 }
2113
2114 /**
2115 * Adjust the total topic count of a forum.
2116 *
2117 * @since 2.0.0 bbPress (r2464)
2118 * @since 2.6.17 bbPress (r7468) Optionally update ancestor forum totals.
2119 *
2120 * @param int $forum_id Optional. Forum id or topic id. It is checked whether it
2121 * is a topic or a forum. If it's a topic, its parent,
2122 * i.e. the forum is automatically retrieved.
2123 * @param bool $update_ancestors Optional. Whether to update ancestor totals.
2124 * @return int Forum topic count.
2125 */
2126 function bbp_update_forum_topic_count( $forum_id = 0, $update_ancestors = false ) {
2127 $forum_id = bbp_get_forum_id( $forum_id );
2128 $old_total_topics = ( true === $update_ancestors )
2129 ? (int) get_post_meta( $forum_id, '_bbp_total_topic_count', true )
2130 : 0;
2131 $children_topic_count = 0;
2132 $total_topics = 0;
2133
2134 // Loop through subforums and add together forum topic counts
2135 $children = bbp_forum_query_subforum_ids( $forum_id );
2136 if ( ! empty( $children ) ) {
2137 foreach ( $children as $child ) {
2138 $children_topic_count += bbp_update_forum_topic_count( $child ); // Recursive
2139 }
2140 }
2141
2142 // Get total topics for this forum
2143 $topics = bbp_get_public_child_count( $forum_id, bbp_get_topic_post_type() );
2144
2145 // Calculate total topics in this forum
2146 $total_topics = (int) ( $topics + $children_topic_count );
2147
2148 // Update the count
2149 update_post_meta( $forum_id, '_bbp_topic_count', $topics );
2150 update_post_meta( $forum_id, '_bbp_total_topic_count', $total_topics );
2151
2152 // Update ancestor total counts by the persisted difference
2153 if ( true === $update_ancestors ) {
2154 bbp_bump_forum_ancestor_count( $forum_id, '_bbp_total_topic_count', $total_topics - $old_total_topics );
2155 }
2156
2157 // Filter & return
2158 return (int) apply_filters( 'bbp_update_forum_topic_count', $total_topics, $forum_id );
2159 }
2160
2161 /**
2162 * Adjust the total hidden topic count of a forum (hidden includes trashed,
2163 * spammed and pending topics).
2164 *
2165 * @since 2.0.0 bbPress (r3349)
2166 * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
2167 * @since 2.6.17 bbPress (r7468) Optionally update ancestor forum totals.
2168 *
2169 * @param int $forum_id Optional. Topic id to update.
2170 * @param int $topic_count Optional. Set the topic count manually.
2171 * @param bool $update_ancestors Optional. Whether to update ancestor totals.
2172 *
2173 * @return int Topic hidden topic count.
2174 */
2175 function bbp_update_forum_topic_count_hidden( $forum_id = 0, $topic_count = false, $update_ancestors = false ) {
2176
2177 // If topic_id was passed as $forum_id, then get its forum
2178 if ( bbp_is_topic( $forum_id ) ) {
2179 $topic_id = bbp_get_topic_id( $forum_id );
2180 $forum_id = bbp_get_topic_forum_id( $topic_id );
2181
2182 // $forum_id is not a topic_id, so validate and proceed
2183 } else {
2184 $forum_id = bbp_get_forum_id( $forum_id );
2185 }
2186
2187 $children_topic_count = 0;
2188 $total_topics = 0;
2189 $old_total_topics = ( true === $update_ancestors )
2190 ? (int) get_post_meta( $forum_id, '_bbp_total_topic_count_hidden', true )
2191 : 0;
2192
2193 // Can't update what isn't there
2194 if ( ! empty( $forum_id ) ) {
2195
2196 // Loop through children and add together hidden topic counts
2197 $children = bbp_forum_query_subforum_ids( $forum_id );
2198 if ( ! empty( $children ) ) {
2199 foreach ( (array) $children as $child ) {
2200 bbp_update_forum_topic_count_hidden( $child );
2201 $children_topic_count += bbp_get_forum_topic_count_hidden( $child, true, true );
2202 }
2203 }
2204
2205 // Get topics of forum
2206 if ( ! is_int( $topic_count ) ) {
2207 $query = new WP_Query(
2208 array(
2209 'fields' => 'ids',
2210 'post_parent' => $forum_id,
2211 'post_status' => bbp_get_non_public_topic_statuses(),
2212 'post_type' => bbp_get_topic_post_type(),
2213 'posts_per_page' => -1,
2214
2215 // Performance
2216 'nopaging' => true,
2217 'suppress_filters' => true,
2218 'update_post_term_cache' => false,
2219 'update_post_meta_cache' => false,
2220 'ignore_sticky_posts' => true,
2221 'no_found_rows' => true
2222 )
2223 );
2224
2225 $topic_count = $query->post_count;
2226
2227 unset( $query );
2228 }
2229
2230 $topic_count = (int) $topic_count;
2231 $total_topics = (int) ( $topic_count + $children_topic_count );
2232
2233 // Update the counts
2234 update_post_meta( $forum_id, '_bbp_topic_count_hidden', $topic_count );
2235 update_post_meta( $forum_id, '_bbp_total_topic_count_hidden', $total_topics );
2236
2237 // Update ancestor total counts by the persisted difference
2238 if ( true === $update_ancestors ) {
2239 bbp_bump_forum_ancestor_count( $forum_id, '_bbp_total_topic_count_hidden', $total_topics - $old_total_topics );
2240 }
2241 }
2242
2243 // Filter & return
2244 return (int) apply_filters( 'bbp_update_forum_topic_count_hidden', $topic_count, $forum_id );
2245 }
2246
2247 /**
2248 * Adjust the total reply count of a forum.
2249 *
2250 * @since 2.0.0 bbPress (r2615)
2251 * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects.
2252 * @since 2.6.17 bbPress (r7468) Count replies only when their parent topics are public.
2253 * @since 2.6.17 bbPress (r7468) Optionally update ancestor forum totals.
2254 *
2255 * @param int $forum_id Optional. Forum id or topic id. It is checked whether it
2256 * is a topic or a forum. If it's a topic, its parent,
2257 * i.e. the forum is automatically retrieved.
2258 * @param bool $update_ancestors Optional. Whether to update ancestor totals.
2259 *
2260 * @return int Forum reply count.
2261 */
2262 function bbp_update_forum_reply_count( $forum_id = 0, $update_ancestors = false ) {
2263
2264 $forum_id = bbp_get_forum_id( $forum_id );
2265 $old_total_replies = ( true === $update_ancestors )
2266 ? (int) get_post_meta( $forum_id, '_bbp_total_reply_count', true )
2267 : 0;
2268 $children_reply_count = 0;
2269
2270 // Loop through children and add together forum reply counts
2271 $children = bbp_forum_query_subforum_ids( $forum_id );
2272 if ( ! empty( $children ) ) {
2273 foreach ( (array) $children as $child ) {
2274 $children_reply_count += bbp_update_forum_reply_count( $child );
2275 }
2276 }
2277
2278 // Don't count replies if the forum is a category
2279 if ( bbp_is_forum_category( $forum_id ) ) {
2280 $reply_count = 0;
2281
2282 // Count public replies whose parent topics are also public
2283 } else {
2284 $bbp_db = bbp_db();
2285 $reply_statuses = bbp_get_public_reply_statuses();
2286 $topic_statuses = bbp_get_public_topic_statuses();
2287
2288 if ( empty( $reply_statuses ) || empty( $topic_statuses ) ) {
2289 $reply_count = 0;
2290 } else {
2291 $reply_placeholders = implode( ', ', array_fill( 0, count( $reply_statuses ), '%s' ) );
2292 $topic_placeholders = implode( ', ', array_fill( 0, count( $topic_statuses ), '%s' ) );
2293 $sql = "SELECT COUNT(*) FROM {$bbp_db->posts} AS replies
2294 INNER JOIN {$bbp_db->posts} AS topics ON replies.post_parent = topics.ID
2295 WHERE topics.post_parent = %d
2296 AND topics.post_type = %s
2297 AND topics.post_status IN ({$topic_placeholders})
2298 AND replies.post_type = %s
2299 AND replies.post_status IN ({$reply_placeholders})";
2300 $query = $bbp_db->prepare(
2301 $sql,
2302 array_merge(
2303 array( $forum_id, bbp_get_topic_post_type() ),
2304 $topic_statuses,
2305 array( bbp_get_reply_post_type() ),
2306 $reply_statuses
2307 )
2308 );
2309 $reply_count = bbp_number_not_negative( $bbp_db->get_var( $query ) );
2310 }
2311 }
2312
2313 // Calculate total replies in this forum
2314 $total_replies = (int) ( $reply_count + $children_reply_count );
2315
2316 // Update the counts
2317 update_post_meta( $forum_id, '_bbp_reply_count', $reply_count );
2318 update_post_meta( $forum_id, '_bbp_total_reply_count', $total_replies );
2319
2320 // Update ancestor total counts by the persisted difference
2321 if ( true === $update_ancestors ) {
2322 bbp_bump_forum_ancestor_count( $forum_id, '_bbp_total_reply_count', $total_replies - $old_total_replies );
2323 }
2324
2325 // Filter & return
2326 return (int) apply_filters( 'bbp_update_forum_reply_count', $total_replies, $forum_id );
2327 }
2328
2329 /**
2330 * Adjust the total hidden reply count of a forum.
2331 *
2332 * @since 2.6.0 bbPress (r6923)
2333 * @since 2.6.17 bbPress (r7468) Optionally update ancestor forum totals.
2334 *
2335 * @param int $forum_id Optional. Forum id or topic id. It is checked whether it
2336 * is a topic or a forum. If it's a topic, its parent,
2337 * i.e. the forum is automatically retrieved.
2338 * @param bool $update_ancestors Optional. Whether to update ancestor totals.
2339 *
2340 * @return int Forum reply count.
2341 */
2342 function bbp_update_forum_reply_count_hidden( $forum_id = 0, $update_ancestors = false ) {
2343
2344 $forum_id = bbp_get_forum_id( $forum_id );
2345 $old_total_replies = ( true === $update_ancestors )
2346 ? (int) get_post_meta( $forum_id, '_bbp_total_reply_count_hidden', true )
2347 : 0;
2348 $children_reply_count = 0;
2349
2350 // Loop through children and add together forum reply counts
2351 $children = bbp_forum_query_subforum_ids( $forum_id );
2352 if ( ! empty( $children ) ) {
2353 foreach ( (array) $children as $child ) {
2354 $children_reply_count += bbp_update_forum_reply_count_hidden( $child );
2355 }
2356 }
2357
2358 // Don't count replies if the forum is a category
2359 $reply_count = ! bbp_is_forum_category( $forum_id )
2360 ? bbp_get_non_public_child_count( $forum_id, bbp_get_reply_post_type() )
2361 : 0;
2362
2363 // Calculate total replies in this forum
2364 $total_replies = (int) ( $reply_count + $children_reply_count );
2365
2366 // Update the counts
2367 update_post_meta( $forum_id, '_bbp_reply_count_hidden', $reply_count );
2368 update_post_meta( $forum_id, '_bbp_total_reply_count_hidden', $total_replies );
2369
2370 // Update ancestor total counts by the persisted difference
2371 if ( true === $update_ancestors ) {
2372 bbp_bump_forum_ancestor_count( $forum_id, '_bbp_total_reply_count_hidden', $total_replies - $old_total_replies );
2373 }
2374
2375 // Filter & return
2376 return (int) apply_filters( 'bbp_update_forum_reply_count_hidden', $total_replies, $forum_id );
2377 }
2378
2379 /**
2380 * Updates the counts of a forum.
2381 *
2382 * This calls a few internal functions that all run manual queries against the
2383 * database to get their results. As such, this function can be costly to run
2384 * but is necessary to keep everything accurate.
2385 *
2386 * @since 2.0.0 bbPress (r2907)
2387 *
2388 * @param array $args Supports these arguments:
2389 * - forum_id: Forum id
2390 * - last_topic_id: Last topic id
2391 * - last_reply_id: Last reply id
2392 * - last_active_id: Last active post id
2393 * - last_active_time: last active time
2394 */
2395 function bbp_update_forum( $args = array() ) {
2396
2397 // Parse arguments against default values
2398 $r = bbp_parse_args(
2399 $args,
2400 array(
2401 'forum_id' => 0,
2402 'post_parent' => 0,
2403 'last_topic_id' => 0,
2404 'last_reply_id' => 0,
2405 'last_active_id' => 0,
2406 'last_active_time' => 0,
2407 'last_active_status' => bbp_get_public_status_id()
2408 ),
2409 'update_forum'
2410 );
2411
2412 // Update the forum parent
2413 bbp_update_forum_id( $r['forum_id'], $r['post_parent'] );
2414
2415 // Last topic and reply ID's
2416 bbp_update_forum_last_topic_id( $r['forum_id'], $r['last_topic_id'] );
2417 bbp_update_forum_last_reply_id( $r['forum_id'], $r['last_reply_id'] );
2418
2419 // Active dance
2420 $r['last_active_id'] = bbp_update_forum_last_active_id( $r['forum_id'], $r['last_active_id'] );
2421
2422 // If no active time was passed, get it from the last_active_id
2423 if ( empty( $r['last_active_time'] ) ) {
2424 $r['last_active_time'] = get_post_field( 'post_date', $r['last_active_id'] );
2425 }
2426
2427 if ( bbp_get_public_status_id() === $r['last_active_status'] ) {
2428 bbp_update_forum_last_active_time( $r['forum_id'], $r['last_active_time'] );
2429 }
2430
2431 // Subforum counts are updated on bbp_transition_post_status,
2432 // bbp_post_updated, and bbp_deleted_forum.
2433
2434 // Only update topic count if we've deleted a topic
2435 if ( in_array( current_filter(), array( 'bbp_deleted_topic', 'save_post' ), true ) ) {
2436 bbp_update_forum_reply_count( $r['forum_id'] );
2437 bbp_update_forum_topic_count( $r['forum_id'] );
2438 bbp_update_forum_topic_count_hidden( $r['forum_id'] );
2439 bbp_update_forum_reply_count_hidden( $r['forum_id'] );
2440 }
2441
2442 // Update parent forums
2443 bbp_update_forum_walker( $r );
2444
2445 // Bump the custom query cache
2446 wp_cache_set( 'last_changed', microtime(), 'bbpress_posts' );
2447 }
2448
2449 /**
2450 * Walk up the forum hierarchy and update parent forums.
2451 *
2452 * @since 2.6.17 bbPress (r7456)
2453 *
2454 * @param array $args Parsed arguments from bbp_update_forum().
2455 * @return false|null False if there is no parent forum, otherwise null.
2456 */
2457 function bbp_update_forum_walker( $args = array() ) {
2458
2459 // Bail if this forum has no parent
2460 if ( empty( $args['post_parent'] ) ) {
2461 return false;
2462 }
2463
2464 // Update the parent and continue walking
2465 $args['forum_id'] = bbp_get_forum_id( $args['post_parent'] );
2466
2467 // Bail if the parent is not a forum
2468 if ( empty( $args['forum_id'] ) ) {
2469 return false;
2470 }
2471
2472 $args['post_parent'] = get_post_field( 'post_parent', $args['forum_id'] );
2473
2474 bbp_update_forum( $args );
2475 }
2476
2477 /** Helpers *******************************************************************/
2478
2479 /**
2480 * Return an associative array of available topic statuses
2481 *
2482 * Developers note: these statuses are actually stored as meta data, and
2483 * Visibilities are stored in post_status.
2484 *
2485 * @since 2.4.0 bbPress (r5060)
2486 *
2487 * @param int $forum_id Optional. Forum id.
2488 *
2489 * @return array
2490 */
2491 function bbp_get_forum_statuses( $forum_id = 0 ) {
2492
2493 // Filter & return
2494 return (array) apply_filters(
2495 'bbp_get_forum_statuses',
2496 array(
2497 'open' => _x( 'Open', 'Open the forum', 'bbpress' ),
2498 'closed' => _x( 'Closed', 'Close the forum', 'bbpress' )
2499 ),
2500 $forum_id
2501 );
2502 }
2503
2504 /**
2505 * Return an associative array of forum types
2506 *
2507 * @since 2.4.0 bbPress (r5060)
2508 *
2509 * @param int $forum_id Optional. Forum id.
2510 *
2511 * @return array
2512 */
2513 function bbp_get_forum_types( $forum_id = 0 ) {
2514
2515 // Filter & return
2516 return (array) apply_filters(
2517 'bbp_get_forum_types',
2518 array(
2519 'forum' => _x( 'Forum', 'Forum accepts new topics', 'bbpress' ),
2520 'category' => _x( 'Category', 'Forum is a category', 'bbpress' )
2521 ),
2522 $forum_id
2523 );
2524 }
2525
2526 /**
2527 * Return an associative array of forum visibility
2528 *
2529 * Developers note: these visibilities are actually stored in post_status, and
2530 * Statuses are stored in meta data.
2531 *
2532 * @since 2.4.0 bbPress (r5060)
2533 *
2534 * @param int $forum_id Optional. Forum id.
2535 *
2536 * @return array
2537 */
2538 function bbp_get_forum_visibilities( $forum_id = 0 ) {
2539
2540 // Filter & return
2541 return (array) apply_filters(
2542 'bbp_get_forum_visibilities',
2543 array(
2544 bbp_get_public_status_id() => _x( 'Public', 'Make forum public', 'bbpress' ),
2545 bbp_get_private_status_id() => _x( 'Private', 'Make forum private', 'bbpress' ),
2546 bbp_get_hidden_status_id() => _x( 'Hidden', 'Make forum hidden', 'bbpress' )
2547 ),
2548 $forum_id
2549 );
2550 }
2551
2552 /**
2553 * Return array of public forum statuses.
2554 *
2555 * @since 2.6.0 bbPress (r6922)
2556 *
2557 * @return array
2558 */
2559 function bbp_get_public_forum_statuses() {
2560 $statuses = array(
2561 bbp_get_public_status_id()
2562 );
2563
2564 // Filter & return
2565 return (array) apply_filters( 'bbp_get_public_forum_statuses', $statuses );
2566 }
2567
2568 /**
2569 * Return array of non-public forum statuses.
2570 *
2571 * @since 2.6.0 bbPress (r6922)
2572 *
2573 * @return array
2574 */
2575 function bbp_get_non_public_forum_statuses() {
2576 $statuses = array(
2577 bbp_get_private_status_id(),
2578 bbp_get_hidden_status_id()
2579 );
2580
2581 // Filter & return
2582 return (array) apply_filters( 'bbp_get_non_public_forum_statuses', $statuses );
2583 }
2584
2585 /**
2586 * Return forum statuses included in forum and subforum counts.
2587 *
2588 * @since 2.6.17 bbPress (r7454)
2589 *
2590 * @return array
2591 */
2592 function bbp_get_countable_forum_statuses() {
2593 $statuses = array_values( array_unique( array_merge( bbp_get_public_forum_statuses(), bbp_get_non_public_forum_statuses() ) ) );
2594
2595 // Filter & return
2596 return (array) apply_filters( 'bbp_get_countable_forum_statuses', $statuses );
2597 }
2598
2599 /** Queries *******************************************************************/
2600
2601 /**
2602 * Returns the hidden forum ids
2603 *
2604 * Only hidden forum ids are returned. Public and private ids are not.
2605 *
2606 * @since 2.0.0 bbPress (r3008)
2607 */
2608 function bbp_get_hidden_forum_ids() {
2609 $forum_ids = get_option( '_bbp_hidden_forums', array() );
2610 $forum_ids = ! empty( $forum_ids )
2611 ? wp_parse_id_list( $forum_ids )
2612 : array();
2613
2614 // Filter & return
2615 return (array) apply_filters( 'bbp_get_hidden_forum_ids', $forum_ids );
2616 }
2617
2618 /**
2619 * Returns the private forum ids
2620 *
2621 * Only private forum ids are returned. Public and hidden ids are not.
2622 *
2623 * @since 2.0.0 bbPress (r3008)
2624 */
2625 function bbp_get_private_forum_ids() {
2626 $forum_ids = get_option( '_bbp_private_forums', array() );
2627 $forum_ids = ! empty( $forum_ids )
2628 ? wp_parse_id_list( $forum_ids )
2629 : array();
2630
2631 // Filter & return
2632 return (array) apply_filters( 'bbp_get_private_forum_ids', $forum_ids );
2633 }
2634
2635 /**
2636 * Returns the forum IDs that should be excluded from various views & queries,
2637 * based on the current user's capabilities.
2638 *
2639 * These results are automatically filtered by bbp_allow_forums_of_user(), to
2640 * allow per-forum moderators to see forums that would otherwise be private or
2641 * hidden to them.
2642 *
2643 * If you have a need to filter these results based on your own custom
2644 * engagements API usages, please see: bbp_allow_forums_of_user()
2645 *
2646 * @since 2.6.0 bbPress (r6426)
2647 *
2648 * @return array Forum IDs to exclude, or an empty array
2649 */
2650 function bbp_get_excluded_forum_ids() {
2651
2652 // Private forums
2653 $private = ! current_user_can( 'read_private_forums' )
2654 ? bbp_get_private_forum_ids()
2655 : array();
2656
2657 // Hidden forums
2658 $hidden = ! current_user_can( 'read_hidden_forums' )
2659 ? bbp_get_hidden_forum_ids()
2660 : array();
2661
2662 // Merge private & hidden forums together, and remove any empties
2663 $forum_ids = ( ! empty( $private ) || ! empty( $hidden ) )
2664 ? array_filter( wp_parse_id_list( array_merge( $private, $hidden ) ) )
2665 : array();
2666
2667 // Include every descendant, even below a forum with a non-countable status
2668 $parents = $forum_ids;
2669 while ( ! empty( $parents ) ) {
2670 $parent_id = array_shift( $parents );
2671
2672 foreach ( bbp_forum_query_all_subforum_ids( $parent_id ) as $forum_id ) {
2673 if ( ! in_array( $forum_id, $forum_ids, true ) ) {
2674 $forum_ids[] = $forum_id;
2675 $parents[] = $forum_id;
2676 }
2677 }
2678 }
2679
2680 // Normalize forum IDs after adding descendants
2681 $forum_ids = wp_parse_id_list( $forum_ids );
2682
2683 // Filter & return
2684 return (array) apply_filters( 'bbp_get_excluded_forum_ids', $forum_ids, $private, $hidden );
2685 }
2686
2687 /**
2688 * Return all direct child forum IDs for visibility checks.
2689 *
2690 * A non-countable forum can still have public descendants. Traversing only
2691 * countable children would expose those descendants in public queries.
2692 *
2693 * @since 2.6.19 bbPress (r7693)
2694 *
2695 * @param int $forum_id Parent forum ID.
2696 * @return int[] Child forum IDs.
2697 */
2698 function bbp_forum_query_all_subforum_ids( $forum_id ) {
2699 $forum_id = bbp_get_forum_id( $forum_id );
2700
2701 if ( empty( $forum_id ) ) {
2702 return array();
2703 }
2704
2705 $key = md5( serialize( array( $forum_id, bbp_get_forum_post_type(), 'all' ) ) );
2706 $cache_key = "bbp_child_ids:{$key}:" . wp_cache_get_last_changed( 'bbpress_posts' );
2707 $forum_ids = wp_cache_get( $cache_key, 'bbpress_posts' );
2708
2709 if ( false === $forum_ids ) {
2710 $bbp_db = bbp_db();
2711 $query = $bbp_db->prepare(
2712 "SELECT ID FROM {$bbp_db->posts} WHERE post_parent = %d AND post_type = %s ORDER BY ID DESC",
2713 $forum_id,
2714 bbp_get_forum_post_type()
2715 );
2716 $forum_ids = (array) $bbp_db->get_col( $query );
2717
2718 wp_cache_set( $cache_key, $forum_ids, 'bbpress_posts' );
2719 }
2720
2721 return wp_parse_id_list( $forum_ids );
2722 }
2723
2724 /**
2725 * Returns a meta_query that either includes or excludes hidden forum IDs
2726 * from a query.
2727 *
2728 * @since 2.0.0 bbPress (r3085)
2729 *
2730 * @param string Optional. The type of value to return. (string|array|meta_query)
2731 */
2732 function bbp_exclude_forum_ids( $type = 'string' ) {
2733
2734 // Setup arrays
2735 $forum_ids = array();
2736
2737 // Types
2738 $types = array(
2739 'array' => array(),
2740 'string' => '',
2741 'meta_query' => array()
2742 );
2743
2744 // Exclude for everyone but keymasters
2745 if ( ! bbp_is_user_keymaster() ) {
2746
2747 // Get forum IDs to exclude
2748 $forum_ids = bbp_get_excluded_forum_ids();
2749
2750 // Store return values in static types array
2751 if ( ! empty( $forum_ids ) ) {
2752
2753 // Setup types
2754 $types['array'] = $forum_ids;
2755 $types['string'] = implode( ',', $forum_ids );
2756 $types['meta_query'] = array(
2757 'key' => '_bbp_forum_id',
2758 'value' => $forum_ids,
2759 'type' => 'NUMERIC',
2760 'compare' => 'NOT IN'
2761 );
2762 }
2763 }
2764
2765 // There are forums that need to be excluded
2766 $retval = $types[ $type ];
2767
2768 // Filter & return
2769 return apply_filters( 'bbp_exclude_forum_ids', $retval, $forum_ids, $type );
2770 }
2771
2772 /**
2773 * Adjusts forum, topic, and reply queries to exclude items that might be
2774 * contained inside hidden or private forums that the user does not have the
2775 * capability to view.
2776 *
2777 * Doing it with an action allows us to trap all WP_Query's rather than needing
2778 * to hardcode this logic into each query. It also protects forum content for
2779 * plugins that might be doing their own queries.
2780 *
2781 * @since 2.3.0 bbPress (r4808)
2782 * @since 2.6.19 bbPress (r7692) Exclude restricted forum IDs from post__in queries.
2783 *
2784 * @param WP_Query $posts_query
2785 *
2786 * @return WP_Query
2787 */
2788 function bbp_pre_get_posts_normalize_forum_visibility( $posts_query = null ) {
2789
2790 // Bail if all forums are explicitly allowed
2791 if ( true === apply_filters( 'bbp_include_all_forums', false, $posts_query ) ) {
2792 return;
2793 }
2794
2795 // Bail if $posts_query is not an object or of incorrect class
2796 if ( ! is_object( $posts_query ) || ! is_a( $posts_query, 'WP_Query' ) ) {
2797 return;
2798 }
2799
2800 // Preserve the main post list query without exempting admin-ajax queries
2801 if ( is_admin() && ! wp_doing_ajax() && isset( $_REQUEST['post_status'] ) && isset( $GLOBALS['pagenow'] ) && ( 'edit.php' === $GLOBALS['pagenow'] ) && $posts_query->is_main_query() && current_user_can( 'moderate' ) ) {
2802 return;
2803 }
2804
2805 // Get the raw query post types.
2806 $post_type_query_var = $posts_query->get( 'post_type' );
2807 $post_types = bbp_get_string_array_values( $post_type_query_var );
2808
2809 // Resolve the post types included in "any" and implicit search queries.
2810 if ( ( 'any' === $post_type_query_var ) || ( empty( $post_types ) && $posts_query->is_search() ) ) {
2811 $post_types = get_post_types( array( 'exclude_from_search' => false ) );
2812 }
2813
2814 // Bail if no post types to normalize
2815 if ( empty( $post_types ) ) {
2816 return;
2817 }
2818
2819 // Compare queried post-types to supported post-types
2820 $bbp_post_types = array_intersect( $post_types, bbp_get_post_types() );
2821
2822 // Bail if no bbPress post type is being queried
2823 if ( empty( $bbp_post_types ) ) {
2824 return;
2825 }
2826
2827 // Bail if this query has already been normalized
2828 if ( $posts_query->get( '_bbp_forum_visibility_normalized' ) ) {
2829 return;
2830 }
2831
2832 // Mark this query as normalized
2833 $posts_query->set( '_bbp_forum_visibility_normalized', true );
2834
2835 // Separate non-bbPress post types from supported bbPress post types.
2836 $non_bbp_post_types = array_diff( $post_types, $bbp_post_types );
2837 $content_post_types = array_intersect(
2838 $bbp_post_types,
2839 array( bbp_get_topic_post_type(), bbp_get_reply_post_type() )
2840 );
2841
2842 /**
2843 * Clause filters do not run when a query suppresses filters. Remove bbPress
2844 * post types from mixed queries so protected content continues to fail closed
2845 * without changing the requested non-bbPress post types.
2846 */
2847 if ( ! empty( $non_bbp_post_types ) && $posts_query->get( 'suppress_filters' ) ) {
2848 $posts_query->set( 'post_type', array_values( $non_bbp_post_types ) );
2849 return;
2850 }
2851
2852 // Get forums to exclude.
2853 $forum_ids = bbp_exclude_forum_ids( 'array' );
2854
2855 // Forums
2856 if ( in_array( bbp_get_forum_post_type(), $post_types, true ) ) {
2857 $content_post_statuses = array();
2858
2859 /**
2860 * Preserve the statuses requested for topics and replies before adding
2861 * forum visibilities to a shared query. Without a post-type-aware status
2862 * clause, private and hidden content inherits the broader forum statuses.
2863 */
2864 if ( ! empty( $content_post_types ) ) {
2865 $content_post_statuses = bbp_get_string_array_values( $posts_query->get( 'post_status' ) );
2866
2867 if ( empty( $content_post_statuses ) ) {
2868 $content_post_statuses = bbp_get_public_topic_statuses();
2869 } elseif ( in_array( 'any', $content_post_statuses, true ) ) {
2870 // Match WordPress handling of an explicitly requested "any" status.
2871 $content_post_statuses = get_post_stati( array( 'exclude_from_search' => false ) );
2872 }
2873
2874 $posts_query->set( '_bbp_forum_visibility_post_types', $content_post_types );
2875 $posts_query->set( '_bbp_forum_visibility_post_statuses', $content_post_statuses );
2876 }
2877
2878 // Add all supported forum visibilities to bbPress-only queries.
2879 if ( empty( $non_bbp_post_types ) ) {
2880 $posts_query->set(
2881 'post_status',
2882 array_unique(
2883 array_merge(
2884 array_keys( bbp_get_forum_visibilities() ),
2885 $content_post_statuses
2886 )
2887 )
2888 );
2889 }
2890
2891 // Excluding some forums
2892 if ( ! empty( $forum_ids ) ) {
2893
2894 /**
2895 * WordPress ignores post__not_in when an explicit post ID is
2896 * queried. Replace an inaccessible forum ID with an impossible
2897 * inclusion so the restricted forum is not loaded into the query.
2898 */
2899 $forum_id = absint( $posts_query->get( 'p' ) );
2900 if ( ! empty( $forum_id ) && in_array( $forum_id, $forum_ids, true ) ) {
2901 $posts_query->set( 'p', 0 );
2902 $posts_query->set( 'post__in', array( 0 ) );
2903 }
2904
2905 // WordPress ignores post__not_in when post__in has IDs.
2906 $post_in = wp_parse_id_list( $posts_query->get( 'post__in' ) );
2907 if ( ! empty( $post_in ) ) {
2908 $post_in = array_values( array_diff( $post_in, $forum_ids ) );
2909 $posts_query->set( 'post__in', ! empty( $post_in ) ? $post_in : array( 0 ) );
2910 }
2911
2912 // Get any existing not-in queries
2913 $not_in = (array) $posts_query->get( 'post__not_in', array() );
2914
2915 // Add our not-in to existing
2916 $not_in = wp_parse_id_list( array_merge( $not_in, $forum_ids ) );
2917
2918 // Set the new not-in val
2919 $posts_query->set( 'post__not_in', $not_in );
2920 }
2921 }
2922
2923 // Bail if there are no forums to exclude.
2924 if ( empty( $forum_ids ) ) {
2925 return;
2926 }
2927
2928 /**
2929 * Mixed queries need a post-type-aware SQL clause. A meta query would use an
2930 * inner join and unintentionally remove non-bbPress posts that do not have
2931 * bbPress forum metadata.
2932 */
2933 if ( ! empty( $non_bbp_post_types ) ) {
2934 $posts_query->set( '_bbp_forum_visibility_post_types', $content_post_types );
2935 $posts_query->set( '_bbp_forum_visibility_forum_ids', $forum_ids );
2936 return;
2937 }
2938
2939 // Get forum meta query.
2940 $forum_meta_query = bbp_exclude_forum_ids( 'meta_query' );
2941
2942 // Excluding some forums
2943 if ( is_array( $forum_meta_query ) && ! empty( $forum_meta_query['key'] ) && ! empty( $forum_meta_query['value'] ) ) {
2944
2945 // Get any existing meta queries
2946 $meta_query = (array) $posts_query->get( 'meta_query', array() );
2947
2948 // Keep the exclusion required when existing clauses use OR
2949 if ( isset( $meta_query['relation'] ) && ( 'OR' === strtoupper( $meta_query['relation'] ) ) ) {
2950 $meta_query = array(
2951 'relation' => 'AND',
2952 $meta_query,
2953 $forum_meta_query
2954 );
2955 } else {
2956 $meta_query[] = $forum_meta_query;
2957 }
2958
2959 // Set the new meta_query val
2960 $posts_query->set( 'meta_query', $meta_query );
2961 }
2962 }
2963
2964 /**
2965 * Excludes protected bbPress content from mixed post-type queries.
2966 *
2967 * A normal meta query cannot scope its join to bbPress post types, causing
2968 * unrelated WordPress posts and third-party post types without `_bbp_forum_id`
2969 * metadata to be removed. This clause leaves forums and non-bbPress rows
2970 * untouched while requiring topic and reply rows to have forum metadata that
2971 * does not reference an excluded forum. Forums are excluded by ID before this
2972 * clause runs.
2973 *
2974 * Queries with `suppress_filters` enabled are handled conservatively in
2975 * bbp_pre_get_posts_normalize_forum_visibility(), because this filter will not
2976 * run for those queries.
2977 *
2978 * @since 2.6.15 bbPress (r7414)
2979 *
2980 * @param string $where SQL WHERE clause.
2981 * @param WP_Query $posts_query WordPress posts query.
2982 * @return string SQL WHERE clause.
2983 */
2984 function _bbp_forum_visibility_where( $where = '', $posts_query = null ) {
2985
2986 // Bail if $posts_query is not an object or of incorrect class
2987 if ( ! is_object( $posts_query ) || ! is_a( $posts_query, 'WP_Query' ) ) {
2988 return $where;
2989 }
2990
2991 // Get the query-specific visibility constraints.
2992 $post_types = bbp_get_string_array_values( $posts_query->get( '_bbp_forum_visibility_post_types' ) );
2993 $post_statuses = bbp_get_string_array_values( $posts_query->get( '_bbp_forum_visibility_post_statuses' ) );
2994 $forum_ids = wp_parse_id_list( $posts_query->get( '_bbp_forum_visibility_forum_ids' ) );
2995
2996 // Bail if this query does not need a post-type-aware visibility clause.
2997 if ( empty( $post_types ) || ( empty( $post_statuses ) && empty( $forum_ids ) ) ) {
2998 return $where;
2999 }
3000
3001 // Get the database object.
3002 $bbp_db = bbp_db();
3003
3004 // Prepare post-type placeholders.
3005 $post_type_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) );
3006
3007 // Restrict topic and reply statuses in mixed forum queries.
3008 if ( ! empty( $post_statuses ) ) {
3009 $post_status_placeholders = implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) );
3010 $status_values = array_merge( $post_types, $post_statuses );
3011 $where .= $bbp_db->prepare(
3012 " AND (
3013 {$bbp_db->posts}.post_type NOT IN ({$post_type_placeholders})
3014 OR {$bbp_db->posts}.post_status IN ({$post_status_placeholders})
3015 )",
3016 $status_values
3017 );
3018 }
3019
3020 // Bail if there are no forum IDs to exclude.
3021 if ( empty( $forum_ids ) ) {
3022 return $where;
3023 }
3024
3025 // Prepare forum-ID placeholders and values.
3026 $forum_id_placeholders = implode( ', ', array_fill( 0, count( $forum_ids ), '%d' ) );
3027 $values = array_merge( $post_types, $forum_ids );
3028
3029 /**
3030 * Require topic and reply rows to have forum metadata, and exclude rows with
3031 * forum metadata pointing at a forum the current user cannot view. All other
3032 * rows bypass these checks and retain their original query behavior.
3033 */
3034 $visibility_where = $bbp_db->prepare(
3035 " AND (
3036 {$bbp_db->posts}.post_type NOT IN ({$post_type_placeholders})
3037 OR (
3038 EXISTS (
3039 SELECT 1
3040 FROM {$bbp_db->postmeta} AS bbp_forum_visibility_exists
3041 WHERE bbp_forum_visibility_exists.post_id = {$bbp_db->posts}.ID
3042 AND bbp_forum_visibility_exists.meta_key = '_bbp_forum_id'
3043 )
3044 AND NOT EXISTS (
3045 SELECT 1
3046 FROM {$bbp_db->postmeta} AS bbp_forum_visibility_excluded
3047 WHERE bbp_forum_visibility_excluded.post_id = {$bbp_db->posts}.ID
3048 AND bbp_forum_visibility_excluded.meta_key = '_bbp_forum_id'
3049 AND CAST( bbp_forum_visibility_excluded.meta_value AS UNSIGNED ) IN ({$forum_id_placeholders})
3050 )
3051 )
3052 )",
3053 $values
3054 );
3055
3056 /**
3057 * Filters the forum visibility SQL appended to mixed post-type queries.
3058 *
3059 * @since 2.6.15 bbPress (r7414)
3060 *
3061 * @param string $visibility_where Forum visibility SQL clause.
3062 * @param WP_Query $posts_query WordPress posts query.
3063 * @param string[] $post_types bbPress post types protected by the clause.
3064 * @param int[] $forum_ids Forum IDs excluded from the query.
3065 */
3066 $visibility_where = apply_filters(
3067 'bbp_forum_visibility_posts_where',
3068 $visibility_where,
3069 $posts_query,
3070 $post_types,
3071 $forum_ids
3072 );
3073
3074 // Append the visibility clause.
3075 return $where . $visibility_where;
3076 }
3077
3078 /**
3079 * Returns the forum's topic ids
3080 *
3081 * Only topics with published and closed statuses are returned
3082 *
3083 * @since 2.0.0 bbPress (r2895)
3084 *
3085 * @param int $forum_id Forum id
3086 */
3087 function bbp_forum_query_topic_ids( $forum_id ) {
3088 $topic_ids = bbp_get_public_child_ids( $forum_id, bbp_get_topic_post_type() );
3089
3090 // Filter & return
3091 return (array) apply_filters( 'bbp_forum_query_topic_ids', $topic_ids, $forum_id );
3092 }
3093
3094 /**
3095 * Returns the forum's subforum ids.
3096 *
3097 * Only forums with countable statuses are returned.
3098 *
3099 * @since 2.0.0 bbPress (r2895)
3100 * @since 2.6.17 bbPress (r7468) Restrict results to countable statuses instead of using
3101 * bbp_get_all_child_ids().
3102 *
3103 * @param int $forum_id Forum id.
3104 */
3105 function bbp_forum_query_subforum_ids( $forum_id ) {
3106 $forum_id = bbp_get_forum_id( $forum_id );
3107 $statuses = bbp_get_countable_forum_statuses();
3108 $subforum_ids = array();
3109
3110 // Query and cache countable subforums. The public child-ID helper excludes
3111 // private and hidden forums, while the all-child helper includes trash, so
3112 // neither existing helper represents the statuses counted here.
3113 if ( ! empty( $forum_id ) && ! empty( $statuses ) ) {
3114 $key = md5(
3115 serialize(
3116 array(
3117 'parent_id' => $forum_id,
3118 'post_type' => bbp_get_forum_post_type(),
3119 'post_status' => $statuses
3120 )
3121 )
3122 );
3123 $cache_key = "bbp_child_ids:{$key}:" . wp_cache_get_last_changed( 'bbpress_posts' );
3124 $subforum_ids = wp_cache_get( $cache_key, 'bbpress_posts' );
3125
3126 if ( false === $subforum_ids ) {
3127 $bbp_db = bbp_db();
3128 $placeholders = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) );
3129 $query = $bbp_db->prepare(
3130 "SELECT ID FROM {$bbp_db->posts} WHERE post_parent = %d AND post_type = %s AND post_status IN ({$placeholders}) ORDER BY ID DESC",
3131 array_merge( array( $forum_id, bbp_get_forum_post_type() ), $statuses )
3132 );
3133 $subforum_ids = (array) $bbp_db->get_col( $query );
3134
3135 wp_cache_set( $cache_key, $subforum_ids, 'bbpress_posts' );
3136 }
3137 }
3138
3139 // Filter & return
3140 return (array) apply_filters( 'bbp_forum_query_subforum_ids', $subforum_ids, $forum_id );
3141 }
3142
3143 /**
3144 * Returns the forum's last reply id
3145 *
3146 * @since 2.0.0 bbPress (r2895)
3147 * @since 2.6.0 bbPress (r5954) Replace direct queries with WP_Query() objects
3148 *
3149 * @param int $forum_id Forum id.
3150 * @param int $topic_ids Optional. Topic ids.
3151 */
3152 function bbp_forum_query_last_reply_id( $forum_id = 0, $topic_ids = 0 ) {
3153
3154 // Validate forum
3155 $forum_id = bbp_get_forum_id( $forum_id );
3156
3157 // Get topic ID's if none were passed
3158 if ( empty( $topic_ids ) ) {
3159 $topic_ids = bbp_forum_query_topic_ids( $forum_id );
3160 }
3161
3162 $query = new WP_Query(
3163 array(
3164 'fields' => 'ids',
3165 'suppress_filters' => true,
3166 'post_parent__in' => $topic_ids,
3167 'post_status' => bbp_get_public_status_id(),
3168 'post_type' => bbp_get_reply_post_type(),
3169 'posts_per_page' => 1,
3170 'orderby' => array(
3171 'post_date' => 'DESC',
3172 'ID' => 'DESC'
3173 ),
3174
3175 // Performance
3176 'update_post_term_cache' => false,
3177 'update_post_meta_cache' => false,
3178 'ignore_sticky_posts' => true,
3179 'no_found_rows' => true
3180 )
3181 );
3182
3183 $reply_id = array_shift( $query->posts );
3184
3185 unset( $query );
3186
3187 // Filter & return
3188 return (int) apply_filters( 'bbp_forum_query_last_reply_id', $reply_id, $forum_id );
3189 }
3190
3191 /** Listeners *****************************************************************/
3192
3193 /**
3194 * Check if it's a hidden forum or a topic or reply of a hidden forum and if
3195 * the user can't view it, then sets a 404
3196 *
3197 * @since 2.0.0 bbPress (r3430)
3198 */
3199 function bbp_forum_enforce_hidden() {
3200
3201 // Bail if not viewing a single item or if user has caps
3202 if ( ! is_singular() || bbp_is_user_keymaster() || current_user_can( 'read_hidden_forums' ) ) {
3203 return;
3204 }
3205
3206 // Define local variables
3207 $forum_id = 0;
3208 $wp_query = bbp_get_wp_query();
3209 $post_id = ! empty( $wp_query->post->ID )
3210 ? $wp_query->post->ID
3211 : 0;
3212
3213 // Check post type
3214 switch ( $wp_query->get( 'post_type' ) ) {
3215
3216 // Forum
3217 case bbp_get_forum_post_type() :
3218 $forum_id = bbp_get_forum_id( $post_id );
3219 break;
3220
3221 // Topic
3222 case bbp_get_topic_post_type() :
3223 $forum_id = bbp_get_topic_forum_id( $post_id );
3224 break;
3225
3226 // Reply
3227 case bbp_get_reply_post_type() :
3228 $forum_id = bbp_get_reply_forum_id( $post_id );
3229 break;
3230 }
3231
3232 // If forum is explicitly hidden and user not capable, set 404
3233 if ( ! empty( $forum_id ) && bbp_is_forum_hidden( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
3234 bbp_set_404( $wp_query );
3235 }
3236 }
3237
3238 /**
3239 * Check if it's a private forum or a topic or reply of a private forum and if
3240 * the user can't view it, then sets a 404
3241 *
3242 * @since 2.0.0 bbPress (r3430)
3243 */
3244 function bbp_forum_enforce_private() {
3245
3246 // Bail if not viewing a single item or if user has caps
3247 if ( ! is_singular() || bbp_is_user_keymaster() || current_user_can( 'read_private_forums' ) ) {
3248 return;
3249 }
3250
3251 // Define local variables
3252 $forum_id = 0;
3253 $wp_query = bbp_get_wp_query();
3254 $post_id = ! empty( $wp_query->post->ID )
3255 ? $wp_query->post->ID
3256 : 0;
3257
3258 // Check post type
3259 switch ( $wp_query->get( 'post_type' ) ) {
3260
3261 // Forum
3262 case bbp_get_forum_post_type() :
3263 $forum_id = bbp_get_forum_id( $post_id );
3264 break;
3265
3266 // Topic
3267 case bbp_get_topic_post_type() :
3268 $forum_id = bbp_get_topic_forum_id( $post_id );
3269 break;
3270
3271 // Reply
3272 case bbp_get_reply_post_type() :
3273 $forum_id = bbp_get_reply_forum_id( $post_id );
3274 break;
3275 }
3276
3277 // If forum is explicitly hidden and user not capable, set 404
3278 if ( ! empty( $forum_id ) && bbp_is_forum_private( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
3279 bbp_set_404( $wp_query );
3280 }
3281 }
3282
3283 /** Permissions ***************************************************************/
3284
3285 /**
3286 * Redirect if unauthorized user is attempting to edit a forum
3287 *
3288 * @since 2.1.0 bbPress (r3607)
3289 */
3290 function bbp_check_forum_edit() {
3291
3292 // Bail if not editing a topic
3293 if ( ! bbp_is_forum_edit() ) {
3294 return;
3295 }
3296
3297 // User cannot edit topic, so redirect back to reply
3298 if ( ! current_user_can( 'edit_forum', bbp_get_forum_id() ) ) {
3299 bbp_redirect( bbp_get_forum_permalink() );
3300 }
3301 }
3302
3303 /**
3304 * Delete all topics (and their replies) for a specific forum ID
3305 *
3306 * @since 2.1.0 bbPress (r3669)
3307 *
3308 * @param int $forum_id
3309 * @return If forum is not valid
3310 */
3311 function bbp_delete_forum_topics( $forum_id = 0 ) {
3312
3313 // Validate forum ID
3314 $forum_id = bbp_get_forum_id( $forum_id );
3315 if ( empty( $forum_id ) ) {
3316 return;
3317 }
3318
3319 // Forum is being permanently deleted, so its content has go too
3320 // Note that we get all post statuses here
3321 $topics = new WP_Query(
3322 array(
3323 'fields' => 'id=>parent',
3324 'post_type' => bbp_get_topic_post_type(),
3325 'post_parent' => $forum_id,
3326 'post_status' => array_keys( get_post_stati() ),
3327 'posts_per_page' => -1,
3328
3329 // Performance
3330 'nopaging' => true,
3331 'suppress_filters' => true,
3332 'update_post_term_cache' => false,
3333 'update_post_meta_cache' => false,
3334 'ignore_sticky_posts' => true,
3335 'no_found_rows' => true
3336 )
3337 );
3338
3339 // Loop through and delete child topics. Topic replies will get deleted by
3340 // the bbp_delete_topic() action.
3341 if ( ! empty( $topics->posts ) ) {
3342 foreach ( $topics->posts as $topic ) {
3343 wp_delete_post( $topic->ID, true );
3344 }
3345
3346 // Reset the $post global
3347 wp_reset_postdata();
3348 }
3349
3350 // Cleanup
3351 unset( $topics );
3352 }
3353
3354 /**
3355 * Trash all topics inside a forum
3356 *
3357 * @since 2.1.0 bbPress (r3669)
3358 *
3359 * @param int $forum_id
3360 * @return If forum is not valid
3361 */
3362 function bbp_trash_forum_topics( $forum_id = 0 ) {
3363
3364 // Validate forum ID
3365 $forum_id = bbp_get_forum_id( $forum_id );
3366 if ( empty( $forum_id ) ) {
3367 return;
3368 }
3369
3370 // Allowed post statuses to pre-trash
3371 $post_stati = array(
3372 bbp_get_public_status_id(),
3373 bbp_get_closed_status_id(),
3374 bbp_get_pending_status_id()
3375 );
3376
3377 // Forum is being trashed, so its topics (and replies) are trashed too
3378 $topics = new WP_Query(
3379 array(
3380 'fields' => 'id=>parent',
3381 'post_type' => bbp_get_topic_post_type(),
3382 'post_parent' => $forum_id,
3383 'post_status' => $post_stati,
3384 'posts_per_page' => -1,
3385
3386 // Performance
3387 'nopaging' => true,
3388 'suppress_filters' => true,
3389 'update_post_term_cache' => false,
3390 'update_post_meta_cache' => false,
3391 'ignore_sticky_posts' => true,
3392 'no_found_rows' => true
3393 )
3394 );
3395
3396 // Loop through and trash child topics. Topic replies will get trashed by
3397 // the bbp_trash_topic() action.
3398 if ( ! empty( $topics->posts ) ) {
3399
3400 // Prevent debug notices
3401 $pre_trashed_topics = array();
3402
3403 // Loop through topics, trash them, and add them to array
3404 foreach ( $topics->posts as $topic ) {
3405 wp_trash_post( $topic->ID, true );
3406 $pre_trashed_topics[] = $topic->ID;
3407 }
3408
3409 // Set a post_meta entry of the topics that were trashed by this action.
3410 // This is so we can possibly untrash them, without untrashing topics
3411 // that were purposefully trashed before.
3412 update_post_meta( $forum_id, '_bbp_pre_trashed_topics', $pre_trashed_topics );
3413
3414 // Reset the $post global
3415 wp_reset_postdata();
3416 }
3417
3418 // Cleanup
3419 unset( $topics );
3420 }
3421
3422 /**
3423 * Untrash all topics inside a forum
3424 *
3425 * @since 2.1.0 bbPress (r3669)
3426 *
3427 * @param int $forum_id
3428 * @return If forum is not valid
3429 */
3430 function bbp_untrash_forum_topics( $forum_id = 0 ) {
3431
3432 // Validate forum ID
3433 $forum_id = bbp_get_forum_id( $forum_id );
3434
3435 if ( empty( $forum_id ) ) {
3436 return;
3437 }
3438
3439 // Get the topics that were not previously trashed
3440 $pre_trashed_topics = get_post_meta( $forum_id, '_bbp_pre_trashed_topics', true );
3441
3442 // There are topics to untrash
3443 if ( ! empty( $pre_trashed_topics ) ) {
3444
3445 // Maybe reverse the trashed topics array
3446 if ( is_array( $pre_trashed_topics ) ) {
3447 $pre_trashed_topics = array_reverse( $pre_trashed_topics );
3448 }
3449
3450 // Loop through topics
3451 foreach ( (array) $pre_trashed_topics as $topic ) {
3452 wp_untrash_post( $topic );
3453 }
3454 }
3455 }
3456
3457 /** Before Delete/Trash/Untrash ***********************************************/
3458
3459 /**
3460 * Called before deleting a forum.
3461 *
3462 * This function is supplemental to the actual forum deletion which is
3463 * handled by WordPress core API functions. It is used to clean up after
3464 * a forum that is being deleted.
3465 *
3466 * @since 2.1.0 bbPress (r3669)
3467 */
3468 function bbp_delete_forum( $forum_id = 0 ) {
3469 $forum_id = bbp_get_forum_id( $forum_id );
3470
3471 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
3472 return false;
3473 }
3474
3475 do_action( 'bbp_delete_forum', $forum_id );
3476 }
3477
3478 /**
3479 * Called before trashing a forum
3480 *
3481 * This function is supplemental to the actual forum being trashed which is
3482 * handled by WordPress core API functions. It is used to clean up after
3483 * a forum that is being trashed.
3484 *
3485 * @since 2.1.0 bbPress (r3669)
3486 */
3487 function bbp_trash_forum( $forum_id = 0 ) {
3488 $forum_id = bbp_get_forum_id( $forum_id );
3489
3490 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
3491 return false;
3492 }
3493
3494 do_action( 'bbp_trash_forum', $forum_id );
3495 }
3496
3497 /**
3498 * Called before untrashing a forum
3499 *
3500 * @since 2.1.0 bbPress (r3669)
3501 */
3502 function bbp_untrash_forum( $forum_id = 0 ) {
3503 $forum_id = bbp_get_forum_id( $forum_id );
3504
3505 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
3506 return false;
3507 }
3508
3509 do_action( 'bbp_untrash_forum', $forum_id );
3510 }
3511
3512 /** After Delete/Trash/Untrash ************************************************/
3513
3514 /**
3515 * Called after deleting a forum
3516 *
3517 * Try not to use this action. All meta & taxonomy terms have already been
3518 * deleted, making them impossible to use.
3519 *
3520 * @since 2.1.0 bbPress (r3669)
3521 * @since 2.6.0 bbPress (r6529) Not recommend for usage
3522 * @since 2.6.17 bbPress (r7454) Added the `$forum` parameter and passed it to the action.
3523 *
3524 * @param int $forum_id Forum ID.
3525 * @param WP_Post|bool $forum Optional. Deleted forum post object.
3526 * @return false|null False if the post is not a forum, otherwise null.
3527 */
3528 function bbp_deleted_forum( $forum_id = 0, $forum = false ) {
3529 $forum_id = bbp_get_forum_id( $forum_id );
3530 $forum = ( $forum instanceof WP_Post )
3531 ? $forum
3532 : get_post( $forum_id );
3533
3534 if ( empty( $forum_id ) || ! $forum || ( bbp_get_forum_post_type() !== $forum->post_type ) ) {
3535 return false;
3536 }
3537
3538 do_action( 'bbp_deleted_forum', $forum_id, $forum );
3539 }
3540
3541 /**
3542 * Called after trashing a forum
3543 *
3544 * @since 2.1.0 bbPress (r3669)
3545 */
3546 function bbp_trashed_forum( $forum_id = 0 ) {
3547 $forum_id = bbp_get_forum_id( $forum_id );
3548
3549 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
3550 return false;
3551 }
3552
3553 do_action( 'bbp_trashed_forum', $forum_id );
3554 }
3555
3556 /**
3557 * Called after untrashing a forum
3558 *
3559 * @since 2.1.0 bbPress (r3669)
3560 */
3561 function bbp_untrashed_forum( $forum_id = 0 ) {
3562 $forum_id = bbp_get_forum_id( $forum_id );
3563
3564 if ( empty( $forum_id ) || ! bbp_is_forum( $forum_id ) ) {
3565 return false;
3566 }
3567
3568 do_action( 'bbp_untrashed_forum', $forum_id );
3569 }
3570