PluginProbe
bbPress / 2.0-beta-2b
bbPress v2.0-beta-2b
2.6.17 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 All 72 releases
bbpress / bbp-includes / bbp-general-functions.php

bbp-general-functions.php in bbPress 2.0-beta-2b, at bbp-includes/bbp-general-functions.php

1,179 lines 39.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress General Functions
5 *
6 * @package bbPress
7 * @subpackage Functions
8 */
9
10 // Exit if accessed directly
11 if ( !defined( 'ABSPATH' ) ) exit;
12
13 /** Post Meta *****************************************************************/
14
15 /**
16 * Update a posts forum meta ID
17 *
18 * @since bbPress (r3181)
19 *
20 * @param int $post_id The post to update
21 * @param int $forum_id The forum
22 */
23 function bbp_update_forum_id( $post_id, $forum_id ) {
24
25 // Allow the forum ID to be updated 'just in time' before save
26 $forum_id = apply_filters( 'bbp_update_forum_id', $forum_id, $post_id );
27
28 // Update the post meta forum ID
29 update_post_meta( $post_id, '_bbp_forum_id', (int) $forum_id );
30 }
31
32 /**
33 * Update a posts topic meta ID
34 *
35 * @since bbPress (r3181)
36 *
37 * @param int $post_id The post to update
38 * @param int $forum_id The forum
39 */
40 function bbp_update_topic_id( $post_id, $topic_id ) {
41
42 // Allow the topic ID to be updated 'just in time' before save
43 $topic_id = apply_filters( 'bbp_update_topic_id', $topic_id, $post_id );
44
45 // Update the post meta topic ID
46 update_post_meta( $post_id, '_bbp_topic_id', (int) $topic_id );
47 }
48
49 /**
50 * Update a posts reply meta ID
51 *
52 * @since bbPress (r3181)
53 *
54 * @param int $post_id The post to update
55 * @param int $forum_id The forum
56 */
57 function bbp_update_reply_id( $post_id, $reply_id ) {
58
59 // Allow the reply ID to be updated 'just in time' before save
60 $reply_id = apply_filters( 'bbp_update_reply_id', $reply_id, $post_id );
61
62 // Update the post meta reply ID
63 update_post_meta( $post_id, '_bbp_reply_id',(int) $reply_id );
64 }
65
66 /** Formatting ****************************************************************/
67
68 /**
69 * A bbPress specific method of formatting numeric values
70 *
71 * @since bbPress (r2486)
72 *
73 * @param string $number Number to format
74 * @param string $decimals Optional. Display decimals
75 * @uses apply_filters() Calls 'bbp_number_format' with the formatted values,
76 * number and display decimals bool
77 * @return string Formatted string
78 */
79 function bbp_number_format( $number, $decimals = false ) {
80
81 // If empty, set $number to '0'
82 if ( empty( $number ) || !is_numeric( $number ) )
83 $number = '0';
84
85 return apply_filters( 'bbp_number_format', number_format( $number, $decimals ), $number, $decimals );
86 }
87
88 /**
89 * Convert time supplied from database query into specified date format.
90 *
91 * @since bbPress (r2455)
92 *
93 * @param int|object $post Optional. Default is global post object. A post_id or
94 * post object
95 * @param string $d Optional. Default is 'U'. Either 'G', 'U', or php date
96 * format
97 * @param bool $translate Optional. Default is false. Whether to translate the
98 * result
99 * @uses mysql2date() To convert the format
100 * @uses apply_filters() Calls 'bbp_convert_date' with the time, date format
101 * and translate bool
102 * @return string Returns timestamp
103 */
104 function bbp_convert_date( $time, $d = 'U', $translate = false ) {
105 $time = mysql2date( $d, $time, $translate );
106
107 return apply_filters( 'bbp_convert_date', $time, $d, $translate );
108 }
109
110 /**
111 * Output formatted time to display human readable time difference.
112 *
113 * @since bbPress (r2544)
114 *
115 * @param $time Unix timestamp from which the difference begins.
116 * @uses bbp_get_time_since() To get the formatted time
117 */
118 function bbp_time_since( $time ) {
119 echo bbp_get_time_since( $time );
120 }
121 /**
122 * Return formatted time to display human readable time difference.
123 *
124 * @since bbPress (r2544)
125 *
126 * @param $time Unix timestamp from which the difference begins.
127 * @uses current_time() To get the current time in mysql format
128 * @uses human_time_diff() To get the time differene in since format
129 * @uses apply_filters() Calls 'bbp_get_time_since' with the time
130 * difference and time
131 * @return string Formatted time
132 */
133 function bbp_get_time_since( $time ) {
134 return apply_filters( 'bbp_get_time_since', human_time_diff( $time, current_time( 'timestamp' ) ), $time );
135 }
136
137 /**
138 * Formats the reason for editing the topic/reply.
139 *
140 * Does these things:
141 * - Trimming
142 * - Removing periods from the end of the string
143 * - Trimming again
144 *
145 * @since bbPress (r2782)
146 *
147 * @param int $topic_id Optional. Topic id
148 * @return string Status of topic
149 */
150 function bbp_format_revision_reason( $reason = '' ) {
151 $reason = (string) $reason;
152
153 // Format reason for proper display
154 if ( empty( $reason ) )
155 return $reason;
156
157 // Trimming
158 $reason = trim( $reason );
159
160 // We add our own full stop.
161 while ( substr( $reason, -1 ) == '.' )
162 $reason = substr( $reason, 0, -1 );
163
164 // Trim again
165 $reason = trim( $reason );
166
167 return $reason;
168 }
169
170 /** Misc **********************************************************************/
171
172 /**
173 * The plugin version of bbPress comes with two topic display options:
174 * - Traditional: Topics are included in the reply loop (default)
175 * - New Style: Topics appear as "lead" posts, ahead of replies
176 *
177 * @since bbPress (r2954)
178 *
179 * @param $show_lead Optional. Default false
180 * @return bool Yes if the topic appears as a lead, otherwise false
181 */
182 function bbp_show_lead_topic( $show_lead = false ) {
183 return apply_filters( 'bbp_show_lead_topic', (bool) $show_lead );
184 }
185
186 /**
187 * Append 'view=all' to query string if it's already there from referer
188 *
189 * @param string $original_link Original Link to be modified
190 * @uses current_user_can() To check if the current user can moderate
191 * @uses add_query_arg() To add args to the url
192 * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link
193 * @return string The link with 'view=all' appended if necessary
194 */
195 function bbp_add_view_all( $original_link ) {
196
197 // Bail if empty
198 if ( empty( $original_link ) )
199 return $original_link;
200
201 // Are we appending the view=all vars?
202 if ( ( !empty( $_GET['view'] ) && ( 'all' == $_GET['view'] ) && current_user_can( 'moderate' ) ) )
203 $link = add_query_arg( array( 'view' => 'all' ), $original_link );
204 else
205 $link = $original_link;
206
207 return apply_filters( 'bbp_add_view_all', $link, $original_link );
208 }
209
210 /**
211 * Assist pagination by returning correct page number
212 *
213 * @since bbPress (r2628)
214 *
215 * @uses get_query_var() To get the 'paged' value
216 * @return int Current page number
217 */
218 function bbp_get_paged() {
219
220 // Make sure to not paginate widget queries
221 if ( !bbp_is_query_name( 'bbp_widget' ) && ( $paged = get_query_var( 'paged' ) ) )
222 return (int) $paged;
223
224 // Default to first page
225 return 1;
226 }
227
228 /**
229 * Fix post author id on post save
230 *
231 * When a logged in user changes the status of an anonymous reply or topic, or
232 * edits it, the post_author field is set to the logged in user's id. This
233 * function fixes that.
234 *
235 * @since bbPress (r2734)
236 *
237 * @param array $data Post data
238 * @param array $postarr Original post array (includes post id)
239 * @uses bbp_get_topic_post_type() To get the topic post type
240 * @uses bbp_get_reply_post_type() To get the reply post type
241 * @uses bbp_is_topic_anonymous() To check if the topic is by an anonymous user
242 * @uses bbp_is_reply_anonymous() To check if the reply is by an anonymous user
243 * @return array Data
244 */
245 function bbp_fix_post_author( $data = array(), $postarr = array() ) {
246 global $bbp;
247
248 // Post is not being updated or the post_author is already 0, return
249 if ( empty( $postarr['ID'] ) || empty( $data['post_author'] ) )
250 return $data;
251
252 // Post is not a topic or reply, return
253 if ( !in_array( $data['post_type'], array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) ) )
254 return $data;
255
256 // Is the post by an anonymous user?
257 if ( ( bbp_get_topic_post_type() == $data['post_type'] && !bbp_is_topic_anonymous( $postarr['ID'] ) ) ||
258 ( bbp_get_reply_post_type() == $data['post_type'] && !bbp_is_reply_anonymous( $postarr['ID'] ) ) )
259 return $data;
260
261 // The post is being updated. It is a topic or a reply and is written by an anonymous user.
262 // Set the post_author back to 0
263 $data['post_author'] = 0;
264
265 return $data;
266 }
267
268 /**
269 * Check the date against the _bbp_edit_lock setting.
270 *
271 * @since bbPress (r3133)
272 *
273 * @param string $post_date_gmt
274 *
275 * @uses get_option() Get the edit lock time
276 * @uses current_time() Get the current time
277 * @uses strtotime() Convert strings to time
278 * @uses apply_filters() Allow output to be manipulated
279 *
280 * @return bool
281 */
282 function bbp_past_edit_lock( $post_date_gmt ) {
283
284 // Assume editing is allowed
285 $retval = false;
286
287 // Bail if empty date
288 if ( ! empty( $post_date_gmt ) ) {
289
290 // Period of time
291 $lockable = '+' . get_option( '_bbp_edit_lock', '5' ) . ' minutes';
292
293 // Now
294 $cur_time = current_time( 'timestamp', true );
295
296 // Add lockable time to post time
297 $lock_time = strtotime( $lockable, strtotime( $post_date_gmt ) );
298
299 // Compare
300 if ( $cur_time >= $lock_time ) {
301 $retval = true;
302 }
303 }
304
305 return apply_filters( 'bbp_past_edit_lock', (bool) $retval, $cur_time, $lock_time, $post_date_gmt );
306 }
307
308 /** Statistics ****************************************************************/
309
310 /**
311 * Get the forum statistics
312 *
313 * @since bbPress (r2769)
314 *
315 * @param mixed $args Optional. The function supports these arguments (all
316 * default to true):
317 * - count_users: Count users?
318 * - count_forums: Count forums?
319 * - count_topics: Count topics? If set to false, private, spammed and trashed
320 * topics are also not counted.
321 * - count_private_topics: Count private topics? (only counted if the current
322 * user has read_private_topics cap)
323 * - count_spammed_topics: Count spammed topics? (only counted if the current
324 * user has edit_others_topics cap)
325 * - count_trashed_topics: Count trashed topics? (only counted if the current
326 * user has view_trash cap)
327 * - count_replies: Count replies? If set to false, private, spammed and
328 * trashed replies are also not counted.
329 * - count_private_replies: Count private replies? (only counted if the current
330 * user has read_private_replies cap)
331 * - count_spammed_replies: Count spammed replies? (only counted if the current
332 * user has edit_others_replies cap)
333 * - count_trashed_replies: Count trashed replies? (only counted if the current
334 * user has view_trash cap)
335 * - count_tags: Count tags? If set to false, empty tags are also not counted
336 * - count_empty_tags: Count empty tags?
337 * @uses bbp_count_users() To count the number of registered users
338 * @uses bbp_get_forum_post_type() To get the forum post type
339 * @uses bbp_get_topic_post_type() To get the topic post type
340 * @uses bbp_get_reply_post_type() To get the reply post type
341 * @uses wp_count_posts() To count the number of forums, topics and replies
342 * @uses wp_count_terms() To count the number of topic tags
343 * @uses current_user_can() To check if the user is capable of doing things
344 * @uses number_format_i18n() To format the number
345 * @uses apply_filters() Calls 'bbp_get_statistics' with the statistics and args
346 * @return object Walked forum tree
347 */
348 function bbp_get_statistics( $args = '' ) {
349 global $bbp;
350
351 $defaults = array (
352 'count_users' => true,
353 'count_forums' => true,
354 'count_topics' => true,
355 'count_private_topics' => true,
356 'count_spammed_topics' => true,
357 'count_trashed_topics' => true,
358 'count_replies' => true,
359 'count_private_replies' => true,
360 'count_spammed_replies' => true,
361 'count_trashed_replies' => true,
362 'count_tags' => true,
363 'count_empty_tags' => true
364 );
365
366 $r = wp_parse_args( $args, $defaults );
367 extract( $r );
368
369 // Users
370 if ( !empty( $count_users ) )
371 $user_count = bbp_get_total_users();
372
373 // Forums
374 if ( !empty( $count_forums ) ) {
375 $forum_count = wp_count_posts( bbp_get_forum_post_type() );
376 $forum_count = $forum_count->publish;
377 }
378
379 // Topics
380 if ( !empty( $count_topics ) ) {
381
382 $all_topics = wp_count_posts( bbp_get_topic_post_type() );
383
384 // Published (publish + closed)
385 $topic_count = $all_topics->publish + $all_topics->{$bbp->closed_status_id};
386
387 if ( current_user_can( 'read_private_topics' ) || current_user_can( 'edit_others_topics' ) || current_user_can( 'view_trash' ) ) {
388
389 // Private
390 $topics['private'] = ( !empty( $count_private_topics ) && current_user_can( 'read_private_topics' ) ) ? (int) $all_topics->private : 0;
391
392 // Spam
393 $topics['spammed'] = ( !empty( $count_spammed_topics ) && current_user_can( 'edit_others_topics' ) ) ? (int) $all_topics->{$bbp->spam_status_id} : 0;
394
395 // Trash
396 $topics['trashed'] = ( !empty( $count_trashed_topics ) && current_user_can( 'view_trash' ) ) ? (int) $all_topics->{$bbp->trash_status_id} : 0;
397
398 // Total hidden (private + spam + trash)
399 $hidden_topic_count = $topics['private'] + $topics['spammed'] + $topics['trashed'];
400
401 // Generate the hidden topic count's title attribute
402 $topic_titles[] = !empty( $topics['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $topics['private'] ) ) : '';
403 $topic_titles[] = !empty( $topics['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $topics['spammed'] ) ) : '';
404 $topic_titles[] = !empty( $topics['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $topics['trashed'] ) ) : '';
405
406 // Compile the hidden topic title
407 $hidden_topic_title = implode( ' | ', array_filter( $topic_titles ) );
408 }
409 }
410
411 // Replies
412 if ( !empty( $count_replies ) ) {
413
414 $all_replies = wp_count_posts( bbp_get_reply_post_type() );
415
416 // Published
417 $reply_count = $all_replies->publish;
418
419 if ( current_user_can( 'read_private_replies' ) || current_user_can( 'edit_others_replies' ) || current_user_can( 'view_trash' ) ) {
420
421 // Private
422 $replies['private'] = ( !empty( $count_private_replies ) && current_user_can( 'read_private_replies' ) ) ? (int) $all_replies->private : 0;
423
424 // Spam
425 $replies['spammed'] = ( !empty( $count_spammed_replies ) && current_user_can( 'edit_others_replies' ) ) ? (int) $all_replies->{$bbp->spam_status_id} : 0;
426
427 // Trash
428 $replies['trashed'] = ( !empty( $count_trashed_replies ) && current_user_can( 'view_trash' ) ) ? (int) $all_replies->{$bbp->trash_status_id} : 0;
429
430 // Total hidden (private + spam + trash)
431 $hidden_reply_count = $replies['private'] + $replies['spammed'] + $replies['trashed'];
432
433 // Generate the hidden topic count's title attribute
434 $reply_titles[] = !empty( $replies['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $replies['private'] ) ) : '';
435 $reply_titles[] = !empty( $replies['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $replies['spammed'] ) ) : '';
436 $reply_titles[] = !empty( $replies['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $replies['trashed'] ) ) : '';
437
438 // Compile the hidden replies title
439 $hidden_reply_title = implode( ' | ', array_filter( $reply_titles ) );
440
441 }
442 }
443
444 // Topic Tags
445 if ( !empty( $count_tags ) ) {
446
447 // Get the count
448 $topic_tag_count = wp_count_terms( $bbp->topic_tag_id, array( 'hide_empty' => true ) );
449
450 // Empty tags
451 if ( !empty( $count_empty_tags ) && current_user_can( 'edit_topic_tags' ) ) {
452 $empty_topic_tag_count = wp_count_terms( $bbp->topic_tag_id ) - $topic_tag_count;
453 }
454 }
455
456 // Tally the tallies
457 $statistics = compact( 'user_count', 'forum_count', 'topic_count', 'hidden_topic_count', 'reply_count', 'hidden_reply_count', 'topic_tag_count', 'empty_topic_tag_count' );
458 $statistics = array_map( 'absint', $statistics );
459 $statistics = array_map( 'number_format_i18n', $statistics );
460
461 // Add the hidden (topic/reply) count title attribute strings because we don't need to run the math functions on these (see above)
462 if ( isset( $hidden_topic_title ) )
463 $statistics['hidden_topic_title'] = $hidden_topic_title;
464
465 if ( isset( $hidden_reply_title ) )
466 $statistics['hidden_reply_title'] = $hidden_reply_title;
467
468 return apply_filters( 'bbp_get_statistics', $statistics, $args );
469 }
470
471 /** Views *********************************************************************/
472
473 /**
474 * Get the registered views
475 *
476 * Does nothing much other than return the {@link $bbp->views} variable
477 *
478 * @since bbPress (r2789)
479 *
480 * @return array Views
481 */
482 function bbp_get_views() {
483 global $bbp;
484
485 return $bbp->views;
486 }
487
488 /**
489 * Register a bbPress view
490 *
491 * @todo Implement feeds - See {@link http://trac.bbpress.org/ticket/1422}
492 *
493 * @since bbPress (r2789)
494 *
495 * @param string $view View name
496 * @param string $title View title
497 * @param mixed $query_args {@link bbp_has_topics()} arguments.
498 * @param bool $feed Have a feed for the view? Defaults to true. NOT IMPLEMENTED
499 * @uses sanitize_title() To sanitize the view name
500 * @uses esc_html() To sanitize the view title
501 * @return array The just registered (but processed) view
502 */
503 function bbp_register_view( $view, $title, $query_args = '', $feed = true ) {
504 global $bbp;
505
506 $view = sanitize_title( $view );
507 $title = esc_html( $title );
508
509 if ( empty( $view ) || empty( $title ) )
510 return false;
511
512 $query_args = wp_parse_args( $query_args );
513
514 // Set exclude_stickies to true if it wasn't supplied
515 if ( !isset( $query_args['show_stickies'] ) )
516 $query_args['show_stickies'] = false;
517
518 $bbp->views[$view]['title'] = $title;
519 $bbp->views[$view]['query'] = $query_args;
520 $bbp->views[$view]['feed'] = $feed;
521
522 return $bbp->views[$view];
523 }
524
525 /**
526 * Deregister a bbPress view
527 *
528 * @since bbPress (r2789)
529 *
530 * @param string $view View name
531 * @uses sanitize_title() To sanitize the view name
532 * @return bool False if the view doesn't exist, true on success
533 */
534 function bbp_deregister_view( $view ) {
535 global $bbp;
536
537 $view = sanitize_title( $view );
538
539 if ( !isset( $bbp->views[$view] ) )
540 return false;
541
542 unset( $bbp->views[$view] );
543
544 return true;
545 }
546
547 /**
548 * Run the view's query
549 *
550 * @since bbPress (r2789)
551 *
552 * @param string $view Optional. View id
553 * @param mixed $new_args New arguments. See {@link bbp_has_topics()}
554 * @uses bbp_get_view_id() To get the view id
555 * @uses bbp_get_view_query_args() To get the view query args
556 * @uses sanitize_title() To sanitize the view name
557 * @uses bbp_has_topics() To make the topics query
558 * @return bool False if the view doesn't exist, otherwise if topics are there
559 */
560 function bbp_view_query( $view = '', $new_args = '' ) {
561 global $bbp;
562
563 if ( !$view = bbp_get_view_id( $view ) )
564 return false;
565
566 $query_args = bbp_get_view_query_args( $view );
567
568 if ( !empty( $new_args ) ) {
569 $new_args = wp_parse_args( $new_args );
570 $query_args = array_merge( $query_args, $new_args );
571 }
572
573 return bbp_has_topics( $query_args );
574 }
575
576 /**
577 * Run the view's query's arguments
578 *
579 * @since bbPress (r2789)
580 *
581 * @param string $view View name
582 * @uses bbp_get_view_id() To get the view id
583 * @uses sanitize_title() To sanitize the view name
584 * @return array Query arguments
585 */
586 function bbp_get_view_query_args( $view ) {
587 global $bbp;
588
589 if ( !$views = bbp_get_view_id( $view ) )
590 return false;
591
592 return $bbp->views[$view]['query'];
593 }
594
595 /** New/edit topic/reply helpers **********************************************/
596
597 /**
598 * Filter anonymous post data
599 *
600 * We use REMOTE_ADDR here directly. If you are behind a proxy, you should
601 * ensure that it is properly set, such as in wp-config.php, for your
602 * environment. See {@link http://core.trac.wordpress.org/ticket/9235}
603 *
604 * If there are any errors, those are directly added to {@link bbPress:errors}
605 *
606 * @since bbPress (r2734)
607 *
608 * @param mixed $args Optional. If no args are there, then $_POST values are
609 * used.
610 * @param bool $is_edit Optional. Is the topic/reply being edited? There are no
611 * IP checks then.
612 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_name' with the
613 * anonymous user name
614 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_email' with the
615 * anonymous user email
616 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_ip' with the
617 * anonymous user's ip address
618 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_website' with the
619 * anonymous user website
620 * @return bool|array False on errors, values in an array on success
621 */
622 function bbp_filter_anonymous_post_data( $args = '', $is_edit = false ) {
623 global $bbp;
624
625 // Assign variables
626 $defaults = array (
627 'bbp_anonymous_name' => !empty( $_POST['bbp_anonymous_name'] ) ? $_POST['bbp_anonymous_name'] : false,
628 'bbp_anonymous_email' => !empty( $_POST['bbp_anonymous_email'] ) ? $_POST['bbp_anonymous_email'] : false,
629 'bbp_anonymous_website' => !empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false,
630 );
631
632 $r = wp_parse_args( $args, $defaults );
633 extract( $r );
634
635 // Filter variables and add errors if necessary
636 if ( !$bbp_anonymous_name = apply_filters( 'bbp_pre_anonymous_post_author_name', $bbp_anonymous_name ) )
637 $bbp->errors->add( 'bbp_anonymous_name', __( '<strong>ERROR</strong>: Invalid author name submitted!', 'bbpress' ) );
638
639 if ( !$bbp_anonymous_email = apply_filters( 'bbp_pre_anonymous_post_author_email', $bbp_anonymous_email ) )
640 $bbp->errors->add( 'bbp_anonymous_email', __( '<strong>ERROR</strong>: Invalid email address submitted!', 'bbpress' ) );
641
642 // Website is optional
643 $bbp_anonymous_website = apply_filters( 'bbp_pre_anonymous_post_author_website', $bbp_anonymous_website );
644
645 if ( !is_wp_error( $bbp->errors ) || !$bbp->errors->get_error_codes() )
646 $retval = compact( 'bbp_anonymous_name', 'bbp_anonymous_email', 'bbp_anonymous_website' );
647 else
648 $retval = false;
649
650 // Finally, return sanitized data or false
651 return apply_filters( 'bbp_filter_anonymous_post_data', $retval, $args );
652 }
653
654 /**
655 * Check for duplicate topics/replies
656 *
657 * Check to make sure that a user is not making a duplicate post
658 *
659 * @since bbPress (r2763)
660 *
661 * @param array $post_data Contains information about the comment
662 * @uses current_user_can() To check if the current user can throttle
663 * @uses get_meta_sql() To generate the meta sql for checking anonymous email
664 * @uses apply_filters() Calls 'bbp_check_for_duplicate_query' with the
665 * duplicate check query and post data
666 * @uses wpdb::get_var() To execute our query and get the var back
667 * @uses get_post_meta() To get the anonymous user email post meta
668 * @uses do_action() Calls 'bbp_post_duplicate_trigger' with the post data when
669 * it is found that it is a duplicate
670 * @return bool True if it is not a duplicate, false if it is
671 */
672 function bbp_check_for_duplicate( $post_data ) {
673
674 // No duplicate checks for those who can throttle
675 if ( current_user_can( 'throttle' ) )
676 return true;
677
678 global $bbp, $wpdb;
679
680 extract( $post_data, EXTR_SKIP );
681
682 // Check for anonymous post
683 if ( empty( $post_author ) && !empty( $anonymous_data['bbp_anonymous_email'] ) ) {
684
685 // WP 3.2
686 if ( function_exists( 'get_meta_sql' ) )
687 $clauses = get_meta_sql( array( array( 'key' => '_bbp_anonymous_email', 'value' => $anonymous_data['bbp_anonymous_email'] ) ), 'post', $wpdb->posts, 'ID' );
688
689 // WP 3.1
690 elseif ( function_exists( '_get_meta_sql' ) )
691 $clauses = _get_meta_sql( array( array( 'key' => '_bbp_anonymous_email', 'value' => $anonymous_data['bbp_anonymous_email'] ) ), 'post', $wpdb->posts, 'ID' );
692
693 $join = $clauses['join'];
694 $where = $clauses['where'];
695 } else{
696 $join = $where = '';
697 }
698
699 // Simple duplicate check
700 // Expected slashed ($post_type, $post_parent, $post_author, $post_content, $anonymous_data)
701 $dupe = "SELECT ID FROM {$wpdb->posts} {$join} WHERE post_type = '{$post_type}' AND post_status != '{$bbp->trash_status_id}' AND post_author = {$post_author} AND post_content = '{$post_content}' {$where}";
702 $dupe .= !empty( $post_parent ) ? " AND post_parent = '{$post_parent}'" : '';
703 $dupe .= " LIMIT 1";
704 $dupe = apply_filters( 'bbp_check_for_duplicate_query', $dupe, $post_data );
705
706 if ( $wpdb->get_var( $dupe ) ) {
707 do_action( 'bbp_check_for_duplicate_trigger', $post_data );
708 return false;
709 }
710
711 return true;
712 }
713
714 /**
715 * Check for flooding
716 *
717 * Check to make sure that a user is not making too many posts in a short amount
718 * of time.
719 *
720 * @since bbPress (r2734)
721 *
722 * @param false|array $anonymous_data Optional - if it's an anonymous post. Do
723 * not supply if supplying $author_id.
724 * Should have key 'bbp_author_ip'.
725 * Should be sanitized (see
726 * {@link bbp_filter_anonymous_post_data()}
727 * for sanitization)
728 * @param int $author_id Optional. Supply if it's a post by a logged in user.
729 * Do not supply if supplying $anonymous_data.
730 * @uses get_option() To get the throttle time
731 * @uses get_transient() To get the last posted transient of the ip
732 * @uses get_user_meta() To get the last posted meta of the user
733 * @uses current_user_can() To check if the current user can throttle
734 * @return bool True if there is no flooding, true if there is
735 */
736 function bbp_check_for_flood( $anonymous_data = false, $author_id = 0 ) {
737
738 // Option disabled. No flood checks.
739 if ( !$throttle_time = get_option( '_bbp_throttle_time' ) )
740 return true;
741
742 if ( !empty( $anonymous_data ) && is_array( $anonymous_data ) ) {
743 $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' );
744 if ( !empty( $last_posted ) && time() < $last_posted + $throttle_time )
745 return false;
746 } elseif ( !empty( $author_id ) ) {
747 $author_id = (int) $author_id;
748 $last_posted = get_user_meta( $author_id, '_bbp_last_posted', true );
749
750 if ( isset( $last_posted ) && time() < $last_posted + $throttle_time && !current_user_can( 'throttle' ) )
751 return false;
752 } else {
753 return false;
754 }
755
756 return true;
757 }
758
759 /** Subscriptions *************************************************************/
760
761 /**
762 * Sends notification emails for new posts
763 *
764 * Gets new post's ID and check if there are subscribed users to that topic, and
765 * if there are, send notifications
766 *
767 * @since bbPress (r2668)
768 *
769 * @todo When Akismet is made to work with bbPress posts, add a check if the
770 * post is spam or not, to avoid sending out mails for spam posts
771 *
772 * @param int $reply_id ID of the newly made reply
773 * @uses bbp_is_subscriptions_active() To check if the subscriptions are active
774 * @uses bbp_get_reply() To get the reply
775 * @uses bbp_get_topic() To get the reply's topic
776 * @uses get_the_author_meta() To get the author's display name
777 * @uses do_action() Calls 'bbp_pre_notify_subscribers' with the reply id and
778 * topic id
779 * @uses bbp_get_topic_subscribers() To get the topic subscribers
780 * @uses apply_filters() Calls 'bbp_subscription_mail_message' with the
781 * message, reply id, topic id and user id
782 * @uses get_userdata() To get the user data
783 * @uses wp_mail() To send the mail
784 * @uses do_action() Calls 'bbp_post_notify_subscribers' with the reply id
785 * and topic id
786 * @return bool True on success, false on failure
787 */
788 function bbp_notify_subscribers( $reply_id = 0 ) {
789 global $bbp, $wpdb;
790
791 if ( !bbp_is_subscriptions_active() )
792 return false;
793
794 if ( empty( $reply_id ) )
795 return false;
796
797 if ( !$reply = bbp_get_reply( $reply_id ) )
798 return false;
799
800 if ( empty( $reply->post_parent ) )
801 return false;
802
803 if ( !$topic = bbp_get_topic( $reply->post_parent ) )
804 return false;
805
806 if ( !$poster_name = get_the_author_meta( 'display_name', $reply->post_author ) )
807 return false;
808
809 do_action( 'bbp_pre_notify_subscribers', $reply->ID, $topic->ID );
810
811 // Get the users who have favorited the topic and have subscriptions on
812 if ( !$user_ids = bbp_get_topic_subscribers( $topic->ID, true ) )
813 return false;
814
815 // Loop through users
816 foreach ( (array) $user_ids as $user_id ) {
817
818 // Don't send notifications to the person who made the post
819 if ( $user_id == $reply->post_author )
820 continue;
821
822 // For plugins
823 if ( !$message = apply_filters( 'bbp_subscription_mail_message', __( "%1\$s wrote:\n\n%2\$s\n\nPost Link: %3\$s\n\nYou're getting this mail because you subscribed to the topic, visit the topic and login to unsubscribe." ), $reply->ID, $topic->ID, $user_id ) )
824 continue;
825
826 // Get user data of this user
827 $user = get_userdata( $user_id );
828
829 // Send notification email
830 wp_mail(
831 $user->user_email,
832 apply_filters( 'bbp_subscription_mail_title', '[' . get_option( 'blogname' ) . '] ' . $topic->post_title, $reply->ID, $topic->ID ),
833 sprintf( $message, $poster_name, strip_tags( $reply->post_content ), bbp_get_reply_permalink( $reply->ID ) )
834 );
835 }
836
837 do_action( 'bbp_post_notify_subscribers', $reply->ID, $topic->ID );
838
839 return true;
840 }
841
842 /** Login *********************************************************************/
843
844 /**
845 * Change the logout URL to /login and add smart redirect
846 *
847 * This assumes that your login page is 'domain.com/login'
848 *
849 * @param string $url URL
850 * @param string $redirect_to Where to redirect to?
851 * @uses add_query_arg() To add args to the url
852 * @uses apply_filters() Calls 'bbp_logout_url' with the url and redirect to
853 * @return string The url
854 */
855 function bbp_logout_url( $url = '', $redirect_to = '' ) {
856
857 // Rejig the $redirect_to
858 if ( !isset( $_SERVER['REDIRECT_URL'] ) || ( !$redirect_to = home_url( $_SERVER['REDIRECT_URL'] ) ) )
859 $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
860
861 // Make sure we are directing somewhere
862 if ( empty( $redirect_to ) )
863 $redirect_to = home_url( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' );
864
865 // Sanitize $redirect_to and add it to full $url
866 $redirect_to = esc_url( add_query_arg( array( 'loggedout' => 'true' ), $redirect_to ) );
867 $url = add_query_arg( array( 'redirect_to' => $redirect_to ), $url );
868
869 // Filter and return
870 return apply_filters( 'bbp_logout_url', $url, $redirect_to );
871 }
872
873 /** Queries *******************************************************************/
874
875 /**
876 * Adds ability to include or exclude specific post_parent ID's
877 *
878 * @since bbPress (r2996)
879 *
880 * @global DB $wpdb
881 * @global WP $wp
882 * @param string $where
883 * @param WP_Query $object
884 * @return string
885 */
886 function bbp_query_post_parent__in( $where, $object ) {
887 global $wpdb, $wp;
888
889 // Noop if WP core supports this already
890 if ( in_array( 'post_parent__in', $wp->private_query_vars ) )
891 return $where;
892
893 // Only 1 post_parent so return $where
894 if ( is_numeric( $object->query_vars['post_parent'] ) )
895 return $where;
896
897 // Including specific post_parent's
898 if ( ! empty( $object->query_vars['post_parent__in'] ) ) {
899 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__in'][0] ) );
900 $where .= " AND $wpdb->posts.post_parent IN ($ids)";
901
902 // Excluding specific post_parent's
903 } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) {
904 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__not_in'][0] ) );
905 $where .= " AND $wpdb->posts.post_parent NOT IN ($ids)";
906 }
907
908 // Return possibly modified $where
909 return $where;
910 }
911 add_filter( 'posts_where', 'bbp_query_post_parent__in', 10, 2 );
912
913 /**
914 * Query the DB and get the last public post_id that has parent_id as post_parent
915 *
916 * @param int $parent_id Parent id
917 * @param string $post_type Post type. Defaults to 'post'
918 * @uses bbp_get_topic_post_type() To get the topic post type
919 * @uses wp_cache_get() To check if there is a cache of the last child id
920 * @uses wpdb::prepare() To prepare the query
921 * @uses wpdb::get_var() To get the result of the query in a variable
922 * @uses wp_cache_set() To set the cache for future use
923 * @uses apply_filters() Calls 'bbp_get_public_child_last_id' with the child
924 * id, parent id and post type
925 * @return int The last active post_id
926 */
927 function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) {
928 global $wpdb;
929
930 // Bail if nothing passed
931 if ( empty( $parent_id ) )
932 return false;
933
934 // The ID of the cached query
935 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_last_id';
936 $post_status = array( 'publish' );
937
938 // Add closed status if topic post type
939 if ( $post_type == bbp_get_topic_post_type() )
940 $post_status[] = $bbp->closed_status_id;
941
942 // Join post statuses together
943 $post_status = "'" . join( "', '", $post_status ) . "'";
944
945 // Check for cache and set if needed
946 if ( !$child_id = wp_cache_get( $cache_id, 'bbpress' ) ) {
947 $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 ) );
948 wp_cache_set( $cache_id, $child_id, 'bbpress' );
949 }
950
951 // Filter and return
952 return apply_filters( 'bbp_get_public_child_last_id', (int) $child_id, (int) $parent_id, $post_type );
953 }
954
955 /**
956 * Query the DB and get a count of public children
957 *
958 * @param int $parent_id Parent id
959 * @param string $post_type Post type. Defaults to 'post'
960 * @uses bbp_get_topic_post_type() To get the topic post type
961 * @uses wp_cache_get() To check if there is a cache of the children count
962 * @uses wpdb::prepare() To prepare the query
963 * @uses wpdb::get_var() To get the result of the query in a variable
964 * @uses wp_cache_set() To set the cache for future use
965 * @uses apply_filters() Calls 'bbp_get_public_child_count' with the child
966 * count, parent id and post type
967 * @return int The number of children
968 */
969 function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) {
970 global $wpdb, $bbp;
971
972 // Bail if nothing passed
973 if ( empty( $parent_id ) )
974 return false;
975
976 // The ID of the cached query
977 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_count';
978 $post_status = array( 'publish' );
979
980 // Add closed status if topic post type
981 if ( $post_type == bbp_get_topic_post_type() )
982 $post_status[] = $bbp->closed_status_id;
983
984 // Join post statuses together
985 $post_status = "'" . join( "', '", $post_status ) . "'";
986
987 // Check for cache and set if needed
988 if ( !$child_count = wp_cache_get( $cache_id, 'bbpress' ) ) {
989 $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 ) );
990 wp_cache_set( $cache_id, $child_count, 'bbpress' );
991 }
992
993 // Filter and return
994 return apply_filters( 'bbp_get_public_child_count', (int) $child_count, (int) $parent_id, $post_type );
995 }
996
997 /**
998 * Query the DB and get a the child id's of public children
999 *
1000 * @param int $parent_id Parent id
1001 * @param string $post_type Post type. Defaults to 'post'
1002 * @uses bbp_get_topic_post_type() To get the topic post type
1003 * @uses wp_cache_get() To check if there is a cache of the children
1004 * @uses wpdb::prepare() To prepare the query
1005 * @uses wpdb::get_col() To get the result of the query in an array
1006 * @uses wp_cache_set() To set the cache for future use
1007 * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,
1008 * parent id and post type
1009 * @return array The array of children
1010 */
1011 function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) {
1012 global $wpdb, $bbp;
1013
1014 // Bail if nothing passed
1015 if ( empty( $parent_id ) )
1016 return false;
1017
1018 // The ID of the cached query
1019 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_ids';
1020 $post_status = array( 'publish' );
1021
1022 // Add closed status if topic post type
1023 if ( $post_type == bbp_get_topic_post_type() )
1024 $post_status[] = $bbp->closed_status_id;
1025
1026 // Join post statuses together
1027 $post_status = "'" . join( "', '", $post_status ) . "'";
1028
1029 // Check for cache and set if needed
1030 if ( !$child_ids = wp_cache_get( $cache_id, 'bbpress' ) ) {
1031 $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 ) );
1032 wp_cache_set( $cache_id, $child_ids, 'bbpress' );
1033 }
1034
1035 // Filter and return
1036 return apply_filters( 'bbp_get_public_child_ids', $child_ids, (int) $parent_id, $post_type );
1037 }
1038
1039 /** Feeds *********************************************************************/
1040
1041 /**
1042 * This function is hooked into the WordPress 'request' action and is
1043 * responsible for sniffing out the query vars and serving up RSS2 feeds if
1044 * the stars align and the user has requested a feed of any bbPress type.
1045 *
1046 * @since bbPress (r3171)
1047 *
1048 * @global WP_Query $wp_query
1049 * @global bbPress $bbp
1050 * @param array $query_vars
1051 * @return array
1052 */
1053 function bbp_request_feed_trap( $query_vars ) {
1054 global $wp_query, $bbp;
1055
1056 // Looking at a feed
1057 if ( isset( $query_vars['feed'] ) ) {
1058
1059 // Forum Feed
1060 if ( isset( $query_vars['post_type'] ) ) {
1061
1062 // What bbPress post type are we looking for feeds on?
1063 switch ( $query_vars['post_type'] ) {
1064
1065 // Forum
1066 case bbp_get_forum_post_type() :
1067
1068 // Single forum
1069 if ( isset( $query_vars[bbp_get_forum_post_type()] ) ) {
1070
1071 // Load up our own query
1072 $wp_query = new WP_Query( array(
1073 'post_type' => bbp_get_forum_post_type(),
1074 'name' => $query_vars[bbp_get_forum_post_type()]
1075 ) );
1076
1077 // Forum replies
1078 if ( !empty( $_GET['type'] ) && ( bbp_get_reply_post_type() == $_GET['type'] ) ) {
1079
1080 // The query
1081 $the_query = array(
1082 'author' => 0,
1083 'post_type' => bbp_get_reply_post_type(),
1084 'post_parent' => 'any',
1085 'post_status' => join( ',', array( 'publish', $bbp->closed_status_id ) ),
1086 'posts_per_page' => get_option( '_bbp_replies_per_rss_page', 25 ),
1087 'order' => 'DESC',
1088 'meta_query' => array( array(
1089 'key' => '_bbp_forum_id',
1090 'value' => bbp_get_forum_id(),
1091 'compare' => '='
1092 ) )
1093 );
1094
1095 // Output the feed
1096 bbp_display_replies_feed_rss2( $the_query );
1097
1098 // Forum topics
1099 } else {
1100
1101 // The query
1102 $the_query = array(
1103 'author' => 0,
1104 'post_type' => bbp_get_topic_post_type(),
1105 'post_parent' => bbp_get_forum_id(),
1106 'post_status' => join( ',', array( 'publish', $bbp->closed_status_id ) ),
1107 'posts_per_page' => get_option( '_bbp_topics_per_rss_page', 25 ),
1108 'order' => 'DESC',
1109 'meta_query' => array( array(
1110 'key' => '_bbp_forum_id',
1111 'value' => bbp_get_forum_id(),
1112 'compare' => '='
1113 ) )
1114 );
1115
1116 // Output the feed
1117 bbp_display_topics_feed_rss2( $the_query );
1118 }
1119 }
1120
1121 break;
1122
1123 // Topic - Show replies
1124 case bbp_get_topic_post_type() :
1125
1126 // Single topic
1127 if ( isset( $query_vars[bbp_get_topic_post_type()] ) ) {
1128
1129 // Load up our own query
1130 $wp_query = new WP_Query( array(
1131 'post_type' => bbp_get_topic_post_type(),
1132 'name' => $query_vars[bbp_get_topic_post_type()]
1133 ) );
1134
1135 // Output the feed
1136 bbp_display_replies_feed_rss2();
1137
1138 // All topics
1139 } else {
1140
1141 // The query
1142 $the_query = array(
1143 'author' => 0,
1144 'post_parent' => 'any',
1145 'posts_per_page' => get_option( '_bbp_topics_per_rss_page', 25 ),
1146 'show_stickies' => false,
1147 );
1148
1149 // Output the feed
1150 bbp_display_topics_feed_rss2( $the_query );
1151 }
1152
1153 break;
1154
1155 // Replies
1156 case bbp_get_reply_post_type() :
1157
1158 // The query
1159 $the_query = array(
1160 'posts_per_page' => get_option( '_bbp_replies_per_rss_page', 25 ),
1161 'meta_query' => array( array( ) )
1162 );
1163
1164 // All replies
1165 if ( !isset( $query_vars[bbp_get_reply_post_type()] ) ) {
1166 bbp_display_replies_feed_rss2( $the_query );
1167 }
1168
1169 break;
1170 }
1171 }
1172 }
1173
1174 // No feed so continue on
1175 return $query_vars;
1176 }
1177
1178 ?>
1179