PluginProbe
bbPress / 2.6.15
bbPress v2.6.15
trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.2.2 All 71 releases
bbpress / includes / topics / functions.php

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

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