diff -r c7c0fbc09788 -r 5e8dcbe22c24 web/wp-content/plugins/bbpress/includes/common/functions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/wp-content/plugins/bbpress/includes/common/functions.php Tue Dec 04 18:43:10 2012 -0800 @@ -0,0 +1,1669 @@ + $since ) { + $output = $unknown_text; + + // We only want to output two chunks of time here, eg: + // x years, xx months + // x days, xx hours + // so there's only two bits of calculation below: + } else { + + // Step one: the first chunk + for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) { + $seconds = $chunks[$i][0]; + + // Finding the biggest chunk (if the chunk fits, break) + $count = floor( $since / $seconds ); + if ( 0 != $count ) { + break; + } + } + + // If $i iterates all the way to $j, then the event happened 0 seconds ago + if ( !isset( $chunks[$i] ) ) { + $output = $right_now_text; + + } else { + + // Set output var + $output = ( 1 == $count ) ? '1 '. $chunks[$i][1] : $count . ' ' . $chunks[$i][2]; + + // Step two: the second chunk + if ( $i + 2 < $j ) { + $seconds2 = $chunks[$i + 1][0]; + $name2 = $chunks[$i + 1][1]; + $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ); + + // Add to output var + if ( 0 != $count2 ) { + $output .= ( 1 == $count2 ) ? _x( ',', 'Separator in time since', 'bbpress' ) . ' 1 '. $name2 : _x( ',', 'Separator in time since', 'bbpress' ) . ' ' . $count2 . ' ' . $chunks[$i + 1][2]; + } + } + + // No output, so happened right now + if ( ! (int) trim( $output ) ) { + $output = $right_now_text; + } + } + } + + // Append 'ago' to the end of time-since if not 'right now' + if ( $output != $right_now_text ) { + $output = sprintf( $ago_text, $output ); + } + + return apply_filters( 'bbp_get_time_since', $output, $older_date, $newer_date ); + } + +/** + * Formats the reason for editing the topic/reply. + * + * Does these things: + * - Trimming + * - Removing periods from the end of the string + * - Trimming again + * + * @since bbPress (r2782) + * + * @param int $topic_id Optional. Topic id + * @return string Status of topic + */ +function bbp_format_revision_reason( $reason = '' ) { + $reason = (string) $reason; + + // Format reason for proper display + if ( empty( $reason ) ) + return $reason; + + // Trimming + $reason = trim( $reason ); + + // We add our own full stop. + while ( substr( $reason, -1 ) == '.' ) + $reason = substr( $reason, 0, -1 ); + + // Trim again + $reason = trim( $reason ); + + return $reason; +} + +/** Misc **********************************************************************/ + +/** + * Append 'view=all' to query string if it's already there from referer + * + * @since bbPress (r3325) + * + * @param string $original_link Original Link to be modified + * @param bool $force Override bbp_get_view_all() check + * @uses current_user_can() To check if the current user can moderate + * @uses add_query_arg() To add args to the url + * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link + * @return string The link with 'view=all' appended if necessary + */ +function bbp_add_view_all( $original_link = '', $force = false ) { + + // Are we appending the view=all vars? + if ( bbp_get_view_all() || !empty( $force ) ) + $link = add_query_arg( array( 'view' => 'all' ), $original_link ); + else + $link = $original_link; + + return apply_filters( 'bbp_add_view_all', $link, $original_link ); +} + +/** + * Remove 'view=all' from query string + * + * @since bbPress (r3325) + * + * @param string $original_link Original Link to be modified + * @uses current_user_can() To check if the current user can moderate + * @uses add_query_arg() To add args to the url + * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link + * @return string The link with 'view=all' appended if necessary + */ +function bbp_remove_view_all( $original_link = '' ) { + return apply_filters( 'bbp_add_view_all', remove_query_arg( 'view', $original_link ), $original_link ); +} + +/** + * If current user can and is vewing all topics/replies + * + * @since bbPress (r3325) + * + * @uses current_user_can() To check if the current user can moderate + * @uses apply_filters() Calls 'bbp_get_view_all' with the link and original link + * @return bool Whether current user can and is viewing all + */ +function bbp_get_view_all( $cap = 'moderate' ) { + $retval = ( ( !empty( $_GET['view'] ) && ( 'all' == $_GET['view'] ) && current_user_can( $cap ) ) ); + return apply_filters( 'bbp_get_view_all', (bool) $retval ); +} + +/** + * Assist pagination by returning correct page number + * + * @since bbPress (r2628) + * + * @uses get_query_var() To get the 'paged' value + * @return int Current page number + */ +function bbp_get_paged() { + global $wp_query; + + // Check the query var + if ( get_query_var( 'paged' ) ) { + $paged = get_query_var( 'paged' ); + + // Check query paged + } elseif ( !empty( $wp_query->query['paged'] ) ) { + $paged = $wp_query->query['paged']; + } + + // Paged found + if ( !empty( $paged ) ) + return (int) $paged; + + // Default to first page + return 1; +} + +/** + * Fix post author id on post save + * + * When a logged in user changes the status of an anonymous reply or topic, or + * edits it, the post_author field is set to the logged in user's id. This + * function fixes that. + * + * @since bbPress (r2734) + * + * @param array $data Post data + * @param array $postarr Original post array (includes post id) + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses bbp_get_reply_post_type() To get the reply post type + * @uses bbp_is_topic_anonymous() To check if the topic is by an anonymous user + * @uses bbp_is_reply_anonymous() To check if the reply is by an anonymous user + * @return array Data + */ +function bbp_fix_post_author( $data = array(), $postarr = array() ) { + + // Post is not being updated or the post_author is already 0, return + if ( empty( $postarr['ID'] ) || empty( $data['post_author'] ) ) + return $data; + + // Post is not a topic or reply, return + if ( !in_array( $data['post_type'], array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) ) ) + return $data; + + // Is the post by an anonymous user? + if ( ( bbp_get_topic_post_type() == $data['post_type'] && !bbp_is_topic_anonymous( $postarr['ID'] ) ) || + ( bbp_get_reply_post_type() == $data['post_type'] && !bbp_is_reply_anonymous( $postarr['ID'] ) ) ) + return $data; + + // The post is being updated. It is a topic or a reply and is written by an anonymous user. + // Set the post_author back to 0 + $data['post_author'] = 0; + + return $data; +} + +/** + * Check the date against the _bbp_edit_lock setting. + * + * @since bbPress (r3133) + * + * @param string $post_date_gmt + * + * @uses get_option() Get the edit lock time + * @uses current_time() Get the current time + * @uses strtotime() Convert strings to time + * @uses apply_filters() Allow output to be manipulated + * + * @return bool + */ +function bbp_past_edit_lock( $post_date_gmt ) { + + // Assume editing is allowed + $retval = false; + + // Bail if empty date + if ( ! empty( $post_date_gmt ) ) { + + // Period of time + $lockable = '+' . get_option( '_bbp_edit_lock', '5' ) . ' minutes'; + + // Now + $cur_time = current_time( 'timestamp', true ); + + // Add lockable time to post time + $lock_time = strtotime( $lockable, strtotime( $post_date_gmt ) ); + + // Compare + if ( $cur_time >= $lock_time ) { + $retval = true; + } + } + + return apply_filters( 'bbp_past_edit_lock', (bool) $retval, $cur_time, $lock_time, $post_date_gmt ); +} + +/** Statistics ****************************************************************/ + +/** + * Get the forum statistics + * + * @since bbPress (r2769) + * + * @param mixed $args Optional. The function supports these arguments (all + * default to true): + * - count_users: Count users? + * - count_forums: Count forums? + * - count_topics: Count topics? If set to false, private, spammed and trashed + * topics are also not counted. + * - count_private_topics: Count private topics? (only counted if the current + * user has read_private_topics cap) + * - count_spammed_topics: Count spammed topics? (only counted if the current + * user has edit_others_topics cap) + * - count_trashed_topics: Count trashed topics? (only counted if the current + * user has view_trash cap) + * - count_replies: Count replies? If set to false, private, spammed and + * trashed replies are also not counted. + * - count_private_replies: Count private replies? (only counted if the current + * user has read_private_replies cap) + * - count_spammed_replies: Count spammed replies? (only counted if the current + * user has edit_others_replies cap) + * - count_trashed_replies: Count trashed replies? (only counted if the current + * user has view_trash cap) + * - count_tags: Count tags? If set to false, empty tags are also not counted + * - count_empty_tags: Count empty tags? + * @uses bbp_count_users() To count the number of registered users + * @uses bbp_get_forum_post_type() To get the forum post type + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses bbp_get_reply_post_type() To get the reply post type + * @uses wp_count_posts() To count the number of forums, topics and replies + * @uses wp_count_terms() To count the number of topic tags + * @uses current_user_can() To check if the user is capable of doing things + * @uses number_format_i18n() To format the number + * @uses apply_filters() Calls 'bbp_get_statistics' with the statistics and args + * @return object Walked forum tree + */ +function bbp_get_statistics( $args = '' ) { + + $defaults = array ( + 'count_users' => true, + 'count_forums' => true, + 'count_topics' => true, + 'count_private_topics' => true, + 'count_spammed_topics' => true, + 'count_trashed_topics' => true, + 'count_replies' => true, + 'count_private_replies' => true, + 'count_spammed_replies' => true, + 'count_trashed_replies' => true, + 'count_tags' => true, + 'count_empty_tags' => true + ); + $r = bbp_parse_args( $args, $defaults, 'get_statistics' ); + extract( $r ); + + // Users + if ( !empty( $count_users ) ) + $user_count = bbp_get_total_users(); + + // Forums + if ( !empty( $count_forums ) ) { + $forum_count = wp_count_posts( bbp_get_forum_post_type() ); + $forum_count = $forum_count->publish; + } + + // Post statuses + $private = bbp_get_private_status_id(); + $spam = bbp_get_spam_status_id(); + $trash = bbp_get_trash_status_id(); + $closed = bbp_get_closed_status_id(); + + // Topics + if ( !empty( $count_topics ) ) { + + $all_topics = wp_count_posts( bbp_get_topic_post_type() ); + + // Published (publish + closed) + $topic_count = $all_topics->publish + $all_topics->{$closed}; + + if ( current_user_can( 'read_private_topics' ) || current_user_can( 'edit_others_topics' ) || current_user_can( 'view_trash' ) ) { + + // Private + $topics['private'] = ( !empty( $count_private_topics ) && current_user_can( 'read_private_topics' ) ) ? (int) $all_topics->{$private} : 0; + + // Spam + $topics['spammed'] = ( !empty( $count_spammed_topics ) && current_user_can( 'edit_others_topics' ) ) ? (int) $all_topics->{$spam} : 0; + + // Trash + $topics['trashed'] = ( !empty( $count_trashed_topics ) && current_user_can( 'view_trash' ) ) ? (int) $all_topics->{$trash} : 0; + + // Total hidden (private + spam + trash) + $topic_count_hidden = $topics['private'] + $topics['spammed'] + $topics['trashed']; + + // Generate the hidden topic count's title attribute + $topic_titles[] = !empty( $topics['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $topics['private'] ) ) : ''; + $topic_titles[] = !empty( $topics['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $topics['spammed'] ) ) : ''; + $topic_titles[] = !empty( $topics['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $topics['trashed'] ) ) : ''; + + // Compile the hidden topic title + $hidden_topic_title = implode( ' | ', array_filter( $topic_titles ) ); + } + } + + // Replies + if ( !empty( $count_replies ) ) { + + $all_replies = wp_count_posts( bbp_get_reply_post_type() ); + + // Published + $reply_count = $all_replies->publish; + + if ( current_user_can( 'read_private_replies' ) || current_user_can( 'edit_others_replies' ) || current_user_can( 'view_trash' ) ) { + + // Private + $replies['private'] = ( !empty( $count_private_replies ) && current_user_can( 'read_private_replies' ) ) ? (int) $all_replies->{$private} : 0; + + // Spam + $replies['spammed'] = ( !empty( $count_spammed_replies ) && current_user_can( 'edit_others_replies' ) ) ? (int) $all_replies->{$spam} : 0; + + // Trash + $replies['trashed'] = ( !empty( $count_trashed_replies ) && current_user_can( 'view_trash' ) ) ? (int) $all_replies->{$trash} : 0; + + // Total hidden (private + spam + trash) + $reply_count_hidden = $replies['private'] + $replies['spammed'] + $replies['trashed']; + + // Generate the hidden topic count's title attribute + $reply_titles[] = !empty( $replies['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $replies['private'] ) ) : ''; + $reply_titles[] = !empty( $replies['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $replies['spammed'] ) ) : ''; + $reply_titles[] = !empty( $replies['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $replies['trashed'] ) ) : ''; + + // Compile the hidden replies title + $hidden_reply_title = implode( ' | ', array_filter( $reply_titles ) ); + + } + } + + // Topic Tags + if ( !empty( $count_tags ) && bbp_allow_topic_tags() ) { + + // Get the count + $topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id(), array( 'hide_empty' => true ) ); + + // Empty tags + if ( !empty( $count_empty_tags ) && current_user_can( 'edit_topic_tags' ) ) { + $empty_topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id() ) - $topic_tag_count; + } + } + + // Tally the tallies + $statistics = compact( 'user_count', 'forum_count', 'topic_count', 'topic_count_hidden', 'reply_count', 'reply_count_hidden', 'topic_tag_count', 'empty_topic_tag_count' ); + $statistics = array_map( 'absint', $statistics ); + $statistics = array_map( 'number_format_i18n', $statistics ); + + // Add the hidden (topic/reply) count title attribute strings because we don't need to run the math functions on these (see above) + if ( isset( $hidden_topic_title ) ) + $statistics['hidden_topic_title'] = $hidden_topic_title; + + if ( isset( $hidden_reply_title ) ) + $statistics['hidden_reply_title'] = $hidden_reply_title; + + return apply_filters( 'bbp_get_statistics', $statistics, $args ); +} + +/** New/edit topic/reply helpers **********************************************/ + +/** + * Filter anonymous post data + * + * We use REMOTE_ADDR here directly. If you are behind a proxy, you should + * ensure that it is properly set, such as in wp-config.php, for your + * environment. See {@link http://core.trac.wordpress.org/ticket/9235} + * + * Note that bbp_pre_anonymous_filters() is responsible for sanitizing each + * of the filtered core anonymous values here. + * + * If there are any errors, those are directly added to {@link bbPress:errors} + * + * @since bbPress (r2734) + * + * @param mixed $args Optional. If no args are there, then $_POST values are + * used. + * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_name' with the + * anonymous user name + * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_email' with the + * anonymous user email + * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_website' with the + * anonymous user website + * @return bool|array False on errors, values in an array on success + */ +function bbp_filter_anonymous_post_data( $args = '' ) { + + // Assign variables + $defaults = array ( + 'bbp_anonymous_name' => !empty( $_POST['bbp_anonymous_name'] ) ? $_POST['bbp_anonymous_name'] : false, + 'bbp_anonymous_email' => !empty( $_POST['bbp_anonymous_email'] ) ? $_POST['bbp_anonymous_email'] : false, + 'bbp_anonymous_website' => !empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false, + ); + $r = bbp_parse_args( $args, $defaults, 'filter_anonymous_post_data' ); + extract( $r ); + + // Filter variables and add errors if necessary + $bbp_anonymous_name = apply_filters( 'bbp_pre_anonymous_post_author_name', $bbp_anonymous_name ); + if ( empty( $bbp_anonymous_name ) ) + bbp_add_error( 'bbp_anonymous_name', __( 'ERROR: Invalid author name submitted!', 'bbpress' ) ); + + $bbp_anonymous_email = apply_filters( 'bbp_pre_anonymous_post_author_email', $bbp_anonymous_email ); + if ( empty( $bbp_anonymous_email ) ) + bbp_add_error( 'bbp_anonymous_email', __( 'ERROR: Invalid email address submitted!', 'bbpress' ) ); + + // Website is optional + $bbp_anonymous_website = apply_filters( 'bbp_pre_anonymous_post_author_website', $bbp_anonymous_website ); + + if ( !bbp_has_errors() ) + $retval = compact( 'bbp_anonymous_name', 'bbp_anonymous_email', 'bbp_anonymous_website' ); + else + $retval = false; + + // Finally, return sanitized data or false + return apply_filters( 'bbp_filter_anonymous_post_data', $retval, $args ); +} + +/** + * Check for duplicate topics/replies + * + * Check to make sure that a user is not making a duplicate post + * + * @since bbPress (r2763) + * + * @param array $post_data Contains information about the comment + * @uses current_user_can() To check if the current user can throttle + * @uses get_meta_sql() To generate the meta sql for checking anonymous email + * @uses apply_filters() Calls 'bbp_check_for_duplicate_query' with the + * duplicate check query and post data + * @uses wpdb::get_var() To execute our query and get the var back + * @uses get_post_meta() To get the anonymous user email post meta + * @uses do_action() Calls 'bbp_post_duplicate_trigger' with the post data when + * it is found that it is a duplicate + * @return bool True if it is not a duplicate, false if it is + */ +function bbp_check_for_duplicate( $post_data ) { + + // No duplicate checks for those who can throttle + if ( current_user_can( 'throttle' ) ) + return true; + + global $wpdb; + + extract( $post_data, EXTR_SKIP ); + + // Check for anonymous post + if ( empty( $post_author ) && ( isset( $anonymous_data ) && !empty( $anonymous_data['bbp_anonymous_email'] ) ) ) { + $clauses = get_meta_sql( array( array( + 'key' => '_bbp_anonymous_email', + 'value' => $anonymous_data['bbp_anonymous_email'] + ) ), 'post', $wpdb->posts, 'ID' ); + + $join = $clauses['join']; + $where = $clauses['where']; + } else{ + $join = $where = ''; + } + + // Simple duplicate check + // Expected slashed ($post_type, $post_parent, $post_author, $post_content, $anonymous_data) + $status = bbp_get_trash_status_id(); + $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}"; + $dupe .= !empty( $post_parent ) ? " AND post_parent = '{$post_parent}'" : ''; + $dupe .= " LIMIT 1"; + $dupe = apply_filters( 'bbp_check_for_duplicate_query', $dupe, $post_data ); + + if ( $wpdb->get_var( $dupe ) ) { + do_action( 'bbp_check_for_duplicate_trigger', $post_data ); + return false; + } + + return true; +} + +/** + * Check for flooding + * + * Check to make sure that a user is not making too many posts in a short amount + * of time. + * + * @since bbPress (r2734) + * + * @param false|array $anonymous_data Optional - if it's an anonymous post. Do + * not supply if supplying $author_id. + * Should have key 'bbp_author_ip'. + * Should be sanitized (see + * {@link bbp_filter_anonymous_post_data()} + * for sanitization) + * @param int $author_id Optional. Supply if it's a post by a logged in user. + * Do not supply if supplying $anonymous_data. + * @uses get_option() To get the throttle time + * @uses get_transient() To get the last posted transient of the ip + * @uses bbp_get_user_last_posted() To get the last posted time of the user + * @uses current_user_can() To check if the current user can throttle + * @return bool True if there is no flooding, false if there is + */ +function bbp_check_for_flood( $anonymous_data = false, $author_id = 0 ) { + + // Option disabled. No flood checks. + $throttle_time = get_option( '_bbp_throttle_time' ); + if ( empty( $throttle_time ) ) + return true; + + // User is anonymous, so check a transient based on the IP + if ( !empty( $anonymous_data ) && is_array( $anonymous_data ) ) { + $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' ); + + if ( !empty( $last_posted ) && time() < $last_posted + $throttle_time ) { + return false; + } + + // User is logged in, so check their last posted time + } elseif ( !empty( $author_id ) ) { + $author_id = (int) $author_id; + $last_posted = bbp_get_user_last_posted( $author_id ); + + if ( isset( $last_posted ) && time() < $last_posted + $throttle_time && !current_user_can( 'throttle' ) ) { + return false; + } + } else { + return false; + } + + return true; +} + +/** + * Checks topics and replies against the discussion moderation of blocked keys + * + * @since bbPress (r3581) + * + * @param array $anonymous_data Anonymous user data + * @param int $author_id Topic or reply author ID + * @param string $title The title of the content + * @param string $content The content being posted + * @uses is_super_admin() Allow super admins to bypass blacklist + * @uses bbp_current_author_ip() To get current user IP address + * @uses bbp_current_author_ua() To get current user agent + * @return bool True if test is passed, false if fail + */ +function bbp_check_for_moderation( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) { + + // Bail if super admin is author + if ( is_super_admin( $author_id ) ) + return true; + + // Define local variable(s) + $_post = array(); + $match_out = ''; + + /** Blacklist *************************************************************/ + + // Get the moderation keys + $blacklist = trim( get_option( 'moderation_keys' ) ); + + // Bail if blacklist is empty + if ( empty( $blacklist ) ) + return true; + + /** User Data *************************************************************/ + + // Map anonymous user data + if ( !empty( $anonymous_data ) ) { + $_post['author'] = $anonymous_data['bbp_anonymous_name']; + $_post['email'] = $anonymous_data['bbp_anonymous_email']; + $_post['url'] = $anonymous_data['bbp_anonymous_website']; + + // Map current user data + } elseif ( !empty( $author_id ) ) { + + // Get author data + $user = get_userdata( $author_id ); + + // If data exists, map it + if ( !empty( $user ) ) { + $_post['author'] = $user->display_name; + $_post['email'] = $user->user_email; + $_post['url'] = $user->user_url; + } + } + + // Current user IP and user agent + $_post['user_ip'] = bbp_current_author_ip(); + $_post['user_ua'] = bbp_current_author_ua(); + + // Post title and content + $_post['title'] = $title; + $_post['content'] = $content; + + /** Max Links *************************************************************/ + + $max_links = get_option( 'comment_max_links' ); + if ( !empty( $max_links ) ) { + + // How many links? + $num_links = preg_match_all( '/]*href/i', $content, $match_out ); + + // Allow for bumping the max to include the user's URL + $num_links = apply_filters( 'comment_max_links_url', $num_links, $_post['url'] ); + + // Das ist zu viele links! + if ( $num_links >= $max_links ) { + return false; + } + } + + /** Words *****************************************************************/ + + // Get words separated by new lines + $words = explode( "\n", $blacklist ); + + // Loop through words + foreach ( (array) $words as $word ) { + + // Trim the whitespace from the word + $word = trim( $word ); + + // Skip empty lines + if ( empty( $word ) ) { continue; } + + // Do some escaping magic so that '#' chars in the + // spam words don't break things: + $word = preg_quote( $word, '#' ); + $pattern = "#$word#i"; + + // Loop through post data + foreach( $_post as $post_data ) { + + // Check each user data for current word + if ( preg_match( $pattern, $post_data ) ) { + + // Post does not pass + return false; + } + } + } + + // Check passed successfully + return true; +} + +/** + * Checks topics and replies against the discussion blacklist of blocked keys + * + * @since bbPress (r3446) + * + * @param array $anonymous_data Anonymous user data + * @param int $author_id Topic or reply author ID + * @param string $title The title of the content + * @param string $content The content being posted + * @uses is_super_admin() Allow super admins to bypass blacklist + * @uses bbp_current_author_ip() To get current user IP address + * @uses bbp_current_author_ua() To get current user agent + * @return bool True if test is passed, false if fail + */ +function bbp_check_for_blacklist( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) { + + // Bail if super admin is author + if ( is_super_admin( $author_id ) ) + return true; + + // Define local variable + $_post = array(); + + /** Blacklist *************************************************************/ + + // Get the moderation keys + $blacklist = trim( get_option( 'blacklist_keys' ) ); + + // Bail if blacklist is empty + if ( empty( $blacklist ) ) + return true; + + /** User Data *************************************************************/ + + // Map anonymous user data + if ( !empty( $anonymous_data ) ) { + $_post['author'] = $anonymous_data['bbp_anonymous_name']; + $_post['email'] = $anonymous_data['bbp_anonymous_email']; + $_post['url'] = $anonymous_data['bbp_anonymous_website']; + + // Map current user data + } elseif ( !empty( $author_id ) ) { + + // Get author data + $user = get_userdata( $author_id ); + + // If data exists, map it + if ( !empty( $user ) ) { + $_post['author'] = $user->display_name; + $_post['email'] = $user->user_email; + $_post['url'] = $user->user_url; + } + } + + // Current user IP and user agent + $_post['user_ip'] = bbp_current_author_ip(); + $_post['user_ua'] = bbp_current_author_ua(); + + // Post title and content + $_post['title'] = $title; + $_post['content'] = $content; + + /** Words *****************************************************************/ + + // Get words separated by new lines + $words = explode( "\n", $blacklist ); + + // Loop through words + foreach ( (array) $words as $word ) { + + // Trim the whitespace from the word + $word = trim( $word ); + + // Skip empty lines + if ( empty( $word ) ) { continue; } + + // Do some escaping magic so that '#' chars in the + // spam words don't break things: + $word = preg_quote( $word, '#' ); + $pattern = "#$word#i"; + + // Loop through post data + foreach( $_post as $post_data ) { + + // Check each user data for current word + if ( preg_match( $pattern, $post_data ) ) { + + // Post does not pass + return false; + } + } + } + + // Check passed successfully + return true; +} + +/** Subscriptions *************************************************************/ + +/** + * Sends notification emails for new posts + * + * Gets new post's ID and check if there are subscribed users to that topic, and + * if there are, send notifications + * + * @since bbPress (r2668) + * + * @param int $reply_id ID of the newly made reply + * @uses bbp_is_subscriptions_active() To check if the subscriptions are active + * @uses bbp_get_reply_id() To validate the reply ID + * @uses bbp_get_reply() To get the reply + * @uses bbp_get_reply_topic_id() To get the topic ID of the reply + * @uses bbp_is_reply_published() To make sure the reply is published + * @uses bbp_get_topic_id() To validate the topic ID + * @uses bbp_get_topic() To get the reply's topic + * @uses bbp_is_topic_published() To make sure the topic is published + * @uses get_the_author_meta() To get the author's display name + * @uses do_action() Calls 'bbp_pre_notify_subscribers' with the reply id and + * topic id + * @uses bbp_get_topic_subscribers() To get the topic subscribers + * @uses apply_filters() Calls 'bbp_subscription_mail_message' with the + * message, reply id, topic id and user id + * @uses get_userdata() To get the user data + * @uses wp_mail() To send the mail + * @uses do_action() Calls 'bbp_post_notify_subscribers' with the reply id + * and topic id + * @return bool True on success, false on failure + */ +function bbp_notify_subscribers( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = false, $reply_author = 0 ) { + + // Bail if subscriptions are turned off + if ( !bbp_is_subscriptions_active() ) + return false; + + /** Validation ************************************************************/ + + $reply_id = bbp_get_reply_id( $reply_id ); + $topic_id = bbp_get_topic_id( $topic_id ); + $forum_id = bbp_get_forum_id( $forum_id ); + + /** Reply *****************************************************************/ + + // Bail if reply is not published + if ( !bbp_is_reply_published( $reply_id ) ) + return false; + + /** Topic *****************************************************************/ + + // Bail if topic is not published + if ( !bbp_is_topic_published( $topic_id ) ) + return false; + + /** User ******************************************************************/ + + // Get subscribers and bail if empty + $user_ids = bbp_get_topic_subscribers( $topic_id, true ); + if ( empty( $user_ids ) ) + return false; + + // Poster name + $reply_author_name = bbp_get_reply_author_display_name( $reply_id ); + + /** Mail ******************************************************************/ + + do_action( 'bbp_pre_notify_subscribers', $reply_id, $topic_id, $user_ids ); + + // Remove filters from reply content and topic title to prevent content + // from being encoded with HTML entities, wrapped in paragraph tags, etc... + remove_all_filters( 'bbp_get_reply_content' ); + remove_all_filters( 'bbp_get_topic_title' ); + + // Strip tags from text + $topic_title = strip_tags( bbp_get_topic_title( $topic_id ) ); + $reply_content = strip_tags( bbp_get_reply_content( $reply_id ) ); + $reply_url = bbp_get_reply_url( $reply_id ); + $blog_name = get_option( 'blogname' ); + + // Loop through users + foreach ( (array) $user_ids as $user_id ) { + + // Don't send notifications to the person who made the post + if ( !empty( $reply_author ) && (int) $user_id == (int) $reply_author ) + continue; + + // For plugins to filter messages per reply/topic/user + $message = sprintf( __( '%1$s wrote: + +%2$s + +Post Link: %3$s + +----------- + +You are receiving this email because you subscribed to a forum topic. + +Login and visit the topic to unsubscribe from these emails.', 'bbpress' ), + + $reply_author_name, + $reply_content, + $reply_url + ); + + $message = apply_filters( 'bbp_subscription_mail_message', $message, $reply_id, $topic_id, $user_id ); + if ( empty( $message ) ) + continue; + + // For plugins to filter titles per reply/topic/user + $subject = apply_filters( 'bbp_subscription_mail_title', '[' . $blog_name . '] ' . $topic_title, $reply_id, $topic_id, $user_id ); + if ( empty( $subject ) ) + continue; + + // Custom headers + $headers = apply_filters( 'bbp_subscription_mail_headers', array() ); + + // Get user data of this user + $user = get_userdata( $user_id ); + + // Send notification email + wp_mail( $user->user_email, $subject, $message, $headers ); + } + + do_action( 'bbp_post_notify_subscribers', $reply_id, $topic_id, $user_ids ); + + return true; +} + +/** Login *********************************************************************/ + +/** + * Return a clean and reliable logout URL + * + * @param string $url URL + * @param string $redirect_to Where to redirect to? + * @uses add_query_arg() To add args to the url + * @uses apply_filters() Calls 'bbp_logout_url' with the url and redirect to + * @return string The url + */ +function bbp_logout_url( $url = '', $redirect_to = '' ) { + + // Make sure we are directing somewhere + if ( empty( $redirect_to ) && !strstr( $url, 'redirect_to' ) ) { + + // Rejig the $redirect_to + if ( !isset( $_SERVER['REDIRECT_URL'] ) || ( $redirect_to != home_url( $_SERVER['REDIRECT_URL'] ) ) ) { + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ''; + } + + $redirect_to = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + + // Sanitize $redirect_to and add it to full $url + $redirect_to = add_query_arg( array( 'loggedout' => 'true' ), esc_url( $redirect_to ) ); + $url = add_query_arg( array( 'redirect_to' => urlencode( $redirect_to ) ), $url ); + } + + // Filter and return + return apply_filters( 'bbp_logout_url', $url, $redirect_to ); +} + +/** Queries *******************************************************************/ + +/** + * Merge user defined arguments into defaults array. + * + * This function is used throughout bbPress to allow for either a string or array + * to be merged into another array. It is identical to wp_parse_args() except + * it allows for arguments to be passively or aggressively filtered using the + * optional $filter_key parameter. + * + * @since bbPress (r3839) + * + * @param string|array $args Value to merge with $defaults + * @param array $defaults Array that serves as the defaults. + * @param string $filter_key String to key the filters from + * @return array Merged user defined values with defaults. + */ +function bbp_parse_args( $args, $defaults = '', $filter_key = '' ) { + + // Setup a temporary array from $args + if ( is_object( $args ) ) + $r = get_object_vars( $args ); + elseif ( is_array( $args ) ) + $r =& $args; + else + wp_parse_str( $args, $r ); + + // Passively filter the args before the parse + if ( !empty( $filter_key ) ) + $r = apply_filters( 'bbp_before_' . $filter_key . '_parse_args', $r ); + + // Parse + if ( is_array( $defaults ) ) + $r = array_merge( $defaults, $r ); + + // Aggressively filter the args after the parse + if ( !empty( $filter_key ) ) + $r = apply_filters( 'bbp_after_' . $filter_key . '_parse_args', $r ); + + // Return the parsed results + return $r; +} + +/** + * Adds ability to include or exclude specific post_parent ID's + * + * @since bbPress (r2996) + * + * @global DB $wpdb + * @global WP $wp + * @param string $where + * @param WP_Query $object + * @return string + */ +function bbp_query_post_parent__in( $where, $object = '' ) { + global $wpdb, $wp; + + // Noop if WP core supports this already + if ( in_array( 'post_parent__in', $wp->private_query_vars ) ) + return $where; + + // Bail if no object passed + if ( empty( $object ) ) + return $where; + + // Only 1 post_parent so return $where + if ( is_numeric( $object->query_vars['post_parent'] ) ) + return $where; + + // Including specific post_parent's + if ( ! empty( $object->query_vars['post_parent__in'] ) ) { + $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__in'] ) ); + $where .= " AND $wpdb->posts.post_parent IN ($ids)"; + + // Excluding specific post_parent's + } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) { + $ids = implode( ',', array_map( 'absint', $object->query_vars['post_parent__not_in'] ) ); + $where .= " AND $wpdb->posts.post_parent NOT IN ($ids)"; + } + + // Return possibly modified $where + return $where; +} + +/** + * Query the DB and get the last public post_id that has parent_id as post_parent + * + * @param int $parent_id Parent id + * @param string $post_type Post type. Defaults to 'post' + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses wp_cache_get() To check if there is a cache of the last child id + * @uses wpdb::prepare() To prepare the query + * @uses wpdb::get_var() To get the result of the query in a variable + * @uses wp_cache_set() To set the cache for future use + * @uses apply_filters() Calls 'bbp_get_public_child_last_id' with the child + * id, parent id and post type + * @return int The last active post_id + */ +function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) { + global $wpdb; + + // Bail if nothing passed + if ( empty( $parent_id ) ) + return false; + + // The ID of the cached query + $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_last_id'; + $post_status = array( bbp_get_public_status_id() ); + + // Add closed status if topic post type + if ( $post_type == bbp_get_topic_post_type() ) + $post_status[] = bbp_get_closed_status_id(); + + // Join post statuses together + $post_status = "'" . join( "', '", $post_status ) . "'"; + + // Check for cache and set if needed + $child_id = wp_cache_get( $cache_id, 'bbpress' ); + if ( empty( $child_id ) ) { + $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 ) ); + wp_cache_set( $cache_id, $child_id, 'bbpress' ); + } + + // Filter and return + return apply_filters( 'bbp_get_public_child_last_id', (int) $child_id, (int) $parent_id, $post_type ); +} + +/** + * Query the DB and get a count of public children + * + * @param int $parent_id Parent id + * @param string $post_type Post type. Defaults to 'post' + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses wp_cache_get() To check if there is a cache of the children count + * @uses wpdb::prepare() To prepare the query + * @uses wpdb::get_var() To get the result of the query in a variable + * @uses wp_cache_set() To set the cache for future use + * @uses apply_filters() Calls 'bbp_get_public_child_count' with the child + * count, parent id and post type + * @return int The number of children + */ +function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) { + global $wpdb; + + // Bail if nothing passed + if ( empty( $parent_id ) ) + return false; + + // The ID of the cached query + $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_count'; + $post_status = array( bbp_get_public_status_id() ); + + // Add closed status if topic post type + if ( $post_type == bbp_get_topic_post_type() ) + $post_status[] = bbp_get_closed_status_id(); + + // Join post statuses together + $post_status = "'" . join( "', '", $post_status ) . "'"; + + // Check for cache and set if needed + $child_count = wp_cache_get( $cache_id, 'bbpress' ); + if ( empty( $child_count ) ) { + $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 ) ); + wp_cache_set( $cache_id, $child_count, 'bbpress' ); + } + + // Filter and return + return apply_filters( 'bbp_get_public_child_count', (int) $child_count, (int) $parent_id, $post_type ); +} + +/** + * Query the DB and get a the child id's of public children + * + * @param int $parent_id Parent id + * @param string $post_type Post type. Defaults to 'post' + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses wp_cache_get() To check if there is a cache of the children + * @uses wpdb::prepare() To prepare the query + * @uses wpdb::get_col() To get the result of the query in an array + * @uses wp_cache_set() To set the cache for future use + * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids, + * parent id and post type + * @return array The array of children + */ +function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) { + global $wpdb; + + // Bail if nothing passed + if ( empty( $parent_id ) ) + return false; + + // The ID of the cached query + $cache_id = 'bbp_parent_public_' . $parent_id . '_type_' . $post_type . '_child_ids'; + $post_status = array( bbp_get_public_status_id() ); + + // Add closed status if topic post type + if ( $post_type == bbp_get_topic_post_type() ) + $post_status[] = bbp_get_closed_status_id(); + + // Join post statuses together + $post_status = "'" . join( "', '", $post_status ) . "'"; + + // Check for cache and set if needed + $child_ids = wp_cache_get( $cache_id, 'bbpress' ); + if ( empty( $child_ids ) ) { + $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 ) ); + wp_cache_set( $cache_id, $child_ids, 'bbpress' ); + } + + // Filter and return + return apply_filters( 'bbp_get_public_child_ids', $child_ids, (int) $parent_id, $post_type ); +} +/** + * Query the DB and get a the child id's of all children + * + * @param int $parent_id Parent id + * @param string $post_type Post type. Defaults to 'post' + * @uses bbp_get_topic_post_type() To get the topic post type + * @uses wp_cache_get() To check if there is a cache of the children + * @uses wpdb::prepare() To prepare the query + * @uses wpdb::get_col() To get the result of the query in an array + * @uses wp_cache_set() To set the cache for future use + * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids, + * parent id and post type + * @return array The array of children + */ +function bbp_get_all_child_ids( $parent_id = 0, $post_type = 'post' ) { + global $wpdb; + + // Bail if nothing passed + if ( empty( $parent_id ) ) + return false; + + // The ID of the cached query + $cache_id = 'bbp_parent_all_' . $parent_id . '_type_' . $post_type . '_child_ids'; + $post_status = array( bbp_get_public_status_id() ); + + // Extra post statuses based on post type + switch ( $post_type ) { + + // Forum + case bbp_get_forum_post_type() : + $post_status[] = bbp_get_private_status_id(); + $post_status[] = bbp_get_hidden_status_id(); + break; + + // Topic + case bbp_get_topic_post_type() : + $post_status[] = bbp_get_closed_status_id(); + $post_status[] = bbp_get_trash_status_id(); + $post_status[] = bbp_get_spam_status_id(); + break; + + // Reply + case bbp_get_reply_post_type() : + $post_status[] = bbp_get_trash_status_id(); + $post_status[] = bbp_get_spam_status_id(); + break; + } + + // Join post statuses together + $post_status = "'" . join( "', '", $post_status ) . "'"; + + // Check for cache and set if needed + $child_ids = wp_cache_get( $cache_id, 'bbpress' ); + if ( empty( $child_ids ) ) { + $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 ) ); + wp_cache_set( $cache_id, $child_ids, 'bbpress' ); + } + + // Filter and return + return apply_filters( 'bbp_get_all_child_ids', $child_ids, (int) $parent_id, $post_type ); +} + +/** Globals *******************************************************************/ + +/** + * Get the unfiltered value of a global $post's key + * + * Used most frequently when editing a forum/topic/reply + * + * @since bbPress (r3694) + * + * @global WP_Query $post + * @param string $field Name of the key + * @param string $context How to sanitize - raw|edit|db|display|attribute|js + * @return string Field value + */ +function bbp_get_global_post_field( $field = 'ID', $context = 'edit' ) { + global $post; + + $retval = isset( $post->$field ) ? $post->$field : ''; + $retval = sanitize_post_field( $field, $retval, $post->ID, $context ); + + return apply_filters( 'bbp_get_global_post_field', $retval, $post ); +} + +/** Nonces ********************************************************************/ + +/** + * Makes sure the user requested an action from another page on this site. + * + * To avoid security exploits within the theme. + * + * @since bbPress (r4022) + * + * @uses do_action() Calls 'bbp_check_referer' on $action. + * @param string $action Action nonce + * @param string $query_arg where to look for nonce in $_REQUEST + */ +function bbp_verify_nonce_request( $action = '', $query_arg = '_wpnonce' ) { + + // Get the home URL + $home_url = strtolower( home_url() ); + + // Build the currently requested URL + $scheme = is_ssl() ? 'https://' : 'http://'; + $requested_url = strtolower( $scheme . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + + // Filter the requested URL, for configurations like reverse proxying + $matched_url = apply_filters( 'bbp_verify_nonce_request_url', $requested_url ); + + // Check the nonce + $result = isset( $_REQUEST[$query_arg] ) ? wp_verify_nonce( $_REQUEST[$query_arg], $action ) : false; + + // Nonce check failed + if ( empty( $result ) || empty( $action ) || ( strpos( $matched_url, $home_url ) !== 0 ) ) + $result = false; + + // Do extra things + do_action( 'bbp_verify_nonce_request', $action, $result ); + + return $result; +} + +/** Feeds *********************************************************************/ + +/** + * This function is hooked into the WordPress 'request' action and is + * responsible for sniffing out the query vars and serving up RSS2 feeds if + * the stars align and the user has requested a feed of any bbPress type. + * + * @since bbPress (r3171) + * + * @param array $query_vars + * @return array + */ +function bbp_request_feed_trap( $query_vars = array() ) { + + // Looking at a feed + if ( isset( $query_vars['feed'] ) ) { + + // Forum/Topic/Reply Feed + if ( isset( $query_vars['post_type'] ) ) { + + // What bbPress post type are we looking for feeds on? + switch ( $query_vars['post_type'] ) { + + // Forum + case bbp_get_forum_post_type() : + + // Define local variable(s) + $meta_query = array(); + + // Single forum + if ( isset( $query_vars[bbp_get_forum_post_type()] ) ) { + + // Get the forum by the path + $forum = get_page_by_path( $query_vars[bbp_get_forum_post_type()], OBJECT, bbp_get_forum_post_type() ); + $forum_id = $forum->ID; + + // Load up our own query + query_posts( array( + 'post_type' => bbp_get_forum_post_type(), + 'ID' => $forum_id, + 'feed' => true + ) ); + + // Restrict to specific forum ID + $meta_query = array( array( + 'key' => '_bbp_forum_id', + 'value' => $forum_id, + 'type' => 'numeric', + 'compare' => '=' + ) ); + } + + // Only forum replies + if ( !empty( $_GET['type'] ) && ( bbp_get_reply_post_type() == $_GET['type'] ) ) { + + // The query + $the_query = array( + 'author' => 0, + 'feed' => true, + 'post_type' => bbp_get_reply_post_type(), + 'post_parent' => 'any', + 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ), + 'posts_per_page' => bbp_get_replies_per_rss_page(), + 'order' => 'DESC', + 'meta_query' => $meta_query + ); + + // Output the feed + bbp_display_replies_feed_rss2( $the_query ); + + // Only forum topics + } elseif ( !empty( $_GET['type'] ) && ( bbp_get_topic_post_type() == $_GET['type'] ) ) { + + // The query + $the_query = array( + 'author' => 0, + 'feed' => true, + 'post_type' => bbp_get_topic_post_type(), + 'post_parent' => $forum_id, + 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ), + 'posts_per_page' => bbp_get_topics_per_rss_page(), + 'order' => 'DESC' + ); + + // Output the feed + bbp_display_topics_feed_rss2( $the_query ); + + // All forum topics and replies + } else { + + // Exclude private/hidden forums if not looking at single + if ( empty( $query_vars['forum'] ) ) + $meta_query = array( bbp_exclude_forum_ids( 'meta_query' ) ); + + // The query + $the_query = array( + 'author' => 0, + 'feed' => true, + 'post_type' => array( bbp_get_reply_post_type(), bbp_get_topic_post_type() ), + 'post_parent' => 'any', + 'post_status' => join( ',', array( bbp_get_public_status_id(), bbp_get_closed_status_id() ) ), + 'posts_per_page' => bbp_get_replies_per_rss_page(), + 'order' => 'DESC', + 'meta_query' => $meta_query + ); + + // Output the feed + bbp_display_replies_feed_rss2( $the_query ); + } + + break; + + // Topic feed - Show replies + case bbp_get_topic_post_type() : + + // Single topic + if ( isset( $query_vars[bbp_get_topic_post_type()] ) ) { + + // Load up our own query + query_posts( array( + 'post_type' => bbp_get_topic_post_type(), + 'name' => $query_vars[bbp_get_topic_post_type()], + 'feed' => true + ) ); + + // Output the feed + bbp_display_replies_feed_rss2( array( 'feed' => true ) ); + + // All topics + } else { + + // The query + $the_query = array( + 'author' => 0, + 'feed' => true, + 'post_parent' => 'any', + 'posts_per_page' => bbp_get_topics_per_rss_page(), + 'show_stickies' => false + ); + + // Output the feed + bbp_display_topics_feed_rss2( $the_query ); + } + + break; + + // Replies + case bbp_get_reply_post_type() : + + // The query + $the_query = array( + 'posts_per_page' => bbp_get_replies_per_rss_page(), + 'meta_query' => array( array( ) ), + 'feed' => true + ); + + // All replies + if ( !isset( $query_vars[bbp_get_reply_post_type()] ) ) { + bbp_display_replies_feed_rss2( $the_query ); + } + + break; + } + + // Single Topic Vview + } elseif ( isset( $query_vars['bbp_view'] ) ) { + + // Get the view + $view = $query_vars['bbp_view']; + + // We have a view to display a feed + if ( !empty( $view ) ) { + + // Get the view query + $the_query = bbp_get_view_query_args( $view ); + + // Output the feed + bbp_display_topics_feed_rss2( $the_query ); + } + } + + // @todo User profile feeds + } + + // No feed so continue on + return $query_vars; +} + +/** Templates ******************************************************************/ + +/** + * Used to guess if page exists at requested path + * + * @since bbPress (r3304) + * + * @uses get_option() To see if pretty permalinks are enabled + * @uses get_page_by_path() To see if page exists at path + * + * @param string $path + * @return mixed False if no page, Page object if true + */ +function bbp_get_page_by_path( $path = '' ) { + + // Default to false + $retval = false; + + // Path is not empty + if ( !empty( $path ) ) { + + // Pretty permalinks are on so path might exist + if ( get_option( 'permalink_structure' ) ) { + $retval = get_page_by_path( $path ); + } + } + + return apply_filters( 'bbp_get_page_by_path', $retval, $path ); +} + +/** + * Sets the 404 status. + * + * Used primarily with topics/replies inside hidden forums. + * + * @since bbPress (r3051) + * + * @global WP_Query $wp_query + * @uses WP_Query::set_404() + */ +function bbp_set_404() { + global $wp_query; + + if ( ! isset( $wp_query ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.', 'bbpress' ), '3.1' ); + return false; + } + + $wp_query->set_404(); +}