PluginProbe
bbPress / 2.2.2
bbPress v2.2.2
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 / common / functions.php

functions.php in bbPress 2.2.2, at includes/common/functions.php

1,670 lines 55.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Common Functions
5 *
6 * Common functions are ones that are used by more than one component, like
7 * forums, topics, replies, users, topic tags, etc...
8 *
9 * @package bbPress
10 * @subpackage Functions
11 */
12
13 // Exit if accessed directly
14 if ( !defined( 'ABSPATH' ) ) exit;
15
16 /** Formatting ****************************************************************/
17
18 /**
19 * A bbPress specific method of formatting numeric values
20 *
21 * @since bbPress (r2486)
22 *
23 * @param string $number Number to format
24 * @param string $decimals Optional. Display decimals
25 * @uses apply_filters() Calls 'bbp_number_format' with the formatted values,
26 * number and display decimals bool
27 * @return string Formatted string
28 */
29 function bbp_number_format( $number = 0, $decimals = false, $dec_point = '.', $thousands_sep = ',' ) {
30
31 // If empty, set $number to (int) 0
32 if ( ! is_numeric( $number ) )
33 $number = 0;
34
35 return apply_filters( 'bbp_number_format', number_format( $number, $decimals, $dec_point, $thousands_sep ), $number, $decimals, $dec_point, $thousands_sep );
36 }
37
38 /**
39 * A bbPress specific method of formatting numeric values
40 *
41 * @since bbPress (r3857)
42 *
43 * @param string $number Number to format
44 * @param string $decimals Optional. Display decimals
45 * @uses apply_filters() Calls 'bbp_number_format' with the formatted values,
46 * number and display decimals bool
47 * @return string Formatted string
48 */
49 function bbp_number_format_i18n( $number = 0, $decimals = false ) {
50
51 // If empty, set $number to (int) 0
52 if ( ! is_numeric( $number ) )
53 $number = 0;
54
55 return apply_filters( 'bbp_number_format_i18n', number_format_i18n( $number, $decimals ), $number, $decimals );
56 }
57
58 /**
59 * Convert time supplied from database query into specified date format.
60 *
61 * @since bbPress (r2455)
62 *
63 * @param int|object $post Optional. Default is global post object. A post_id or
64 * post object
65 * @param string $d Optional. Default is 'U'. Either 'G', 'U', or php date
66 * format
67 * @param bool $translate Optional. Default is false. Whether to translate the
68 * result
69 * @uses mysql2date() To convert the format
70 * @uses apply_filters() Calls 'bbp_convert_date' with the time, date format
71 * and translate bool
72 * @return string Returns timestamp
73 */
74 function bbp_convert_date( $time, $d = 'U', $translate = false ) {
75 $time = mysql2date( $d, $time, $translate );
76
77 return apply_filters( 'bbp_convert_date', $time, $d, $translate );
78 }
79
80 /**
81 * Output formatted time to display human readable time difference.
82 *
83 * @since bbPress (r2544)
84 *
85 * @param string $older_date Unix timestamp from which the difference begins.
86 * @param string $newer_date Optional. Unix timestamp from which the
87 * difference ends. False for current time.
88 * @uses bbp_get_time_since() To get the formatted time
89 */
90 function bbp_time_since( $older_date, $newer_date = false ) {
91 echo bbp_get_time_since( $older_date, $newer_date = false );
92 }
93 /**
94 * Return formatted time to display human readable time difference.
95 *
96 * @since bbPress (r2544)
97 *
98 * @param string $older_date Unix timestamp from which the difference begins.
99 * @param string $newer_date Optional. Unix timestamp from which the
100 * difference ends. False for current time.
101 * @uses current_time() To get the current time in mysql format
102 * @uses human_time_diff() To get the time differene in since format
103 * @uses apply_filters() Calls 'bbp_get_time_since' with the time
104 * difference and time
105 * @return string Formatted time
106 */
107 function bbp_get_time_since( $older_date, $newer_date = false ) {
108
109 // Setup the strings
110 $unknown_text = apply_filters( 'bbp_core_time_since_unknown_text', __( 'sometime', 'bbpress' ) );
111 $right_now_text = apply_filters( 'bbp_core_time_since_right_now_text', __( 'right now', 'bbpress' ) );
112 $ago_text = apply_filters( 'bbp_core_time_since_ago_text', __( '%s ago', 'bbpress' ) );
113
114 // array of time period chunks
115 $chunks = array(
116 array( 60 * 60 * 24 * 365 , __( 'year', 'bbpress' ), __( 'years', 'bbpress' ) ),
117 array( 60 * 60 * 24 * 30 , __( 'month', 'bbpress' ), __( 'months', 'bbpress' ) ),
118 array( 60 * 60 * 24 * 7, __( 'week', 'bbpress' ), __( 'weeks', 'bbpress' ) ),
119 array( 60 * 60 * 24 , __( 'day', 'bbpress' ), __( 'days', 'bbpress' ) ),
120 array( 60 * 60 , __( 'hour', 'bbpress' ), __( 'hours', 'bbpress' ) ),
121 array( 60 , __( 'minute', 'bbpress' ), __( 'minutes', 'bbpress' ) ),
122 array( 1, __( 'second', 'bbpress' ), __( 'seconds', 'bbpress' ) )
123 );
124
125 if ( !empty( $older_date ) && !is_numeric( $older_date ) ) {
126 $time_chunks = explode( ':', str_replace( ' ', ':', $older_date ) );
127 $date_chunks = explode( '-', str_replace( ' ', '-', $older_date ) );
128 $older_date = gmmktime( (int) $time_chunks[1], (int) $time_chunks[2], (int) $time_chunks[3], (int) $date_chunks[1], (int) $date_chunks[2], (int) $date_chunks[0] );
129 }
130
131 // $newer_date will equal false if we want to know the time elapsed
132 // between a date and the current time. $newer_date will have a value if
133 // we want to work out time elapsed between two known dates.
134 $newer_date = ( !$newer_date ) ? strtotime( current_time( 'mysql' ) ) : $newer_date;
135
136 // Difference in seconds
137 $since = $newer_date - $older_date;
138
139 // Something went wrong with date calculation and we ended up with a negative date.
140 if ( 0 > $since ) {
141 $output = $unknown_text;
142
143 // We only want to output two chunks of time here, eg:
144 // x years, xx months
145 // x days, xx hours
146 // so there's only two bits of calculation below:
147 } else {
148
149 // Step one: the first chunk
150 for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) {
151 $seconds = $chunks[$i][0];
152
153 // Finding the biggest chunk (if the chunk fits, break)
154 $count = floor( $since / $seconds );
155 if ( 0 != $count ) {
156 break;
157 }
158 }
159
160 // If $i iterates all the way to $j, then the event happened 0 seconds ago
161 if ( !isset( $chunks[$i] ) ) {
162 $output = $right_now_text;
163
164 } else {
165
166 // Set output var
167 $output = ( 1 == $count ) ? '1 '. $chunks[$i][1] : $count . ' ' . $chunks[$i][2];
168
169 // Step two: the second chunk
170 if ( $i + 2 < $j ) {
171 $seconds2 = $chunks[$i + 1][0];
172 $name2 = $chunks[$i + 1][1];
173 $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 );
174
175 // Add to output var
176 if ( 0 != $count2 ) {
177 $output .= ( 1 == $count2 ) ? _x( ',', 'Separator in time since', 'bbpress' ) . ' 1 '. $name2 : _x( ',', 'Separator in time since', 'bbpress' ) . ' ' . $count2 . ' ' . $chunks[$i + 1][2];
178 }
179 }
180
181 // No output, so happened right now
182 if ( ! (int) trim( $output ) ) {
183 $output = $right_now_text;
184 }
185 }
186 }
187
188 // Append 'ago' to the end of time-since if not 'right now'
189 if ( $output != $right_now_text ) {
190 $output = sprintf( $ago_text, $output );
191 }
192
193 return apply_filters( 'bbp_get_time_since', $output, $older_date, $newer_date );
194 }
195
196 /**
197 * Formats the reason for editing the topic/reply.
198 *
199 * Does these things:
200 * - Trimming
201 * - Removing periods from the end of the string
202 * - Trimming again
203 *
204 * @since bbPress (r2782)
205 *
206 * @param int $topic_id Optional. Topic id
207 * @return string Status of topic
208 */
209 function bbp_format_revision_reason( $reason = '' ) {
210 $reason = (string) $reason;
211
212 // Format reason for proper display
213 if ( empty( $reason ) )
214 return $reason;
215
216 // Trimming
217 $reason = trim( $reason );
218
219 // We add our own full stop.
220 while ( substr( $reason, -1 ) == '.' )
221 $reason = substr( $reason, 0, -1 );
222
223 // Trim again
224 $reason = trim( $reason );
225
226 return $reason;
227 }
228
229 /** Misc **********************************************************************/
230
231 /**
232 * Append 'view=all' to query string if it's already there from referer
233 *
234 * @since bbPress (r3325)
235 *
236 * @param string $original_link Original Link to be modified
237 * @param bool $force Override bbp_get_view_all() check
238 * @uses current_user_can() To check if the current user can moderate
239 * @uses add_query_arg() To add args to the url
240 * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link
241 * @return string The link with 'view=all' appended if necessary
242 */
243 function bbp_add_view_all( $original_link = '', $force = false ) {
244
245 // Are we appending the view=all vars?
246 if ( bbp_get_view_all() || !empty( $force ) )
247 $link = add_query_arg( array( 'view' => 'all' ), $original_link );
248 else
249 $link = $original_link;
250
251 return apply_filters( 'bbp_add_view_all', $link, $original_link );
252 }
253
254 /**
255 * Remove 'view=all' from query string
256 *
257 * @since bbPress (r3325)
258 *
259 * @param string $original_link Original Link to be modified
260 * @uses current_user_can() To check if the current user can moderate
261 * @uses add_query_arg() To add args to the url
262 * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link
263 * @return string The link with 'view=all' appended if necessary
264 */
265 function bbp_remove_view_all( $original_link = '' ) {
266 return apply_filters( 'bbp_add_view_all', remove_query_arg( 'view', $original_link ), $original_link );
267 }
268
269 /**
270 * If current user can and is vewing all topics/replies
271 *
272 * @since bbPress (r3325)
273 *
274 * @uses current_user_can() To check if the current user can moderate
275 * @uses apply_filters() Calls 'bbp_get_view_all' with the link and original link
276 * @return bool Whether current user can and is viewing all
277 */
278 function bbp_get_view_all( $cap = 'moderate' ) {
279 $retval = ( ( !empty( $_GET['view'] ) && ( 'all' == $_GET['view'] ) && current_user_can( $cap ) ) );
280 return apply_filters( 'bbp_get_view_all', (bool) $retval );
281 }
282
283 /**
284 * Assist pagination by returning correct page number
285 *
286 * @since bbPress (r2628)
287 *
288 * @uses get_query_var() To get the 'paged' value
289 * @return int Current page number
290 */
291 function bbp_get_paged() {
292 global $wp_query;
293
294 // Check the query var
295 if ( get_query_var( 'paged' ) ) {
296 $paged = get_query_var( 'paged' );
297
298 // Check query paged
299 } elseif ( !empty( $wp_query->query['paged'] ) ) {
300 $paged = $wp_query->query['paged'];
301 }
302
303 // Paged found
304 if ( !empty( $paged ) )
305 return (int) $paged;
306
307 // Default to first page
308 return 1;
309 }
310
311 /**
312 * Fix post author id on post save
313 *
314 * When a logged in user changes the status of an anonymous reply or topic, or
315 * edits it, the post_author field is set to the logged in user's id. This
316 * function fixes that.
317 *
318 * @since bbPress (r2734)
319 *
320 * @param array $data Post data
321 * @param array $postarr Original post array (includes post id)
322 * @uses bbp_get_topic_post_type() To get the topic post type
323 * @uses bbp_get_reply_post_type() To get the reply post type
324 * @uses bbp_is_topic_anonymous() To check if the topic is by an anonymous user
325 * @uses bbp_is_reply_anonymous() To check if the reply is by an anonymous user
326 * @return array Data
327 */
328 function bbp_fix_post_author( $data = array(), $postarr = array() ) {
329
330 // Post is not being updated or the post_author is already 0, return
331 if ( empty( $postarr['ID'] ) || empty( $data['post_author'] ) )
332 return $data;
333
334 // Post is not a topic or reply, return
335 if ( !in_array( $data['post_type'], array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) ) )
336 return $data;
337
338 // Is the post by an anonymous user?
339 if ( ( bbp_get_topic_post_type() == $data['post_type'] && !bbp_is_topic_anonymous( $postarr['ID'] ) ) ||
340 ( bbp_get_reply_post_type() == $data['post_type'] && !bbp_is_reply_anonymous( $postarr['ID'] ) ) )
341 return $data;
342
343 // The post is being updated. It is a topic or a reply and is written by an anonymous user.
344 // Set the post_author back to 0
345 $data['post_author'] = 0;
346
347 return $data;
348 }
349
350 /**
351 * Check the date against the _bbp_edit_lock setting.
352 *
353 * @since bbPress (r3133)
354 *
355 * @param string $post_date_gmt
356 *
357 * @uses get_option() Get the edit lock time
358 * @uses current_time() Get the current time
359 * @uses strtotime() Convert strings to time
360 * @uses apply_filters() Allow output to be manipulated
361 *
362 * @return bool
363 */
364 function bbp_past_edit_lock( $post_date_gmt ) {
365
366 // Assume editing is allowed
367 $retval = false;
368
369 // Bail if empty date
370 if ( ! empty( $post_date_gmt ) ) {
371
372 // Period of time
373 $lockable = '+' . get_option( '_bbp_edit_lock', '5' ) . ' minutes';
374
375 // Now
376 $cur_time = current_time( 'timestamp', true );
377
378 // Add lockable time to post time
379 $lock_time = strtotime( $lockable, strtotime( $post_date_gmt ) );
380
381 // Compare
382 if ( $cur_time >= $lock_time ) {
383 $retval = true;
384 }
385 }
386
387 return apply_filters( 'bbp_past_edit_lock', (bool) $retval, $cur_time, $lock_time, $post_date_gmt );
388 }
389
390 /** Statistics ****************************************************************/
391
392 /**
393 * Get the forum statistics
394 *
395 * @since bbPress (r2769)
396 *
397 * @param mixed $args Optional. The function supports these arguments (all
398 * default to true):
399 * - count_users: Count users?
400 * - count_forums: Count forums?
401 * - count_topics: Count topics? If set to false, private, spammed and trashed
402 * topics are also not counted.
403 * - count_private_topics: Count private topics? (only counted if the current
404 * user has read_private_topics cap)
405 * - count_spammed_topics: Count spammed topics? (only counted if the current
406 * user has edit_others_topics cap)
407 * - count_trashed_topics: Count trashed topics? (only counted if the current
408 * user has view_trash cap)
409 * - count_replies: Count replies? If set to false, private, spammed and
410 * trashed replies are also not counted.
411 * - count_private_replies: Count private replies? (only counted if the current
412 * user has read_private_replies cap)
413 * - count_spammed_replies: Count spammed replies? (only counted if the current
414 * user has edit_others_replies cap)
415 * - count_trashed_replies: Count trashed replies? (only counted if the current
416 * user has view_trash cap)
417 * - count_tags: Count tags? If set to false, empty tags are also not counted
418 * - count_empty_tags: Count empty tags?
419 * @uses bbp_count_users() To count the number of registered users
420 * @uses bbp_get_forum_post_type() To get the forum post type
421 * @uses bbp_get_topic_post_type() To get the topic post type
422 * @uses bbp_get_reply_post_type() To get the reply post type
423 * @uses wp_count_posts() To count the number of forums, topics and replies
424 * @uses wp_count_terms() To count the number of topic tags
425 * @uses current_user_can() To check if the user is capable of doing things
426 * @uses number_format_i18n() To format the number
427 * @uses apply_filters() Calls 'bbp_get_statistics' with the statistics and args
428 * @return object Walked forum tree
429 */
430 function bbp_get_statistics( $args = '' ) {
431
432 $defaults = array (
433 'count_users' => true,
434 'count_forums' => true,
435 'count_topics' => true,
436 'count_private_topics' => true,
437 'count_spammed_topics' => true,
438 'count_trashed_topics' => true,
439 'count_replies' => true,
440 'count_private_replies' => true,
441 'count_spammed_replies' => true,
442 'count_trashed_replies' => true,
443 'count_tags' => true,
444 'count_empty_tags' => true
445 );
446 $r = bbp_parse_args( $args, $defaults, 'get_statistics' );
447 extract( $r );
448
449 // Users
450 if ( !empty( $count_users ) )
451 $user_count = bbp_get_total_users();
452
453 // Forums
454 if ( !empty( $count_forums ) ) {
455 $forum_count = wp_count_posts( bbp_get_forum_post_type() );
456 $forum_count = $forum_count->publish;
457 }
458
459 // Post statuses
460 $private = bbp_get_private_status_id();
461 $spam = bbp_get_spam_status_id();
462 $trash = bbp_get_trash_status_id();
463 $closed = bbp_get_closed_status_id();
464
465 // Topics
466 if ( !empty( $count_topics ) ) {
467
468 $all_topics = wp_count_posts( bbp_get_topic_post_type() );
469
470 // Published (publish + closed)
471 $topic_count = $all_topics->publish + $all_topics->{$closed};
472
473 if ( current_user_can( 'read_private_topics' ) || current_user_can( 'edit_others_topics' ) || current_user_can( 'view_trash' ) ) {
474
475 // Private
476 $topics['private'] = ( !empty( $count_private_topics ) && current_user_can( 'read_private_topics' ) ) ? (int) $all_topics->{$private} : 0;
477
478 // Spam
479 $topics['spammed'] = ( !empty( $count_spammed_topics ) && current_user_can( 'edit_others_topics' ) ) ? (int) $all_topics->{$spam} : 0;
480
481 // Trash
482 $topics['trashed'] = ( !empty( $count_trashed_topics ) && current_user_can( 'view_trash' ) ) ? (int) $all_topics->{$trash} : 0;
483
484 // Total hidden (private + spam + trash)
485 $topic_count_hidden = $topics['private'] + $topics['spammed'] + $topics['trashed'];
486
487 // Generate the hidden topic count's title attribute
488 $topic_titles[] = !empty( $topics['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $topics['private'] ) ) : '';
489 $topic_titles[] = !empty( $topics['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $topics['spammed'] ) ) : '';
490 $topic_titles[] = !empty( $topics['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $topics['trashed'] ) ) : '';
491
492 // Compile the hidden topic title
493 $hidden_topic_title = implode( ' | ', array_filter( $topic_titles ) );
494 }
495 }
496
497 // Replies
498 if ( !empty( $count_replies ) ) {
499
500 $all_replies = wp_count_posts( bbp_get_reply_post_type() );
501
502 // Published
503 $reply_count = $all_replies->publish;
504
505 if ( current_user_can( 'read_private_replies' ) || current_user_can( 'edit_others_replies' ) || current_user_can( 'view_trash' ) ) {
506
507 // Private
508 $replies['private'] = ( !empty( $count_private_replies ) && current_user_can( 'read_private_replies' ) ) ? (int) $all_replies->{$private} : 0;
509
510 // Spam
511 $replies['spammed'] = ( !empty( $count_spammed_replies ) && current_user_can( 'edit_others_replies' ) ) ? (int) $all_replies->{$spam} : 0;
512
513 // Trash
514 $replies['trashed'] = ( !empty( $count_trashed_replies ) && current_user_can( 'view_trash' ) ) ? (int) $all_replies->{$trash} : 0;
515
516 // Total hidden (private + spam + trash)
517 $reply_count_hidden = $replies['private'] + $replies['spammed'] + $replies['trashed'];
518
519 // Generate the hidden topic count's title attribute
520 $reply_titles[] = !empty( $replies['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $replies['private'] ) ) : '';
521 $reply_titles[] = !empty( $replies['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $replies['spammed'] ) ) : '';
522 $reply_titles[] = !empty( $replies['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $replies['trashed'] ) ) : '';
523
524 // Compile the hidden replies title
525 $hidden_reply_title = implode( ' | ', array_filter( $reply_titles ) );
526
527 }
528 }
529
530 // Topic Tags
531 if ( !empty( $count_tags ) && bbp_allow_topic_tags() ) {
532
533 // Get the count
534 $topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id(), array( 'hide_empty' => true ) );
535
536 // Empty tags
537 if ( !empty( $count_empty_tags ) && current_user_can( 'edit_topic_tags' ) ) {
538 $empty_topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id() ) - $topic_tag_count;
539 }
540 }
541
542 // Tally the tallies
543 $statistics = compact( 'user_count', 'forum_count', 'topic_count', 'topic_count_hidden', 'reply_count', 'reply_count_hidden', 'topic_tag_count', 'empty_topic_tag_count' );
544 $statistics = array_map( 'absint', $statistics );
545 $statistics = array_map( 'number_format_i18n', $statistics );
546
547 // Add the hidden (topic/reply) count title attribute strings because we don't need to run the math functions on these (see above)
548 if ( isset( $hidden_topic_title ) )
549 $statistics['hidden_topic_title'] = $hidden_topic_title;
550
551 if ( isset( $hidden_reply_title ) )
552 $statistics['hidden_reply_title'] = $hidden_reply_title;
553
554 return apply_filters( 'bbp_get_statistics', $statistics, $args );
555 }
556
557 /** New/edit topic/reply helpers **********************************************/
558
559 /**
560 * Filter anonymous post data
561 *
562 * We use REMOTE_ADDR here directly. If you are behind a proxy, you should
563 * ensure that it is properly set, such as in wp-config.php, for your
564 * environment. See {@link http://core.trac.wordpress.org/ticket/9235}
565 *
566 * Note that bbp_pre_anonymous_filters() is responsible for sanitizing each
567 * of the filtered core anonymous values here.
568 *
569 * If there are any errors, those are directly added to {@link bbPress:errors}
570 *
571 * @since bbPress (r2734)
572 *
573 * @param mixed $args Optional. If no args are there, then $_POST values are
574 * used.
575 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_name' with the
576 * anonymous user name
577 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_email' with the
578 * anonymous user email
579 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_website' with the
580 * anonymous user website
581 * @return bool|array False on errors, values in an array on success
582 */
583 function bbp_filter_anonymous_post_data( $args = '' ) {
584
585 // Assign variables
586 $defaults = array (
587 'bbp_anonymous_name' => !empty( $_POST['bbp_anonymous_name'] ) ? $_POST['bbp_anonymous_name'] : false,
588 'bbp_anonymous_email' => !empty( $_POST['bbp_anonymous_email'] ) ? $_POST['bbp_anonymous_email'] : false,
589 'bbp_anonymous_website' => !empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false,
590 );
591 $r = bbp_parse_args( $args, $defaults, 'filter_anonymous_post_data' );
592 extract( $r );
593
594 // Filter variables and add errors if necessary
595 $bbp_anonymous_name = apply_filters( 'bbp_pre_anonymous_post_author_name', $bbp_anonymous_name );
596 if ( empty( $bbp_anonymous_name ) )
597 bbp_add_error( 'bbp_anonymous_name', __( '<strong>ERROR</strong>: Invalid author name submitted!', 'bbpress' ) );
598
599 $bbp_anonymous_email = apply_filters( 'bbp_pre_anonymous_post_author_email', $bbp_anonymous_email );
600 if ( empty( $bbp_anonymous_email ) )
601 bbp_add_error( 'bbp_anonymous_email', __( '<strong>ERROR</strong>: Invalid email address submitted!', 'bbpress' ) );
602
603 // Website is optional
604 $bbp_anonymous_website = apply_filters( 'bbp_pre_anonymous_post_author_website', $bbp_anonymous_website );
605
606 if ( !bbp_has_errors() )
607 $retval = compact( 'bbp_anonymous_name', 'bbp_anonymous_email', 'bbp_anonymous_website' );
608 else
609 $retval = false;
610
611 // Finally, return sanitized data or false
612 return apply_filters( 'bbp_filter_anonymous_post_data', $retval, $args );
613 }
614
615 /**
616 * Check for duplicate topics/replies
617 *
618 * Check to make sure that a user is not making a duplicate post
619 *
620 * @since bbPress (r2763)
621 *
622 * @param array $post_data Contains information about the comment
623 * @uses current_user_can() To check if the current user can throttle
624 * @uses get_meta_sql() To generate the meta sql for checking anonymous email
625 * @uses apply_filters() Calls 'bbp_check_for_duplicate_query' with the
626 * duplicate check query and post data
627 * @uses wpdb::get_var() To execute our query and get the var back
628 * @uses get_post_meta() To get the anonymous user email post meta
629 * @uses do_action() Calls 'bbp_post_duplicate_trigger' with the post data when
630 * it is found that it is a duplicate
631 * @return bool True if it is not a duplicate, false if it is
632 */
633 function bbp_check_for_duplicate( $post_data ) {
634
635 // No duplicate checks for those who can throttle
636 if ( current_user_can( 'throttle' ) )
637 return true;
638
639 global $wpdb;
640
641 extract( $post_data, EXTR_SKIP );
642
643 // Check for anonymous post
644 if ( empty( $post_author ) && ( isset( $anonymous_data ) && !empty( $anonymous_data['bbp_anonymous_email'] ) ) ) {
645 $clauses = get_meta_sql( array( array(
646 'key' => '_bbp_anonymous_email',
647 'value' => $anonymous_data['bbp_anonymous_email']
648 ) ), 'post', $wpdb->posts, 'ID' );
649
650 $join = $clauses['join'];
651 $where = $clauses['where'];
652 } else{
653 $join = $where = '';
654 }
655
656 // Simple duplicate check
657 // Expected slashed ($post_type, $post_parent, $post_author, $post_content, $anonymous_data)
658 $status = bbp_get_trash_status_id();
659 $dupe = "SELECT ID FROM {$wpdb->posts} {$join} WHERE post_type = '{$post_type}' AND post_status != '{$status}' AND post_author = {$post_author} AND post_content = '{$post_content}' {$where}";
660 $dupe .= !empty( $post_parent ) ? " AND post_parent = '{$post_parent}'" : '';
661 $dupe .= " LIMIT 1";
662 $dupe = apply_filters( 'bbp_check_for_duplicate_query', $dupe, $post_data );
663
664 if ( $wpdb->get_var( $dupe ) ) {
665 do_action( 'bbp_check_for_duplicate_trigger', $post_data );
666 return false;
667 }
668
669 return true;
670 }
671
672 /**
673 * Check for flooding
674 *
675 * Check to make sure that a user is not making too many posts in a short amount
676 * of time.
677 *
678 * @since bbPress (r2734)
679 *
680 * @param false|array $anonymous_data Optional - if it's an anonymous post. Do
681 * not supply if supplying $author_id.
682 * Should have key 'bbp_author_ip'.
683 * Should be sanitized (see
684 * {@link bbp_filter_anonymous_post_data()}
685 * for sanitization)
686 * @param int $author_id Optional. Supply if it's a post by a logged in user.
687 * Do not supply if supplying $anonymous_data.
688 * @uses get_option() To get the throttle time
689 * @uses get_transient() To get the last posted transient of the ip
690 * @uses bbp_get_user_last_posted() To get the last posted time of the user
691 * @uses current_user_can() To check if the current user can throttle
692 * @return bool True if there is no flooding, false if there is
693 */
694 function bbp_check_for_flood( $anonymous_data = false, $author_id = 0 ) {
695
696 // Option disabled. No flood checks.
697 $throttle_time = get_option( '_bbp_throttle_time' );
698 if ( empty( $throttle_time ) )
699 return true;
700
701 // User is anonymous, so check a transient based on the IP
702 if ( !empty( $anonymous_data ) && is_array( $anonymous_data ) ) {
703 $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' );
704
705 if ( !empty( $last_posted ) && time() < $last_posted + $throttle_time ) {
706 return false;
707 }
708
709 // User is logged in, so check their last posted time
710 } elseif ( !empty( $author_id ) ) {
711 $author_id = (int) $author_id;
712 $last_posted = bbp_get_user_last_posted( $author_id );
713
714 if ( isset( $last_posted ) && time() < $last_posted + $throttle_time && !current_user_can( 'throttle' ) ) {
715 return false;
716 }
717 } else {
718 return false;
719 }
720
721 return true;
722 }
723
724 /**
725 * Checks topics and replies against the discussion moderation of blocked keys
726 *
727 * @since bbPress (r3581)
728 *
729 * @param array $anonymous_data Anonymous user data
730 * @param int $author_id Topic or reply author ID
731 * @param string $title The title of the content
732 * @param string $content The content being posted
733 * @uses is_super_admin() Allow super admins to bypass blacklist
734 * @uses bbp_current_author_ip() To get current user IP address
735 * @uses bbp_current_author_ua() To get current user agent
736 * @return bool True if test is passed, false if fail
737 */
738 function bbp_check_for_moderation( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) {
739
740 // Bail if super admin is author
741 if ( is_super_admin( $author_id ) )
742 return true;
743
744 // Define local variable(s)
745 $_post = array();
746 $match_out = '';
747
748 /** Blacklist *************************************************************/
749
750 // Get the moderation keys
751 $blacklist = trim( get_option( 'moderation_keys' ) );
752
753 // Bail if blacklist is empty
754 if ( empty( $blacklist ) )
755 return true;
756
757 /** User Data *************************************************************/
758
759 // Map anonymous user data
760 if ( !empty( $anonymous_data ) ) {
761 $_post['author'] = $anonymous_data['bbp_anonymous_name'];
762 $_post['email'] = $anonymous_data['bbp_anonymous_email'];
763 $_post['url'] = $anonymous_data['bbp_anonymous_website'];
764
765 // Map current user data
766 } elseif ( !empty( $author_id ) ) {
767
768 // Get author data
769 $user = get_userdata( $author_id );
770
771 // If data exists, map it
772 if ( !empty( $user ) ) {
773 $_post['author'] = $user->display_name;
774 $_post['email'] = $user->user_email;
775 $_post['url'] = $user->user_url;
776 }
777 }
778
779 // Current user IP and user agent
780 $_post['user_ip'] = bbp_current_author_ip();
781 $_post['user_ua'] = bbp_current_author_ua();
782
783 // Post title and content
784 $_post['title'] = $title;
785 $_post['content'] = $content;
786
787 /** Max Links *************************************************************/
788
789 $max_links = get_option( 'comment_max_links' );
790 if ( !empty( $max_links ) ) {
791
792 // How many links?
793 $num_links = preg_match_all( '/<a [^>]*href/i', $content, $match_out );
794
795 // Allow for bumping the max to include the user's URL
796 $num_links = apply_filters( 'comment_max_links_url', $num_links, $_post['url'] );
797
798 // Das ist zu viele links!
799 if ( $num_links >= $max_links ) {
800 return false;
801 }
802 }
803
804 /** Words *****************************************************************/
805
806 // Get words separated by new lines
807 $words = explode( "\n", $blacklist );
808
809 // Loop through words
810 foreach ( (array) $words as $word ) {
811
812 // Trim the whitespace from the word
813 $word = trim( $word );
814
815 // Skip empty lines
816 if ( empty( $word ) ) { continue; }
817
818 // Do some escaping magic so that '#' chars in the
819 // spam words don't break things:
820 $word = preg_quote( $word, '#' );
821 $pattern = "#$word#i";
822
823 // Loop through post data
824 foreach( $_post as $post_data ) {
825
826 // Check each user data for current word
827 if ( preg_match( $pattern, $post_data ) ) {
828
829 // Post does not pass
830 return false;
831 }
832 }
833 }
834
835 // Check passed successfully
836 return true;
837 }
838
839 /**
840 * Checks topics and replies against the discussion blacklist of blocked keys
841 *
842 * @since bbPress (r3446)
843 *
844 * @param array $anonymous_data Anonymous user data
845 * @param int $author_id Topic or reply author ID
846 * @param string $title The title of the content
847 * @param string $content The content being posted
848 * @uses is_super_admin() Allow super admins to bypass blacklist
849 * @uses bbp_current_author_ip() To get current user IP address
850 * @uses bbp_current_author_ua() To get current user agent
851 * @return bool True if test is passed, false if fail
852 */
853 function bbp_check_for_blacklist( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) {
854
855 // Bail if super admin is author
856 if ( is_super_admin( $author_id ) )
857 return true;
858
859 // Define local variable
860 $_post = array();
861
862 /** Blacklist *************************************************************/
863
864 // Get the moderation keys
865 $blacklist = trim( get_option( 'blacklist_keys' ) );
866
867 // Bail if blacklist is empty
868 if ( empty( $blacklist ) )
869 return true;
870
871 /** User Data *************************************************************/
872
873 // Map anonymous user data
874 if ( !empty( $anonymous_data ) ) {
875 $_post['author'] = $anonymous_data['bbp_anonymous_name'];
876 $_post['email'] = $anonymous_data['bbp_anonymous_email'];
877 $_post['url'] = $anonymous_data['bbp_anonymous_website'];
878
879 // Map current user data
880 } elseif ( !empty( $author_id ) ) {
881
882 // Get author data
883 $user = get_userdata( $author_id );
884
885 // If data exists, map it
886 if ( !empty( $user ) ) {
887 $_post['author'] = $user->display_name;
888 $_post['email'] = $user->user_email;
889 $_post['url'] = $user->user_url;
890 }
891 }
892
893 // Current user IP and user agent
894 $_post['user_ip'] = bbp_current_author_ip();
895 $_post['user_ua'] = bbp_current_author_ua();
896
897 // Post title and content
898 $_post['title'] = $title;
899 $_post['content'] = $content;
900
901 /** Words *****************************************************************/
902
903 // Get words separated by new lines
904 $words = explode( "\n", $blacklist );
905
906 // Loop through words
907 foreach ( (array) $words as $word ) {
908
909 // Trim the whitespace from the word
910 $word = trim( $word );
911
912 // Skip empty lines
913 if ( empty( $word ) ) { continue; }
914
915 // Do some escaping magic so that '#' chars in the
916 // spam words don't break things:
917 $word = preg_quote( $word, '#' );
918 $pattern = "#$word#i";
919
920 // Loop through post data
921 foreach( $_post as $post_data ) {
922
923 // Check each user data for current word
924 if ( preg_match( $pattern, $post_data ) ) {
925
926 // Post does not pass
927 return false;
928 }
929 }
930 }
931
932 // Check passed successfully
933 return true;
934 }
935
936 /** Subscriptions *************************************************************/
937
938 /**
939 * Sends notification emails for new posts
940 *
941 * Gets new post's ID and check if there are subscribed users to that topic, and
942 * if there are, send notifications
943 *
944 * @since bbPress (r2668)
945 *
946 * @param int $reply_id ID of the newly made reply
947 * @uses bbp_is_subscriptions_active() To check if the subscriptions are active
948 * @uses bbp_get_reply_id() To validate the reply ID
949 * @uses bbp_get_reply() To get the reply
950 * @uses bbp_get_reply_topic_id() To get the topic ID of the reply
951 * @uses bbp_is_reply_published() To make sure the reply is published
952 * @uses bbp_get_topic_id() To validate the topic ID
953 * @uses bbp_get_topic() To get the reply's topic
954 * @uses bbp_is_topic_published() To make sure the topic is published
955 * @uses get_the_author_meta() To get the author's display name
956 * @uses do_action() Calls 'bbp_pre_notify_subscribers' with the reply id and
957 * topic id
958 * @uses bbp_get_topic_subscribers() To get the topic subscribers
959 * @uses apply_filters() Calls 'bbp_subscription_mail_message' with the
960 * message, reply id, topic id and user id
961 * @uses get_userdata() To get the user data
962 * @uses wp_mail() To send the mail
963 * @uses do_action() Calls 'bbp_post_notify_subscribers' with the reply id
964 * and topic id
965 * @return bool True on success, false on failure
966 */
967 function bbp_notify_subscribers( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = false, $reply_author = 0 ) {
968
969 // Bail if subscriptions are turned off
970 if ( !bbp_is_subscriptions_active() )
971 return false;
972
973 /** Validation ************************************************************/
974
975 $reply_id = bbp_get_reply_id( $reply_id );
976 $topic_id = bbp_get_topic_id( $topic_id );
977 $forum_id = bbp_get_forum_id( $forum_id );
978
979 /** Reply *****************************************************************/
980
981 // Bail if reply is not published
982 if ( !bbp_is_reply_published( $reply_id ) )
983 return false;
984
985 /** Topic *****************************************************************/
986
987 // Bail if topic is not published
988 if ( !bbp_is_topic_published( $topic_id ) )
989 return false;
990
991 /** User ******************************************************************/
992
993 // Get subscribers and bail if empty
994 $user_ids = bbp_get_topic_subscribers( $topic_id, true );
995 if ( empty( $user_ids ) )
996 return false;
997
998 // Poster name
999 $reply_author_name = bbp_get_reply_author_display_name( $reply_id );
1000
1001 /** Mail ******************************************************************/
1002
1003 do_action( 'bbp_pre_notify_subscribers', $reply_id, $topic_id, $user_ids );
1004
1005 // Remove filters from reply content and topic title to prevent content
1006 // from being encoded with HTML entities, wrapped in paragraph tags, etc...
1007 remove_all_filters( 'bbp_get_reply_content' );
1008 remove_all_filters( 'bbp_get_topic_title' );
1009
1010 // Strip tags from text
1011 $topic_title = strip_tags( bbp_get_topic_title( $topic_id ) );
1012 $reply_content = strip_tags( bbp_get_reply_content( $reply_id ) );
1013 $reply_url = bbp_get_reply_url( $reply_id );
1014 $blog_name = get_option( 'blogname' );
1015
1016 // Loop through users
1017 foreach ( (array) $user_ids as $user_id ) {
1018
1019 // Don't send notifications to the person who made the post
1020 if ( !empty( $reply_author ) && (int) $user_id == (int) $reply_author )
1021 continue;
1022
1023 // For plugins to filter messages per reply/topic/user
1024 $message = sprintf( __( '%1$s wrote:
1025
1026 %2$s
1027
1028 Post Link: %3$s
1029
1030 -----------
1031
1032 You are receiving this email because you subscribed to a forum topic.
1033
1034 Login and visit the topic to unsubscribe from these emails.', 'bbpress' ),
1035
1036 $reply_author_name,
1037 $reply_content,
1038 $reply_url
1039 );
1040
1041 $message = apply_filters( 'bbp_subscription_mail_message', $message, $reply_id, $topic_id, $user_id );
1042 if ( empty( $message ) )
1043 continue;
1044
1045 // For plugins to filter titles per reply/topic/user
1046 $subject = apply_filters( 'bbp_subscription_mail_title', '[' . $blog_name . '] ' . $topic_title, $reply_id, $topic_id, $user_id );
1047 if ( empty( $subject ) )
1048 continue;
1049
1050 // Custom headers
1051 $headers = apply_filters( 'bbp_subscription_mail_headers', array() );
1052
1053 // Get user data of this user
1054 $user = get_userdata( $user_id );
1055
1056 // Send notification email
1057 wp_mail( $user->user_email, $subject, $message, $headers );
1058 }
1059
1060 do_action( 'bbp_post_notify_subscribers', $reply_id, $topic_id, $user_ids );
1061
1062 return true;
1063 }
1064
1065 /** Login *********************************************************************/
1066
1067 /**
1068 * Return a clean and reliable logout URL
1069 *
1070 * @param string $url URL
1071 * @param string $redirect_to Where to redirect to?
1072 * @uses add_query_arg() To add args to the url
1073 * @uses apply_filters() Calls 'bbp_logout_url' with the url and redirect to
1074 * @return string The url
1075 */
1076 function bbp_logout_url( $url = '', $redirect_to = '' ) {
1077
1078 // Make sure we are directing somewhere
1079 if ( empty( $redirect_to ) && !strstr( $url, 'redirect_to' ) ) {
1080
1081 // Rejig the $redirect_to
1082 if ( !isset( $_SERVER['REDIRECT_URL'] ) || ( $redirect_to != home_url( $_SERVER['REDIRECT_URL'] ) ) ) {
1083 $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
1084 }
1085
1086 $redirect_to = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
1087
1088 // Sanitize $redirect_to and add it to full $url
1089 $redirect_to = add_query_arg( array( 'loggedout' => 'true' ), esc_url( $redirect_to ) );
1090 $url = add_query_arg( array( 'redirect_to' => urlencode( $redirect_to ) ), $url );
1091 }
1092
1093 // Filter and return
1094 return apply_filters( 'bbp_logout_url', $url, $redirect_to );
1095 }
1096
1097 /** Queries *******************************************************************/
1098
1099 /**
1100 * Merge user defined arguments into defaults array.
1101 *
1102 * This function is used throughout bbPress to allow for either a string or array
1103 * to be merged into another array. It is identical to wp_parse_args() except
1104 * it allows for arguments to be passively or aggressively filtered using the
1105 * optional $filter_key parameter.
1106 *
1107 * @since bbPress (r3839)
1108 *
1109 * @param string|array $args Value to merge with $defaults
1110 * @param array $defaults Array that serves as the defaults.
1111 * @param string $filter_key String to key the filters from
1112 * @return array Merged user defined values with defaults.
1113 */
1114 function bbp_parse_args( $args, $defaults = '', $filter_key = '' ) {
1115
1116 // Setup a temporary array from $args
1117 if ( is_object( $args ) )
1118 $r = get_object_vars( $args );
1119 elseif ( is_array( $args ) )
1120 $r =& $args;
1121 else
1122 wp_parse_str( $args, $r );
1123
1124 // Passively filter the args before the parse
1125 if ( !empty( $filter_key ) )
1126 $r = apply_filters( 'bbp_before_' . $filter_key . '_parse_args', $r );
1127
1128 // Parse
1129 if ( is_array( $defaults ) )
1130 $r = array_merge( $defaults, $r );
1131
1132 // Aggressively filter the args after the parse
1133 if ( !empty( $filter_key ) )
1134 $r = apply_filters( 'bbp_after_' . $filter_key . '_parse_args', $r );
1135
1136 // Return the parsed results
1137 return $r;
1138 }
1139
1140 /**
1141 * Adds ability to include or exclude specific post_parent ID's
1142 *
1143 * @since bbPress (r2996)
1144 *
1145 * @global DB $wpdb
1146 * @global WP $wp
1147 * @param string $where
1148 * @param WP_Query $object
1149 * @return string
1150 */
1151 function bbp_query_post_parent__in( $where, $object = '' ) {
1152 global $wpdb, $wp;
1153
1154 // Noop if WP core supports this already
1155 if ( in_array( 'post_parent__in', $wp->private_query_vars ) )
1156 return $where;
1157
1158 // Bail if no object passed
1159 if ( empty( $object ) )
1160 return $where;
1161
1162 // Only 1 post_parent so return $where
1163 if ( is_numeric( $object->query_vars['post_parent'] ) )
1164 return $where;
1165
1166 // Including specific post_parent's
1167 if ( ! empty( $object->query_vars['post_parent__in'] ) ) {
1168 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__in'] ) );
1169 $where .= " AND $wpdb->posts.post_parent IN ($ids)";
1170
1171 // Excluding specific post_parent's
1172 } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) {
1173 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__not_in'] ) );
1174 $where .= " AND $wpdb->posts.post_parent NOT IN ($ids)";
1175 }
1176
1177 // Return possibly modified $where
1178 return $where;
1179 }
1180
1181 /**
1182 * Query the DB and get the last public post_id that has parent_id as post_parent
1183 *
1184 * @param int $parent_id Parent id
1185 * @param string $post_type Post type. Defaults to 'post'
1186 * @uses bbp_get_topic_post_type() To get the topic post type
1187 * @uses wp_cache_get() To check if there is a cache of the last child id
1188 * @uses wpdb::prepare() To prepare the query
1189 * @uses wpdb::get_var() To get the result of the query in a variable
1190 * @uses wp_cache_set() To set the cache for future use
1191 * @uses apply_filters() Calls 'bbp_get_public_child_last_id' with the child
1192 * id, parent id and post type
1193 * @return int The last active post_id
1194 */
1195 function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) {
1196 global $wpdb;
1197
1198 // Bail if nothing passed
1199 if ( empty( $parent_id ) )
1200 return false;
1201
1202 // The ID of the cached query
1203 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_last_id';
1204 $post_status = array( bbp_get_public_status_id() );
1205
1206 // Add closed status if topic post type
1207 if ( $post_type == bbp_get_topic_post_type() )
1208 $post_status[] = bbp_get_closed_status_id();
1209
1210 // Join post statuses together
1211 $post_status = "'" . join( "', '", $post_status ) . "'";
1212
1213 // Check for cache and set if needed
1214 $child_id = wp_cache_get( $cache_id, 'bbpress' );
1215 if ( empty( $child_id ) ) {
1216 $child_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC LIMIT 1;", $parent_id, $post_type ) );
1217 wp_cache_set( $cache_id, $child_id, 'bbpress' );
1218 }
1219
1220 // Filter and return
1221 return apply_filters( 'bbp_get_public_child_last_id', (int) $child_id, (int) $parent_id, $post_type );
1222 }
1223
1224 /**
1225 * Query the DB and get a count of public children
1226 *
1227 * @param int $parent_id Parent id
1228 * @param string $post_type Post type. Defaults to 'post'
1229 * @uses bbp_get_topic_post_type() To get the topic post type
1230 * @uses wp_cache_get() To check if there is a cache of the children count
1231 * @uses wpdb::prepare() To prepare the query
1232 * @uses wpdb::get_var() To get the result of the query in a variable
1233 * @uses wp_cache_set() To set the cache for future use
1234 * @uses apply_filters() Calls 'bbp_get_public_child_count' with the child
1235 * count, parent id and post type
1236 * @return int The number of children
1237 */
1238 function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) {
1239 global $wpdb;
1240
1241 // Bail if nothing passed
1242 if ( empty( $parent_id ) )
1243 return false;
1244
1245 // The ID of the cached query
1246 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_count';
1247 $post_status = array( bbp_get_public_status_id() );
1248
1249 // Add closed status if topic post type
1250 if ( $post_type == bbp_get_topic_post_type() )
1251 $post_status[] = bbp_get_closed_status_id();
1252
1253 // Join post statuses together
1254 $post_status = "'" . join( "', '", $post_status ) . "'";
1255
1256 // Check for cache and set if needed
1257 $child_count = wp_cache_get( $cache_id, 'bbpress' );
1258 if ( empty( $child_count ) ) {
1259 $child_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s';", $parent_id, $post_type ) );
1260 wp_cache_set( $cache_id, $child_count, 'bbpress' );
1261 }
1262
1263 // Filter and return
1264 return apply_filters( 'bbp_get_public_child_count', (int) $child_count, (int) $parent_id, $post_type );
1265 }
1266
1267 /**
1268 * Query the DB and get a the child id's of public children
1269 *
1270 * @param int $parent_id Parent id
1271 * @param string $post_type Post type. Defaults to 'post'
1272 * @uses bbp_get_topic_post_type() To get the topic post type
1273 * @uses wp_cache_get() To check if there is a cache of the children
1274 * @uses wpdb::prepare() To prepare the query
1275 * @uses wpdb::get_col() To get the result of the query in an array
1276 * @uses wp_cache_set() To set the cache for future use
1277 * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,
1278 * parent id and post type
1279 * @return array The array of children
1280 */
1281 function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) {
1282 global $wpdb;
1283
1284 // Bail if nothing passed
1285 if ( empty( $parent_id ) )
1286 return false;
1287
1288 // The ID of the cached query
1289 $cache_id = 'bbp_parent_public_' . $parent_id . '_type_' . $post_type . '_child_ids';
1290 $post_status = array( bbp_get_public_status_id() );
1291
1292 // Add closed status if topic post type
1293 if ( $post_type == bbp_get_topic_post_type() )
1294 $post_status[] = bbp_get_closed_status_id();
1295
1296 // Join post statuses together
1297 $post_status = "'" . join( "', '", $post_status ) . "'";
1298
1299 // Check for cache and set if needed
1300 $child_ids = wp_cache_get( $cache_id, 'bbpress' );
1301 if ( empty( $child_ids ) ) {
1302 $child_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC;", $parent_id, $post_type ) );
1303 wp_cache_set( $cache_id, $child_ids, 'bbpress' );
1304 }
1305
1306 // Filter and return
1307 return apply_filters( 'bbp_get_public_child_ids', $child_ids, (int) $parent_id, $post_type );
1308 }
1309 /**
1310 * Query the DB and get a the child id's of all children
1311 *
1312 * @param int $parent_id Parent id
1313 * @param string $post_type Post type. Defaults to 'post'
1314 * @uses bbp_get_topic_post_type() To get the topic post type
1315 * @uses wp_cache_get() To check if there is a cache of the children
1316 * @uses wpdb::prepare() To prepare the query
1317 * @uses wpdb::get_col() To get the result of the query in an array
1318 * @uses wp_cache_set() To set the cache for future use
1319 * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,
1320 * parent id and post type
1321 * @return array The array of children
1322 */
1323 function bbp_get_all_child_ids( $parent_id = 0, $post_type = 'post' ) {
1324 global $wpdb;
1325
1326 // Bail if nothing passed
1327 if ( empty( $parent_id ) )
1328 return false;
1329
1330 // The ID of the cached query
1331 $cache_id = 'bbp_parent_all_' . $parent_id . '_type_' . $post_type . '_child_ids';
1332 $post_status = array( bbp_get_public_status_id() );
1333
1334 // Extra post statuses based on post type
1335 switch ( $post_type ) {
1336
1337 // Forum
1338 case bbp_get_forum_post_type() :
1339 $post_status[] = bbp_get_private_status_id();
1340 $post_status[] = bbp_get_hidden_status_id();
1341 break;
1342
1343 // Topic
1344 case bbp_get_topic_post_type() :
1345 $post_status[] = bbp_get_closed_status_id();
1346 $post_status[] = bbp_get_trash_status_id();
1347 $post_status[] = bbp_get_spam_status_id();
1348 break;
1349
1350 // Reply
1351 case bbp_get_reply_post_type() :
1352 $post_status[] = bbp_get_trash_status_id();
1353 $post_status[] = bbp_get_spam_status_id();
1354 break;
1355 }
1356
1357 // Join post statuses together
1358 $post_status = "'" . join( "', '", $post_status ) . "'";
1359
1360 // Check for cache and set if needed
1361 $child_ids = wp_cache_get( $cache_id, 'bbpress' );
1362 if ( empty( $child_ids ) ) {
1363 $child_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC;", $parent_id, $post_type ) );
1364 wp_cache_set( $cache_id, $child_ids, 'bbpress' );
1365 }
1366
1367 // Filter and return
1368 return apply_filters( 'bbp_get_all_child_ids', $child_ids, (int) $parent_id, $post_type );
1369 }
1370
1371 /** Globals *******************************************************************/
1372
1373 /**
1374 * Get the unfiltered value of a global $post's key
1375 *
1376 * Used most frequently when editing a forum/topic/reply
1377 *
1378 * @since bbPress (r3694)
1379 *
1380 * @global WP_Query $post
1381 * @param string $field Name of the key
1382 * @param string $context How to sanitize - raw|edit|db|display|attribute|js
1383 * @return string Field value
1384 */
1385 function bbp_get_global_post_field( $field = 'ID', $context = 'edit' ) {
1386 global $post;
1387
1388 $retval = isset( $post->$field ) ? $post->$field : '';
1389 $retval = sanitize_post_field( $field, $retval, $post->ID, $context );
1390
1391 return apply_filters( 'bbp_get_global_post_field', $retval, $post );
1392 }
1393
1394 /** Nonces ********************************************************************/
1395
1396 /**
1397 * Makes sure the user requested an action from another page on this site.
1398 *
1399 * To avoid security exploits within the theme.
1400 *
1401 * @since bbPress (r4022)
1402 *
1403 * @uses do_action() Calls 'bbp_check_referer' on $action.
1404 * @param string $action Action nonce
1405 * @param string $query_arg where to look for nonce in $_REQUEST
1406 */
1407 function bbp_verify_nonce_request( $action = '', $query_arg = '_wpnonce' ) {
1408
1409 // Get the home URL
1410 $home_url = strtolower( home_url() );
1411
1412 // Build the currently requested URL
1413 $scheme = is_ssl() ? 'https://' : 'http://';
1414 $requested_url = strtolower( $scheme . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1415
1416 // Filter the requested URL, for configurations like reverse proxying
1417 $matched_url = apply_filters( 'bbp_verify_nonce_request_url', $requested_url );
1418
1419 // Check the nonce
1420 $result = isset( $_REQUEST[$query_arg] ) ? wp_verify_nonce( $_REQUEST[$query_arg], $action ) : false;
1421
1422 // Nonce check failed
1423 if ( empty( $result ) || empty( $action ) || ( strpos( $matched_url, $home_url ) !== 0 ) )
1424 $result = false;
1425
1426 // Do extra things
1427 do_action( 'bbp_verify_nonce_request', $action, $result );
1428
1429 return $result;
1430 }
1431
1432 /** Feeds *********************************************************************/
1433
1434 /**
1435 * This function is hooked into the WordPress 'request' action and is
1436 * responsible for sniffing out the query vars and serving up RSS2 feeds if
1437 * the stars align and the user has requested a feed of any bbPress type.
1438 *
1439 * @since bbPress (r3171)
1440 *
1441 * @param array $query_vars
1442 * @return array
1443 */
1444 function bbp_request_feed_trap( $query_vars = array() ) {
1445
1446 // Looking at a feed
1447 if ( isset( $query_vars['feed'] ) ) {
1448
1449 // Forum/Topic/Reply Feed
1450 if ( isset( $query_vars['post_type'] ) ) {
1451
1452 // What bbPress post type are we looking for feeds on?
1453 switch ( $query_vars['post_type'] ) {
1454
1455 // Forum
1456 case bbp_get_forum_post_type() :
1457
1458 // Define local variable(s)
1459 $meta_query = array();
1460
1461 // Single forum
1462 if ( isset( $query_vars[bbp_get_forum_post_type()] ) ) {
1463
1464 // Get the forum by the path
1465 $forum = get_page_by_path( $query_vars[bbp_get_forum_post_type()], OBJECT, bbp_get_forum_post_type() );
1466 $forum_id = $forum->ID;
1467
1468 // Load up our own query
1469 query_posts( array(
1470 'post_type' => bbp_get_forum_post_type(),
1471 'ID' => $forum_id,
1472 'feed' => true
1473 ) );
1474
1475 // Restrict to specific forum ID
1476 $meta_query = array( array(
1477 'key' => '_bbp_forum_id',
1478 'value' => $forum_id,
1479 'type' => 'numeric',
1480 'compare' => '='
1481 ) );
1482 }
1483
1484 // Only forum replies
1485 if ( !empty( $_GET['type'] ) && ( bbp_get_reply_post_type() == $_GET['type'] ) ) {
1486
1487 // The query
1488 $the_query = array(
1489 'author' => 0,
1490 'feed' => true,
1491 'post_type' => bbp_get_reply_post_type(),
1492 'post_parent' => 'any',
1493 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ),
1494 'posts_per_page' => bbp_get_replies_per_rss_page(),
1495 'order' => 'DESC',
1496 'meta_query' => $meta_query
1497 );
1498
1499 // Output the feed
1500 bbp_display_replies_feed_rss2( $the_query );
1501
1502 // Only forum topics
1503 } elseif ( !empty( $_GET['type'] ) && ( bbp_get_topic_post_type() == $_GET['type'] ) ) {
1504
1505 // The query
1506 $the_query = array(
1507 'author' => 0,
1508 'feed' => true,
1509 'post_type' => bbp_get_topic_post_type(),
1510 'post_parent' => $forum_id,
1511 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ),
1512 'posts_per_page' => bbp_get_topics_per_rss_page(),
1513 'order' => 'DESC'
1514 );
1515
1516 // Output the feed
1517 bbp_display_topics_feed_rss2( $the_query );
1518
1519 // All forum topics and replies
1520 } else {
1521
1522 // Exclude private/hidden forums if not looking at single
1523 if ( empty( $query_vars['forum'] ) )
1524 $meta_query = array( bbp_exclude_forum_ids( 'meta_query' ) );
1525
1526 // The query
1527 $the_query = array(
1528 'author' => 0,
1529 'feed' => true,
1530 'post_type' => array( bbp_get_reply_post_type(), bbp_get_topic_post_type() ),
1531 'post_parent' => 'any',
1532 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ),
1533 'posts_per_page' => bbp_get_replies_per_rss_page(),
1534 'order' => 'DESC',
1535 'meta_query' => $meta_query
1536 );
1537
1538 // Output the feed
1539 bbp_display_replies_feed_rss2( $the_query );
1540 }
1541
1542 break;
1543
1544 // Topic feed - Show replies
1545 case bbp_get_topic_post_type() :
1546
1547 // Single topic
1548 if ( isset( $query_vars[bbp_get_topic_post_type()] ) ) {
1549
1550 // Load up our own query
1551 query_posts( array(
1552 'post_type' => bbp_get_topic_post_type(),
1553 'name' => $query_vars[bbp_get_topic_post_type()],
1554 'feed' => true
1555 ) );
1556
1557 // Output the feed
1558 bbp_display_replies_feed_rss2( array( 'feed' => true ) );
1559
1560 // All topics
1561 } else {
1562
1563 // The query
1564 $the_query = array(
1565 'author' => 0,
1566 'feed' => true,
1567 'post_parent' => 'any',
1568 'posts_per_page' => bbp_get_topics_per_rss_page(),
1569 'show_stickies' => false
1570 );
1571
1572 // Output the feed
1573 bbp_display_topics_feed_rss2( $the_query );
1574 }
1575
1576 break;
1577
1578 // Replies
1579 case bbp_get_reply_post_type() :
1580
1581 // The query
1582 $the_query = array(
1583 'posts_per_page' => bbp_get_replies_per_rss_page(),
1584 'meta_query' => array( array( ) ),
1585 'feed' => true
1586 );
1587
1588 // All replies
1589 if ( !isset( $query_vars[bbp_get_reply_post_type()] ) ) {
1590 bbp_display_replies_feed_rss2( $the_query );
1591 }
1592
1593 break;
1594 }
1595
1596 // Single Topic Vview
1597 } elseif ( isset( $query_vars['bbp_view'] ) ) {
1598
1599 // Get the view
1600 $view = $query_vars['bbp_view'];
1601
1602 // We have a view to display a feed
1603 if ( !empty( $view ) ) {
1604
1605 // Get the view query
1606 $the_query = bbp_get_view_query_args( $view );
1607
1608 // Output the feed
1609 bbp_display_topics_feed_rss2( $the_query );
1610 }
1611 }
1612
1613 // @todo User profile feeds
1614 }
1615
1616 // No feed so continue on
1617 return $query_vars;
1618 }
1619
1620 /** Templates ******************************************************************/
1621
1622 /**
1623 * Used to guess if page exists at requested path
1624 *
1625 * @since bbPress (r3304)
1626 *
1627 * @uses get_option() To see if pretty permalinks are enabled
1628 * @uses get_page_by_path() To see if page exists at path
1629 *
1630 * @param string $path
1631 * @return mixed False if no page, Page object if true
1632 */
1633 function bbp_get_page_by_path( $path = '' ) {
1634
1635 // Default to false
1636 $retval = false;
1637
1638 // Path is not empty
1639 if ( !empty( $path ) ) {
1640
1641 // Pretty permalinks are on so path might exist
1642 if ( get_option( 'permalink_structure' ) ) {
1643 $retval = get_page_by_path( $path );
1644 }
1645 }
1646
1647 return apply_filters( 'bbp_get_page_by_path', $retval, $path );
1648 }
1649
1650 /**
1651 * Sets the 404 status.
1652 *
1653 * Used primarily with topics/replies inside hidden forums.
1654 *
1655 * @since bbPress (r3051)
1656 *
1657 * @global WP_Query $wp_query
1658 * @uses WP_Query::set_404()
1659 */
1660 function bbp_set_404() {
1661 global $wp_query;
1662
1663 if ( ! isset( $wp_query ) ) {
1664 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.', 'bbpress' ), '3.1' );
1665 return false;
1666 }
1667
1668 $wp_query->set_404();
1669 }
1670