PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.2
Forumax – AI Powered Advanced Community Forum Plugin v2.4.2
2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 2.1.0 All 29 releases
bbp-core / lib / bbpress / includes / replies / functions.php

functions.php in Forumax – AI Powered Advanced Community Forum Plugin 2.4.2, at lib/bbpress/includes/replies/functions.php

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