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

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

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