PluginProbe
bbPress / 2.0-beta-1
bbPress v2.0-beta-1
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-1, at bbp-includes/bbp-general-functions.php

1,171 lines 39.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress General Functions
5 *
6 * @package bbPress
7 * @subpackage Functions
8 */
9
10 // Redirect 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 $private_topics = ( !empty( $count_private_topics ) && current_user_can( 'read_private_topics' ) ) ? (int) $all_topics->private : 0;
391
392 // Spam
393 $spammed_topics = ( !empty( $count_spammed_topics ) && current_user_can( 'edit_others_topics' ) ) ? (int) $all_topics->{$bbp->spam_status_id} : 0;
394
395 // Trash
396 $trashed_topics = ( !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 = $private_topics + $spammed_topics + $trashed_topics;
400
401 // Generate the hidden topic count's title attribute
402 $hidden_topic_title = !empty( $private_topics ) ? sprintf( __( 'Private: %s | ', 'bbpress' ), number_format_i18n( $private_topics ) ) : '';
403 $hidden_topic_title .= !empty( $spammed_topics ) ? sprintf( __( 'Spammed: %s | ', 'bbpress' ), number_format_i18n( $spammed_topics ) ) : '';
404 $hidden_topic_title .= !empty( $trashed_topics ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $trashed_topics ) ) : '';
405
406 }
407
408 }
409
410 // Replies
411 if ( !empty( $count_replies ) ) {
412
413 $all_replies = wp_count_posts( bbp_get_reply_post_type() );
414
415 // Published
416 $reply_count = $all_replies->publish;
417
418 if ( current_user_can( 'read_private_replies' ) || current_user_can( 'edit_others_replies' ) || current_user_can( 'view_trash' ) ) {
419
420 // Private
421 $private_replies = ( !empty( $count_private_replies ) && current_user_can( 'read_private_replies' ) ) ? (int) $all_replies->private : 0;
422
423 // Spam
424 $spammed_replies = ( !empty( $count_spammed_replies ) && current_user_can( 'edit_others_replies' ) ) ? (int) $all_replies->{$bbp->spam_status_id} : 0;
425
426 // Trash
427 $trashed_replies = ( !empty( $count_trashed_replies ) && current_user_can( 'view_trash' ) ) ? (int) $all_replies->{$bbp->trash_status_id} : 0;
428
429 // Total hidden (private + spam + trash)
430 $hidden_reply_count = $private_replies + $spammed_replies + $trashed_replies;
431
432 // Generate the hidden reply count's title attribute
433 $hidden_reply_title = !empty( $private_replies ) ? sprintf( __( 'Private: %s | ', 'bbpress' ), number_format_i18n( $private_replies ) ) : '';
434 $hidden_reply_title .= !empty( $spammed_replies ) ? sprintf( __( 'Spammed: %s | ', 'bbpress' ), number_format_i18n( $spammed_replies ) ) : '';
435 $hidden_reply_title .= !empty( $trashed_replies ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $trashed_replies ) ) : '';
436
437 }
438
439 }
440
441 // Topic Tags
442 if ( !empty( $count_tags ) ) {
443 $topic_tag_count = wp_count_terms( $bbp->topic_tag_id, array( 'hide_empty' => true ) );
444
445 if ( !empty( $count_empty_tags ) && current_user_can( 'edit_topic_tags' ) )
446 $empty_topic_tag_count = wp_count_terms( $bbp->topic_tag_id ) - $topic_tag_count;
447 }
448
449 $statistics = compact( 'user_count', 'forum_count', 'topic_count', 'hidden_topic_count', 'reply_count', 'hidden_reply_count', 'topic_tag_count', 'empty_topic_tag_count' );
450 $statistics = array_map( 'absint', $statistics );
451 $statistics = array_map( 'number_format_i18n', $statistics );
452
453 // Add the hidden (topic/reply) count title attribute strings because we don't need to run the math functions on these (see above)
454 if ( isset( $hidden_topic_title ) )
455 $statistics['hidden_topic_title'] = $hidden_topic_title;
456
457 if ( isset( $hidden_reply_title ) )
458 $statistics['hidden_reply_title'] = $hidden_reply_title;
459
460 return apply_filters( 'bbp_get_statistics', $statistics, $args );
461 }
462
463 /** Views *********************************************************************/
464
465 /**
466 * Get the registered views
467 *
468 * Does nothing much other than return the {@link $bbp->views} variable
469 *
470 * @since bbPress (r2789)
471 *
472 * @return array Views
473 */
474 function bbp_get_views() {
475 global $bbp;
476
477 return $bbp->views;
478 }
479
480 /**
481 * Register a bbPress view
482 *
483 * @todo Implement feeds - See {@link http://trac.bbpress.org/ticket/1422}
484 *
485 * @since bbPress (r2789)
486 *
487 * @param string $view View name
488 * @param string $title View title
489 * @param mixed $query_args {@link bbp_has_topics()} arguments.
490 * @param bool $feed Have a feed for the view? Defaults to true. NOT IMPLEMENTED
491 * @uses sanitize_title() To sanitize the view name
492 * @uses esc_html() To sanitize the view title
493 * @return array The just registered (but processed) view
494 */
495 function bbp_register_view( $view, $title, $query_args = '', $feed = true ) {
496 global $bbp;
497
498 $view = sanitize_title( $view );
499 $title = esc_html( $title );
500
501 if ( empty( $view ) || empty( $title ) )
502 return false;
503
504 $query_args = wp_parse_args( $query_args );
505
506 // Set exclude_stickies to true if it wasn't supplied
507 if ( !isset( $query_args['show_stickies'] ) )
508 $query_args['show_stickies'] = false;
509
510 $bbp->views[$view]['title'] = $title;
511 $bbp->views[$view]['query'] = $query_args;
512 $bbp->views[$view]['feed'] = $feed;
513
514 return $bbp->views[$view];
515 }
516
517 /**
518 * Deregister a bbPress view
519 *
520 * @since bbPress (r2789)
521 *
522 * @param string $view View name
523 * @uses sanitize_title() To sanitize the view name
524 * @return bool False if the view doesn't exist, true on success
525 */
526 function bbp_deregister_view( $view ) {
527 global $bbp;
528
529 $view = sanitize_title( $view );
530
531 if ( !isset( $bbp->views[$view] ) )
532 return false;
533
534 unset( $bbp->views[$view] );
535
536 return true;
537 }
538
539 /**
540 * Run the view's query
541 *
542 * @since bbPress (r2789)
543 *
544 * @param string $view Optional. View id
545 * @param mixed $new_args New arguments. See {@link bbp_has_topics()}
546 * @uses bbp_get_view_id() To get the view id
547 * @uses bbp_get_view_query_args() To get the view query args
548 * @uses sanitize_title() To sanitize the view name
549 * @uses bbp_has_topics() To make the topics query
550 * @return bool False if the view doesn't exist, otherwise if topics are there
551 */
552 function bbp_view_query( $view = '', $new_args = '' ) {
553 global $bbp;
554
555 if ( !$view = bbp_get_view_id( $view ) )
556 return false;
557
558 $query_args = bbp_get_view_query_args( $view );
559
560 if ( !empty( $new_args ) ) {
561 $new_args = wp_parse_args( $new_args );
562 $query_args = array_merge( $query_args, $new_args );
563 }
564
565 return bbp_has_topics( $query_args );
566 }
567
568 /**
569 * Run the view's query's arguments
570 *
571 * @since bbPress (r2789)
572 *
573 * @param string $view View name
574 * @uses bbp_get_view_id() To get the view id
575 * @uses sanitize_title() To sanitize the view name
576 * @return array Query arguments
577 */
578 function bbp_get_view_query_args( $view ) {
579 global $bbp;
580
581 if ( !$views = bbp_get_view_id( $view ) )
582 return false;
583
584 return $bbp->views[$view]['query'];
585 }
586
587 /** New/edit topic/reply helpers **********************************************/
588
589 /**
590 * Filter anonymous post data
591 *
592 * We use REMOTE_ADDR here directly. If you are behind a proxy, you should
593 * ensure that it is properly set, such as in wp-config.php, for your
594 * environment. See {@link http://core.trac.wordpress.org/ticket/9235}
595 *
596 * If there are any errors, those are directly added to {@link bbPress:errors}
597 *
598 * @since bbPress (r2734)
599 *
600 * @param mixed $args Optional. If no args are there, then $_POST values are
601 * used.
602 * @param bool $is_edit Optional. Is the topic/reply being edited? There are no
603 * IP checks then.
604 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_name' with the
605 * anonymous user name
606 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_email' with the
607 * anonymous user email
608 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_ip' with the
609 * anonymous user's ip address
610 * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_website' with the
611 * anonymous user website
612 * @return bool|array False on errors, values in an array on success
613 */
614 function bbp_filter_anonymous_post_data( $args = '', $is_edit = false ) {
615 global $bbp;
616
617 // Assign variables
618 $defaults = array (
619 'bbp_anonymous_name' => !empty( $_POST['bbp_anonymous_name'] ) ? $_POST['bbp_anonymous_name'] : false,
620 'bbp_anonymous_email' => !empty( $_POST['bbp_anonymous_email'] ) ? $_POST['bbp_anonymous_email'] : false,
621 'bbp_anonymous_website' => !empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false,
622 );
623
624 $r = wp_parse_args( $args, $defaults );
625 extract( $r );
626
627 // Filter variables and add errors if necessary
628 if ( !$bbp_anonymous_name = apply_filters( 'bbp_pre_anonymous_post_author_name', $bbp_anonymous_name ) )
629 $bbp->errors->add( 'bbp_anonymous_name', __( '<strong>ERROR</strong>: Invalid author name submitted!', 'bbpress' ) );
630
631 if ( !$bbp_anonymous_email = apply_filters( 'bbp_pre_anonymous_post_author_email', $bbp_anonymous_email ) )
632 $bbp->errors->add( 'bbp_anonymous_email', __( '<strong>ERROR</strong>: Invalid email address submitted!', 'bbpress' ) );
633
634 // Website is optional
635 $bbp_anonymous_website = apply_filters( 'bbp_pre_anonymous_post_author_website', $bbp_anonymous_website );
636
637 if ( !is_wp_error( $bbp->errors ) || !$bbp->errors->get_error_codes() )
638 $retval = compact( 'bbp_anonymous_name', 'bbp_anonymous_email', 'bbp_anonymous_website' );
639 else
640 $retval = false;
641
642 // Finally, return sanitized data or false
643 return apply_filters( 'bbp_filter_anonymous_post_data', $retval, $args );
644 }
645
646 /**
647 * Check for duplicate topics/replies
648 *
649 * Check to make sure that a user is not making a duplicate post
650 *
651 * @since bbPress (r2763)
652 *
653 * @param array $post_data Contains information about the comment
654 * @uses current_user_can() To check if the current user can throttle
655 * @uses get_meta_sql() To generate the meta sql for checking anonymous email
656 * @uses apply_filters() Calls 'bbp_check_for_duplicate_query' with the
657 * duplicate check query and post data
658 * @uses wpdb::get_var() To execute our query and get the var back
659 * @uses get_post_meta() To get the anonymous user email post meta
660 * @uses do_action() Calls 'bbp_post_duplicate_trigger' with the post data when
661 * it is found that it is a duplicate
662 * @return bool True if it is not a duplicate, false if it is
663 */
664 function bbp_check_for_duplicate( $post_data ) {
665
666 // No duplicate checks for those who can throttle
667 if ( current_user_can( 'throttle' ) )
668 return true;
669
670 global $bbp, $wpdb;
671
672 extract( $post_data, EXTR_SKIP );
673
674 // Check for anonymous post
675 if ( empty( $post_author ) && !empty( $anonymous_data['bbp_anonymous_email'] ) ) {
676
677 // WP 3.2
678 if ( function_exists( 'get_meta_sql' ) )
679 $clauses = get_meta_sql( array( array( 'key' => '_bbp_anonymous_email', 'value' => $anonymous_data['bbp_anonymous_email'] ) ), 'post', $wpdb->posts, 'ID' );
680
681 // WP 3.1
682 elseif ( function_exists( '_get_meta_sql' ) )
683 $clauses = _get_meta_sql( array( array( 'key' => '_bbp_anonymous_email', 'value' => $anonymous_data['bbp_anonymous_email'] ) ), 'post', $wpdb->posts, 'ID' );
684
685 $join = $clauses['join'];
686 $where = $clauses['where'];
687 } else{
688 $join = $where = '';
689 }
690
691 // Simple duplicate check
692 // Expected slashed ($post_type, $post_parent, $post_author, $post_content, $anonymous_data)
693 $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}";
694 $dupe .= !empty( $post_parent ) ? " AND post_parent = '{$post_parent}'" : '';
695 $dupe .= " LIMIT 1";
696 $dupe = apply_filters( 'bbp_check_for_duplicate_query', $dupe, $post_data );
697
698 if ( $wpdb->get_var( $dupe ) ) {
699 do_action( 'bbp_check_for_duplicate_trigger', $post_data );
700 return false;
701 }
702
703 return true;
704 }
705
706 /**
707 * Check for flooding
708 *
709 * Check to make sure that a user is not making too many posts in a short amount
710 * of time.
711 *
712 * @since bbPress (r2734)
713 *
714 * @param false|array $anonymous_data Optional - if it's an anonymous post. Do
715 * not supply if supplying $author_id.
716 * Should have key 'bbp_author_ip'.
717 * Should be sanitized (see
718 * {@link bbp_filter_anonymous_post_data()}
719 * for sanitization)
720 * @param int $author_id Optional. Supply if it's a post by a logged in user.
721 * Do not supply if supplying $anonymous_data.
722 * @uses get_option() To get the throttle time
723 * @uses get_transient() To get the last posted transient of the ip
724 * @uses get_user_meta() To get the last posted meta of the user
725 * @uses current_user_can() To check if the current user can throttle
726 * @return bool True if there is no flooding, true if there is
727 */
728 function bbp_check_for_flood( $anonymous_data = false, $author_id = 0 ) {
729
730 // Option disabled. No flood checks.
731 if ( !$throttle_time = get_option( '_bbp_throttle_time' ) )
732 return true;
733
734 if ( !empty( $anonymous_data ) && is_array( $anonymous_data ) ) {
735 $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' );
736 if ( !empty( $last_posted ) && time() < $last_posted + $throttle_time )
737 return false;
738 } elseif ( !empty( $author_id ) ) {
739 $author_id = (int) $author_id;
740 $last_posted = get_user_meta( $author_id, '_bbp_last_posted', true );
741
742 if ( isset( $last_posted ) && time() < $last_posted + $throttle_time && !current_user_can( 'throttle' ) )
743 return false;
744 } else {
745 return false;
746 }
747
748 return true;
749 }
750
751 /** Subscriptions *************************************************************/
752
753 /**
754 * Sends notification emails for new posts
755 *
756 * Gets new post's ID and check if there are subscribed users to that topic, and
757 * if there are, send notifications
758 *
759 * @since bbPress (r2668)
760 *
761 * @todo When Akismet is made to work with bbPress posts, add a check if the
762 * post is spam or not, to avoid sending out mails for spam posts
763 *
764 * @param int $reply_id ID of the newly made reply
765 * @uses bbp_is_subscriptions_active() To check if the subscriptions are active
766 * @uses bbp_get_reply() To get the reply
767 * @uses bbp_get_topic() To get the reply's topic
768 * @uses get_the_author_meta() To get the author's display name
769 * @uses do_action() Calls 'bbp_pre_notify_subscribers' with the reply id and
770 * topic id
771 * @uses bbp_get_topic_subscribers() To get the topic subscribers
772 * @uses apply_filters() Calls 'bbp_subscription_mail_message' with the
773 * message, reply id, topic id and user id
774 * @uses get_userdata() To get the user data
775 * @uses wp_mail() To send the mail
776 * @uses do_action() Calls 'bbp_post_notify_subscribers' with the reply id
777 * and topic id
778 * @return bool True on success, false on failure
779 */
780 function bbp_notify_subscribers( $reply_id = 0 ) {
781 global $bbp, $wpdb;
782
783 if ( !bbp_is_subscriptions_active() )
784 return false;
785
786 if ( empty( $reply_id ) )
787 return false;
788
789 if ( !$reply = bbp_get_reply( $reply_id ) )
790 return false;
791
792 if ( empty( $reply->post_parent ) )
793 return false;
794
795 if ( !$topic = bbp_get_topic( $reply->post_parent ) )
796 return false;
797
798 if ( !$poster_name = get_the_author_meta( 'display_name', $reply->post_author ) )
799 return false;
800
801 do_action( 'bbp_pre_notify_subscribers', $reply->ID, $topic->ID );
802
803 // Get the users who have favorited the topic and have subscriptions on
804 if ( !$user_ids = bbp_get_topic_subscribers( $topic->ID, true ) )
805 return false;
806
807 // Loop through users
808 foreach ( (array) $user_ids as $user_id ) {
809
810 // Don't send notifications to the person who made the post
811 if ( $user_id == $reply->post_author )
812 continue;
813
814 // For plugins
815 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 ) )
816 continue;
817
818 // Get user data of this user
819 $user = get_userdata( $user_id );
820
821 // Send notification email
822 wp_mail(
823 $user->user_email,
824 apply_filters( 'bbp_subscription_mail_title', '[' . get_option( 'blogname' ) . '] ' . $topic->post_title, $reply->ID, $topic->ID ),
825 sprintf( $message, $poster_name, strip_tags( $reply->post_content ), bbp_get_reply_permalink( $reply->ID ) )
826 );
827 }
828
829 do_action( 'bbp_post_notify_subscribers', $reply->ID, $topic->ID );
830
831 return true;
832 }
833
834 /** Login *********************************************************************/
835
836 /**
837 * Change the logout URL to /login and add smart redirect
838 *
839 * This assumes that your login page is 'domain.com/login'
840 *
841 * @param string $url URL
842 * @param string $redirect_to Where to redirect to?
843 * @uses add_query_arg() To add args to the url
844 * @uses apply_filters() Calls 'bbp_logout_url' with the url and redirect to
845 * @return string The url
846 */
847 function bbp_logout_url( $url = '', $redirect_to = '' ) {
848
849 // Rejig the $redirect_to
850 if ( !isset( $_SERVER['REDIRECT_URL'] ) || ( !$redirect_to = home_url( $_SERVER['REDIRECT_URL'] ) ) )
851 $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
852
853 // Make sure we are directing somewhere
854 if ( empty( $redirect_to ) )
855 $redirect_to = home_url( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' );
856
857 // Sanitize $redirect_to and add it to full $url
858 $redirect_to = esc_url( add_query_arg( array( 'loggedout' => 'true' ), $redirect_to ) );
859 $url = add_query_arg( array( 'redirect_to' => $redirect_to ), $url );
860
861 // Filter and return
862 return apply_filters( 'bbp_logout_url', $url, $redirect_to );
863 }
864
865 /** Queries *******************************************************************/
866
867 /**
868 * Adds ability to include or exclude specific post_parent ID's
869 *
870 * @since bbPress (r2996)
871 *
872 * @global DB $wpdb
873 * @global WP $wp
874 * @param string $where
875 * @param WP_Query $object
876 * @return string
877 */
878 function bbp_query_post_parent__in( $where, $object ) {
879 global $wpdb, $wp;
880
881 // Noop if WP core supports this already
882 if ( in_array( 'post_parent__in', $wp->private_query_vars ) )
883 return $where;
884
885 // Only 1 post_parent so return $where
886 if ( is_numeric( $object->query_vars['post_parent'] ) )
887 return $where;
888
889 // Including specific post_parent's
890 if ( ! empty( $object->query_vars['post_parent__in'] ) ) {
891 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__in'][0] ) );
892 $where .= " AND $wpdb->posts.post_parent IN ($ids)";
893
894 // Excluding specific post_parent's
895 } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) {
896 $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__not_in'][0] ) );
897 $where .= " AND $wpdb->posts.post_parent NOT IN ($ids)";
898 }
899
900 // Return possibly modified $where
901 return $where;
902 }
903 add_filter( 'posts_where', 'bbp_query_post_parent__in', 10, 2 );
904
905 /**
906 * Query the DB and get the last public post_id that has parent_id as post_parent
907 *
908 * @param int $parent_id Parent id
909 * @param string $post_type Post type. Defaults to 'post'
910 * @uses bbp_get_topic_post_type() To get the topic post type
911 * @uses wp_cache_get() To check if there is a cache of the last child id
912 * @uses wpdb::prepare() To prepare the query
913 * @uses wpdb::get_var() To get the result of the query in a variable
914 * @uses wp_cache_set() To set the cache for future use
915 * @uses apply_filters() Calls 'bbp_get_public_child_last_id' with the child
916 * id, parent id and post type
917 * @return int The last active post_id
918 */
919 function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) {
920 global $wpdb;
921
922 // Bail if nothing passed
923 if ( empty( $parent_id ) )
924 return false;
925
926 // The ID of the cached query
927 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_last_id';
928 $post_status = array( 'publish' );
929
930 // Add closed status if topic post type
931 if ( $post_type == bbp_get_topic_post_type() )
932 $post_status[] = $bbp->closed_status_id;
933
934 // Join post statuses together
935 $post_status = "'" . join( "', '", $post_status ) . "'";
936
937 // Check for cache and set if needed
938 if ( !$child_id = wp_cache_get( $cache_id, 'bbpress' ) ) {
939 $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 ) );
940 wp_cache_set( $cache_id, $child_id, 'bbpress' );
941 }
942
943 // Filter and return
944 return apply_filters( 'bbp_get_public_child_last_id', (int) $child_id, (int) $parent_id, $post_type );
945 }
946
947 /**
948 * Query the DB and get a count of public children
949 *
950 * @param int $parent_id Parent id
951 * @param string $post_type Post type. Defaults to 'post'
952 * @uses bbp_get_topic_post_type() To get the topic post type
953 * @uses wp_cache_get() To check if there is a cache of the children count
954 * @uses wpdb::prepare() To prepare the query
955 * @uses wpdb::get_var() To get the result of the query in a variable
956 * @uses wp_cache_set() To set the cache for future use
957 * @uses apply_filters() Calls 'bbp_get_public_child_count' with the child
958 * count, parent id and post type
959 * @return int The number of children
960 */
961 function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) {
962 global $wpdb, $bbp;
963
964 // Bail if nothing passed
965 if ( empty( $parent_id ) )
966 return false;
967
968 // The ID of the cached query
969 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_count';
970 $post_status = array( 'publish' );
971
972 // Add closed status if topic post type
973 if ( $post_type == bbp_get_topic_post_type() )
974 $post_status[] = $bbp->closed_status_id;
975
976 // Join post statuses together
977 $post_status = "'" . join( "', '", $post_status ) . "'";
978
979 // Check for cache and set if needed
980 if ( !$child_count = wp_cache_get( $cache_id, 'bbpress' ) ) {
981 $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 ) );
982 wp_cache_set( $cache_id, $child_count, 'bbpress' );
983 }
984
985 // Filter and return
986 return apply_filters( 'bbp_get_public_child_count', (int) $child_count, (int) $parent_id, $post_type );
987 }
988
989 /**
990 * Query the DB and get a the child id's of public children
991 *
992 * @param int $parent_id Parent id
993 * @param string $post_type Post type. Defaults to 'post'
994 * @uses bbp_get_topic_post_type() To get the topic post type
995 * @uses wp_cache_get() To check if there is a cache of the children
996 * @uses wpdb::prepare() To prepare the query
997 * @uses wpdb::get_col() To get the result of the query in an array
998 * @uses wp_cache_set() To set the cache for future use
999 * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,
1000 * parent id and post type
1001 * @return array The array of children
1002 */
1003 function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) {
1004 global $wpdb, $bbp;
1005
1006 // Bail if nothing passed
1007 if ( empty( $parent_id ) )
1008 return false;
1009
1010 // The ID of the cached query
1011 $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_ids';
1012 $post_status = array( 'publish' );
1013
1014 // Add closed status if topic post type
1015 if ( $post_type == bbp_get_topic_post_type() )
1016 $post_status[] = $bbp->closed_status_id;
1017
1018 // Join post statuses together
1019 $post_status = "'" . join( "', '", $post_status ) . "'";
1020
1021 // Check for cache and set if needed
1022 if ( !$child_ids = wp_cache_get( $cache_id, 'bbpress' ) ) {
1023 $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 ) );
1024 wp_cache_set( $cache_id, $child_ids, 'bbpress' );
1025 }
1026
1027 // Filter and return
1028 return apply_filters( 'bbp_get_public_child_ids', $child_ids, (int) $parent_id, $post_type );
1029 }
1030
1031 /** Feeds *********************************************************************/
1032
1033 /**
1034 * This function is hooked into the WordPress 'request' action and is
1035 * responsible for sniffing out the query vars and serving up RSS2 feeds if
1036 * the stars align and the user has requested a feed of any bbPress type.
1037 *
1038 * @since bbPress (r3171)
1039 *
1040 * @global WP_Query $wp_query
1041 * @global bbPress $bbp
1042 * @param array $query_vars
1043 * @return array
1044 */
1045 function bbp_request_feed_trap( $query_vars ) {
1046 global $wp_query, $bbp;
1047
1048 // Looking at a feed
1049 if ( isset( $query_vars['feed'] ) ) {
1050
1051 // Forum Feed
1052 if ( isset( $query_vars['post_type'] ) ) {
1053
1054 // What bbPress post type are we looking for feeds on?
1055 switch ( $query_vars['post_type'] ) {
1056
1057 // Forum
1058 case bbp_get_forum_post_type() :
1059
1060 // Single forum
1061 if ( isset( $query_vars[bbp_get_forum_post_type()] ) ) {
1062
1063 // Load up our own query
1064 $wp_query = new WP_Query( array(
1065 'post_type' => bbp_get_forum_post_type(),
1066 'name' => $query_vars[bbp_get_forum_post_type()]
1067 ) );
1068
1069 // Forum replies
1070 if ( !empty( $_GET['type'] ) && ( bbp_get_reply_post_type() == $_GET['type'] ) ) {
1071
1072 // The query
1073 $the_query = array(
1074 'author' => 0,
1075 'post_type' => bbp_get_reply_post_type(),
1076 'post_parent' => 'any',
1077 'post_status' => join( ',', array( 'publish', $bbp->closed_status_id ) ),
1078 'posts_per_page' => get_option( '_bbp_replies_per_rss_page', 25 ),
1079 'order' => 'DESC',
1080 'meta_query' => array( array(
1081 'key' => '_bbp_forum_id',
1082 'value' => bbp_get_forum_id(),
1083 'compare' => '='
1084 ) )
1085 );
1086
1087 // Output the feed
1088 bbp_display_replies_feed_rss2( $the_query );
1089
1090 // Forum topics
1091 } else {
1092
1093 // The query
1094 $the_query = array(
1095 'author' => 0,
1096 'post_type' => bbp_get_topic_post_type(),
1097 'post_parent' => bbp_get_forum_id(),
1098 'post_status' => join( ',', array( 'publish', $bbp->closed_status_id ) ),
1099 'posts_per_page' => get_option( '_bbp_topics_per_rss_page', 25 ),
1100 'order' => 'DESC',
1101 'meta_query' => array( array(
1102 'key' => '_bbp_forum_id',
1103 'value' => bbp_get_forum_id(),
1104 'compare' => '='
1105 ) )
1106 );
1107
1108 // Output the feed
1109 bbp_display_topics_feed_rss2( $the_query );
1110 }
1111 }
1112
1113 break;
1114
1115 // Topic - Show replies
1116 case bbp_get_topic_post_type() :
1117
1118 // Single topic
1119 if ( isset( $query_vars[bbp_get_topic_post_type()] ) ) {
1120
1121 // Load up our own query
1122 $wp_query = new WP_Query( array(
1123 'post_type' => bbp_get_topic_post_type(),
1124 'name' => $query_vars[bbp_get_topic_post_type()]
1125 ) );
1126
1127 // Output the feed
1128 bbp_display_replies_feed_rss2();
1129
1130 // All topics
1131 } else {
1132
1133 // The query
1134 $the_query = array(
1135 'author' => 0,
1136 'post_parent' => 'any',
1137 'posts_per_page' => get_option( '_bbp_topics_per_rss_page', 25 ),
1138 'show_stickies' => false,
1139 );
1140
1141 // Output the feed
1142 bbp_display_topics_feed_rss2( $the_query );
1143 }
1144
1145 break;
1146
1147 // Replies
1148 case bbp_get_reply_post_type() :
1149
1150 // The query
1151 $the_query = array(
1152 'posts_per_page' => get_option( '_bbp_replies_per_rss_page', 25 ),
1153 'meta_query' => array( array( ) )
1154 );
1155
1156 // All replies
1157 if ( !isset( $query_vars[bbp_get_reply_post_type()] ) ) {
1158 bbp_display_replies_feed_rss2( $the_query );
1159 }
1160
1161 break;
1162 }
1163 }
1164 }
1165
1166 // No feed so continue on
1167 return $query_vars;
1168 }
1169
1170 ?>
1171