PluginProbe
bbPress / 2.6.6
bbPress v2.6.6
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.6, at includes/replies/functions.php

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