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

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

4,087 lines 121.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Topic 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 topic to function properly.
18 *
19 * @since 2.0.0 bbPress (r2970)
20 *
21 * @param array $topic_data Forum post data
22 * @param array $topic_meta Forum meta data
23 */
24 function bbp_insert_topic( $topic_data = array(), $topic_meta = array() ) {
25
26 // Parse arguments against default values
27 $topic_data = bbp_parse_args(
28 $topic_data,
29 array(
30 'post_parent' => 0, // forum ID
31 'post_status' => bbp_get_public_status_id(),
32 'post_type' => bbp_get_topic_post_type(),
33 'post_author' => bbp_get_current_user_id(),
34 'post_password' => '',
35 'post_content' => '',
36 'post_title' => '',
37 'comment_status' => 'closed',
38 'menu_order' => 0
39 ),
40 'insert_topic'
41 );
42
43 // Insert topic
44 $topic_id = wp_insert_post( $topic_data, false );
45
46 // Bail if no topic was added
47 if ( empty( $topic_id ) ) {
48 return false;
49 }
50
51 // Parse arguments against default values
52 $topic_meta = bbp_parse_args(
53 $topic_meta,
54 array(
55 'author_ip' => bbp_current_author_ip(),
56 'forum_id' => 0,
57 'topic_id' => $topic_id,
58 'voice_count' => 1,
59 'reply_count' => 0,
60 'reply_count_hidden' => 0,
61 'last_reply_id' => 0,
62 'last_active_id' => $topic_id,
63 'last_active_time' => get_post_field( 'post_date', $topic_id, 'db' )
64 ),
65 'insert_topic_meta'
66 );
67
68 // Insert topic meta
69 foreach ( $topic_meta as $meta_key => $meta_value ) {
70
71 // Prefix if not prefixed
72 if ( '_bbp_' !== substr( $meta_key, 0, 5 ) ) {
73 $meta_key = '_bbp_' . $meta_key;
74 }
75
76 // Update the meta
77 update_post_meta( $topic_id, $meta_key, $meta_value );
78 }
79
80 // Update the topic and hierarchy
81 bbp_update_topic( $topic_id, $topic_meta['forum_id'], array(), $topic_data['post_author'], false );
82
83 /**
84 * Fires after topic has been inserted via `bbp_insert_topic`.
85 *
86 * @since 2.6.0 bbPress (r6036)
87 *
88 * @param int $topic_id The topic id.
89 * @param int $topic_meta['forum_id'] The topic forum meta.
90 */
91 do_action( 'bbp_insert_topic', (int) $topic_id, (int) $topic_meta['forum_id'] );
92
93 // Return topic_id
94 return $topic_id;
95 }
96
97 /** Post Form Handlers ********************************************************/
98
99 /**
100 * Handles the front end topic submission
101 *
102 * @param string $action The requested action to compare this function to
103 */
104 function bbp_new_topic_handler( $action = '' ) {
105
106 // Bail if action is not bbp-new-topic
107 if ( 'bbp-new-topic' !== $action ) {
108 return;
109 }
110
111 // Nonce check
112 if ( ! bbp_verify_nonce_request( 'bbp-new-topic' ) ) {
113 bbp_add_error( 'bbp_new_topic_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
114 return;
115 }
116
117 // Define local variable(s)
118 $view_all = false;
119 $forum_id = $topic_author = 0;
120 $topic_title = $topic_content = '';
121 $anonymous_data = array();
122 $terms = array( bbp_get_topic_tag_tax_id() => array() );
123
124 /** Topic Author **********************************************************/
125
126 // User is anonymous
127 if ( bbp_is_anonymous() ) {
128
129 // Filter anonymous data (variable is used later)
130 $anonymous_data = bbp_filter_anonymous_post_data();
131
132 // Anonymous data checks out, so set cookies, etc...
133 bbp_set_current_anonymous_user_data( $anonymous_data );
134
135 // User is logged in
136 } else {
137
138 // User cannot create topics
139 if ( ! current_user_can( 'publish_topics' ) ) {
140 bbp_add_error( 'bbp_topic_permission', __( '<strong>Error</strong>: You do not have permission to create new topics.', 'bbpress' ) );
141 return;
142 }
143
144 // Topic author is current user
145 $topic_author = bbp_get_current_user_id();
146 }
147
148 // Remove kses filters from title and content for capable users and if the nonce is verified
149 if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_topic'] ) && wp_create_nonce( 'bbp-unfiltered-html-topic_new' ) === $_POST['_bbp_unfiltered_html_topic'] ) {
150 remove_filter( 'bbp_new_topic_pre_title', 'wp_filter_kses' );
151 remove_filter( 'bbp_new_topic_pre_content', 'bbp_encode_bad', 10 );
152 remove_filter( 'bbp_new_topic_pre_content', 'bbp_filter_kses', 30 );
153 }
154
155 /** Topic Title ***********************************************************/
156
157 if ( ! empty( $_POST['bbp_topic_title'] ) ) {
158 $topic_title = sanitize_text_field( $_POST['bbp_topic_title'] );
159 }
160
161 // Filter and sanitize
162 $topic_title = apply_filters( 'bbp_new_topic_pre_title', $topic_title );
163
164 // No topic title
165 if ( empty( $topic_title ) ) {
166 bbp_add_error( 'bbp_topic_title', __( '<strong>Error</strong>: Your topic needs a title.', 'bbpress' ) );
167 }
168
169 // Title too long
170 if ( bbp_is_title_too_long( $topic_title ) ) {
171 bbp_add_error( 'bbp_topic_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
172 }
173
174 /** Topic Content *********************************************************/
175
176 if ( ! empty( $_POST['bbp_topic_content'] ) ) {
177 $topic_content = $_POST['bbp_topic_content'];
178 }
179
180 // Filter and sanitize
181 $topic_content = apply_filters( 'bbp_new_topic_pre_content', $topic_content );
182
183 // No topic content
184 if ( empty( $topic_content ) ) {
185 bbp_add_error( 'bbp_topic_content', __( '<strong>Error</strong>: Your topic cannot be empty.', 'bbpress' ) );
186 }
187
188 /** Topic Forum ***********************************************************/
189
190 // Error check the POST'ed topic id
191 if ( isset( $_POST['bbp_forum_id'] ) ) {
192
193 // Empty Forum id was passed
194 if ( empty( $_POST['bbp_forum_id'] ) ) {
195 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID is missing.', 'bbpress' ) );
196
197 // Forum id is not a number
198 } elseif ( ! is_numeric( $_POST['bbp_forum_id'] ) ) {
199 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID must be a number.', 'bbpress' ) );
200
201 // Forum id might be valid
202 } else {
203
204 // Get the forum id
205 $posted_forum_id = intval( $_POST['bbp_forum_id'] );
206
207 // Forum id is empty
208 if ( 0 === $posted_forum_id ) {
209 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID is missing.', 'bbpress' ) );
210
211 // Forum id is a negative number
212 } elseif ( 0 > $posted_forum_id ) {
213 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID cannot be a negative number.', 'bbpress' ) );
214
215 // Forum does not exist
216 } elseif ( ! bbp_get_forum( $posted_forum_id ) ) {
217 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum does not exist.', 'bbpress' ) );
218
219 // Use the POST'ed forum id
220 } else {
221 $forum_id = $posted_forum_id;
222 }
223 }
224 }
225
226 // Forum exists
227 if ( ! empty( $forum_id ) ) {
228
229 // Forum is a category
230 if ( bbp_is_forum_category( $forum_id ) ) {
231 bbp_add_error( 'bbp_new_topic_forum_category', __( '<strong>Error</strong>: This forum is a category. No topics can be created in this forum.', 'bbpress' ) );
232
233 // Forum is not a category
234 } else {
235
236 // Forum is closed and user cannot access
237 if ( bbp_is_forum_closed( $forum_id ) && ! current_user_can( 'edit_forum', $forum_id ) ) {
238 bbp_add_error( 'bbp_new_topic_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new topics.', 'bbpress' ) );
239 }
240
241 // Forum is private and user cannot access
242 if ( bbp_is_forum_private( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
243 bbp_add_error( 'bbp_new_topic_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new topics in it.', 'bbpress' ) );
244
245 // Forum is hidden and user cannot access
246 } elseif ( bbp_is_forum_hidden( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
247 bbp_add_error( 'bbp_new_topic_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new topics in it.', 'bbpress' ) );
248 }
249 }
250 }
251
252 /** Topic Flooding ********************************************************/
253
254 if ( ! bbp_check_for_flood( $anonymous_data, $topic_author ) ) {
255 bbp_add_error( 'bbp_topic_flood', __( '<strong>Error</strong>: Slow down; you move too fast.', 'bbpress' ) );
256 }
257
258 /** Topic Duplicate *******************************************************/
259
260 $dupe_args = array(
261 'post_type' => bbp_get_topic_post_type(),
262 'post_author' => $topic_author,
263 'post_content' => $topic_content,
264 'post_parent' => $forum_id,
265 'anonymous_data' => $anonymous_data
266 );
267
268 if ( ! bbp_check_for_duplicate( $dupe_args ) ) {
269 bbp_add_error( 'bbp_topic_duplicate', __( '<strong>Error</strong>: Duplicate topic detected; it looks as though you&#8217;ve already said that.', 'bbpress' ) );
270 }
271
272 /** Topic Bad Words *******************************************************/
273
274 if ( ! bbp_check_for_moderation( $anonymous_data, $topic_author, $topic_title, $topic_content, true ) ) {
275 bbp_add_error( 'bbp_topic_moderation', __( '<strong>Error</strong>: Your topic cannot be created at this time.', 'bbpress' ) );
276 }
277
278 /** Topic Status **********************************************************/
279
280 // Get available topic statuses
281 $topic_statuses = bbp_get_topic_statuses();
282
283 // Default to published
284 $topic_status = bbp_get_public_status_id();
285
286 // Maybe force into pending
287 if ( ! bbp_check_for_moderation( $anonymous_data, $topic_author, $topic_title, $topic_content ) ) {
288 $topic_status = bbp_get_pending_status_id();
289
290 // Check for possible posted topic status
291 } elseif ( ! empty( $_POST['bbp_topic_status'] ) && in_array( $_POST['bbp_topic_status'], array_keys( $topic_statuses ), true ) ) {
292
293 // Allow capable users to explicitly override the status
294 if ( current_user_can( 'moderate', $forum_id ) ) {
295 $topic_status = sanitize_key( $_POST['bbp_topic_status'] );
296
297 // Not capable
298 } else {
299 bbp_add_error( 'bbp_new_topic_status', __( '<strong>Error</strong>: You do not have permission to do that.', 'bbpress' ) );
300 }
301 }
302
303 /** Topic Tags ************************************************************/
304
305 if ( bbp_allow_topic_tags() && ! empty( $_POST['bbp_topic_tags'] ) ) {
306
307 // Escape tag input
308 $terms = sanitize_text_field( $_POST['bbp_topic_tags'] );
309
310 // Explode by comma
311 if ( strstr( $terms, ',' ) ) {
312 $terms = explode( ',', $terms );
313 }
314
315 // Add topic tag ID as main key
316 $terms = array( bbp_get_topic_tag_tax_id() => $terms );
317 }
318
319 /** Additional Actions (Before Save) **************************************/
320
321 do_action( 'bbp_new_topic_pre_extras', $forum_id );
322
323 // Bail if errors
324 if ( bbp_has_errors() ) {
325 return;
326 }
327
328 /** No Errors *************************************************************/
329
330 // Add the content of the form to $topic_data as an array.
331 // Just in time manipulation of topic data before being created
332 $topic_data = apply_filters(
333 'bbp_new_topic_pre_insert',
334 array(
335 'post_author' => $topic_author,
336 'post_title' => $topic_title,
337 'post_content' => $topic_content,
338 'post_status' => $topic_status,
339 'post_parent' => $forum_id,
340 'post_type' => bbp_get_topic_post_type(),
341 'tax_input' => $terms,
342 'comment_status' => 'closed'
343 )
344 );
345
346 // Insert topic
347 $topic_id = wp_insert_post( $topic_data, true );
348
349 /** No Errors *************************************************************/
350
351 if ( ! empty( $topic_id ) && ! is_wp_error( $topic_id ) ) {
352
353 /** Close Check *******************************************************/
354
355 // If the topic is closed, close it properly
356 if ( ( get_post_field( 'post_status', $topic_id ) === bbp_get_closed_status_id() ) || bbp_get_closed_status_id() === ( $topic_data['post_status'] ) ) {
357
358 // Close the topic
359 bbp_close_topic( $topic_id );
360 }
361
362 /** Trash Check *******************************************************/
363
364 // If the forum is trash, or the topic_status is switched to
365 // trash, trash the topic properly
366 if ( ( get_post_field( 'post_status', $forum_id ) === bbp_get_trash_status_id() ) || bbp_get_trash_status_id() === ( $topic_data['post_status'] ) ) {
367
368 // Trash the topic
369 wp_trash_post( $topic_id );
370
371 // Force view=all
372 $view_all = true;
373 }
374
375 /** Spam Check ********************************************************/
376
377 // If the topic is spam, officially spam this topic
378 if ( bbp_get_spam_status_id() === $topic_data['post_status'] ) {
379 add_post_meta( $topic_id, '_bbp_spam_meta_status', bbp_get_public_status_id() );
380
381 // Force view=all
382 $view_all = true;
383 }
384
385 /** Update counts, etc... *********************************************/
386
387 do_action( 'bbp_new_topic', $topic_id, $forum_id, $anonymous_data, $topic_data['post_author'] );
388
389 /** Additional Actions (After Save) ***********************************/
390
391 do_action( 'bbp_new_topic_post_extras', $topic_id );
392
393 /** Redirect **********************************************************/
394
395 // Redirect to
396 $redirect_to = bbp_get_redirect_to();
397
398 // Get the topic URL
399 $redirect_url = bbp_get_topic_permalink( $topic_id, $redirect_to );
400
401 // Add view all?
402 if ( bbp_get_view_all() || ! empty( $view_all ) ) {
403
404 // User can moderate, so redirect to topic with view all set
405 if ( current_user_can( 'moderate', $topic_id ) ) {
406 $redirect_url = bbp_add_view_all( $redirect_url );
407
408 // User cannot moderate, so redirect to forum
409 } else {
410 $redirect_url = bbp_get_forum_permalink( $forum_id );
411 }
412 }
413
414 // Allow to be filtered
415 $redirect_url = apply_filters( 'bbp_new_topic_redirect_to', $redirect_url, $redirect_to, $topic_id );
416
417 /** Successful Save ***************************************************/
418
419 // Redirect back to new topic
420 bbp_redirect( $redirect_url );
421
422 /** Errors ****************************************************************/
423
424 // WP_Error
425 } elseif ( is_wp_error( $topic_id ) ) {
426 /* translators: %s: Error message(s) */
427 bbp_add_error( 'bbp_topic_error', sprintf( __( '<strong>Error</strong>: The following problem(s) occurred: %s', 'bbpress' ), $topic_id->get_error_message() ) );
428
429 // Generic error
430 } else {
431 bbp_add_error( 'bbp_topic_error', __( '<strong>Error</strong>: The topic was not created.', 'bbpress' ) );
432 }
433 }
434
435 /**
436 * Handles the front end edit topic submission
437 *
438 * @since 2.6.19 bbPress (r7680) Enforce the edit lock on submissions.
439 *
440 * @param string $action The requested action to compare this function to
441 */
442 function bbp_edit_topic_handler( $action = '' ) {
443
444 // Bail if action is not bbp-edit-topic
445 if ( 'bbp-edit-topic' !== $action ) {
446 return;
447 }
448
449 // Define local variable(s)
450 $revisions_removed = false;
451 $topic = $topic_id = $topic_author = $forum_id = 0;
452 $topic_title = $topic_content = $topic_edit_reason = '';
453 $anonymous_data = array();
454
455 /** Topic *****************************************************************/
456
457 // Topic id was not passed
458 if ( empty( $_POST['bbp_topic_id'] ) ) {
459 bbp_add_error( 'bbp_edit_topic_id', __( '<strong>Error</strong>: Topic ID not found.', 'bbpress' ) );
460 return;
461
462 // Topic id was passed
463 } elseif ( is_numeric( $_POST['bbp_topic_id'] ) ) {
464 $topic_id = (int) $_POST['bbp_topic_id'];
465 $topic = bbp_get_topic( $topic_id );
466 }
467
468 // Topic does not exist
469 if ( empty( $topic ) ) {
470 bbp_add_error( 'bbp_edit_topic_not_found', __( '<strong>Error</strong>: The topic you want to edit was not found.', 'bbpress' ) );
471 return;
472
473 // User cannot edit this topic
474 } elseif ( ! current_user_can( 'edit_topic', $topic_id ) ) {
475 bbp_add_error( 'bbp_edit_topic_permission', __( '<strong>Error</strong>: You do not have permission to edit that topic.', 'bbpress' ) );
476 return;
477
478 // Authors cannot bypass the edit lock by submitting from another page
479 } elseif ( ( bbp_get_current_user_id() === (int) $topic->post_author ) && ! current_user_can( 'moderate', $topic_id ) && bbp_past_edit_lock( $topic->post_date_gmt ) ) {
480 bbp_add_error( 'bbp_edit_topic_permission', __( '<strong>Error</strong>: You do not have permission to edit that topic.', 'bbpress' ) );
481 return;
482
483 // It is an anonymous post
484 } elseif ( bbp_is_topic_anonymous( $topic_id ) ) {
485
486 // Filter anonymous data
487 $anonymous_data = bbp_filter_anonymous_post_data();
488
489 // Set topic author
490 } else {
491 $topic_author = bbp_get_topic_author_id( $topic_id );
492 }
493
494 // Nonce check
495 if ( ! bbp_verify_nonce_request( 'bbp-edit-topic_' . $topic_id ) ) {
496 bbp_add_error( 'bbp_edit_topic_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
497 return;
498 }
499
500 // Remove kses filters from title and content for capable users and if the nonce is verified
501 if ( current_user_can( 'unfiltered_html' ) && ! empty( $_POST['_bbp_unfiltered_html_topic'] ) && ( wp_create_nonce( 'bbp-unfiltered-html-topic_' . $topic_id ) === $_POST['_bbp_unfiltered_html_topic'] ) ) {
502 remove_filter( 'bbp_edit_topic_pre_title', 'wp_filter_kses' );
503 remove_filter( 'bbp_edit_topic_pre_content', 'bbp_encode_bad', 10 );
504 remove_filter( 'bbp_edit_topic_pre_content', 'bbp_filter_kses', 30 );
505 }
506
507 /** Topic Forum ***********************************************************/
508
509 // Forum id was not passed
510 if ( empty( $_POST['bbp_forum_id'] ) ) {
511 bbp_add_error( 'bbp_topic_forum_id', __( '<strong>Error</strong>: Forum ID is missing.', 'bbpress' ) );
512
513 // Forum id was passed
514 } elseif ( is_numeric( $_POST['bbp_forum_id'] ) ) {
515 $forum_id = (int) $_POST['bbp_forum_id'];
516 }
517
518 // Current forum this topic is in
519 $current_forum_id = bbp_get_topic_forum_id( $topic_id );
520
521 // Forum exists
522 if ( ! empty( $forum_id ) && ( $forum_id !== $current_forum_id ) ) {
523
524 // Forum is a category
525 if ( bbp_is_forum_category( $forum_id ) ) {
526 bbp_add_error( 'bbp_edit_topic_forum_category', __( '<strong>Error</strong>: This forum is a category. No topics can be created in it.', 'bbpress' ) );
527
528 // Forum is not a category
529 } else {
530
531 // Forum is closed and user cannot access
532 if ( bbp_is_forum_closed( $forum_id ) && ! current_user_can( 'edit_forum', $forum_id ) ) {
533 bbp_add_error( 'bbp_edit_topic_forum_closed', __( '<strong>Error</strong>: This forum has been closed to new topics.', 'bbpress' ) );
534 }
535
536 // Forum is private and user cannot access
537 if ( bbp_is_forum_private( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
538 bbp_add_error( 'bbp_edit_topic_forum_private', __( '<strong>Error</strong>: This forum is private and you do not have the capability to read or create new topics in it.', 'bbpress' ) );
539
540 // Forum is hidden and user cannot access
541 } elseif ( bbp_is_forum_hidden( $forum_id ) && ! current_user_can( 'read_forum', $forum_id ) ) {
542 bbp_add_error( 'bbp_edit_topic_forum_hidden', __( '<strong>Error</strong>: This forum is hidden and you do not have the capability to read or create new topics in it.', 'bbpress' ) );
543 }
544 }
545 }
546
547 /** Topic Title ***********************************************************/
548
549 if ( ! empty( $_POST['bbp_topic_title'] ) ) {
550 $topic_title = sanitize_text_field( $_POST['bbp_topic_title'] );
551 }
552
553 // Filter and sanitize
554 $topic_title = apply_filters( 'bbp_edit_topic_pre_title', $topic_title, $topic_id );
555
556 // No topic title
557 if ( empty( $topic_title ) ) {
558 bbp_add_error( 'bbp_edit_topic_title', __( '<strong>Error</strong>: Your topic needs a title.', 'bbpress' ) );
559 }
560
561 // Title too long
562 if ( bbp_is_title_too_long( $topic_title ) ) {
563 bbp_add_error( 'bbp_topic_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
564 }
565
566 /** Topic Content *********************************************************/
567
568 if ( ! empty( $_POST['bbp_topic_content'] ) ) {
569 $topic_content = $_POST['bbp_topic_content'];
570 }
571
572 // Filter and sanitize
573 $topic_content = apply_filters( 'bbp_edit_topic_pre_content', $topic_content, $topic_id );
574
575 // No topic content
576 if ( empty( $topic_content ) ) {
577 bbp_add_error( 'bbp_edit_topic_content', __( '<strong>Error</strong>: Your topic cannot be empty.', 'bbpress' ) );
578 }
579
580 /** Topic Bad Words *******************************************************/
581
582 if ( ! bbp_check_for_moderation( $anonymous_data, $topic_author, $topic_title, $topic_content, true ) ) {
583 bbp_add_error( 'bbp_topic_moderation', __( '<strong>Error</strong>: Your topic cannot be edited at this time.', 'bbpress' ) );
584 }
585
586 /** Topic Status **********************************************************/
587
588 // Get available topic statuses
589 $topic_statuses = bbp_get_topic_statuses( $topic_id );
590
591 // Use existing post_status
592 $topic_status = $topic->post_status;
593
594 // Maybe force into pending
595 if ( bbp_is_topic_public( $topic_id ) && ! bbp_check_for_moderation( $anonymous_data, $topic_author, $topic_title, $topic_content ) ) {
596 $topic_status = bbp_get_pending_status_id();
597
598 // Check for possible posted topic status
599 } elseif ( ! empty( $_POST['bbp_topic_status'] ) && in_array( $_POST['bbp_topic_status'], array_keys( $topic_statuses ), true ) ) {
600
601 // Allow capable users to explicitly override the status
602 if ( current_user_can( 'moderate', $topic_id ) ) {
603 $topic_status = sanitize_key( $_POST['bbp_topic_status'] );
604
605 // Not capable
606 } else {
607 bbp_add_error( 'bbp_edit_topic_status', __( '<strong>Error</strong>: You do not have permission to do that.', 'bbpress' ) );
608 }
609 }
610
611 /** Topic Tags ************************************************************/
612
613 // Replace allowed terms
614 if ( bbp_allow_topic_tags() && isset( $_POST['bbp_topic_tags'] ) ) {
615 $terms = array( bbp_get_topic_tag_tax_id() => bbp_get_topic_tag_names_for_update( $topic_id, $_POST['bbp_topic_tags'] ) );
616
617 // Existing terms
618 } else {
619 $terms = array( bbp_get_topic_tag_tax_id() => explode( ',', bbp_get_topic_tag_names( $topic_id, ',' ) ) );
620 }
621
622 /** Additional Actions (Before Save) **************************************/
623
624 do_action( 'bbp_edit_topic_pre_extras', $topic_id );
625
626 // Bail if errors
627 if ( bbp_has_errors() ) {
628 return;
629 }
630
631 /** No Errors *************************************************************/
632
633 // Add the content of the form to $topic_data as an array
634 // Just in time manipulation of topic data before being edited
635 $topic_data = apply_filters(
636 'bbp_edit_topic_pre_insert',
637 array(
638 'ID' => $topic_id,
639 'post_title' => $topic_title,
640 'post_content' => $topic_content,
641 'post_status' => $topic_status,
642 'post_parent' => $forum_id,
643 'post_author' => $topic_author,
644 'post_type' => bbp_get_topic_post_type(),
645 'tax_input' => $terms,
646 )
647 );
648
649 // Toggle revisions to avoid duplicates
650 if ( post_type_supports( bbp_get_topic_post_type(), 'revisions' ) ) {
651 $revisions_removed = true;
652 remove_post_type_support( bbp_get_topic_post_type(), 'revisions' );
653 }
654
655 // Insert topic
656 $topic_id = wp_update_post( $topic_data );
657
658 // Toggle revisions back on
659 if ( true === $revisions_removed ) {
660 $revisions_removed = false;
661 add_post_type_support( bbp_get_topic_post_type(), 'revisions' );
662 }
663
664 /** No Errors *************************************************************/
665
666 if ( ! empty( $topic_id ) && ! is_wp_error( $topic_id ) ) {
667
668 // Update counts, etc...
669 do_action( 'bbp_edit_topic', $topic_id, $forum_id, $anonymous_data, $topic_data['post_author'], true /* Is edit */ );
670
671 /** Revisions *********************************************************/
672
673 // Update locks
674 update_post_meta( $topic_id, '_edit_last', bbp_get_current_user_id() );
675 delete_post_meta( $topic_id, '_edit_lock' );
676
677 // Revision Reason
678 if ( ! empty( $_POST['bbp_topic_edit_reason'] ) ) {
679 $topic_edit_reason = sanitize_text_field( $_POST['bbp_topic_edit_reason'] );
680 }
681
682 // Update revision log
683 if ( ! empty( $_POST['bbp_log_topic_edit'] ) && ( '1' === $_POST['bbp_log_topic_edit'] ) ) {
684 $revision_id = wp_save_post_revision( $topic_id );
685 if ( ! empty( $revision_id ) ) {
686 bbp_update_topic_revision_log(
687 array(
688 'topic_id' => $topic_id,
689 'revision_id' => $revision_id,
690 'author_id' => bbp_get_current_user_id(),
691 'reason' => $topic_edit_reason
692 )
693 );
694 }
695 }
696
697 /** Move Topic ********************************************************/
698
699 // If the new forum id is not equal to the old forum id, run the
700 // bbp_move_topic action and pass the topic's forum id as the
701 // first arg and topic id as the second to update counts.
702 if ( $forum_id !== $topic->post_parent ) {
703 bbp_move_topic_handler( $topic_id, $topic->post_parent, $forum_id );
704 }
705
706 /** Additional Actions (After Save) ***********************************/
707
708 do_action( 'bbp_edit_topic_post_extras', $topic_id );
709
710 /** Redirect **********************************************************/
711
712 // Redirect to
713 $redirect_to = bbp_get_redirect_to();
714
715 // View all?
716 $view_all = bbp_get_view_all( 'edit_others_replies' );
717
718 // Get the topic URL
719 $topic_url = bbp_get_topic_permalink( $topic_id, $redirect_to );
720
721 // Add view all?
722 if ( ! empty( $view_all ) ) {
723 $topic_url = bbp_add_view_all( $topic_url );
724 }
725
726 // Allow to be filtered
727 $topic_url = apply_filters( 'bbp_edit_topic_redirect_to', $topic_url, $view_all, $redirect_to );
728
729 /** Successful Edit ***************************************************/
730
731 // Redirect back to new topic
732 bbp_redirect( $topic_url );
733
734 /** Errors ****************************************************************/
735
736 } else {
737 $append_error = ( is_wp_error( $topic_id ) && $topic_id->get_error_message() ) ? $topic_id->get_error_message() . ' ' : '';
738 bbp_add_error(
739 'bbp_topic_error',
740 sprintf(
741 /* translators: %s: Error message */
742 __( '<strong>Error</strong>: The following problem(s) have been found with your topic: %sPlease try again.', 'bbpress' ),
743 $append_error
744 )
745 );
746 }
747 }
748
749 /**
750 * Handle all the extra meta stuff from posting a new topic
751 *
752 * @param int $topic_id Optional. Topic id
753 * @param int $forum_id Optional. Forum id
754 * @param array $anonymous_data Optional - if it's an anonymous post. Do not
755 * supply if supplying $author_id. Should be
756 * sanitized (see {@link bbp_filter_anonymous_post_data()}
757 * @param int $author_id Author id
758 * @param bool $is_edit Optional. Is the post being edited? Defaults to false.
759 */
760 function bbp_update_topic( $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $author_id = 0, $is_edit = false ) {
761
762 // Validate the ID's passed from 'bbp_new_topic' action
763 $topic_id = bbp_get_topic_id( $topic_id );
764 $forum_id = bbp_get_forum_id( $forum_id );
765
766 // Bail if there is no topic
767 if ( empty( $topic_id ) ) {
768 return;
769 }
770
771 // Check author_id
772 if ( empty( $author_id ) ) {
773 $author_id = bbp_get_current_user_id();
774 }
775
776 // Forum/Topic meta (early, for use in downstream functions)
777 bbp_update_topic_forum_id( $topic_id, $forum_id );
778 bbp_update_topic_topic_id( $topic_id, $topic_id );
779
780 // Get the topic types
781 $topic_types = bbp_get_topic_types( $topic_id );
782
783 // Sticky check after 'bbp_new_topic' action so forum ID meta is set
784 if ( ! empty( $_POST['bbp_stick_topic'] ) && in_array( $_POST['bbp_stick_topic'], array_keys( $topic_types ), true ) ) {
785
786 // What's the caps?
787 if ( current_user_can( 'moderate', $topic_id ) && ( current_user_can( 'moderate' ) || ( 'super' !== $_POST['bbp_stick_topic'] && ! bbp_is_topic_super_sticky( $topic_id ) ) ) ) {
788
789 // What's the haps?
790 switch ( $_POST['bbp_stick_topic'] ) {
791
792 // Sticky in this forum
793 case 'stick' :
794 bbp_stick_topic( $topic_id );
795 break;
796
797 // Super sticky in all forums
798 case 'super' :
799 bbp_stick_topic( $topic_id, true );
800 break;
801
802 // Unsticky from everywhere
803 case 'unstick' :
804 default :
805 bbp_unstick_topic( $topic_id );
806 break;
807 }
808 }
809 }
810
811 // If anonymous post, store name, email, website and ip in post_meta.
812 if ( ! empty( $anonymous_data ) ) {
813
814 // Update anonymous meta data (not cookies)
815 bbp_update_anonymous_post_author( $topic_id, $anonymous_data, bbp_get_topic_post_type() );
816
817 // Set transient for throttle check (only on new, not edit)
818 if ( empty( $is_edit ) ) {
819 set_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted', time(), HOUR_IN_SECONDS );
820 }
821 }
822
823 // Handle Subscription Checkbox
824 if ( bbp_is_subscriptions_active() && ! empty( $author_id ) ) {
825
826 // Check if subscribed
827 $subscribed = bbp_is_user_subscribed( $author_id, $topic_id );
828
829 // Check for action
830 $subscheck = ( ! empty( $_POST['bbp_topic_subscription'] ) && ( 'bbp_subscribe' === $_POST['bbp_topic_subscription'] ) )
831 ? true
832 : false;
833
834 // Subscribed and unsubscribing
835 if ( ( true === $subscribed ) && ( false === $subscheck ) ) {
836 bbp_remove_user_subscription( $author_id, $topic_id );
837
838 // Not subscribed and subscribing
839 } elseif ( ( false === $subscribed ) && ( true === $subscheck ) ) {
840 bbp_add_user_subscription( $author_id, $topic_id );
841 }
842 }
843
844 // Update associated topic values if this is a new topic
845 if ( empty( $is_edit ) ) {
846
847 // Update poster activity time
848 bbp_update_user_last_posted( $author_id );
849
850 // Update poster IP
851 update_post_meta( $topic_id, '_bbp_author_ip', bbp_current_author_ip(), false );
852
853 // Last active time
854 $last_active = get_post_field( 'post_date', $topic_id );
855
856 // Reply topic meta
857 bbp_update_topic_last_reply_id ( $topic_id, 0 );
858 bbp_update_topic_last_active_id ( $topic_id, $topic_id );
859 bbp_update_topic_last_active_time ( $topic_id, $last_active );
860 bbp_update_topic_reply_count ( $topic_id, 0 );
861 bbp_update_topic_reply_count_hidden ( $topic_id, 0 );
862
863 // Voice count is updated after engagements on bbp_new_topic and bbp_insert_topic.
864
865 // Walk up ancestors and do the dirty work
866 bbp_update_topic_walker( $topic_id, $last_active, $forum_id, 0, false );
867 }
868
869 // Bump the custom query cache
870 wp_cache_set( 'last_changed', microtime(), 'bbpress_posts' );
871 }
872
873 /**
874 * Walks up the post_parent tree from the current topic_id, and updates the
875 * meta data of forums above it. This calls several functions that all run
876 * manual queries against the database to get their results. As such, this
877 * function can be costly to run but is necessary to keep everything accurate.
878 *
879 * @since 2.0.0 bbPress (r2895)
880 *
881 * @param int $topic_id Topic id
882 * @param string $last_active_time Optional. Last active time
883 * @param int $forum_id Optional. Forum id
884 * @param int $reply_id Optional. Reply id
885 * @param bool $refresh Reset all the previous parameters? Defaults to true.
886 */
887 function bbp_update_topic_walker( $topic_id, $last_active_time = '', $forum_id = 0, $reply_id = 0, $refresh = true ) {
888
889 // Validate topic_id
890 $topic_id = bbp_get_topic_id( $topic_id );
891
892 // Define local variable(s)
893 $active_id = 0;
894
895 // Topic was passed
896 if ( ! empty( $topic_id ) ) {
897
898 // Get the forum ID if none was passed
899 if ( empty( $forum_id ) ) {
900 $forum_id = bbp_get_topic_forum_id( $topic_id );
901 }
902
903 // Set the active_id based on topic_id/reply_id
904 $active_id = empty( $reply_id ) ? $topic_id : $reply_id;
905 }
906
907 // Topic status
908 $topic_status = get_post_status( $topic_id );
909
910 // Do not replace public forum activity with a non-public topic
911 if ( ( false === $refresh ) && ! bbp_is_topic_public( $topic_id ) ) {
912 return;
913 }
914
915 // If we want a full refresh, retain the resolved forum and query its values
916 if ( true === $refresh ) {
917 $topic_id = $reply_id = $active_id = $last_active_time = 0;
918 $topic_status = bbp_get_public_status_id();
919 }
920
921 // Update this forum and its ancestors
922 $forum = bbp_get_forum( $forum_id );
923 if ( ! empty( $forum ) ) {
924 bbp_update_forum(
925 array(
926 'forum_id' => $forum->ID,
927 'post_parent' => $forum->post_parent,
928 'last_topic_id' => $topic_id,
929 'last_reply_id' => $reply_id,
930 'last_active_id' => $active_id,
931 'last_active_time' => $last_active_time,
932 'last_active_status' => $topic_status
933 )
934 );
935 }
936 }
937
938 /**
939 * Handle the moving of a topic from one forum to another. This includes walking
940 * up the old and new branches and updating the counts.
941 *
942 * @since 2.0.0 bbPress (r2907)
943 *
944 * @param int $topic_id The topic id.
945 * @param int $old_forum_id Old forum id.
946 * @param int $new_forum_id New forum id.
947 */
948 function bbp_move_topic_handler( $topic_id, $old_forum_id, $new_forum_id ) {
949
950 // Validate parameters
951 $topic_id = bbp_get_topic_id( $topic_id );
952 $old_forum_id = bbp_get_forum_id( $old_forum_id );
953 $new_forum_id = bbp_get_forum_id( $new_forum_id );
954
955 // Clean old and new forum caches before proceeding, to ensure subsequent
956 // calls to forum objects are using updated data.
957 clean_post_cache( $old_forum_id );
958 clean_post_cache( $new_forum_id );
959
960 // Update topic forum's ID
961 bbp_update_topic_forum_id( $topic_id, $new_forum_id );
962
963 // Update topic post parent with the new forum ID
964 wp_update_post(
965 array(
966 'ID' => $topic_id,
967 'post_parent' => $new_forum_id,
968 )
969 );
970
971 /** Stickies **************************************************************/
972
973 // Get forum stickies
974 $old_stickies = bbp_get_stickies( $old_forum_id );
975
976 // Only proceed if stickies are found
977 if ( ! empty( $old_stickies ) ) {
978
979 // Define local variables
980 $updated_stickies = array();
981
982 // Loop through stickies of forum and add misses to the updated array
983 foreach ( (array) $old_stickies as $sticky_topic_id ) {
984 if ( $topic_id !== $sticky_topic_id ) {
985 $updated_stickies[] = $sticky_topic_id;
986 }
987 }
988
989 // If stickies are different, update or delete them
990 if ( $updated_stickies !== $old_stickies ) {
991
992 // No more stickies so delete the meta
993 if ( empty( $updated_stickies ) ) {
994 delete_post_meta( $old_forum_id, '_bbp_sticky_topics' );
995
996 // Still stickies so update the meta
997 } else {
998 update_post_meta( $old_forum_id, '_bbp_sticky_topics', $updated_stickies );
999 }
1000
1001 // Topic was sticky, so restick in new forum
1002 bbp_stick_topic( $topic_id );
1003 }
1004 }
1005
1006 /** Topic Replies *********************************************************/
1007
1008 // Get the topics replies
1009 $replies = bbp_get_all_child_ids( $topic_id, bbp_get_reply_post_type() );
1010
1011 // Update the forum_id of all replies in the topic
1012 foreach ( $replies as $reply_id ) {
1013 bbp_update_reply_forum_id( $reply_id, $new_forum_id );
1014 }
1015
1016 /** Old forum_id **********************************************************/
1017
1018 // Get topic ancestors
1019 $old_forum_ancestors = array_values( array_unique( array_merge( array( $old_forum_id ), (array) get_post_ancestors( $old_forum_id ) ) ) );
1020
1021 // Public counts
1022 if ( bbp_is_topic_public( $topic_id ) ) {
1023
1024 // Update old forum counts.
1025 bbp_decrease_forum_topic_count( $old_forum_id );
1026
1027 // Update new forum counts.
1028 bbp_increase_forum_topic_count( $new_forum_id );
1029
1030 // Non-public counts
1031 } else {
1032
1033 // Update old forum counts.
1034 bbp_decrease_forum_topic_count_hidden( $old_forum_id );
1035
1036 // Update new forum counts.
1037 bbp_increase_forum_topic_count_hidden( $new_forum_id );
1038 }
1039
1040 // Get reply counts.
1041 $public_reply_count = bbp_get_public_child_count( $topic_id, bbp_get_reply_post_type() );
1042 $hidden_reply_count = bbp_get_non_public_child_count( $topic_id, bbp_get_reply_post_type() );
1043
1044 // Bump reply counts.
1045 bbp_bump_forum_reply_count( $old_forum_id, -$public_reply_count );
1046 bbp_bump_forum_reply_count( $new_forum_id, $public_reply_count );
1047 bbp_bump_forum_reply_count_hidden( $old_forum_id, -$hidden_reply_count );
1048 bbp_bump_forum_reply_count_hidden( $new_forum_id, $hidden_reply_count );
1049
1050 // Loop through ancestors and update them
1051 if ( ! empty( $old_forum_ancestors ) ) {
1052 foreach ( $old_forum_ancestors as $ancestor ) {
1053 if ( bbp_is_forum( $ancestor ) ) {
1054 bbp_update_forum(
1055 array(
1056 'forum_id' => $ancestor,
1057 )
1058 );
1059 }
1060 }
1061 }
1062
1063 /** New forum_id **********************************************************/
1064
1065 // Make sure we're not walking twice
1066 if ( ! in_array( $new_forum_id, $old_forum_ancestors, true ) ) {
1067
1068 // Get topic ancestors
1069 $new_forum_ancestors = array_values( array_unique( array_merge( array( $new_forum_id ), (array) get_post_ancestors( $new_forum_id ) ) ) );
1070
1071 // Make sure we're not walking twice
1072 $new_forum_ancestors = array_diff( $new_forum_ancestors, $old_forum_ancestors );
1073
1074 // Loop through ancestors and update them
1075 if ( ! empty( $new_forum_ancestors ) ) {
1076 foreach ( $new_forum_ancestors as $ancestor ) {
1077 if ( bbp_is_forum( $ancestor ) ) {
1078 bbp_update_forum(
1079 array(
1080 'forum_id' => $ancestor,
1081 )
1082 );
1083 }
1084 }
1085 }
1086 }
1087 }
1088
1089 /**
1090 * Merge topic handler
1091 *
1092 * Handles the front end merge topic submission
1093 *
1094 * @since 2.0.0 bbPress (r2756)
1095 *
1096 * @param string $action The requested action to compare this function to
1097 */
1098 function bbp_merge_topic_handler( $action = '' ) {
1099
1100 // Bail if action is not bbp-merge-topic
1101 if ( 'bbp-merge-topic' !== $action ) {
1102 return;
1103 }
1104
1105 // Define local variable(s)
1106 $source_topic_id = $destination_topic_id = 0;
1107 $source_topic = $destination_topic = 0;
1108 $subscribers = $favoriters = $replies = array();
1109
1110 /** Source Topic **********************************************************/
1111
1112 // Topic id
1113 if ( empty( $_POST['bbp_topic_id'] ) ) {
1114 bbp_add_error( 'bbp_merge_topic_source_id', __( '<strong>Error</strong>: Topic ID not found.', 'bbpress' ) );
1115 } else {
1116 $source_topic_id = (int) $_POST['bbp_topic_id'];
1117 }
1118
1119 // Nonce check
1120 if ( ! bbp_verify_nonce_request( 'bbp-merge-topic_' . $source_topic_id ) ) {
1121 bbp_add_error( 'bbp_merge_topic_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1122 }
1123
1124 // Get source topic
1125 $source_topic = bbp_get_topic( $source_topic_id );
1126
1127 // Source topic not found
1128 if ( empty( $source_topic ) ) {
1129 bbp_add_error( 'bbp_merge_topic_source_not_found', __( '<strong>Error</strong>: The topic you want to merge was not found.', 'bbpress' ) );
1130 }
1131
1132 // User must moderate and edit the source topic
1133 if ( ! empty( $source_topic ) && ( ! current_user_can( 'moderate', $source_topic->ID ) || ! current_user_can( 'edit_topic', $source_topic->ID ) ) ) {
1134 bbp_add_error( 'bbp_merge_topic_source_permission', __( '<strong>Error</strong>: You do not have permission to edit the source topic.', 'bbpress' ) );
1135 }
1136
1137 /** Destination Topic *****************************************************/
1138
1139 // Topic id
1140 if ( empty( $_POST['bbp_destination_topic'] ) ) {
1141 bbp_add_error( 'bbp_merge_topic_destination_id', __( '<strong>Error</strong>: Destination topic ID not found.', 'bbpress' ) );
1142 } else {
1143 $destination_topic_id = (int) $_POST['bbp_destination_topic'];
1144 }
1145
1146 // Get the destination topic
1147 $destination_topic = bbp_get_topic( $destination_topic_id );
1148
1149 // Destination topic not found
1150 if ( empty( $destination_topic ) ) {
1151 bbp_add_error( 'bbp_merge_topic_destination_not_found', __( '<strong>Error</strong>: The topic you want to merge to was not found.', 'bbpress' ) );
1152 }
1153
1154 // A topic cannot be merged with itself
1155 if ( ! empty( $source_topic ) && ! empty( $destination_topic ) && ( $source_topic->ID === $destination_topic->ID ) ) {
1156 bbp_add_error( 'bbp_merge_topic_destination_same', __( '<strong>Error</strong>: The destination topic must be different from the source topic.', 'bbpress' ) );
1157 }
1158
1159 // User must moderate and edit the destination topic
1160 if ( ! empty( $destination_topic ) && ( ! current_user_can( 'moderate', $destination_topic->ID ) || ! current_user_can( 'edit_topic', $destination_topic->ID ) ) ) {
1161 bbp_add_error( 'bbp_merge_topic_destination_permission', __( '<strong>Error</strong>: You do not have permission to edit the destination topic.', 'bbpress' ) );
1162 }
1163
1164 // Bail if errors
1165 if ( bbp_has_errors() ) {
1166 return;
1167 }
1168
1169 /** No Errors *************************************************************/
1170
1171 // Update counts, etc...
1172 do_action( 'bbp_merge_topic', $destination_topic->ID, $source_topic->ID );
1173
1174 /** Date Check ************************************************************/
1175
1176 // Check if the destination topic is older than the source topic
1177 if ( strtotime( $source_topic->post_date ) < strtotime( $destination_topic->post_date ) ) {
1178
1179 // Set destination topic post_date to 1 second before source topic
1180 // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1181 $destination_post_date = date( 'Y-m-d H:i:s', strtotime( $source_topic->post_date ) - 1 );
1182
1183 // Update destination topic
1184 wp_update_post(
1185 array(
1186 'ID' => $destination_topic_id,
1187 'post_date' => $destination_post_date,
1188 'post_date_gmt' => get_gmt_from_date( $destination_post_date )
1189 )
1190 );
1191 }
1192
1193 /** Engagements ***********************************************************/
1194
1195 // Get engagements from source topic
1196 $engagements = bbp_get_topic_engagements( $source_topic->ID );
1197
1198 // Maybe migrate engagements
1199 if ( ! empty( $engagements ) ) {
1200 foreach ( $engagements as $engager ) {
1201 bbp_add_user_engagement( $engager, $destination_topic->ID );
1202 }
1203 }
1204
1205 /** Subscriptions *********************************************************/
1206
1207 // Get subscribers from source topic
1208 $subscribers = bbp_get_subscribers( $source_topic->ID );
1209
1210 // Maybe migrate subscriptions
1211 if ( ! empty( $subscribers ) && ! empty( $_POST['bbp_topic_subscribers'] ) && ( '1' === $_POST['bbp_topic_subscribers'] ) ) {
1212 foreach ( $subscribers as $subscriber ) {
1213 bbp_add_user_subscription( $subscriber, $destination_topic->ID );
1214 }
1215 }
1216
1217 /** Favorites *************************************************************/
1218
1219 // Get favoriters from source topic
1220 $favoriters = bbp_get_topic_favoriters( $source_topic->ID );
1221
1222 // Maybe migrate favorites
1223 if ( ! empty( $favoriters ) && ! empty( $_POST['bbp_topic_favoriters'] ) && ( '1' === $_POST['bbp_topic_favoriters'] ) ) {
1224 foreach ( $favoriters as $favoriter ) {
1225 bbp_add_user_favorite( $favoriter, $destination_topic->ID );
1226 }
1227 }
1228
1229 /** Tags ******************************************************************/
1230
1231 // Get the source topic tags
1232 $source_topic_tags = wp_get_post_terms( $source_topic->ID, bbp_get_topic_tag_tax_id(), array( 'fields' => 'names' ) );
1233
1234 // Tags to possibly merge
1235 if ( ! empty( $source_topic_tags ) && ! is_wp_error( $source_topic_tags ) ) {
1236
1237 // Shift the tags if told to
1238 if ( ! empty( $_POST['bbp_topic_tags'] ) && ( '1' === $_POST['bbp_topic_tags'] ) ) {
1239 wp_set_post_terms( $destination_topic->ID, $source_topic_tags, bbp_get_topic_tag_tax_id(), true );
1240 }
1241
1242 // Delete the tags from the source topic
1243 wp_delete_object_term_relationships( $source_topic->ID, bbp_get_topic_tag_tax_id() );
1244 }
1245
1246 /** Source Topic **********************************************************/
1247
1248 // Status
1249 bbp_open_topic( $source_topic->ID );
1250
1251 // Sticky
1252 bbp_unstick_topic( $source_topic->ID );
1253
1254 // Delete source topic's last & count meta data
1255 delete_post_meta( $source_topic->ID, '_bbp_last_reply_id' );
1256 delete_post_meta( $source_topic->ID, '_bbp_last_active_id' );
1257 delete_post_meta( $source_topic->ID, '_bbp_last_active_time' );
1258 delete_post_meta( $source_topic->ID, '_bbp_voice_count' );
1259 delete_post_meta( $source_topic->ID, '_bbp_reply_count' );
1260 delete_post_meta( $source_topic->ID, '_bbp_reply_count_hidden' );
1261
1262 // Delete source topics user relationships
1263 delete_post_meta( $source_topic->ID, '_bbp_favorite' );
1264 delete_post_meta( $source_topic->ID, '_bbp_subscription' );
1265 delete_post_meta( $source_topic->ID, '_bbp_engagement' );
1266
1267 // Get the replies of the source topic
1268 $replies = (array) get_posts(
1269 array(
1270 'post_parent' => $source_topic->ID,
1271 'post_type' => bbp_get_reply_post_type(),
1272 'posts_per_page' => -1,
1273 'order' => 'ASC'
1274 )
1275 );
1276
1277 // Prepend the source topic to its replies array for processing
1278 array_unshift( $replies, $source_topic );
1279
1280 if ( ! empty( $replies ) ) {
1281
1282 /** Merge Replies *****************************************************/
1283
1284 // Change the post_parent of each reply to the destination topic id
1285 foreach ( $replies as $reply ) {
1286
1287 // Update the reply
1288 wp_update_post(
1289 array(
1290 'ID' => $reply->ID,
1291 'post_title' => '',
1292 'post_name' => false,
1293 'post_type' => bbp_get_reply_post_type(),
1294 'post_parent' => $destination_topic->ID,
1295 'guid' => ''
1296 )
1297 );
1298
1299 // Adjust reply meta values
1300 bbp_update_reply_topic_id( $reply->ID, $destination_topic->ID );
1301 bbp_update_reply_forum_id( $reply->ID, bbp_get_topic_forum_id( $destination_topic->ID ) );
1302
1303 // Update the reply position
1304 bbp_update_reply_position( $reply->ID );
1305
1306 // Do additional actions per merged reply
1307 do_action( 'bbp_merged_topic_reply', $reply->ID, $destination_topic->ID );
1308 }
1309 }
1310
1311 /** Successful Merge ******************************************************/
1312
1313 // Update topic's last meta data
1314 bbp_update_topic_last_reply_id ( $destination_topic->ID );
1315 bbp_update_topic_last_active_id ( $destination_topic->ID );
1316 bbp_update_topic_last_active_time( $destination_topic->ID );
1317
1318 // Send the post parent of the source topic as it has been shifted
1319 // (possibly to a new forum) so we need to update the counts of the
1320 // old forum as well as the new one
1321 do_action( 'bbp_merged_topic', $destination_topic->ID, $source_topic->ID, $source_topic->post_parent );
1322
1323 // Redirect back to new topic
1324 bbp_redirect( bbp_get_topic_permalink( $destination_topic->ID ) );
1325 }
1326
1327 /**
1328 * Fix counts on topic merge.
1329 *
1330 * When a topic is merged, update the counts of source and destination topic
1331 * and their forums.
1332 *
1333 * @since 2.0.0 bbPress (r2756)
1334 * @since 2.6.17 bbPress (r7468) Recount both forums and topic engagements.
1335 *
1336 * @param int $destination_topic_id Destination topic id.
1337 * @param int $source_topic_id Source topic id.
1338 * @param int $source_topic_forum_id Source topic's forum id.
1339 */
1340 function bbp_merge_topic_count( $destination_topic_id, $source_topic_id, $source_topic_forum_id ) {
1341 $destination_forum_id = bbp_get_topic_forum_id( $destination_topic_id );
1342
1343 /** Source Topic **********************************************************/
1344
1345 // Forum Topic Counts
1346 bbp_update_forum_topic_count( $source_topic_forum_id, true );
1347 bbp_update_forum_topic_count_hidden( $source_topic_forum_id, false, true );
1348
1349 // Forum Reply Counts
1350 foreach ( bbp_get_unique_array_values( array( $source_topic_forum_id, $destination_forum_id ) ) as $forum_id ) {
1351 bbp_update_forum_reply_count( $forum_id, true );
1352 bbp_update_forum_reply_count_hidden( $forum_id, true );
1353 }
1354
1355 /** Destination Topic *****************************************************/
1356
1357 // Topic Reply Counts
1358 bbp_update_topic_reply_count( $destination_topic_id );
1359
1360 // Topic Hidden Reply Counts
1361 bbp_update_topic_reply_count_hidden( $destination_topic_id );
1362
1363 // Topic Engagement and Voice Counts
1364 bbp_recalculate_topic_engagements( $destination_topic_id );
1365 bbp_update_topic_voice_count( $destination_topic_id );
1366
1367 // Transfer the converted source topic author's contribution to replies
1368 if ( bbp_is_reply_published( $source_topic_id ) ) {
1369 $user_id = bbp_get_reply_author_id( $source_topic_id );
1370 bbp_bump_user_topic_count( $user_id, -1 );
1371 bbp_bump_user_reply_count( $user_id, 1 );
1372 }
1373
1374 do_action( 'bbp_merge_topic_count', $destination_topic_id, $source_topic_id, $source_topic_forum_id );
1375 }
1376
1377 /**
1378 * Split topic handler
1379 *
1380 * Handles the front end split topic submission
1381 *
1382 * @since 2.0.0 bbPress (r2756)
1383 * @since 2.6.17 bbPress (r7468) Recount both forums and topic engagements.
1384 *
1385 * @param string $action The requested action to compare this function to
1386 */
1387 function bbp_split_topic_handler( $action = '' ) {
1388
1389 // Bail if action is not 'bbp-split-topic'
1390 if ( 'bbp-split-topic' !== $action ) {
1391 return;
1392 }
1393
1394 // Prevent debug notices
1395 $from_reply_id = $destination_topic_id = 0;
1396 $destination_topic_title = '';
1397 $destination_topic = $from_reply = $source_topic = '';
1398 $split_option = false;
1399
1400 /** Split Reply ***********************************************************/
1401
1402 if ( empty( $_POST['bbp_reply_id'] ) ) {
1403 bbp_add_error( 'bbp_split_topic_reply_id', __( '<strong>Error</strong>: A reply ID is required.', 'bbpress' ) );
1404 } else {
1405 $from_reply_id = (int) $_POST['bbp_reply_id'];
1406 }
1407
1408 $from_reply = bbp_get_reply( $from_reply_id );
1409
1410 // Reply exists
1411 if ( empty( $from_reply ) ) {
1412 bbp_add_error( 'bbp_split_topic_r_not_found', __( '<strong>Error</strong>: The reply you want to split from was not found.', 'bbpress' ) );
1413 }
1414
1415 /** Topic to Split ********************************************************/
1416
1417 // Get the topic being split
1418 if ( ! empty( $from_reply ) ) {
1419 $source_topic = bbp_get_topic( $from_reply->post_parent );
1420 }
1421
1422 // No topic
1423 if ( ! empty( $from_reply ) && empty( $source_topic ) ) {
1424 bbp_add_error( 'bbp_split_topic_source_not_found', __( '<strong>Error</strong>: The topic you want to split was not found.', 'bbpress' ) );
1425 }
1426
1427 // Nonce check failed
1428 if ( ! empty( $source_topic ) && ! bbp_verify_nonce_request( 'bbp-split-topic_' . $source_topic->ID ) ) {
1429 bbp_add_error( 'bbp_split_topic_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1430 }
1431
1432 // User must moderate and edit the source topic
1433 if ( ! empty( $source_topic ) && ( ! current_user_can( 'moderate', $source_topic->ID ) || ! current_user_can( 'edit_topic', $source_topic->ID ) ) ) {
1434 bbp_add_error( 'bbp_split_topic_source_permission', __( '<strong>Error</strong>: You do not have permission to edit the source topic.', 'bbpress' ) );
1435 }
1436
1437 // How to Split
1438 if ( ! empty( $_POST['bbp_topic_split_option'] ) ) {
1439 $split_option = sanitize_key( $_POST['bbp_topic_split_option'] );
1440 }
1441
1442 // Invalid split option
1443 if ( empty( $split_option ) || ! in_array( $split_option, array( 'existing', 'reply' ), true ) ) {
1444 bbp_add_error( 'bbp_split_topic_option', __( '<strong>Error</strong>: You need to choose a valid split option.', 'bbpress' ) );
1445
1446 // Valid Split Option
1447 } else {
1448
1449 // What kind of split
1450 switch ( $split_option ) {
1451
1452 // Into an existing topic
1453 case 'existing' :
1454
1455 // Get destination topic id
1456 if ( empty( $_POST['bbp_destination_topic'] ) ) {
1457 bbp_add_error( 'bbp_split_topic_destination_id', __( '<strong>Error</strong>: A topic ID is required.', 'bbpress' ) );
1458 } else {
1459 $destination_topic_id = (int) $_POST['bbp_destination_topic'];
1460 }
1461
1462 // Get the destination topic
1463 $destination_topic = bbp_get_topic( $destination_topic_id );
1464
1465 // No destination topic
1466 if ( empty( $destination_topic ) ) {
1467 bbp_add_error( 'bbp_split_topic_destination_not_found', __( '<strong>Error</strong>: The topic you want to split to was not found.', 'bbpress' ) );
1468 }
1469
1470 // A topic cannot be split into itself
1471 if ( ! empty( $source_topic ) && ! empty( $destination_topic ) && ( $source_topic->ID === $destination_topic->ID ) ) {
1472 bbp_add_error( 'bbp_split_topic_destination_same', __( '<strong>Error</strong>: The destination topic must be different from the source topic.', 'bbpress' ) );
1473 }
1474
1475 // User must moderate and edit the destination topic
1476 if ( ! empty( $destination_topic ) && ( ! current_user_can( 'moderate', $destination_topic->ID ) || ! current_user_can( 'edit_topic', $destination_topic->ID ) ) ) {
1477 bbp_add_error( 'bbp_split_topic_destination_permission', __( '<strong>Error</strong>: You do not have permission to edit the destination topic.', 'bbpress' ) );
1478 }
1479
1480 break;
1481
1482 // Split at reply into a new topic
1483 case 'reply' :
1484 default :
1485 // User needs to be able to publish topics
1486 if ( current_user_can( 'publish_topics' ) ) {
1487
1488 // Bail before converting the reply if there are errors
1489 if ( bbp_has_errors() ) {
1490 break;
1491 }
1492
1493 // Use the new title that was passed
1494 if ( ! empty( $_POST['bbp_topic_split_destination_title'] ) ) {
1495 $destination_topic_title = sanitize_text_field( $_POST['bbp_topic_split_destination_title'] );
1496
1497 // Use the source topic title
1498 } else {
1499 $destination_topic_title = $source_topic->post_title;
1500 }
1501
1502 // Filter the new topic title
1503 $destination_topic_title = apply_filters( 'bbp_new_topic_pre_title', $destination_topic_title );
1504
1505 // Title cannot be empty
1506 if ( empty( $destination_topic_title ) ) {
1507 bbp_add_error( 'bbp_topic_title', __( '<strong>Error</strong>: Your topic needs a title.', 'bbpress' ) );
1508 }
1509
1510 // Title too long
1511 if ( bbp_is_title_too_long( $destination_topic_title ) ) {
1512 bbp_add_error( 'bbp_topic_title', __( '<strong>Error</strong>: Your title is too long.', 'bbpress' ) );
1513 }
1514
1515 // Bail before converting the reply if there are errors
1516 if ( bbp_has_errors() ) {
1517 break;
1518 }
1519
1520 // Update the topic
1521 $destination_topic_id = wp_update_post(
1522 array(
1523 'ID' => $from_reply->ID,
1524 'post_title' => $destination_topic_title,
1525 'post_name' => false,
1526 'post_type' => bbp_get_topic_post_type(),
1527 'post_parent' => $source_topic->post_parent,
1528 'menu_order' => 0,
1529 'guid' => ''
1530 )
1531 );
1532
1533 // Get the topic
1534 $destination_topic = bbp_get_topic( $destination_topic_id );
1535
1536 // Make sure the new topic knows its a topic
1537 bbp_update_topic_topic_id( $from_reply->ID );
1538
1539 // Shouldn't happen
1540 if ( false === $destination_topic_id || is_wp_error( $destination_topic_id ) || empty( $destination_topic ) ) {
1541 bbp_add_error( 'bbp_split_topic_destination_reply', __( '<strong>Error</strong>: There was a problem converting the reply into the topic. Please try again.', 'bbpress' ) );
1542 }
1543
1544 // User cannot publish posts
1545 } else {
1546 bbp_add_error( 'bbp_split_topic_destination_permission', __( '<strong>Error</strong>: You do not have permission to create new topics. The reply could not be converted into a topic.', 'bbpress' ) );
1547 }
1548
1549 break;
1550 }
1551 }
1552
1553 // Bail if there are errors
1554 if ( bbp_has_errors() ) {
1555 return;
1556 }
1557
1558 /** No Errors - Do the Spit ***********************************************/
1559
1560 // Update counts, etc...
1561 do_action( 'bbp_pre_split_topic', $from_reply->ID, $source_topic->ID, $destination_topic->ID );
1562
1563 /** Date Check ************************************************************/
1564
1565 // Check if the destination topic is older than the from reply
1566 if ( strtotime( $from_reply->post_date ) < strtotime( $destination_topic->post_date ) ) {
1567
1568 // Set destination topic post_date to 1 second before from reply
1569 // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1570 $destination_post_date = date( 'Y-m-d H:i:s', strtotime( $from_reply->post_date ) - 1 );
1571
1572 // Update destination topic
1573 wp_update_post(
1574 array(
1575 'ID' => $destination_topic_id,
1576 'post_date' => $destination_post_date,
1577 'post_date_gmt' => get_gmt_from_date( $destination_post_date )
1578 )
1579 );
1580 }
1581
1582 /** Subscriptions *********************************************************/
1583
1584 // Copy the subscribers
1585 if ( ! empty( $_POST['bbp_topic_subscribers'] ) && '1' === $_POST['bbp_topic_subscribers'] && bbp_is_subscriptions_active() ) {
1586
1587 // Get the subscribers
1588 $subscribers = bbp_get_subscribers( $source_topic->ID );
1589
1590 if ( ! empty( $subscribers ) ) {
1591
1592 // Add subscribers to new topic
1593 foreach ( (array) $subscribers as $subscriber ) {
1594 bbp_add_user_subscription( $subscriber, $destination_topic->ID );
1595 }
1596 }
1597 }
1598
1599 /** Favorites *************************************************************/
1600
1601 // Copy the favoriters if told to
1602 if ( ! empty( $_POST['bbp_topic_favoriters'] ) && ( '1' === $_POST['bbp_topic_favoriters'] ) ) {
1603
1604 // Get the favoriters
1605 $favoriters = bbp_get_topic_favoriters( $source_topic->ID );
1606
1607 if ( ! empty( $favoriters ) ) {
1608
1609 // Add the favoriters to new topic
1610 foreach ( (array) $favoriters as $favoriter ) {
1611 bbp_add_user_favorite( $favoriter, $destination_topic->ID );
1612 }
1613 }
1614 }
1615
1616 /** Tags ******************************************************************/
1617
1618 // Copy the tags if told to
1619 if ( ! empty( $_POST['bbp_topic_tags'] ) && ( '1' === $_POST['bbp_topic_tags'] ) ) {
1620
1621 // Get the source topic tags
1622 $source_topic_tags = wp_get_post_terms( $source_topic->ID, bbp_get_topic_tag_tax_id(), array( 'fields' => 'names' ) );
1623
1624 if ( ! empty( $source_topic_tags ) ) {
1625 wp_set_post_terms( $destination_topic->ID, $source_topic_tags, bbp_get_topic_tag_tax_id(), true );
1626 }
1627 }
1628
1629 /** Split Replies *********************************************************/
1630
1631 // get_posts() is not used because it doesn't allow us to use '>='
1632 // comparision without a filter.
1633 $bbp_db = bbp_db();
1634 $query = $bbp_db->prepare( "SELECT * FROM {$bbp_db->posts} WHERE {$bbp_db->posts}.post_date >= %s AND {$bbp_db->posts}.post_parent = %d AND {$bbp_db->posts}.post_type = %s ORDER BY {$bbp_db->posts}.post_date ASC", $from_reply->post_date, $source_topic->ID, bbp_get_reply_post_type() );
1635 $replies = (array) $bbp_db->get_results( $query );
1636
1637 // Make sure there are replies to loop through
1638 if ( ! empty( $replies ) && ! is_wp_error( $replies ) ) {
1639
1640 // Save reply ids
1641 $reply_ids = array();
1642
1643 // Change the post_parent of each reply to the destination topic id
1644 foreach ( $replies as $reply ) {
1645
1646 // Update the reply
1647 wp_update_post(
1648 array(
1649 'ID' => $reply->ID,
1650 'post_title' => '',
1651 'post_name' => false, // will be automatically generated
1652 'post_parent' => $destination_topic->ID,
1653 'guid' => ''
1654 )
1655 );
1656
1657 // Gather reply ids
1658 $reply_ids[] = $reply->ID;
1659
1660 // Adjust reply meta values
1661 bbp_update_reply_topic_id( $reply->ID, $destination_topic->ID );
1662 bbp_update_reply_forum_id( $reply->ID, bbp_get_topic_forum_id( $destination_topic->ID ) );
1663
1664 // Adjust reply position
1665 bbp_update_reply_position( $reply->ID );
1666
1667 // Adjust reply to values
1668 $reply_to = bbp_get_reply_to( $reply->ID );
1669
1670 // Not a reply to a reply that moved over
1671 if ( ! in_array( $reply_to, $reply_ids, true ) ) {
1672 bbp_update_reply_to( $reply->ID, 0 );
1673 }
1674
1675 // New topic from reply can't be a reply to
1676 if ( ( $from_reply->ID === $destination_topic->ID ) && ( $from_reply->ID === $reply_to ) ) {
1677 bbp_update_reply_to( $reply->ID, 0 );
1678 }
1679
1680 // Do additional actions per split reply
1681 do_action( 'bbp_split_topic_reply', $reply->ID, $destination_topic->ID );
1682 }
1683
1684 // Remove reply to from new topic
1685 if ( $from_reply->ID === $destination_topic->ID ) {
1686 delete_post_meta( $from_reply->ID, '_bbp_reply_to' );
1687 }
1688
1689 // Set the last reply ID and freshness
1690 $last_reply_id = $reply->ID;
1691 $freshness = $reply->post_date;
1692
1693 // Set the last reply ID and freshness to the from_reply
1694 } else {
1695 $last_reply_id = $from_reply->ID;
1696 $freshness = $from_reply->post_date;
1697 }
1698
1699 // It is a new topic and we need to set some default metas to make
1700 // the topic display in bbp_has_topics() list
1701 if ( 'reply' === $split_option ) {
1702 bbp_update_topic_last_reply_id ( $destination_topic->ID, $last_reply_id );
1703 bbp_update_topic_last_active_id ( $destination_topic->ID, $last_reply_id );
1704 bbp_update_topic_last_active_time( $destination_topic->ID, $freshness );
1705 }
1706
1707 // Update source topic ID last active
1708 bbp_update_topic_last_reply_id ( $source_topic->ID );
1709 bbp_update_topic_last_active_id ( $source_topic->ID );
1710 bbp_update_topic_last_active_time( $source_topic->ID );
1711
1712 /** Successful Split ******************************************************/
1713
1714 // Update counts, etc...
1715 do_action( 'bbp_post_split_topic', $from_reply->ID, $source_topic->ID, $destination_topic->ID );
1716
1717 // Redirect back to the topic
1718 bbp_redirect( bbp_get_topic_permalink( $destination_topic->ID ) );
1719 }
1720
1721 /**
1722 * Fix counts on topic split
1723 *
1724 * When a topic is split, update the counts of source and destination topic
1725 * and their forums.
1726 *
1727 * @since 2.0.0 bbPress (r2756)
1728 * @since 2.6.17 bbPress (r7468) Recount both forums and topic engagements.
1729 *
1730 * @param int $from_reply_id From reply id
1731 * @param int $source_topic_id Source topic id
1732 * @param int $destination_topic_id Destination topic id
1733 */
1734 function bbp_split_topic_count( $from_reply_id, $source_topic_id, $destination_topic_id ) {
1735 $source_forum_id = bbp_get_topic_forum_id( $source_topic_id );
1736 $destination_forum_id = bbp_get_topic_forum_id( $destination_topic_id );
1737
1738 // A reply converted into a topic changes its forum's topic counts
1739 if ( bbp_is_topic( $from_reply_id ) ) {
1740 bbp_update_forum_topic_count( $destination_forum_id, true );
1741 bbp_update_forum_topic_count_hidden( $destination_forum_id, false, true );
1742
1743 // Transfer the public contribution between count types
1744 if ( bbp_is_topic_published( $from_reply_id ) ) {
1745 $user_id = bbp_get_topic_author_id( $from_reply_id );
1746 bbp_bump_user_reply_count( $user_id, -1 );
1747 bbp_bump_user_topic_count( $user_id, 1 );
1748 }
1749 }
1750
1751 // Recount replies in both forums
1752 foreach ( bbp_get_unique_array_values( array( $source_forum_id, $destination_forum_id ) ) as $forum_id ) {
1753 bbp_update_forum_reply_count( $forum_id, true );
1754 bbp_update_forum_reply_count_hidden( $forum_id, true );
1755 }
1756
1757 // Topic Reply Counts
1758 bbp_update_topic_reply_count( $source_topic_id );
1759 bbp_update_topic_reply_count( $destination_topic_id );
1760
1761 // Topic Hidden Reply Counts
1762 bbp_update_topic_reply_count_hidden( $source_topic_id );
1763 bbp_update_topic_reply_count_hidden( $destination_topic_id );
1764
1765 // Topic Engagement and Voice Counts
1766 bbp_recalculate_topic_engagements( $source_topic_id );
1767 bbp_recalculate_topic_engagements( $destination_topic_id );
1768 bbp_update_topic_voice_count( $source_topic_id );
1769 bbp_update_topic_voice_count( $destination_topic_id );
1770
1771 do_action( 'bbp_split_topic_count', $from_reply_id, $source_topic_id, $destination_topic_id );
1772 }
1773
1774 /**
1775 * Handles the front end tag management (renaming, merging, destroying)
1776 *
1777 * @since 2.1.0 bbPress (r3671)
1778 *
1779 * @param string $action The requested action to compare this function to
1780 */
1781 function bbp_edit_topic_tag_handler( $action = '' ) {
1782
1783 // Bail if required POST actions aren't passed
1784 if ( empty( $_POST['tag-id'] ) ) {
1785 return;
1786 }
1787
1788 // Setup possible get actions
1789 $possible_actions = array(
1790 'bbp-update-topic-tag',
1791 'bbp-merge-topic-tag',
1792 'bbp-delete-topic-tag'
1793 );
1794
1795 // Bail if actions aren't meant for this function
1796 if ( ! in_array( $action, $possible_actions, true ) ) {
1797 return;
1798 }
1799
1800 // Setup vars
1801 $tag_id = (int) $_POST['tag-id'];
1802 $tag = get_term( $tag_id, bbp_get_topic_tag_tax_id() );
1803
1804 // Tag does not exist
1805 if ( is_wp_error( $tag ) && $tag->get_error_message() ) {
1806 /* translators: %s: Error message(s) */
1807 bbp_add_error( 'bbp_manage_topic_invalid_tag', sprintf( __( '<strong>Error</strong>: The following problem(s) have been found while getting the tag: %s', 'bbpress' ), $tag->get_error_message() ) );
1808 return;
1809 }
1810
1811 // What action are we trying to perform?
1812 switch ( $action ) {
1813
1814 // Update tag
1815 case 'bbp-update-topic-tag' :
1816
1817 // Nonce check
1818 if ( ! bbp_verify_nonce_request( 'update-tag_' . $tag_id ) ) {
1819 bbp_add_error( 'bbp_manage_topic_tag_update_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1820 return;
1821 }
1822
1823 // Can user edit topic tags?
1824 if ( ! current_user_can( 'edit_topic_tag', $tag_id ) ) {
1825 bbp_add_error( 'bbp_manage_topic_tag_update_permission', __( '<strong>Error</strong>: You do not have permission to edit the topic tags.', 'bbpress' ) );
1826 return;
1827 }
1828
1829 // Unsanitized tag name
1830 $name = empty( $_POST['tag-name'] ) ? false : $_POST['tag-name'];
1831
1832 // No tag name was provided
1833 if ( empty( $_POST['tag-name'] ) || ! $name ) {
1834 bbp_add_error( 'bbp_manage_topic_tag_update_name', __( '<strong>Error</strong>: You need to enter a tag name.', 'bbpress' ) );
1835 return;
1836 }
1837
1838 // Attempt to update the tag
1839 $slug = ! empty( $_POST['tag-slug'] ) ? $_POST['tag-slug'] : '';
1840 $description = ! empty( $_POST['tag-description'] ) ? $_POST['tag-description'] : '';
1841 $tag = wp_update_term(
1842 $tag_id,
1843 bbp_get_topic_tag_tax_id(),
1844 array(
1845 'name' => $name,
1846 'slug' => $slug,
1847 'description' => $description
1848 )
1849 );
1850
1851 // Cannot update tag
1852 if ( is_wp_error( $tag ) && $tag->get_error_message() ) {
1853 /* translators: %s: Error message(s) */
1854 bbp_add_error( 'bbp_manage_topic_tag_update_error', sprintf( __( '<strong>Error</strong>: The following problem(s) have been found while updating the tag: %s', 'bbpress' ), $tag->get_error_message() ) );
1855 return;
1856 }
1857
1858 // Redirect
1859 $redirect = get_term_link( $tag_id, bbp_get_topic_tag_tax_id() );
1860
1861 // Update counts, etc...
1862 do_action( 'bbp_update_topic_tag', $tag_id, $tag, $name, $slug, $description );
1863
1864 break;
1865
1866 // Merge two tags
1867 case 'bbp-merge-topic-tag' :
1868
1869 // Nonce check
1870 if ( ! bbp_verify_nonce_request( 'merge-tag_' . $tag_id ) ) {
1871 bbp_add_error( 'bbp_manage_topic_tag_merge_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1872 return;
1873 }
1874
1875 // Can user edit topic tags?
1876 if ( ! current_user_can( 'edit_topic_tags' ) ) {
1877 bbp_add_error( 'bbp_manage_topic_tag_merge_permission', __( '<strong>Error</strong>: You do not have permission to edit the topic tags.', 'bbpress' ) );
1878 return;
1879 }
1880
1881 // Unsanitized name of existing tag
1882 $name = empty( $_POST['tag-existing-name'] ) ? false : $_POST['tag-existing-name'];
1883
1884 // No tag name was provided
1885 if ( empty( $_POST['tag-existing-name'] ) || ! $name ) {
1886 bbp_add_error( 'bbp_manage_topic_tag_merge_name', __( '<strong>Error</strong>: You need to enter a tag name.', 'bbpress' ) );
1887 return;
1888 }
1889
1890 // If term does not exist, create it
1891 $tag = term_exists( $name, bbp_get_topic_tag_tax_id() );
1892 if ( ! $tag ) {
1893 $tag = wp_insert_term( $name, bbp_get_topic_tag_tax_id() );
1894 }
1895
1896 // Problem inserting the new term
1897 if ( is_wp_error( $tag ) && $tag->get_error_message() ) {
1898 /* translators: %s: Error message(s) */
1899 bbp_add_error( 'bbp_manage_topic_tag_merge_error', sprintf( __( '<strong>Error</strong>: The following problem(s) have been found while merging the tags: %s', 'bbpress' ), $tag->get_error_message() ) );
1900 return;
1901 }
1902
1903 // Merging in to...
1904 $to_tag = $tag['term_id'];
1905
1906 // Attempting to merge a tag into itself
1907 if ( $tag_id === $to_tag ) {
1908 bbp_add_error( 'bbp_manage_topic_tag_merge_same', __( '<strong>Error</strong>: The tags which are being merged can not be the same.', 'bbpress' ) );
1909 return;
1910 }
1911
1912 // Delete the old term
1913 $tag = wp_delete_term(
1914 $tag_id,
1915 bbp_get_topic_tag_tax_id(),
1916 array(
1917 'default' => $to_tag,
1918 'force_default' => true
1919 )
1920 );
1921
1922 // Error merging the terms
1923 if ( is_wp_error( $tag ) && $tag->get_error_message() ) {
1924 /* translators: %s: Error message(s) */
1925 bbp_add_error( 'bbp_manage_topic_tag_merge_error', sprintf( __( '<strong>Error</strong>: The following problem(s) have been found while merging the tags: %s', 'bbpress' ), $tag->get_error_message() ) );
1926 return;
1927 }
1928
1929 // Redirect
1930 $redirect = get_term_link( (int) $to_tag, bbp_get_topic_tag_tax_id() );
1931
1932 // Update counts, etc...
1933 do_action( 'bbp_merge_topic_tag', $tag_id, $to_tag, $tag );
1934
1935 break;
1936
1937 // Delete tag
1938 case 'bbp-delete-topic-tag' :
1939
1940 // Nonce check
1941 if ( ! bbp_verify_nonce_request( 'delete-tag_' . $tag_id ) ) {
1942 bbp_add_error( 'bbp_manage_topic_tag_delete_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
1943 return;
1944 }
1945
1946 // Can user delete topic tags?
1947 if ( ! current_user_can( 'delete_topic_tag', $tag_id ) ) {
1948 bbp_add_error( 'bbp_manage_topic_tag_delete_permission', __( '<strong>Error</strong>: You do not have permission to delete the topic tags.', 'bbpress' ) );
1949 return;
1950 }
1951
1952 // Attempt to delete term
1953 $tag = wp_delete_term( $tag_id, bbp_get_topic_tag_tax_id() );
1954
1955 // Error deleting term
1956 if ( is_wp_error( $tag ) && $tag->get_error_message() ) {
1957 /* translators: %s: Error message(s) */
1958 bbp_add_error( 'bbp_manage_topic_tag_delete_error', sprintf( __( '<strong>Error</strong>: The following problem(s) have been found while deleting the tag: %s', 'bbpress' ), $tag->get_error_message() ) );
1959 return;
1960 }
1961
1962 // We don't have any other place to go other than home! Or we may die because of the 404 disease
1963 $redirect = bbp_get_forums_url();
1964
1965 // Update counts, etc...
1966 do_action( 'bbp_delete_topic_tag', $tag_id, $tag );
1967
1968 break;
1969 }
1970
1971 /** Successful Moderation *************************************************/
1972
1973 // Redirect back
1974 $redirect = ( ! empty( $redirect ) && ! is_wp_error( $redirect ) ) ? $redirect : home_url();
1975 bbp_redirect( $redirect );
1976 }
1977
1978 /** Helpers *******************************************************************/
1979
1980 /**
1981 * Return an associative array of available topic statuses
1982 *
1983 * @since 2.4.0 bbPress (r5060)
1984 *
1985 * @param int $topic_id Optional. Topic id.
1986 *
1987 * @return array
1988 */
1989 function bbp_get_topic_statuses( $topic_id = 0 ) {
1990
1991 // Filter & return
1992 return (array) apply_filters(
1993 'bbp_get_topic_statuses',
1994 array(
1995 bbp_get_public_status_id() => _x( 'Open', 'Open the topic', 'bbpress' ),
1996 bbp_get_closed_status_id() => _x( 'Closed', 'Close the topic', 'bbpress' ),
1997 bbp_get_spam_status_id() => _x( 'Spam', 'Spam the topic', 'bbpress' ),
1998 bbp_get_trash_status_id() => _x( 'Trash', 'Trash the topic', 'bbpress' ),
1999 bbp_get_pending_status_id() => _x( 'Pending', 'Unapprove the topic', 'bbpress' )
2000 ),
2001 $topic_id
2002 );
2003 }
2004
2005 /**
2006 * Return an associative array of topic sticky types
2007 *
2008 * @since 2.4.0 bbPress (r5060)
2009 *
2010 * @param int $topic_id Optional. Topic id.
2011 *
2012 * @return array
2013 */
2014 function bbp_get_topic_types( $topic_id = 0 ) {
2015
2016 // Filter & return
2017 return (array) apply_filters(
2018 'bbp_get_topic_types',
2019 array(
2020 'unstick' => _x( 'Normal', 'Unstick a topic', 'bbpress' ),
2021 'stick' => _x( 'Sticky', 'Make topic sticky', 'bbpress' ),
2022 'super' => _x( 'Super Sticky', 'Make topic super sticky', 'bbpress' )
2023 ),
2024 $topic_id
2025 );
2026 }
2027
2028 /**
2029 * Return array of available topic toggle actions
2030 *
2031 * @since 2.6.0 bbPress (r6142)
2032 *
2033 * @param int $topic_id Optional. Topic id.
2034 *
2035 * @return array
2036 */
2037 function bbp_get_topic_toggles( $topic_id = 0 ) {
2038
2039 // Filter & return
2040 return (array) apply_filters(
2041 'bbp_get_toggle_topic_actions',
2042 array(
2043 'bbp_toggle_topic_close',
2044 'bbp_toggle_topic_stick',
2045 'bbp_toggle_topic_spam',
2046 'bbp_toggle_topic_trash',
2047 'bbp_toggle_topic_approve'
2048 ),
2049 $topic_id
2050 );
2051 }
2052
2053 /**
2054 * Return array of public topic statuses.
2055 *
2056 * @since 2.6.0 bbPress (r6384)
2057 *
2058 * @return array
2059 */
2060 function bbp_get_public_topic_statuses() {
2061 $statuses = array(
2062 bbp_get_public_status_id(),
2063 bbp_get_closed_status_id()
2064 );
2065
2066 // Filter & return
2067 return (array) apply_filters( 'bbp_get_public_topic_statuses', $statuses );
2068 }
2069
2070 /**
2071 * Return array of non-public topic statuses.
2072 *
2073 * @since 2.6.0 bbPress (r6644)
2074 *
2075 * @return array
2076 */
2077 function bbp_get_non_public_topic_statuses() {
2078 $statuses = array(
2079 bbp_get_trash_status_id(),
2080 bbp_get_spam_status_id(),
2081 bbp_get_pending_status_id()
2082 );
2083
2084 // Filter & return
2085 return (array) apply_filters( 'bbp_get_non_public_topic_statuses', $statuses );
2086 }
2087
2088 /** Stickies ******************************************************************/
2089
2090 /**
2091 * Return sticky topics of a forum
2092 *
2093 * @since 2.0.0 bbPress (r2592)
2094 *
2095 * @param int $forum_id Optional. If not passed, super stickies are returned.
2096 * @return array IDs of sticky topics of a forum or super stickies
2097 */
2098 function bbp_get_stickies( $forum_id = 0 ) {
2099
2100 // Get stickies (maybe super if empty)
2101 $stickies = empty( $forum_id )
2102 ? bbp_get_super_stickies()
2103 : get_post_meta( $forum_id, '_bbp_sticky_topics', true );
2104
2105 // Cast as array
2106 $stickies = ( empty( $stickies ) || ! is_array( $stickies ) )
2107 ? array()
2108 : wp_parse_id_list( $stickies );
2109
2110 // Filter & return
2111 return (array) apply_filters( 'bbp_get_stickies', $stickies, $forum_id );
2112 }
2113
2114 /**
2115 * Return topics stuck to front page of the forums
2116 *
2117 * @since 2.0.0 bbPress (r2592)
2118 *
2119 * @return array IDs of super sticky topics
2120 */
2121 function bbp_get_super_stickies() {
2122
2123 // Get super stickies
2124 $stickies = get_option( '_bbp_super_sticky_topics', array() );
2125
2126 // Cast as array
2127 $stickies = ( empty( $stickies ) || ! is_array( $stickies ) )
2128 ? array()
2129 : wp_parse_id_list( $stickies );
2130
2131 // Filter & return
2132 return (array) apply_filters( 'bbp_get_super_stickies', $stickies );
2133 }
2134
2135 /** Topics Actions ************************************************************/
2136
2137 /**
2138 * Handles the front end opening/closing, spamming/unspamming,
2139 * sticking/unsticking and trashing/untrashing/deleting of topics
2140 *
2141 * @since 2.0.0 bbPress (r2727)
2142 *
2143 * @param string $action The requested action to compare this function to
2144 */
2145 function bbp_toggle_topic_handler( $action = '' ) {
2146
2147 // Bail if required GET actions aren't passed
2148 if ( empty( $_GET['topic_id'] ) ) {
2149 return;
2150 }
2151
2152 // What's the topic id?
2153 $topic_id = bbp_get_topic_id( (int) $_GET['topic_id'] );
2154
2155 // Get possible topic-handler toggles
2156 $toggles = bbp_get_topic_toggles( $topic_id );
2157
2158 // Bail if actions aren't meant for this function
2159 if ( ! in_array( $action, $toggles, true ) ) {
2160 return;
2161 }
2162
2163 // Make sure topic exists
2164 $topic = bbp_get_topic( $topic_id );
2165 if ( empty( $topic ) ) {
2166 bbp_add_error( 'bbp_toggle_topic_missing', __( '<strong>Error</strong>: This topic could not be found or no longer exists.', 'bbpress' ) );
2167 return;
2168 }
2169
2170 // What is the user doing here?
2171 if ( ! current_user_can( 'edit_topic', $topic_id ) || ( 'bbp_toggle_topic_trash' === $action && ! current_user_can( 'delete_topic', $topic_id ) ) || ( 'bbp_toggle_topic_trash' !== $action && ! current_user_can( 'moderate', $topic_id ) ) ) {
2172 bbp_add_error( 'bbp_toggle_topic_permission', __( '<strong>Error</strong>: You do not have permission to do that.', 'bbpress' ) );
2173 return;
2174 }
2175
2176 // Super stickies affect every forum and require global moderation
2177 if ( ( 'bbp_toggle_topic_stick' === $action ) && ! current_user_can( 'moderate' ) && ( bbp_is_topic_super_sticky( $topic_id ) || ( ! bbp_is_topic_sticky( $topic_id ) && ! empty( $_GET['super'] ) && ( '1' === $_GET['super'] ) ) ) ) {
2178 bbp_add_error( 'bbp_toggle_topic_permission', __( '<strong>Error</strong>: You do not have permission to do that.', 'bbpress' ) );
2179 return;
2180 }
2181
2182 // Sub-action?
2183 $sub_action = ! empty( $_GET['sub_action'] )
2184 ? sanitize_key( $_GET['sub_action'] )
2185 : false;
2186
2187 // Preliminary array
2188 $post_data = array( 'ID' => $topic_id );
2189
2190 // Do the topic toggling
2191 $retval = bbp_toggle_topic(
2192 array(
2193 'id' => $topic_id,
2194 'action' => $action,
2195 'sub_action' => $sub_action,
2196 'data' => $post_data
2197 )
2198 );
2199
2200 // Do additional topic toggle actions
2201 do_action( 'bbp_toggle_topic_handler', $retval['status'], $post_data, $action );
2202
2203 // No errors
2204 if ( ( false !== $retval['status'] ) && ! is_wp_error( $retval['status'] ) ) {
2205 bbp_redirect( $retval['redirect_to'] );
2206
2207 // Handle errors
2208 } else {
2209 bbp_add_error( 'bbp_toggle_topic', $retval['message'] );
2210 }
2211 }
2212
2213 /**
2214 * Do the actual topic toggling
2215 *
2216 * This function is used by `bbp_toggle_topic_handler()` to do the actual heavy
2217 * lifting when it comes to toggling topic. It only really makes sense to call
2218 * within that context, so if you need to call this function directly, make sure
2219 * you're also doing what the handler does too.
2220 *
2221 * @since 2.6.0 bbPress (r6142)
2222 * @access private
2223 *
2224 * @param array $args
2225 */
2226 function bbp_toggle_topic( $args = array() ) {
2227
2228 // Parse the arguments
2229 $r = bbp_parse_args(
2230 $args,
2231 array(
2232 'id' => 0,
2233 'action' => '',
2234 'sub_action' => '',
2235 'data' => array()
2236 )
2237 );
2238
2239 // Build the nonce suffix
2240 $nonce_suffix = bbp_get_topic_post_type() . '_' . (int) $r['id'];
2241
2242 // Default return values
2243 $retval = array(
2244 'status' => 0,
2245 'message' => '',
2246 'redirect_to' => bbp_get_topic_permalink( $r['id'], bbp_get_redirect_to() ),
2247 'view_all' => false
2248 );
2249
2250 // What action are we trying to perform?
2251 switch ( $r['action'] ) {
2252
2253 // Toggle approve/unapprove
2254 case 'bbp_toggle_topic_approve' :
2255 check_ajax_referer( "approve-{$nonce_suffix}" );
2256
2257 $is_pending = bbp_is_topic_pending( $r['id'] );
2258 $retval['view_all'] = ! $is_pending;
2259
2260 // Toggle
2261 $retval['status'] = ( true === $is_pending )
2262 ? bbp_approve_topic( $r['id'] )
2263 : bbp_unapprove_topic( $r['id'] );
2264
2265 // Feedback
2266 $retval['message'] = ( true === $is_pending )
2267 ? __( '<strong>Error</strong>: There was a problem approving the topic.', 'bbpress' )
2268 : __( '<strong>Error</strong>: There was a problem unapproving the topic.', 'bbpress' );
2269
2270 break;
2271
2272 // Toggle open/close
2273 case 'bbp_toggle_topic_close' :
2274 check_ajax_referer( "close-{$nonce_suffix}" );
2275
2276 $is_open = bbp_is_topic_open( $r['id'] );
2277
2278 // Toggle
2279 $retval['status'] = ( true === $is_open )
2280 ? bbp_close_topic( $r['id'] )
2281 : bbp_open_topic( $r['id'] );
2282
2283 // Feedback
2284 $retval['message'] = ( true === $is_open )
2285 ? __( '<strong>Error</strong>: There was a problem closing the topic.', 'bbpress' )
2286 : __( '<strong>Error</strong>: There was a problem opening the topic.', 'bbpress' );
2287
2288 break;
2289
2290 // Toggle sticky/super-sticky/unstick
2291 case 'bbp_toggle_topic_stick' :
2292 check_ajax_referer( "stick-{$nonce_suffix}" );
2293
2294 $is_sticky = bbp_is_topic_sticky( $r['id'] );
2295 $is_super = false === $is_sticky && ! empty( $_GET['super'] ) && ( '1' === $_GET['super'] ) ? true : false;
2296
2297 // Toggle
2298 $retval['status'] = ( true === $is_sticky )
2299 ? bbp_unstick_topic( $r['id'] )
2300 : bbp_stick_topic( $r['id'], $is_super );
2301
2302 // Feedback
2303 $retval['message'] = ( true === $is_sticky )
2304 ? __( '<strong>Error</strong>: There was a problem unsticking the topic.', 'bbpress' )
2305 : __( '<strong>Error</strong>: There was a problem sticking the topic.', 'bbpress' );
2306
2307 break;
2308
2309 // Toggle spam
2310 case 'bbp_toggle_topic_spam' :
2311 check_ajax_referer( "spam-{$nonce_suffix}" );
2312
2313 $is_spam = bbp_is_topic_spam( $r['id'] );
2314 $retval['view_all'] = ! $is_spam;
2315
2316 // Toggle
2317 $retval['status'] = ( true === $is_spam )
2318 ? bbp_unspam_topic( $r['id'] )
2319 : bbp_spam_topic( $r['id'] );
2320
2321 // Feedback
2322 $retval['message'] = ( true === $is_spam )
2323 ? __( '<strong>Error</strong>: There was a problem unmarking the topic as spam.', 'bbpress' )
2324 : __( '<strong>Error</strong>: There was a problem marking the topic as spam.', 'bbpress' );
2325
2326 break;
2327
2328 // Toggle trash
2329 case 'bbp_toggle_topic_trash' :
2330
2331 switch ( $r['sub_action'] ) {
2332 case 'trash':
2333 check_ajax_referer( "trash-{$nonce_suffix}" );
2334
2335 $retval['view_all'] = true;
2336 $retval['status'] = wp_trash_post( $r['id'] );
2337 $retval['message'] = __( '<strong>Error</strong>: There was a problem trashing the topic.', 'bbpress' );
2338 $retval['redirect_to'] = current_user_can( 'view_trash' )
2339 ? bbp_get_topic_permalink( $r['id'] )
2340 : bbp_get_forum_permalink( bbp_get_topic_forum_id( $r['id'] ) );
2341
2342 break;
2343
2344 case 'untrash':
2345 check_ajax_referer( "untrash-{$nonce_suffix}" );
2346
2347 $retval['status'] = wp_untrash_post( $r['id'] );
2348 $retval['message'] = __( '<strong>Error</strong>: There was a problem untrashing the topic.', 'bbpress' );
2349 $retval['redirect_to'] = bbp_get_topic_permalink( $r['id'] );
2350
2351 break;
2352
2353 case 'delete':
2354 check_ajax_referer( "delete-{$nonce_suffix}" );
2355
2356 $retval['status'] = wp_delete_post( $r['id'] );
2357 $retval['message'] = __( '<strong>Error</strong>: There was a problem deleting the topic.', 'bbpress' );
2358 $retval['redirect_to'] = bbp_get_forum_permalink( $retval['status']->post_parent );
2359
2360 break;
2361 }
2362
2363 break;
2364 }
2365
2366 // Add view all if needed
2367 if ( ! empty( $retval['view_all'] ) ) {
2368 $retval['redirect_to'] = bbp_add_view_all( $retval['redirect_to'], true );
2369 }
2370
2371 // Filter & return
2372 return apply_filters( 'bbp_toggle_topic', $retval, $r, $args );
2373 }
2374
2375 /** Favorites & Subscriptions *************************************************/
2376
2377 /**
2378 * Remove a deleted topic from all user favorites
2379 *
2380 * @since 2.0.0 bbPress (r2652)
2381 *
2382 * @param int $topic_id Get the topic id to remove
2383 */
2384 function bbp_remove_topic_from_all_favorites( $topic_id = 0 ) {
2385 $topic_id = bbp_get_topic_id( $topic_id );
2386
2387 // Bail if no topic
2388 if ( empty( $topic_id ) ) {
2389 return;
2390 }
2391
2392 // Get users
2393 $users = (array) bbp_get_topic_favoriters( $topic_id );
2394
2395 // Users exist
2396 if ( ! empty( $users ) ) {
2397
2398 // Loop through users
2399 foreach ( $users as $user ) {
2400
2401 // Remove each user
2402 bbp_remove_user_favorite( $user, $topic_id );
2403 }
2404 }
2405 }
2406
2407 /**
2408 * Remove a deleted topic from all user subscriptions
2409 *
2410 * @since 2.0.0 bbPress (r2668)
2411 *
2412 * @param int $topic_id Get the topic id to remove
2413 */
2414 function bbp_remove_topic_from_all_subscriptions( $topic_id = 0 ) {
2415
2416 // Subscriptions are not active
2417 if ( ! bbp_is_subscriptions_active() ) {
2418 return;
2419 }
2420
2421 // Bail if no topic
2422 $topic_id = bbp_get_topic_id( $topic_id );
2423 if ( empty( $topic_id ) ) {
2424 return;
2425 }
2426
2427 // Remove all users
2428 return bbp_remove_object_from_all_users( $topic_id, '_bbp_subscription', 'post' );
2429 }
2430
2431 /** Count Bumpers *************************************************************/
2432
2433 /**
2434 * Bump the total reply count of a topic
2435 *
2436 * @since 2.1.0 bbPress (r3826)
2437 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
2438 *
2439 * @param int $topic_id Optional. Topic id.
2440 * @param int $difference Optional. Default 1
2441 * @return int Topic reply count
2442 */
2443 function bbp_bump_topic_reply_count( $topic_id = 0, $difference = 1 ) {
2444
2445 // Bail if no bump
2446 if ( empty( $difference ) ) {
2447 return false;
2448 }
2449
2450 // Get counts
2451 $topic_id = bbp_get_topic_id( $topic_id );
2452 $reply_count = bbp_get_topic_reply_count( $topic_id, true );
2453 $difference = (int) $difference;
2454 $new_count = bbp_number_not_negative( $reply_count + $difference );
2455
2456 // Update this topic id's reply count
2457 bbp_bump_count_meta( 'post', $topic_id, '_bbp_reply_count', $difference, $reply_count );
2458
2459 // Filter & return
2460 return (int) apply_filters( 'bbp_bump_topic_reply_count', $new_count, $topic_id, $difference );
2461 }
2462
2463 /**
2464 * Increase the total reply count of a topic by one.
2465 *
2466 * @since 2.6.0 bbPress (r6036)
2467 *
2468 * @param int $topic_id The topic id.
2469 *
2470 * @return void
2471 */
2472 function bbp_increase_topic_reply_count( $topic_id = 0 ) {
2473
2474 // Bail early if no id is passed.
2475 if ( empty( $topic_id ) ) {
2476 return;
2477 }
2478
2479 // If it's a reply, get the topic id.
2480 if ( bbp_is_reply( $topic_id ) ) {
2481 $reply_id = $topic_id;
2482 $topic_id = bbp_get_reply_topic_id( $reply_id );
2483
2484 // Update inverse based on item status
2485 if ( ! bbp_is_reply_public( $reply_id ) ) {
2486 bbp_increase_topic_reply_count_hidden( $topic_id );
2487 return;
2488 }
2489 }
2490
2491 // Bump up
2492 bbp_bump_topic_reply_count( $topic_id );
2493 }
2494
2495 /**
2496 * Decrease the total reply count of a topic by one.
2497 *
2498 * @since 2.6.0 bbPress (r6036)
2499 *
2500 * @param int $topic_id The topic id.
2501 *
2502 * @return void
2503 */
2504 function bbp_decrease_topic_reply_count( $topic_id = 0 ) {
2505
2506 // Bail early if no id is passed.
2507 if ( empty( $topic_id ) ) {
2508 return;
2509 }
2510
2511 // If it's a reply, get the topic id.
2512 if ( bbp_is_reply( $topic_id ) ) {
2513 $reply_id = $topic_id;
2514 $topic_id = bbp_get_reply_topic_id( $reply_id );
2515
2516 // Update inverse based on item status
2517 if ( ! bbp_is_reply_public( $reply_id ) ) {
2518 bbp_decrease_topic_reply_count_hidden( $topic_id );
2519 return;
2520 }
2521 }
2522
2523 // Bump down
2524 bbp_bump_topic_reply_count( $topic_id, -1 );
2525 }
2526
2527 /**
2528 * Bump the total hidden reply count of a topic
2529 *
2530 * @since 2.1.0 bbPress (r3826)
2531 * @since 2.6.17 bbPress (r7468) Use atomic metadata writes and non-negative counts.
2532 *
2533 * @param int $topic_id Optional. Topic id.
2534 * @param int $difference Optional. Default 1
2535 * @return int Topic hidden reply count
2536 */
2537 function bbp_bump_topic_reply_count_hidden( $topic_id = 0, $difference = 1 ) {
2538
2539 // Bail if no bump
2540 if ( empty( $difference ) ) {
2541 return false;
2542 }
2543
2544 // Get counts
2545 $topic_id = bbp_get_topic_id( $topic_id );
2546 $reply_count = bbp_get_topic_reply_count_hidden( $topic_id, true );
2547 $difference = (int) $difference;
2548 $new_count = bbp_number_not_negative( $reply_count + $difference );
2549
2550 // Update this topic id's hidden reply count
2551 bbp_bump_count_meta( 'post', $topic_id, '_bbp_reply_count_hidden', $difference, $reply_count );
2552
2553 // Filter & return
2554 return (int) apply_filters( 'bbp_bump_topic_reply_count_hidden', $new_count, $topic_id, $difference );
2555 }
2556
2557 /**
2558 * Increase the total hidden reply count of a topic by one.
2559 *
2560 * @since 2.6.0 bbPress (r6036)
2561 *
2562 * @param int $topic_id The topic id.
2563 *
2564 * @return void
2565 */
2566 function bbp_increase_topic_reply_count_hidden( $topic_id = 0 ) {
2567
2568 // Bail early if no id is passed.
2569 if ( empty( $topic_id ) ) {
2570 return;
2571 }
2572
2573 // If it's a reply, get the topic id.
2574 if ( bbp_is_reply( $topic_id ) ) {
2575 $reply_id = $topic_id;
2576 $topic_id = bbp_get_reply_topic_id( $reply_id );
2577
2578 // Update inverse based on item status
2579 if ( bbp_is_reply_public( $reply_id ) ) {
2580 bbp_increase_topic_reply_count( $topic_id );
2581 return;
2582 }
2583 }
2584
2585 // Bump up
2586 bbp_bump_topic_reply_count_hidden( $topic_id );
2587 }
2588
2589 /**
2590 * Decrease the total hidden reply count of a topic by one.
2591 *
2592 * @since 2.6.0 bbPress (r6036)
2593 *
2594 * @param int $topic_id The topic id.
2595 *
2596 * @return void
2597 */
2598 function bbp_decrease_topic_reply_count_hidden( $topic_id = 0 ) {
2599
2600 // Bail early if no id is passed.
2601 if ( empty( $topic_id ) ) {
2602 return;
2603 }
2604
2605 // If it's a reply, get the topic id.
2606 if ( bbp_is_reply( $topic_id ) ) {
2607 $reply_id = $topic_id;
2608 $topic_id = bbp_get_reply_topic_id( $reply_id );
2609
2610 // Update inverse based on item status
2611 if ( bbp_is_reply_public( $reply_id ) ) {
2612 bbp_decrease_topic_reply_count( $topic_id );
2613 return;
2614 }
2615 }
2616
2617 // Bump down
2618 bbp_bump_topic_reply_count_hidden( $topic_id, -1 );
2619 }
2620
2621 /**
2622 * Update counts after a topic is inserted via `bbp_insert_topic`.
2623 *
2624 * @since 2.6.0 bbPress (r6036)
2625 *
2626 * @param int $topic_id The topic id.
2627 * @param int $forum_id The forum id.
2628 *
2629 * @return void
2630 */
2631 function bbp_insert_topic_update_counts( $topic_id = 0, $forum_id = 0 ) {
2632
2633 // If the topic is public, update the forum topic counts.
2634 if ( bbp_is_topic_public( $topic_id ) ) {
2635 bbp_increase_forum_topic_count( $forum_id );
2636
2637 // If the topic isn't public only update the forum topic hidden count.
2638 } else {
2639 bbp_increase_forum_topic_count_hidden( $forum_id );
2640 }
2641 }
2642
2643 /** Topic Updaters ************************************************************/
2644
2645 /**
2646 * Update the topic's forum id
2647 *
2648 * @since 2.0.0 bbPress (r2858)
2649 *
2650 * @param int $topic_id Optional. Topic id to update
2651 * @param int $forum_id Optional. Forum id
2652 * @return int Forum id
2653 */
2654 function bbp_update_topic_forum_id( $topic_id = 0, $forum_id = 0 ) {
2655
2656 // If it's a reply, then get the parent (topic id)
2657 $topic_id = bbp_is_reply( $topic_id )
2658 ? bbp_get_reply_topic_id( $topic_id )
2659 : bbp_get_topic_id( $topic_id );
2660
2661 // Forum ID fallback
2662 if ( empty( $forum_id ) ) {
2663 $forum_id = get_post_field( 'post_parent', $topic_id );
2664 }
2665
2666 // Update the forum ID
2667 $forum_id = bbp_update_forum_id( $topic_id, $forum_id );
2668
2669 // Filter & return
2670 return (int) apply_filters( 'bbp_update_topic_forum_id', $forum_id, $topic_id );
2671 }
2672
2673 /**
2674 * Update the topic's topic id
2675 *
2676 * @since 2.0.0 bbPress (r2955)
2677 *
2678 * @param int $topic_id Optional. Topic id to update
2679 * @return int Topic id
2680 */
2681 function bbp_update_topic_topic_id( $topic_id = 0 ) {
2682 $topic_id = bbp_get_topic_id( $topic_id );
2683 $topic_id = bbp_update_topic_id( $topic_id, $topic_id );
2684
2685 // Filter & return
2686 return (int) apply_filters( 'bbp_update_topic_topic_id', $topic_id );
2687 }
2688
2689 /**
2690 * Adjust the total reply count of a topic
2691 *
2692 * @since 2.0.0 bbPress (r2485)
2693 *
2694 * @param int $topic_id Optional. Topic id to update
2695 * @param int $reply_count Optional. Set the reply count manually.
2696 * @return int Topic reply count
2697 */
2698 function bbp_update_topic_reply_count( $topic_id = 0, $reply_count = false ) {
2699
2700 // If it's a reply, then get the parent (topic id)
2701 $topic_id = bbp_is_reply( $topic_id )
2702 ? bbp_get_reply_topic_id( $topic_id )
2703 : bbp_get_topic_id( $topic_id );
2704
2705 // Get replies of topic if not passed
2706 $reply_count = ! is_int( $reply_count )
2707 ? bbp_get_public_child_count( $topic_id, bbp_get_reply_post_type() )
2708 : (int) $reply_count;
2709
2710 update_post_meta( $topic_id, '_bbp_reply_count', $reply_count );
2711
2712 // Filter & return
2713 return (int) apply_filters( 'bbp_update_topic_reply_count', $reply_count, $topic_id );
2714 }
2715
2716 /**
2717 * Adjust the total hidden reply count of a topic (hidden includes trashed,
2718 * spammed and pending replies)
2719 *
2720 * @since 2.0.0 bbPress (r3349)
2721 *
2722 * @param int $topic_id Optional. Topic id to update
2723 * @param int $reply_count Optional. Set the reply count manually
2724 * @return int Topic hidden reply count
2725 */
2726 function bbp_update_topic_reply_count_hidden( $topic_id = 0, $reply_count = false ) {
2727
2728 // If it's a reply, then get the parent (topic id)
2729 $topic_id = bbp_is_reply( $topic_id )
2730 ? bbp_get_reply_topic_id( $topic_id )
2731 : bbp_get_topic_id( $topic_id );
2732
2733 // Get replies of topic
2734 $reply_count = ! is_int( $reply_count )
2735 ? bbp_get_non_public_child_count( $topic_id, bbp_get_reply_post_type() )
2736 : (int) $reply_count;
2737
2738 update_post_meta( $topic_id, '_bbp_reply_count_hidden', $reply_count );
2739
2740 // Filter & return
2741 return (int) apply_filters( 'bbp_update_topic_reply_count_hidden', $reply_count, $topic_id );
2742 }
2743
2744 /**
2745 * Update the topic with the last active post ID
2746 *
2747 * @since 2.0.0 bbPress (r2895)
2748 *
2749 * @param int $topic_id Optional. Topic id to update
2750 * @param int $active_id Optional. active id
2751 * @return int Active id
2752 */
2753 function bbp_update_topic_last_active_id( $topic_id = 0, $active_id = 0 ) {
2754
2755 // If it's a reply, then get the parent (topic id)
2756 $topic_id = bbp_is_reply( $topic_id )
2757 ? bbp_get_reply_topic_id( $topic_id )
2758 : bbp_get_topic_id( $topic_id );
2759
2760 // Get last public active id if not passed
2761 if ( empty( $active_id ) ) {
2762 $active_id = bbp_get_public_child_last_id( $topic_id, bbp_get_reply_post_type() );
2763 }
2764
2765 // Adjust last_id's based on last_reply post_type
2766 if ( empty( $active_id ) || ! bbp_is_reply( $active_id ) ) {
2767 $active_id = $topic_id;
2768 }
2769
2770 $active_id = (int) $active_id;
2771
2772 // Update only if published
2773 update_post_meta( $topic_id, '_bbp_last_active_id', $active_id );
2774
2775 // Filter & return
2776 return (int) apply_filters( 'bbp_update_topic_last_active_id', $active_id, $topic_id );
2777 }
2778
2779 /**
2780 * Update the topics last active date/time (aka freshness)
2781 *
2782 * @since 2.0.0 bbPress (r2895)
2783 *
2784 * @param int $topic_id Optional. Topic id.
2785 * @param string $new_time Optional. New time in mysql format.
2786 * @return string MySQL timestamp of last active reply
2787 */
2788 function bbp_update_topic_last_active_time( $topic_id = 0, $new_time = '' ) {
2789
2790 // If it's a reply, then get the parent (topic id)
2791 $topic_id = bbp_is_reply( $topic_id )
2792 ? bbp_get_reply_topic_id( $topic_id )
2793 : bbp_get_topic_id( $topic_id );
2794
2795 // Check time and use current if empty
2796 if ( empty( $new_time ) ) {
2797 $new_time = get_post_field( 'post_date', bbp_get_public_child_last_id( $topic_id, bbp_get_reply_post_type() ) );
2798 }
2799
2800 // Update only if published
2801 if ( ! empty( $new_time ) ) {
2802 update_post_meta( $topic_id, '_bbp_last_active_time', $new_time );
2803 }
2804
2805 // Filter & return
2806 return apply_filters( 'bbp_update_topic_last_active_time', $new_time, $topic_id );
2807 }
2808
2809 /**
2810 * Update the topic with the most recent reply ID
2811 *
2812 * @since 2.0.0 bbPress (r2627)
2813 *
2814 * @param int $topic_id Optional. Topic id to update
2815 * @param int $reply_id Optional. Reply id
2816 * @return int Reply id
2817 */
2818 function bbp_update_topic_last_reply_id( $topic_id = 0, $reply_id = 0 ) {
2819
2820 // If it's a reply, then get the parent (topic id)
2821 if ( empty( $reply_id ) && bbp_is_reply( $topic_id ) ) {
2822 $reply_id = bbp_get_reply_id( $topic_id );
2823 $topic_id = bbp_get_reply_topic_id( $reply_id );
2824 } else {
2825 $reply_id = bbp_get_reply_id( $reply_id );
2826 $topic_id = bbp_get_topic_id( $topic_id );
2827 }
2828
2829 if ( empty( $reply_id ) ) {
2830 $reply_id = bbp_get_public_child_last_id( $topic_id, bbp_get_reply_post_type() );
2831 }
2832
2833 // Adjust last_id's based on last_reply post_type
2834 if ( empty( $reply_id ) || ! bbp_is_reply( $reply_id ) ) {
2835 $reply_id = 0;
2836 }
2837
2838 $reply_id = (int) $reply_id;
2839
2840 // Update if reply is published
2841 update_post_meta( $topic_id, '_bbp_last_reply_id', $reply_id );
2842
2843 // Filter & return
2844 return (int) apply_filters( 'bbp_update_topic_last_reply_id', $reply_id, $topic_id );
2845 }
2846
2847 /**
2848 * Adjust the total voice count of a topic
2849 *
2850 * @since 2.0.0 bbPress (r2567)
2851 * @since 2.6.0 bbPress (r6515) This must be called after any engagement changes
2852 *
2853 * @param int $topic_id Optional. Topic id to update
2854 * @return int Voice count
2855 */
2856 function bbp_update_topic_voice_count( $topic_id = 0 ) {
2857
2858 // If it's a reply, then get the parent (topic id)
2859 $topic_id = bbp_is_reply( $topic_id )
2860 ? bbp_get_reply_topic_id( $topic_id )
2861 : bbp_get_topic_id( $topic_id );
2862
2863 // Bail if no topic ID
2864 if ( empty( $topic_id ) ) {
2865 return;
2866 }
2867
2868 // Count the engagements
2869 $count = count( bbp_get_topic_engagements( $topic_id ) );
2870
2871 // Update the voice count for this topic id
2872 update_post_meta( $topic_id, '_bbp_voice_count', $count );
2873
2874 // Filter & return
2875 return (int) apply_filters( 'bbp_update_topic_voice_count', $count, $topic_id );
2876 }
2877
2878 /**
2879 * Adjust the total anonymous reply count of a topic
2880 *
2881 * @since 2.0.0 bbPress (r2918)
2882 *
2883 * @param int $topic_id Optional. Topic id to update
2884 * @return int Anonymous reply count
2885 */
2886 function bbp_update_topic_anonymous_reply_count( $topic_id = 0 ) {
2887
2888 // If it's a reply, then get the parent (topic id)
2889 $topic_id = bbp_is_reply( $topic_id )
2890 ? bbp_get_reply_topic_id( $topic_id )
2891 : bbp_get_topic_id( $topic_id );
2892
2893 // Query the DB to get anonymous replies in this topic
2894 $bbp_db = bbp_db();
2895 $query = $bbp_db->prepare( "SELECT COUNT( ID ) FROM {$bbp_db->posts} WHERE ( post_parent = %d AND post_status = %s AND post_type = %s AND post_author = 0 ) OR ( ID = %d AND post_type = %s AND post_author = 0 )", $topic_id, bbp_get_public_status_id(), bbp_get_reply_post_type(), $topic_id, bbp_get_topic_post_type() );
2896 $replies = (int) $bbp_db->get_var( $query );
2897
2898 update_post_meta( $topic_id, '_bbp_anonymous_reply_count', $replies );
2899
2900 // Filter & return
2901 return (int) apply_filters( 'bbp_update_topic_anonymous_reply_count', $replies, $topic_id );
2902 }
2903
2904 /**
2905 * Update the revision log of the topic
2906 *
2907 * @since 2.0.0 bbPress (r2782)
2908 *
2909 * @param array $args Supports these args:
2910 * - topic_id: Topic id
2911 * - author_id: Author id
2912 * - reason: Reason for editing
2913 * - revision_id: Revision id
2914 * @return mixed False on failure, true on success
2915 */
2916 function bbp_update_topic_revision_log( $args = array() ) {
2917
2918 // Parse arguments against default values
2919 $r = bbp_parse_args(
2920 $args,
2921 array(
2922 'reason' => '',
2923 'topic_id' => 0,
2924 'author_id' => 0,
2925 'revision_id' => 0
2926 ),
2927 'update_topic_revision_log'
2928 );
2929
2930 // Populate the variables
2931 $r['reason'] = bbp_format_revision_reason( $r['reason'] );
2932 $r['topic_id'] = bbp_get_topic_id( $r['topic_id'] );
2933 $r['author_id'] = bbp_get_user_id ( $r['author_id'], false, true );
2934 $r['revision_id'] = (int) $r['revision_id'];
2935
2936 // Get the logs and append the new one to those
2937 $revision_log = bbp_get_topic_raw_revision_log( $r['topic_id'] );
2938 $revision_log[ $r['revision_id'] ] = array(
2939 'author' => $r['author_id'],
2940 'reason' => $r['reason']
2941 );
2942
2943 // Return
2944 return update_post_meta( $r['topic_id'], '_bbp_revision_log', $revision_log );
2945 }
2946
2947 /** Topic Actions *************************************************************/
2948
2949 /**
2950 * Closes a topic
2951 *
2952 * @since 2.0.0 bbPress (r2740)
2953 *
2954 * @param int $topic_id Topic id
2955 * @return mixed False or {@link WP_Error} on failure, topic id on success
2956 */
2957 function bbp_close_topic( $topic_id = 0 ) {
2958
2959 // Get topic
2960 $topic = bbp_get_topic( $topic_id );
2961 if ( empty( $topic ) ) {
2962 return $topic;
2963 }
2964
2965 // Get previous topic status meta
2966 $status = bbp_get_closed_status_id();
2967 $topic_status = get_post_meta( $topic_id, '_bbp_status', true );
2968
2969 // Bail if already closed and topic status meta exists
2970 if ( $status === $topic->post_status && ! empty( $topic_status ) ) {
2971 return false;
2972 }
2973
2974 // Set status meta public
2975 $topic_status = $topic->post_status;
2976
2977 // Execute pre close code
2978 do_action( 'bbp_close_topic', $topic_id );
2979
2980 // Add pre close status
2981 add_post_meta( $topic_id, '_bbp_status', $topic_status );
2982
2983 // Set closed status
2984 $topic->post_status = $status;
2985
2986 // Toggle revisions off as we are not altering content
2987 if ( post_type_supports( bbp_get_topic_post_type(), 'revisions' ) ) {
2988 $revisions_removed = true;
2989 remove_post_type_support( bbp_get_topic_post_type(), 'revisions' );
2990 }
2991
2992 // Update topic
2993 $topic_id = wp_update_post( $topic );
2994
2995 // Toggle revisions back on
2996 if ( true === $revisions_removed ) {
2997 $revisions_removed = false;
2998 add_post_type_support( bbp_get_topic_post_type(), 'revisions' );
2999 }
3000
3001 // Execute post close code
3002 do_action( 'bbp_closed_topic', $topic_id );
3003
3004 // Return topic_id
3005 return $topic_id;
3006 }
3007
3008 /**
3009 * Opens a topic
3010 *
3011 * @since 2.0.0 bbPress (r2740)
3012 *
3013 * @param int $topic_id Topic id
3014 * @return mixed False or {@link WP_Error} on failure, topic id on success
3015 */
3016 function bbp_open_topic( $topic_id = 0 ) {
3017
3018 // Get topic
3019 $topic = bbp_get_topic( $topic_id );
3020 if ( empty( $topic ) ) {
3021 return $topic;
3022 }
3023
3024 // Bail if not closed
3025 if ( bbp_get_closed_status_id() !== $topic->post_status ) {
3026 return false;
3027 }
3028
3029 // Execute pre open code
3030 do_action( 'bbp_open_topic', $topic_id );
3031
3032 // Get previous status
3033 $topic_status = get_post_meta( $topic_id, '_bbp_status', true );
3034
3035 // If no previous status, default to publish
3036 if ( empty( $topic_status ) ) {
3037 $topic_status = bbp_get_public_status_id();
3038 }
3039
3040 // Set previous status
3041 $topic->post_status = $topic_status;
3042
3043 // Remove old status meta
3044 delete_post_meta( $topic_id, '_bbp_status' );
3045
3046 // Toggle revisions off as we are not altering content
3047 if ( post_type_supports( bbp_get_topic_post_type(), 'revisions' ) ) {
3048 $revisions_removed = true;
3049 remove_post_type_support( bbp_get_topic_post_type(), 'revisions' );
3050 }
3051
3052 // Update topic
3053 $topic_id = wp_update_post( $topic );
3054
3055 // Toggle revisions back on
3056 if ( true === $revisions_removed ) {
3057 $revisions_removed = false;
3058 add_post_type_support( bbp_get_topic_post_type(), 'revisions' );
3059 }
3060
3061 // Execute post open code
3062 do_action( 'bbp_opened_topic', $topic_id );
3063
3064 // Return topic_id
3065 return $topic_id;
3066 }
3067
3068 /**
3069 * Marks a topic as spam
3070 *
3071 * @since 2.0.0 bbPress (r2740)
3072 *
3073 * @param int $topic_id Topic id
3074 * @return mixed False or {@link WP_Error} on failure, topic id on success
3075 */
3076 function bbp_spam_topic( $topic_id = 0 ) {
3077
3078 // Get the topic
3079 $topic = bbp_get_topic( $topic_id );
3080 if ( empty( $topic ) ) {
3081 return $topic;
3082 }
3083
3084 // Get new status
3085 $status = bbp_get_spam_status_id();
3086
3087 // Bail if topic is spam
3088 if ( $status === $topic->post_status ) {
3089 return false;
3090 }
3091
3092 // Add the original post status as post meta for future restoration
3093 add_post_meta( $topic_id, '_bbp_spam_meta_status', $topic->post_status );
3094
3095 // Execute pre spam code
3096 do_action( 'bbp_spam_topic', $topic_id );
3097
3098 // Set post status to spam
3099 $topic->post_status = $status;
3100
3101 // Empty the topic of its tags
3102 $topic->tax_input = bbp_spam_topic_tags( $topic_id );
3103
3104 // No revisions
3105 remove_action( 'pre_post_update', 'wp_save_post_revision' );
3106
3107 // Update the topic
3108 $topic_id = wp_update_post( $topic );
3109
3110 // Execute post spam code
3111 do_action( 'bbp_spammed_topic', $topic_id );
3112
3113 // Return topic_id
3114 return $topic_id;
3115 }
3116
3117 /**
3118 * Trash replies to a topic when it's marked as spam
3119 *
3120 * Usually you'll want to do this before the topic itself is marked as spam.
3121 *
3122 * @since 2.6.0 bbPress (r5406)
3123 *
3124 * @param int $topic_id
3125 */
3126 function bbp_spam_topic_replies( $topic_id = 0 ) {
3127
3128 // Validation
3129 $topic_id = bbp_get_topic_id( $topic_id );
3130
3131 // Topic is being spammed, so its replies are trashed
3132 $replies = new WP_Query(
3133 array(
3134 'fields' => 'id=>parent',
3135 'post_type' => bbp_get_reply_post_type(),
3136 'post_status' => bbp_get_public_status_id(),
3137 'post_parent' => $topic_id,
3138 'posts_per_page' => -1,
3139
3140 // Performance
3141 'nopaging' => true,
3142 'suppress_filters' => true,
3143 'update_post_term_cache' => false,
3144 'update_post_meta_cache' => false,
3145 'ignore_sticky_posts' => true,
3146 'no_found_rows' => true
3147 )
3148 );
3149
3150 if ( ! empty( $replies->posts ) ) {
3151
3152 // Prevent debug notices
3153 $pre_spammed_replies = array();
3154
3155 // Loop through replies, trash them, and add them to array
3156 foreach ( $replies->posts as $reply ) {
3157 wp_trash_post( $reply->ID );
3158 $pre_spammed_replies[] = $reply->ID;
3159 }
3160
3161 // Set a post_meta entry of the replies that were trashed by this action.
3162 // This is so we can possibly untrash them, without untrashing replies
3163 // that were purposefully trashed before.
3164 update_post_meta( $topic_id, '_bbp_pre_spammed_replies', $pre_spammed_replies );
3165
3166 // Reset the global post data after looping through the above WP_Query
3167 wp_reset_postdata();
3168 }
3169 }
3170
3171 /**
3172 * Store the tags to a topic in post meta before it's marked as spam so they
3173 * can be retrieved and unspammed later.
3174 *
3175 * Usually you'll want to do this before the topic itself is marked as spam.
3176 *
3177 * @since 2.6.0 bbPress (r5406)
3178 *
3179 * @param int $topic_id
3180 */
3181 function bbp_spam_topic_tags( $topic_id = 0 ) {
3182
3183 // Validation
3184 $topic_id = bbp_get_topic_id( $topic_id );
3185
3186 // Get topic tags
3187 $terms = get_the_terms( $topic_id, bbp_get_topic_tag_tax_id() );
3188
3189 // Define local variable(s)
3190 $term_names = array();
3191
3192 // Topic has tags
3193 if ( ! empty( $terms ) ) {
3194
3195 // Loop through and collect term names
3196 foreach ( $terms as $term ) {
3197 $term_names[] = $term->name;
3198 }
3199
3200 // Topic terms have slugs
3201 if ( ! empty( $term_names ) ) {
3202
3203 // Add the original post status as post meta for future restoration
3204 add_post_meta( $topic_id, '_bbp_spam_topic_tags', $term_names );
3205 }
3206 }
3207
3208 return array( bbp_get_topic_tag_tax_id() => '' );
3209 }
3210
3211 /**
3212 * Unspams a topic
3213 *
3214 * @since 2.0.0 bbPress (r2740)
3215 *
3216 * @param int $topic_id Topic id
3217 * @return mixed False or {@link WP_Error} on failure, topic id on success
3218 */
3219 function bbp_unspam_topic( $topic_id = 0 ) {
3220
3221 // Get the topic
3222 $topic = bbp_get_topic( $topic_id );
3223 if ( empty( $topic ) ) {
3224 return $topic;
3225 }
3226
3227 // Bail if already not spam
3228 if ( bbp_get_spam_status_id() !== $topic->post_status ) {
3229 return false;
3230 }
3231
3232 // Execute pre unspam code
3233 do_action( 'bbp_unspam_topic', $topic_id );
3234
3235 // Get pre spam status
3236 $topic_status = get_post_meta( $topic_id, '_bbp_spam_meta_status', true );
3237
3238 // If no previous status, default to publish
3239 if ( empty( $topic_status ) ) {
3240 $topic_status = bbp_get_public_status_id();
3241 }
3242
3243 // Set post status to pre spam
3244 $topic->post_status = $topic_status;
3245 $topic->tax_input = bbp_unspam_topic_tags( $topic_id );
3246
3247 // Delete pre spam meta
3248 delete_post_meta( $topic_id, '_bbp_spam_meta_status' );
3249
3250 // No revisions
3251 remove_action( 'pre_post_update', 'wp_save_post_revision' );
3252
3253 // Update the topic
3254 $topic_id = wp_update_post( $topic );
3255
3256 // Execute post unspam code
3257 do_action( 'bbp_unspammed_topic', $topic_id );
3258
3259 // Return topic_id
3260 return $topic_id;
3261 }
3262
3263 /**
3264 * Untrash replies to a topic previously marked as spam.
3265 *
3266 * Usually you'll want to do this after the topic is unspammed.
3267 *
3268 * @since 2.6.0 bbPress (r5406)
3269 *
3270 * @param int $topic_id
3271 */
3272 function bbp_unspam_topic_replies( $topic_id = 0 ) {
3273
3274 // Validation
3275 $topic_id = bbp_get_topic_id( $topic_id );
3276
3277 // Get the replies that were not previously trashed
3278 $pre_spammed_replies = get_post_meta( $topic_id, '_bbp_pre_spammed_replies', true );
3279
3280 // There are replies to untrash
3281 if ( ! empty( $pre_spammed_replies ) ) {
3282
3283 // Maybe reverse the trashed replies array
3284 if ( is_array( $pre_spammed_replies ) ) {
3285 $pre_spammed_replies = array_reverse( $pre_spammed_replies );
3286 }
3287
3288 // Loop through replies
3289 foreach ( (array) $pre_spammed_replies as $reply ) {
3290 wp_untrash_post( $reply );
3291 }
3292 }
3293
3294 // Clear the trasheed reply meta for the topic
3295 delete_post_meta( $topic_id, '_bbp_pre_spammed_replies' );
3296 }
3297
3298 /**
3299 * Retrieve tags to a topic from post meta before it's unmarked as spam so they.
3300 *
3301 * Usually you'll want to do this before the topic itself is unmarked as spam.
3302 *
3303 * @since 2.6.0 bbPress (r5406)
3304 *
3305 * @param int $topic_id
3306 */
3307 function bbp_unspam_topic_tags( $topic_id = 0 ) {
3308
3309 // Validation
3310 $topic_id = bbp_get_topic_id( $topic_id );
3311
3312 // Get pre-spam topic tags
3313 $terms = get_post_meta( $topic_id, '_bbp_spam_topic_tags', true );
3314
3315 // Delete pre-spam topic tag meta
3316 if ( ! empty( $terms ) ) {
3317 delete_post_meta( $topic_id, '_bbp_spam_topic_tags' );
3318 }
3319
3320 return array( bbp_get_topic_tag_tax_id() => $terms );
3321 }
3322
3323 /**
3324 * Sticks a topic to a forum or front
3325 *
3326 * @since 2.0.0 bbPress (r2754)
3327 *
3328 * @param int $topic_id Optional. Topic id
3329 * @param int $super Should we make the topic a super sticky?
3330 * @return bool True on success, false on failure
3331 */
3332 function bbp_stick_topic( $topic_id = 0, $super = false ) {
3333
3334 // Validation
3335 $topic_id = bbp_get_topic_id( $topic_id );
3336
3337 // Bail if a topic is not a topic (prevents revisions as stickies)
3338 if ( ! bbp_is_topic( $topic_id ) ) {
3339 return false;
3340 }
3341
3342 do_action( 'bbp_stick_topic', $topic_id, $super );
3343
3344 // Maybe get the forum ID if not getting supers
3345 $forum_id = empty( $super )
3346 ? bbp_get_topic_forum_id( $topic_id )
3347 : 0;
3348
3349 // Get the stickies, maybe from the forum ID
3350 $stickies = bbp_get_stickies( $forum_id );
3351
3352 // Add the topic to the stickies
3353 $stickies[] = $topic_id;
3354
3355 // Pull out duplicates and empties
3356 $stickies = array_unique( array_filter( $stickies ) );
3357
3358 // Unset incorrectly stuck revisions
3359 foreach ( (array) $stickies as $key => $id ) {
3360 if ( ! bbp_is_topic( $id ) ) {
3361 unset( $stickies[ $key ] );
3362 }
3363 }
3364
3365 // Reset keys
3366 $stickies = array_values( $stickies );
3367
3368 // Update
3369 $success = ! empty( $super )
3370 ? update_option( '_bbp_super_sticky_topics', $stickies )
3371 : update_post_meta( $forum_id, '_bbp_sticky_topics', $stickies );
3372
3373 do_action( 'bbp_stuck_topic', $topic_id, $super, $success );
3374
3375 return (bool) $success;
3376 }
3377
3378 /**
3379 * Approves a pending topic
3380 *
3381 * @since 2.6.0 bbPress (r5503)
3382 *
3383 * @param int $topic_id Topic id
3384 * @return mixed False or {@link WP_Error} on failure, topic id on success
3385 */
3386 function bbp_approve_topic( $topic_id = 0 ) {
3387
3388 // Get topic
3389 $topic = bbp_get_topic( $topic_id );
3390 if ( empty( $topic ) ) {
3391 return $topic;
3392 }
3393
3394 // Get new status
3395 $status = bbp_get_public_status_id();
3396
3397 // Bail if already approved
3398 if ( $status === $topic->post_status ) {
3399 return false;
3400 }
3401
3402 // Execute pre pending code
3403 do_action( 'bbp_approve_topic', $topic_id );
3404
3405 // Set publish status
3406 $topic->post_status = $status;
3407
3408 // Set post date GMT - prevents post_date override in wp_update_post()
3409 $topic->post_date_gmt = get_gmt_from_date( $topic->post_date );
3410
3411 // No revisions
3412 remove_action( 'pre_post_update', 'wp_save_post_revision' );
3413
3414 // Update topic
3415 $topic_id = wp_update_post( $topic );
3416
3417 // Execute post pending code
3418 do_action( 'bbp_approved_topic', $topic_id );
3419
3420 // Return topic_id
3421 return $topic_id;
3422 }
3423
3424 /**
3425 * Unapproves a topic
3426 *
3427 * @since 2.6.0 bbPress (r5503)
3428 *
3429 * @param int $topic_id Topic id
3430 * @return mixed False or {@link WP_Error} on failure, topic id on success
3431 */
3432 function bbp_unapprove_topic( $topic_id = 0 ) {
3433
3434 // Get topic
3435 $topic = bbp_get_topic( $topic_id );
3436 if ( empty( $topic ) ) {
3437 return $topic;
3438 }
3439
3440 // Get new status
3441 $status = bbp_get_pending_status_id();
3442
3443 // Bail if already unapproved
3444 if ( ! bbp_is_topic_public( $topic_id ) ) {
3445 return false;
3446 }
3447
3448 // Execute pre open code
3449 do_action( 'bbp_unapprove_topic', $topic_id );
3450
3451 // Set pending status
3452 $topic->post_status = $status;
3453
3454 // No revisions
3455 remove_action( 'pre_post_update', 'wp_save_post_revision' );
3456
3457 // Update topic
3458 $topic_id = wp_update_post( $topic );
3459
3460 // Execute post open code
3461 do_action( 'bbp_unapproved_topic', $topic_id );
3462
3463 // Return topic_id
3464 return $topic_id;
3465 }
3466
3467 /**
3468 * Unsticks a topic both from front and it's forum
3469 *
3470 * @since 2.0.0 bbPress (r2754)
3471 *
3472 * @param int $topic_id Optional. Topic id
3473 * @return bool Always true.
3474 */
3475 function bbp_unstick_topic( $topic_id = 0 ) {
3476
3477 // Get topic sticky status
3478 $topic_id = bbp_get_topic_id( $topic_id );
3479 $super = bbp_is_topic_super_sticky( $topic_id );
3480 $forum_id = empty( $super ) ? bbp_get_topic_forum_id( $topic_id ) : 0;
3481 $stickies = bbp_get_stickies( $forum_id );
3482 $offset = array_search( $topic_id, $stickies );
3483
3484 do_action( 'bbp_unstick_topic', $topic_id );
3485
3486 // Nothing to unstick
3487 if ( empty( $stickies ) ) {
3488 $success = true;
3489
3490 // Topic not in stickies
3491 } elseif ( ! in_array( $topic_id, $stickies, true ) ) {
3492 $success = true;
3493
3494 // Topic not in stickies
3495 } elseif ( false === $offset ) {
3496 $success = true;
3497
3498 // Splice out the offset
3499 } else {
3500 array_splice( $stickies, $offset, 1 );
3501
3502 if ( empty( $stickies ) ) {
3503 $success = ! empty( $super )
3504 ? delete_option( '_bbp_super_sticky_topics' )
3505 : delete_post_meta( $forum_id, '_bbp_sticky_topics' );
3506 } else {
3507 $success = ! empty( $super )
3508 ? update_option( '_bbp_super_sticky_topics', $stickies )
3509 : update_post_meta( $forum_id, '_bbp_sticky_topics', $stickies );
3510 }
3511 }
3512
3513 do_action( 'bbp_unstuck_topic', $topic_id, $success );
3514
3515 return (bool) $success;
3516 }
3517
3518 /** Before Delete/Trash/Untrash ***********************************************/
3519
3520 /**
3521 * Called before deleting a topic.
3522 *
3523 * This function is supplemental to the actual topic deletion which is
3524 * handled by WordPress core API functions. It is used to clean up after
3525 * a topic that is being deleted.
3526 */
3527 function bbp_delete_topic( $topic_id = 0 ) {
3528
3529 // Validate topic ID
3530 $topic_id = bbp_get_topic_id( $topic_id );
3531
3532 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3533 return false;
3534 }
3535
3536 do_action( 'bbp_delete_topic', $topic_id );
3537 }
3538
3539 /**
3540 * Delete replies to a topic when it's deleted
3541 *
3542 * Usually you'll want to do this before the topic itself is deleted.
3543 *
3544 * @since 2.6.0 bbPress (r5406)
3545 *
3546 * @param int $topic_id
3547 */
3548 function bbp_delete_topic_replies( $topic_id = 0 ) {
3549
3550 // Validate topic ID
3551 $topic_id = bbp_get_topic_id( $topic_id );
3552
3553 // Topic is being permanently deleted, so its replies gotta go too
3554 // Note that we get all post statuses here
3555 $replies = new WP_Query(
3556 array(
3557 'fields' => 'id=>parent',
3558 'post_type' => bbp_get_reply_post_type(),
3559 'post_status' => array_keys( get_post_stati() ),
3560 'post_parent' => $topic_id,
3561 'posts_per_page' => -1,
3562
3563 // Performance
3564 'nopaging' => true,
3565 'suppress_filters' => true,
3566 'update_post_term_cache' => false,
3567 'update_post_meta_cache' => false,
3568 'ignore_sticky_posts' => true,
3569 'no_found_rows' => true
3570 )
3571 );
3572
3573 // Loop through and delete child replies
3574 if ( ! empty( $replies->posts ) ) {
3575 foreach ( $replies->posts as $reply ) {
3576 wp_delete_post( $reply->ID, true );
3577 }
3578
3579 // Reset the $post global
3580 wp_reset_postdata();
3581 }
3582 }
3583
3584 /**
3585 * Called before trashing a topic
3586 *
3587 * This function is supplemental to the actual topic being trashed which is
3588 * handled by WordPress core API functions. It is used to clean up after
3589 * a topic that is being trashed.
3590 */
3591 function bbp_trash_topic( $topic_id = 0 ) {
3592
3593 // Validate topic ID
3594 $topic_id = bbp_get_topic_id( $topic_id );
3595
3596 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3597 return false;
3598 }
3599
3600 do_action( 'bbp_trash_topic', $topic_id );
3601 }
3602
3603 /**
3604 * Trash replies to a topic when it's trashed.
3605 *
3606 * Usually you'll want to do this before the topic itself is marked as spam.
3607 *
3608 * @since 2.6.0 bbPress (r5406)
3609 *
3610 * @param int $topic_id
3611 */
3612 function bbp_trash_topic_replies( $topic_id = 0 ) {
3613
3614 // Validate topic ID
3615 $topic_id = bbp_get_topic_id( $topic_id );
3616
3617 // Topic is being trashed, so its replies are trashed too
3618 $replies = new WP_Query(
3619 array(
3620 'fields' => 'id=>parent',
3621 'post_type' => bbp_get_reply_post_type(),
3622 'post_status' => bbp_get_public_status_id(),
3623 'post_parent' => $topic_id,
3624 'posts_per_page' => -1,
3625
3626 // Performance
3627 'nopaging' => true,
3628 'suppress_filters' => true,
3629 'update_post_term_cache' => false,
3630 'update_post_meta_cache' => false,
3631 'ignore_sticky_posts' => true,
3632 'no_found_rows' => true
3633 )
3634 );
3635
3636 if ( ! empty( $replies->posts ) ) {
3637
3638 // Prevent debug notices
3639 $pre_trashed_replies = array();
3640
3641 // Loop through replies, trash them, and add them to array
3642 foreach ( $replies->posts as $reply ) {
3643 wp_trash_post( $reply->ID );
3644 $pre_trashed_replies[] = $reply->ID;
3645 }
3646
3647 // Set a post_meta entry of the replies that were trashed by this action.
3648 // This is so we can possibly untrash them, without untrashing replies
3649 // that were purposefully trashed before.
3650 update_post_meta( $topic_id, '_bbp_pre_trashed_replies', $pre_trashed_replies );
3651
3652 // Reset the $post global
3653 wp_reset_postdata();
3654 }
3655 }
3656
3657 /**
3658 * Called before untrashing a topic
3659 */
3660 function bbp_untrash_topic( $topic_id = 0 ) {
3661 $topic_id = bbp_get_topic_id( $topic_id );
3662
3663 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3664 return false;
3665 }
3666
3667 do_action( 'bbp_untrash_topic', $topic_id );
3668 }
3669
3670 /**
3671 * Untrash replies to a topic previously trashed.
3672 *
3673 * Usually you'll want to do this after the topic is unspammed.
3674 *
3675 * @since 2.6.0 bbPress (r5406)
3676 *
3677 * @param int $topic_id
3678 */
3679 function bbp_untrash_topic_replies( $topic_id = 0 ) {
3680
3681 // Validation
3682 $topic_id = bbp_get_topic_id( $topic_id );
3683
3684 // Get the replies that were not previously trashed
3685 $pre_trashed_replies = get_post_meta( $topic_id, '_bbp_pre_trashed_replies', true );
3686
3687 // There are replies to untrash
3688 if ( ! empty( $pre_trashed_replies ) ) {
3689
3690 // Maybe reverse the trashed replies array
3691 if ( is_array( $pre_trashed_replies ) ) {
3692 $pre_trashed_replies = array_reverse( $pre_trashed_replies );
3693 }
3694
3695 // Loop through replies
3696 foreach ( (array) $pre_trashed_replies as $reply ) {
3697 wp_untrash_post( $reply );
3698 }
3699 }
3700
3701 // Clear the trashed reply meta for the topic
3702 delete_post_meta( $topic_id, '_bbp_pre_trashed_replies' );
3703 }
3704
3705 /** After Delete/Trash/Untrash ************************************************/
3706
3707 /**
3708 * Called after deleting a topic
3709 *
3710 * @since 2.0.0 bbPress (r2895)
3711 */
3712 function bbp_deleted_topic( $topic_id = 0 ) {
3713 $topic_id = bbp_get_topic_id( $topic_id );
3714
3715 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3716 return false;
3717 }
3718
3719 do_action( 'bbp_deleted_topic', $topic_id );
3720 }
3721
3722 /**
3723 * Called after trashing a topic
3724 *
3725 * @since 2.0.0 bbPress (r2895)
3726 */
3727 function bbp_trashed_topic( $topic_id = 0 ) {
3728 $topic_id = bbp_get_topic_id( $topic_id );
3729
3730 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3731 return false;
3732 }
3733
3734 do_action( 'bbp_trashed_topic', $topic_id );
3735 }
3736
3737 /**
3738 * Called after untrashing a topic
3739 *
3740 * @since 2.0.0 bbPress (r2895)
3741 */
3742 function bbp_untrashed_topic( $topic_id = 0 ) {
3743 $topic_id = bbp_get_topic_id( $topic_id );
3744
3745 if ( empty( $topic_id ) || ! bbp_is_topic( $topic_id ) ) {
3746 return false;
3747 }
3748
3749 do_action( 'bbp_untrashed_topic', $topic_id );
3750 }
3751
3752 /** Settings ******************************************************************/
3753
3754 /**
3755 * Return the topics per page setting
3756 *
3757 * @since 2.1.0 bbPress (r3572)
3758 * @return int
3759 */
3760 function bbp_get_topics_per_page( $default = 15 ) {
3761
3762 // Get database option and cast as integer
3763 $retval = get_option( '_bbp_topics_per_page', $default );
3764
3765 // If return val is empty, set it to default
3766 if ( empty( $retval ) ) {
3767 $retval = $default;
3768 }
3769
3770 // Filter & return
3771 return (int) apply_filters( 'bbp_get_topics_per_page', $retval, $default );
3772 }
3773
3774 /**
3775 * Return the topics per RSS page setting
3776 *
3777 * @since 2.1.0 bbPress (r3572)
3778 *
3779 * @param int $default Default replies per page (25)
3780 * @return int
3781 */
3782 function bbp_get_topics_per_rss_page( $default = 25 ) {
3783
3784 // Get database option and cast as integer
3785 $retval = get_option( '_bbp_topics_per_rss_page', $default );
3786
3787 // If return val is empty, set it to default
3788 if ( empty( $retval ) ) {
3789 $retval = $default;
3790 }
3791
3792 // Filter & return
3793 return (int) apply_filters( 'bbp_get_topics_per_rss_page', $retval, $default );
3794 }
3795
3796 /** Topic Tags ****************************************************************/
3797
3798 /**
3799 * Get topic tags for a specific topic ID
3800 *
3801 * @since 2.6.0 bbPress (r5837)
3802 *
3803 * @param int $topic_id
3804 *
3805 * @return string
3806 */
3807 function bbp_get_topic_tags( $topic_id = 0 ) {
3808 $topic_id = bbp_get_topic_id( $topic_id );
3809 $terms = (array) get_the_terms( $topic_id, bbp_get_topic_tag_tax_id() );
3810 $topic_tags = array_filter( $terms );
3811
3812 // Filter & return
3813 return apply_filters( 'bbp_get_topic_tags', $topic_tags, $topic_id );
3814 }
3815
3816 /**
3817 * Get topic tags for a specific topic ID
3818 *
3819 * @since 2.2.0 bbPress (r4166)
3820 *
3821 * @param int $topic_id
3822 * @param string $sep
3823 *
3824 * @return string
3825 */
3826 function bbp_get_topic_tag_names( $topic_id = 0, $sep = ', ' ) {
3827 $topic_tags = bbp_get_topic_tags( $topic_id );
3828 $pluck = wp_list_pluck( $topic_tags, 'name' );
3829 $terms = ! empty( $pluck )
3830 ? implode( $sep, $pluck )
3831 : '';
3832
3833 // Filter & return
3834 return apply_filters( 'bbp_get_topic_tag_names', $terms, $topic_id, $sep );
3835 }
3836
3837 /**
3838 * Get the topic-tag names a user is allowed to set on a topic.
3839 *
3840 * @since 2.6.17 bbPress (r7510)
3841 *
3842 * @param int $topic_id Topic id.
3843 * @param string $tag_names Comma-separated topic-tag names.
3844 * @param int|bool $user_id User id. Default false for the current user.
3845 *
3846 * @return string Comma-separated topic-tag names.
3847 */
3848 function bbp_get_topic_tag_names_for_update( $topic_id = 0, $tag_names = '', $user_id = false ) {
3849 $topic_id = bbp_get_topic_id( $topic_id );
3850 $user_id = ( false === $user_id )
3851 ? bbp_get_current_user_id()
3852 : absint( $user_id );
3853 $existing_tags = bbp_get_topic_tags( $topic_id );
3854 $existing_names = wp_list_pluck( $existing_tags, 'name' );
3855
3856 // Preserve existing tags for malformed input
3857 if ( ! is_string( $tag_names ) ) {
3858 $tag_names = implode( ', ', $existing_names );
3859
3860 // Parse submitted tag names
3861 } else {
3862 $tag_names = sanitize_text_field( $tag_names );
3863 $tag_names = array_filter( array_map( 'trim', explode( ',', $tag_names ) ) );
3864 $retval = user_can( $user_id, 'assign_topic_tags', $topic_id )
3865 ? $tag_names
3866 : array();
3867
3868 // Preserve existing tags that were retained or cannot be removed
3869 foreach ( $existing_tags as $existing_tag ) {
3870 if ( in_array( $existing_tag->name, $tag_names, true ) || ! user_can( $user_id, 'remove_topic_tag', $topic_id, $existing_tag->term_id ) ) {
3871 $retval[] = $existing_tag->name;
3872 }
3873 }
3874
3875 $tag_names = implode( ', ', array_unique( $retval ) );
3876 }
3877
3878 // Filter & return
3879 return apply_filters( 'bbp_get_topic_tag_names_for_update', $tag_names, $topic_id, $user_id );
3880 }
3881
3882 /**
3883 * Will update topic-tag count based on object type.
3884 *
3885 * Function for the default callback for topic-tag taxonomies.
3886 *
3887 * @see https://bbpress.trac.wordpress.org/ticket/3043
3888 * @access private
3889 *
3890 * @since 2.6.0 bbPress (r6254)
3891 *
3892 * @param array $terms List of Term taxonomy IDs.
3893 * @param object $taxonomy Current taxonomy object of terms.
3894 */
3895 function bbp_update_topic_tag_count( $terms, $taxonomy ) {
3896
3897 // Bail if no object types are available
3898 if ( empty( $terms ) || empty( $taxonomy->object_type ) ) {
3899 return;
3900 }
3901
3902 // Get object types
3903 $object_types = (array) $taxonomy->object_type;
3904
3905 foreach ( $object_types as &$object_type ) {
3906 list( $object_type ) = explode( ':', $object_type );
3907 }
3908
3909 $object_types = array_unique( $object_types );
3910
3911 if ( ! empty( $object_types ) ) {
3912 $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) );
3913 }
3914
3915 // Statuses to count
3916 $object_statuses = bbp_get_public_topic_statuses();
3917
3918 // Get database
3919 $bbp_db = bbp_db();
3920
3921 // Loop through terms, maybe update counts
3922 foreach ( (array) $terms as $term ) {
3923 $count = 0;
3924
3925 // Get count, and bump it
3926 if ( ! empty( $object_types ) ) {
3927 $query = "SELECT COUNT(*) FROM {$bbp_db->term_relationships}, {$bbp_db->posts} WHERE {$bbp_db->posts}.ID = {$bbp_db->term_relationships}.object_id AND post_status IN ('" . implode("', '", $object_statuses ) . "') AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d";
3928 $prepare = $bbp_db->prepare( $query, $term );
3929 $count += (int) $bbp_db->get_var( $prepare );
3930 }
3931
3932 /** This action is documented in wp-includes/taxonomy.php */
3933 do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
3934 $bbp_db->update( $bbp_db->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
3935
3936 /** This action is documented in wp-includes/taxonomy.php */
3937 do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
3938 }
3939 }
3940
3941 /** Autoembed *****************************************************************/
3942
3943 /**
3944 * Check if autoembeds are enabled and hook them in if so
3945 *
3946 * @since 2.1.0 bbPress (r3753)
3947 *
3948 * @global WP_Embed $wp_embed
3949 */
3950 function bbp_topic_content_autoembed() {
3951 global $wp_embed;
3952
3953 if ( bbp_use_autoembed() && is_a( $wp_embed, 'WP_Embed' ) ) {
3954 add_filter( 'bbp_get_topic_content', array( $wp_embed, 'autoembed' ), 2 );
3955 }
3956 }
3957
3958 /** Feeds *********************************************************************/
3959
3960 /**
3961 * Output an RSS2 feed of topics, based on the query passed.
3962 *
3963 * @since 2.0.0 bbPress (r3171)
3964 *
3965 * @param array $topics_query
3966 */
3967 function bbp_display_topics_feed_rss2( $topics_query = array() ) {
3968
3969 // User cannot access this forum
3970 if ( bbp_is_single_forum() && ! bbp_user_can_view_forum( array( 'forum_id' => bbp_get_forum_id() ) ) ) {
3971 return;
3972 }
3973
3974 // Feed title
3975 $title = get_bloginfo_rss( 'name' ) . ' &#187; ' . esc_html__( 'All Topics', 'bbpress' );
3976 $title = apply_filters( 'wp_title_rss', $title );
3977
3978 // Display the feed
3979 header( 'Content-Type: ' . feed_content_type( 'rss2' ) . '; charset=' . get_option( 'blog_charset' ), true );
3980 header( 'Status: 200 OK' );
3981 echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>'; ?>
3982
3983 <rss version="2.0"
3984 xmlns:content="http://purl.org/rss/1.0/modules/content/"
3985 xmlns:wfw="http://wellformedweb.org/CommentAPI/"
3986 xmlns:dc="http://purl.org/dc/elements/1.1/"
3987 xmlns:atom="http://www.w3.org/2005/Atom"
3988
3989 <?php do_action( 'bbp_feed' ); ?>
3990 >
3991
3992 <channel>
3993
3994 <title><?php echo $title; // Already escaped ?></title>
3995 <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
3996 <link><?php self_link(); ?></link>
3997 <description><?php // ?></description>
3998 <lastBuildDate><?php echo date( 'r' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date ?></lastBuildDate>
3999 <generator><?php echo esc_url_raw( 'https://bbpress.org/?v=' . convert_chars( bbp_get_version() ) ); ?></generator>
4000 <language><?php bloginfo_rss( 'language' ); ?></language>
4001
4002 <?php do_action( 'bbp_feed_head' ); ?>
4003
4004 <?php if ( bbp_has_topics( $topics_query ) ) : ?>
4005
4006 <?php while ( bbp_topics() ) :
4007
4008 bbp_the_topic(); ?>
4009
4010 <item>
4011 <guid><?php bbp_topic_permalink(); ?></guid>
4012 <title><?php echo apply_filters( 'the_title_rss', bbp_get_topic_title() ); ?></title>
4013 <link><?php bbp_topic_permalink(); ?></link>
4014 <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_meta( bbp_get_topic_id(), '_bbp_last_active_time', true ), false ); ?></pubDate>
4015 <dc:creator><?php the_author(); ?></dc:creator>
4016
4017 <?php if ( ! bbp_get_password_required_id( bbp_get_topic_id() ) ) : ?>
4018
4019 <description>
4020 <![CDATA[
4021 <p><?php printf(
4022 /* translators: %s: Number of replies to the topic */
4023 esc_html__( 'Replies: %s', 'bbpress' ),
4024 bbp_get_topic_reply_count()
4025 ); ?></p>
4026 <?php echo bbp_escape_feed_cdata( bbp_get_topic_content() ); ?>
4027 ]]>
4028 </description>
4029
4030 <?php rss_enclosure(); ?>
4031
4032 <?php endif; ?>
4033
4034 <?php do_action( 'bbp_feed_item' ); ?>
4035
4036 </item>
4037
4038 <?php endwhile; ?>
4039 <?php endif; ?>
4040
4041 <?php do_action( 'bbp_feed_footer' ); ?>
4042
4043 </channel>
4044 </rss>
4045
4046 <?php
4047 exit();
4048 }
4049
4050 /** Permissions ***************************************************************/
4051
4052 /**
4053 * Redirect if unauthorized user is attempting to edit a topic
4054 *
4055 * @since 2.1.0 bbPress (r3607)
4056 */
4057 function bbp_check_topic_edit() {
4058
4059 // Bail if not editing a topic
4060 if ( ! bbp_is_topic_edit() ) {
4061 return;
4062 }
4063
4064 // User cannot edit topic, so redirect back to topic
4065 if ( ! current_user_can( 'edit_topic', bbp_get_topic_id() ) ) {
4066 bbp_redirect( bbp_get_topic_permalink() );
4067 }
4068 }
4069
4070 /**
4071 * Redirect if unauthorized user is attempting to edit a topic tag
4072 *
4073 * @since 2.1.0 bbPress (r3607)
4074 */
4075 function bbp_check_topic_tag_edit() {
4076
4077 // Bail if not editing a topic tag
4078 if ( ! bbp_is_topic_tag_edit() ) {
4079 return;
4080 }
4081
4082 // Bail if current user cannot edit topic tags
4083 if ( ! current_user_can( 'edit_topic_tag', bbp_get_topic_tag_id() ) ) {
4084 bbp_redirect( bbp_get_topic_tag_link() );
4085 }
4086 }
4087