Index: /tags/1.5-beta-3/bp-activity/bp-activity-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-actions.php	(revision 5057)
@@ -0,0 +1,325 @@
+<?php
+/*******************************************************************************
+ * Action functions are exactly the same as screen functions, however they do
+ * not have a template screen associated with them. Usually they will send the
+ * user back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// Allow core components and dependent plugins to register activity actions
+function bp_register_activity_actions() {
+	do_action( 'bp_register_activity_actions' );
+}
+add_action( 'bp_init', 'bp_register_activity_actions', 8 );
+
+function bp_activity_action_permalink_router() {
+	global $bp;
+
+	// Not viewing activity
+	if ( !bp_is_activity_component() || !bp_is_current_action( 'p' ) )
+		return false;
+
+	// No activity to display
+	if ( !bp_action_variable( 0 ) || !is_numeric( bp_action_variable( 0 ) ) )
+		return false;
+
+	// Get the activity details
+	$activity = bp_activity_get_specific( array( 'activity_ids' => bp_action_variable( 0 ), 'show_hidden' => true ) );
+
+	// 404 if activity does not exist
+	if ( empty( $activity['activities'][0] ) ) {
+		bp_do_404();
+		return;
+
+	} else {
+		$activity = $activity['activities'][0];
+	}
+
+	// Do not redirect at default
+	$redirect = false;
+
+	// Redirect based on the type of activity
+	if ( bp_is_active( 'groups' ) && $activity->component == $bp->groups->id ) {
+
+		// Activity is a user update
+		if ( !empty( $activity->user_id ) ) {
+			$redirect = bp_core_get_user_domain( $activity->user_id, $activity->user_nicename, $activity->user_login ) . bp_get_activity_slug() . '/' . $activity->id . '/';
+
+		// Activity is something else
+		} else {
+
+			// Set redirect to group activity stream
+			if ( $group = groups_get_group( array( 'group_id' => $activity->item_id ) ) ) {
+				$redirect = bp_get_group_permalink( $group ) . bp_get_activity_slug() . '/' . $activity->id . '/';
+			}
+		}
+
+	// Set redirect to users' activity stream
+	} else {
+		$redirect = bp_core_get_user_domain( $activity->user_id, $activity->user_nicename, $activity->user_login ) . bp_get_activity_slug() . '/' . $activity->id;
+	}
+
+	// Allow redirect to be filtered
+	if ( !$redirect = apply_filters_ref_array( 'bp_activity_permalink_redirect_url', array( $redirect, &$activity ) ) )
+		bp_core_redirect( bp_get_root_domain() );
+
+	// Redirect to the actual activity permalink page
+	bp_core_redirect( $redirect );
+}
+add_action( 'bp_actions', 'bp_activity_action_permalink_router' );
+
+/**
+ * bp_activity_action_delete_activity()
+ *
+ * Delete specific activity item and redirect to previous page.
+ *
+ * @global object $bp
+ * @since 1.1
+ * @uses do_action() Calls 'bp_activity_action_delete_activity' hook to allow actions to be taken after the activity is deleted.
+ * @uses do_action() Calls 'bp_activity_before_action_delete_activity' hook to allow actions to be taken before the activity is deleted.
+ */
+function bp_activity_action_delete_activity( $activity_id = 0 ) {
+	global $bp;
+
+	// Not viewing activity or action is not delete
+	if ( !bp_is_activity_component() || !bp_is_current_action( 'delete' ) )
+		return false;
+
+	if ( empty( $activity_id ) && bp_action_variable( 0 ) )
+		$activity_id = (int) bp_action_variable( 0 );
+
+	// Not viewing a specific activity item
+	if ( empty( $activity_id ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'bp_activity_delete_link' );
+
+	// Load up the activity item
+	$activity = new BP_Activity_Activity( $activity_id );
+
+	// Check access
+	if ( empty( $activity->user_id ) || !bp_activity_user_can_delete( $activity ) )
+		return false;
+
+	// Call the action before the delete so plugins can still fetch information about it
+	do_action( 'bp_activity_before_action_delete_activity', $activity_id, $activity->user_id );
+
+	// Delete the activity item and provide user feedback
+	if ( bp_activity_delete( array( 'id' => $activity_id, 'user_id' => $activity->user_id ) ) )
+		bp_core_add_message( __( 'Activity deleted successfully', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was an error when deleting that activity', 'buddypress' ), 'error' );
+
+	do_action( 'bp_activity_action_delete_activity', $activity_id, $activity->user_id );
+
+	// Check for the redirect query arg, otherwise let WP handle things
+ 	if ( !empty( $_GET['redirect_to'] ) )
+		bp_core_redirect( esc_url( $_GET['redirect_to'] ) );
+	else
+		bp_core_redirect( wp_get_referer() );
+}
+add_action( 'bp_actions', 'bp_activity_action_delete_activity' );
+
+function bp_activity_action_post_update() {
+	global $bp;
+
+	// Do not proceed if user is not logged in, not viewing activity, or not posting
+	if ( !is_user_logged_in() || bp_is_activity_component() || !bp_is_current_action( 'post' ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'post_update', '_wpnonce_post_update' );
+
+	// Get activity info
+	$content = apply_filters( 'bp_activity_post_update_content', $_POST['whats-new']             );
+	$object  = apply_filters( 'bp_activity_post_update_object',  $_POST['whats-new-post-object'] );
+	$item_id = apply_filters( 'bp_activity_post_update_item_id', $_POST['whats-new-post-in']     );
+
+	// No activity content so provide feedback and redirect
+	if ( empty( $content ) ) {
+		bp_core_add_message( __( 'Please enter some content to post.', 'buddypress' ), 'error' );
+		bp_core_redirect( wp_get_referer() );
+	}
+
+	// No existing item_id
+	if ( empty( $item_id ) ) {
+		$activity_id = bp_activity_post_update( array( 'content' => $content ) );
+
+	// Post to groups object
+	} else if ( 'groups' == $object && bp_is_active( 'groups' ) ) {
+		if ( (int)$item_id ) {
+			$activity_id = groups_post_update( array( 'content' => $content, 'group_id' => $item_id ) );
+		}
+
+	// Special circumstance so let filters handle it
+	} else {
+		$activity_id = apply_filters( 'bp_activity_custom_update', $object, $item_id, $content );
+	}
+
+	// Provide user feedback
+	if ( !empty( $activity_id ) )
+		bp_core_add_message( __( 'Update Posted!', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was an error when posting your update, please try again.', 'buddypress' ), 'error' );
+
+	// Redirect
+	bp_core_redirect( wp_get_referer() );
+}
+add_action( 'bp_actions', 'bp_activity_action_post_update' );
+
+function bp_activity_action_post_comment() {
+	global $bp;
+
+	if ( !is_user_logged_in() || ( bp_is_activity_component() ) || !bp_is_current_action( 'reply' ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'new_activity_comment', '_wpnonce_new_activity_comment' );
+
+	$activity_id = apply_filters( 'bp_activity_post_comment_activity_id', $_POST['comment_form_id'] );
+	$content = apply_filters( 'bp_activity_post_comment_content', $_POST['ac_input_' . $activity_id] );
+
+	if ( empty( $content ) ) {
+		bp_core_add_message( __( 'Please do not leave the comment area blank.', 'buddypress' ), 'error' );
+		bp_core_redirect( wp_get_referer() . '#ac-form-' . $activity_id );
+	}
+
+	$comment_id = bp_activity_new_comment( array(
+		'content' => $content,
+		'activity_id' => $activity_id,
+		'parent_id' => $parent_id
+	));
+
+	if ( !empty( $comment_id ) )
+		bp_core_add_message( __( 'Reply Posted!', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was an error posting that reply, please try again.', 'buddypress' ), 'error' );
+
+	bp_core_redirect( wp_get_referer() . '#ac-form-' . $activity_id );
+}
+add_action( 'bp_actions', 'bp_activity_action_post_comment' );
+
+function bp_activity_action_mark_favorite() {
+	global $bp;
+
+	if ( !is_user_logged_in() || ( bp_is_activity_component() ) || !bp_is_current_action( 'favorite' ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'mark_favorite' );
+
+	if ( bp_activity_add_user_favorite( bp_action_variable( 0 ) ) )
+		bp_core_add_message( __( 'Activity marked as favorite.', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was an error marking that activity as a favorite, please try again.', 'buddypress' ), 'error' );
+
+	bp_core_redirect( wp_get_referer() . '#activity-' . bp_action_variable( 0 ) );
+}
+add_action( 'bp_actions', 'bp_activity_action_mark_favorite' );
+
+function bp_activity_action_remove_favorite() {
+	global $bp;
+
+	if ( !is_user_logged_in() || ( bp_is_activity_component() ) || !bp_is_current_action( 'unfavorite' ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'unmark_favorite' );
+
+	if ( bp_activity_remove_user_favorite( bp_action_variable( 0 ) ) )
+		bp_core_add_message( __( 'Activity removed as favorite.', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was an error removing that activity as a favorite, please try again.', 'buddypress' ), 'error' );
+
+	bp_core_redirect( wp_get_referer() . '#activity-' . bp_action_variable( 0 ) );
+}
+add_action( 'bp_actions', 'bp_activity_action_remove_favorite' );
+
+function bp_activity_action_sitewide_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_current_component( 'activity' ) || !bp_is_current_action( 'feed' ) || bp_is_user() || !empty( $bp->groups->current_group ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-sitewide-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_sitewide_feed' );
+
+function bp_activity_action_personal_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_current_component( 'activity' ) || !bp_is_user() || !bp_is_current_action( 'feed' ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-personal-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_personal_feed' );
+
+function bp_activity_action_friends_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_active( 'friends' ) || !bp_is_current_component( 'activity' ) || !bp_is_user() || !bp_is_current_action( bp_get_friends_slug() ) || !bp_is_action_variable( 'feed', 0 ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-friends-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_friends_feed' );
+
+function bp_activity_action_my_groups_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_active( 'groups' ) || !bp_is_current_component( 'activity' ) || !bp_is_user() || !bp_is_current_action( bp_get_groups_slug() ) || !bp_is_action_variable( 'feed', 0 ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-mygroups-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_my_groups_feed' );
+
+function bp_activity_action_mentions_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_current_component( 'activity' ) || !bp_is_user() || !bp_is_current_action( 'mentions' ) || !bp_is_action_variable( 'feed', 0 ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-mentions-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_mentions_feed' );
+
+function bp_activity_action_favorites_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_current_component( 'activity' ) || !bp_is_user() || !bp_is_current_action( 'favorites' ) || !bp_is_action_variable( 'feed', 0 ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	include_once( 'feeds/bp-activity-favorites-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'bp_activity_action_favorites_feed' );
+
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-classes.php	(revision 5057)
@@ -0,0 +1,551 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_Activity_Activity {
+	var $id;
+	var $item_id;
+	var $secondary_item_id;
+	var $user_id;
+	var $primary_link;
+	var $component;
+	var $type;
+	var $action;
+	var $content;
+	var $date_recorded;
+	var $hide_sitewide = false;
+	var $mptt_left;
+	var $mptt_right;
+
+	function bp_activity_activity( $id = false ) {
+		$this->__construct( $id );
+	}
+
+	function __construct( $id = false ) {
+		global $bp;
+
+		if ( !empty( $id ) ) {
+			$this->id = $id;
+			$this->populate();
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp;
+
+		if ( $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->activity->table_name} WHERE id = %d", $this->id ) ) ) {
+			$this->id                = $row->id;
+			$this->item_id           = $row->item_id;
+			$this->secondary_item_id = $row->secondary_item_id;
+			$this->user_id           = $row->user_id;
+			$this->primary_link      = $row->primary_link;
+			$this->component         = $row->component;
+			$this->type              = $row->type;
+			$this->action            = $row->action;
+			$this->content           = $row->content;
+			$this->date_recorded     = $row->date_recorded;
+			$this->hide_sitewide     = $row->hide_sitewide;
+			$this->mptt_left         = $row->mptt_left;
+			$this->mptt_right        = $row->mptt_right;
+		}
+	}
+
+	function save() {
+		global $wpdb, $bp, $current_user;
+
+		$this->id                = apply_filters_ref_array( 'bp_activity_id_before_save',                array( $this->id,                &$this ) );
+		$this->item_id           = apply_filters_ref_array( 'bp_activity_item_id_before_save',           array( $this->item_id,           &$this ) );
+		$this->secondary_item_id = apply_filters_ref_array( 'bp_activity_secondary_item_id_before_save', array( $this->secondary_item_id, &$this ) );
+		$this->user_id           = apply_filters_ref_array( 'bp_activity_user_id_before_save',           array( $this->user_id,           &$this ) );
+		$this->primary_link      = apply_filters_ref_array( 'bp_activity_primary_link_before_save',      array( $this->primary_link,      &$this ) );
+		$this->component         = apply_filters_ref_array( 'bp_activity_component_before_save',         array( $this->component,         &$this ) );
+		$this->type              = apply_filters_ref_array( 'bp_activity_type_before_save',              array( $this->type,              &$this ) );
+		$this->action            = apply_filters_ref_array( 'bp_activity_action_before_save',            array( $this->action,            &$this ) );
+		$this->content           = apply_filters_ref_array( 'bp_activity_content_before_save',           array( $this->content,           &$this ) );
+		$this->date_recorded     = apply_filters_ref_array( 'bp_activity_date_recorded_before_save',     array( $this->date_recorded,     &$this ) );
+		$this->hide_sitewide     = apply_filters_ref_array( 'bp_activity_hide_sitewide_before_save',     array( $this->hide_sitewide,     &$this ) );
+		$this->mptt_left         = apply_filters_ref_array( 'bp_activity_mptt_left_before_save',         array( $this->mptt_left,         &$this ) );
+		$this->mptt_right        = apply_filters_ref_array( 'bp_activity_mptt_right_before_save',        array( $this->mptt_right,        &$this ) );
+
+		// Use this, not the filters above
+		do_action_ref_array( 'bp_activity_before_save', array( &$this ) );
+
+		if ( !$this->component || !$this->type )
+			return false;
+
+		if ( !$this->primary_link )
+			$this->primary_link = $bp->loggedin_user->domain;
+
+		// If we have an existing ID, update the activity item, otherwise insert it.
+		if ( $this->id )
+			$q = $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET user_id = %d, component = %s, type = %s, action = %s, content = %s, primary_link = %s, date_recorded = %s, item_id = %s, secondary_item_id = %s, hide_sitewide = %d WHERE id = %d", $this->user_id, $this->component, $this->type, $this->action, $this->content, $this->primary_link, $this->date_recorded, $this->item_id, $this->secondary_item_id, $this->hide_sitewide, $this->id );
+		else
+			$q = $wpdb->prepare( "INSERT INTO {$bp->activity->table_name} ( user_id, component, type, action, content, primary_link, date_recorded, item_id, secondary_item_id, hide_sitewide ) VALUES ( %d, %s, %s, %s, %s, %s, %s, %s, %s, %d )", $this->user_id, $this->component, $this->type, $this->action, $this->content, $this->primary_link, $this->date_recorded, $this->item_id, $this->secondary_item_id, $this->hide_sitewide );
+
+		if ( !$wpdb->query( $q ) )
+			return false;
+
+		if ( empty( $this->id ) )
+			$this->id = $wpdb->insert_id;
+
+		do_action_ref_array( 'bp_activity_after_save', array( &$this ) );
+
+		return true;
+	}
+
+	// Static Functions
+
+	function get( $max = false, $page = 1, $per_page = 25, $sort = 'DESC', $search_terms = false, $filter = false, $display_comments = false, $show_hidden = false, $exclude = false, $in = false ) {
+		global $wpdb, $bp;
+
+		// Select conditions
+		$select_sql = "SELECT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name";
+
+		$from_sql = " FROM {$bp->activity->table_name} a LEFT JOIN {$wpdb->users} u ON a.user_id = u.ID";
+
+		// Where conditions
+		$where_conditions = array();
+
+		// Searching
+		if ( $search_terms ) {
+			$search_terms = $wpdb->escape( $search_terms );
+			$where_conditions['search_sql'] = "a.content LIKE '%%" . like_escape( $search_terms ) . "%%'";
+		}
+
+		// Filtering
+		if ( $filter && $filter_sql = BP_Activity_Activity::get_filter_sql( $filter ) )
+			$where_conditions['filter_sql'] = $filter_sql;
+
+		// Sorting
+		if ( $sort != 'ASC' && $sort != 'DESC' )
+			$sort = 'DESC';
+
+		// Hide Hidden Items?
+		if ( !$show_hidden )
+			$where_conditions['hidden_sql'] = "a.hide_sitewide = 0";
+
+		// Exclude specified items
+		if ( $exclude )
+			$where_conditions['exclude'] = "a.id NOT IN ({$exclude})";
+
+		// The specific ids to which you want to limit the query
+		if ( !empty( $in ) ) {
+			if ( is_array( $in ) ) {
+				$in = implode ( ',', array_map( 'absint', $in ) );
+			} else {
+				$in = implode ( ',', array_map( 'absint', explode ( ',', $in ) ) );
+			}
+
+			$where_conditions['in'] = "a.id IN ({$in})";
+		}
+
+		// Alter the query based on whether we want to show activity item
+		// comments in the stream like normal comments or threaded below
+		// the activity.
+		if ( false === $display_comments || 'threaded' === $display_comments )
+			$where_conditions[] = "a.type != 'activity_comment'";
+
+		$where_sql = 'WHERE ' . join( ' AND ', $where_conditions );
+
+		if ( $per_page && $page ) {
+			$pag_sql = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
+			$activities = $wpdb->get_results( apply_filters( 'bp_activity_get_user_join_filter', $wpdb->prepare( "{$select_sql} {$from_sql} {$where_sql} ORDER BY a.date_recorded {$sort} {$pag_sql}" ), $select_sql, $from_sql, $where_sql, $sort, $pag_sql ) );
+		} else {
+			$activities = $wpdb->get_results( apply_filters( 'bp_activity_get_user_join_filter', $wpdb->prepare( "{$select_sql} {$from_sql} {$where_sql} ORDER BY a.date_recorded {$sort}" ), $select_sql, $from_sql, $where_sql, $sort ) );
+		}
+
+		$total_activities_sql = apply_filters( 'bp_activity_total_activities_sql', $wpdb->prepare( "SELECT count(a.id) FROM {$bp->activity->table_name} a {$where_sql} ORDER BY a.date_recorded {$sort}" ), $where_sql, $sort );
+
+		$total_activities = $wpdb->get_var( $total_activities_sql );
+
+		// Get the fullnames of users so we don't have to query in the loop
+		if ( bp_is_active( 'xprofile' ) && $activities ) {
+			foreach ( (array)$activities as $activity ) {
+				if ( (int)$activity->user_id )
+					$activity_user_ids[] = $activity->user_id;
+			}
+
+			$activity_user_ids = implode( ',', array_unique( (array)$activity_user_ids ) );
+			if ( !empty( $activity_user_ids ) ) {
+				if ( $names = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, value AS user_fullname FROM {$bp->profile->table_name_data} WHERE field_id = 1 AND user_id IN ({$activity_user_ids})" ) ) ) {
+					foreach ( (array)$names as $name )
+						$tmp_names[$name->user_id] = $name->user_fullname;
+
+					foreach ( (array)$activities as $i => $activity ) {
+						if ( !empty( $tmp_names[$activity->user_id] ) )
+							$activities[$i]->user_fullname = $tmp_names[$activity->user_id];
+					}
+
+					unset( $names );
+					unset( $tmp_names );
+				}
+			}
+		}
+
+		if ( $activities && $display_comments )
+			$activities = BP_Activity_Activity::append_comments( $activities );
+
+		// If $max is set, only return up to the max results
+		if ( !empty( $max ) ) {
+			if ( (int)$total_activities > (int)$max )
+				$total_activities = $max;
+		}
+
+		return array( 'activities' => $activities, 'total' => (int)$total_activities );
+	}
+
+	/**
+	 * In BuddyPress 1.2.x, this was used to retrieve specific activity stream items (for example, on an activity's permalink page).
+	 * As of 1.5.x, use BP_Activity_Activity::get( ..., $in ) instead.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated Use BP_Activity_Activity::get( ..., $in ) instead.
+	 * @param mixed $activity_ids Array or comma-separated string of activity IDs to retrieve
+	 * @param int $max Maximum number of results to return. (Optional; default is no maximum)
+	 * @param int $page The set of results that the user is viewing. Used in pagination. (Optional; default is 1)
+	 * @param int $per_page Specifies how many results per page. Used in pagination. (Optional; default is 25)
+	 * @param string MySQL column sort; ASC or DESC. (Optional; default is DESC)
+	 * @param bool $display_comments Retrieve an activity item's associated comments or not. (Optional; default is false)
+	 * @return array
+	 * @since 1.2
+	 */
+	function get_specific( $activity_ids, $max = false, $page = 1, $per_page = 25, $sort = 'DESC', $display_comments = false ) {
+		_deprecated_function( __FUNCTION__, '1.5', 'Use BP_Activity_Activity::get( ..., $in ) instead.' );
+		return BP_Activity_Activity::get( $max, $page, $per_page, $sort, false, false, $display_comments, false, false, $activity_ids );
+	}
+
+	function get_id( $user_id, $component, $type, $item_id, $secondary_item_id, $action, $content, $date_recorded ) {
+		global $bp, $wpdb;
+
+		$where_args = false;
+
+		if ( !empty( $user_id ) )
+			$where_args[] = $wpdb->prepare( "user_id = %d", $user_id );
+
+		if ( !empty( $component ) )
+			$where_args[] = $wpdb->prepare( "component = %s", $component );
+
+		if ( !empty( $type ) )
+			$where_args[] = $wpdb->prepare( "type = %s", $type );
+
+		if ( !empty( $item_id ) )
+			$where_args[] = $wpdb->prepare( "item_id = %s", $item_id );
+
+		if ( !empty( $secondary_item_id ) )
+			$where_args[] = $wpdb->prepare( "secondary_item_id = %s", $secondary_item_id );
+
+		if ( !empty( $action ) )
+			$where_args[] = $wpdb->prepare( "action = %s", $action );
+
+		if ( !empty( $content ) )
+			$where_args[] = $wpdb->prepare( "content = %s", $content );
+
+		if ( !empty( $date_recorded ) )
+			$where_args[] = $wpdb->prepare( "date_recorded = %s", $date_recorded );
+
+		if ( !empty( $where_args ) )
+			$where_sql = 'WHERE ' . join( ' AND ', $where_args );
+		else
+			return false;
+
+		return $wpdb->get_var( "SELECT id FROM {$bp->activity->table_name} {$where_sql}" );
+	}
+
+	function delete( $args ) {
+		global $wpdb, $bp;
+
+		$defaults = array(
+			'id'                => false,
+			'action'            => false,
+			'content'           => false,
+			'component'         => false,
+			'type'              => false,
+			'primary_link'      => false,
+			'user_id'           => false,
+			'item_id'           => false,
+			'secondary_item_id' => false,
+			'date_recorded'     => false,
+			'hide_sitewide'     => false
+		);
+		$params = wp_parse_args( $args, $defaults );
+		extract( $params );
+
+		$where_args = false;
+
+		if ( !empty( $id ) )
+			$where_args[] = $wpdb->prepare( "id = %d", $id );
+
+		if ( !empty( $user_id ) )
+			$where_args[] = $wpdb->prepare( "user_id = %d", $user_id );
+
+		if ( !empty( $action ) )
+			$where_args[] = $wpdb->prepare( "action = %s", $action );
+
+		if ( !empty( $content ) )
+			$where_args[] = $wpdb->prepare( "content = %s", $content );
+
+		if ( !empty( $component ) )
+			$where_args[] = $wpdb->prepare( "component = %s", $component );
+
+		if ( !empty( $type ) )
+			$where_args[] = $wpdb->prepare( "type = %s", $type );
+
+		if ( !empty( $primary_link ) )
+			$where_args[] = $wpdb->prepare( "primary_link = %s", $primary_link );
+
+		if ( !empty( $item_id ) )
+			$where_args[] = $wpdb->prepare( "item_id = %s", $item_id );
+
+		if ( !empty( $secondary_item_id ) )
+			$where_args[] = $wpdb->prepare( "secondary_item_id = %s", $secondary_item_id );
+
+		if ( !empty( $date_recorded ) )
+			$where_args[] = $wpdb->prepare( "date_recorded = %s", $date_recorded );
+
+		if ( !empty( $hide_sitewide ) )
+			$where_args[] = $wpdb->prepare( "hide_sitewide = %d", $hide_sitewide );
+
+		if ( !empty( $where_args ) )
+			$where_sql = 'WHERE ' . join( ' AND ', $where_args );
+		else
+			return false;
+
+		// Fetch the activity IDs so we can delete any comments for this activity item
+		$activity_ids = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} {$where_sql}" ) );
+
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name} {$where_sql}" ) ) )
+			return false;
+
+		if ( $activity_ids ) {
+			BP_Activity_Activity::delete_activity_item_comments( $activity_ids );
+			BP_Activity_Activity::delete_activity_meta_entries( $activity_ids );
+
+			return $activity_ids;
+		}
+
+		return $activity_ids;
+	}
+
+	function delete_activity_item_comments( $activity_ids ) {
+		global $bp, $wpdb;
+
+		if ( is_array( $activity_ids ) )
+			$activity_ids = implode ( ',', array_map( 'absint', $activity_ids ) );
+		else
+			$activity_ids = implode ( ',', array_map( 'absint', explode ( ',', $activity_ids ) ) );
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name} WHERE type = 'activity_comment' AND item_id IN ({$activity_ids})" ) );
+	}
+
+	function delete_activity_meta_entries( $activity_ids ) {
+		global $bp, $wpdb;
+
+		if ( is_array( $activity_ids ) )
+			$activity_ids = implode ( ',', array_map( 'absint', $activity_ids ) );
+		else
+			$activity_ids = implode ( ',', array_map( 'absint', explode ( ',', $activity_ids ) ) );
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name_meta} WHERE activity_id IN ({$activity_ids})" ) );
+	}
+
+	function append_comments( $activities ) {
+		global $bp, $wpdb;
+
+		$activity_comments = array();
+
+		/* Now fetch the activity comments and parse them into the correct position in the activities array. */
+		foreach( (array)$activities as $activity ) {
+			if ( 'activity_comment' != $activity->type && $activity->mptt_left && $activity->mptt_right )
+				$activity_comments[$activity->id] = BP_Activity_Activity::get_activity_comments( $activity->id, $activity->mptt_left, $activity->mptt_right );
+		}
+
+		/* Merge the comments with the activity items */
+		foreach( (array)$activities as $key => $activity )
+			if ( isset( $activity_comments[$activity->id] ) )
+				$activities[$key]->children = $activity_comments[$activity->id];
+
+		return $activities;
+	}
+
+	function get_activity_comments( $activity_id, $left, $right ) {
+		global $wpdb, $bp;
+
+		if ( !$comments = wp_cache_get( 'bp_activity_comments_' . $activity_id ) ) {
+			// Select the user's fullname with the query
+			if ( bp_is_active( 'xprofile' ) ) {
+				$fullname_select = ", pd.value as user_fullname";
+				$fullname_from = ", {$bp->profile->table_name_data} pd ";
+				$fullname_where = "AND pd.user_id = a.user_id AND pd.field_id = 1";
+
+			// Prevent debug errors
+			} else {
+				$fullname_select = $fullname_from = $fullname_where = '';
+			}
+
+			// Retrieve all descendants of the $root node
+			$descendants = $wpdb->get_results( apply_filters( 'bp_activity_comments_user_join_filter', $wpdb->prepare( "SELECT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name{$fullname_select} FROM {$bp->activity->table_name} a, {$wpdb->users} u{$fullname_from} WHERE u.ID = a.user_id {$fullname_where} AND a.type = 'activity_comment' AND a.item_id = %d AND a.mptt_left BETWEEN %d AND %d ORDER BY a.date_recorded ASC", $activity_id, $left, $right ), $activity_id, $left, $right ) );
+
+			// Loop descendants and build an assoc array
+			foreach ( (array)$descendants as $d ) {
+				$d->children = array();
+
+				// If we have a reference on the parent
+				if ( isset( $ref[ $d->secondary_item_id ] ) ) {
+					$ref[ $d->secondary_item_id ]->children[ $d->id ] = $d;
+					$ref[ $d->id ] =& $ref[ $d->secondary_item_id ]->children[ $d->id ];
+
+				// If we don't have a reference on the parent, put in the root level
+				} else {
+					$comments[ $d->id ] = $d;
+					$ref[ $d->id ] =& $comments[ $d->id ];
+				}
+			}
+			wp_cache_set( 'bp_activity_comments_' . $activity_id, $comments, 'bp' );
+		}
+
+		return $comments;
+	}
+
+	function rebuild_activity_comment_tree( $parent_id, $left = 1 ) {
+		global $wpdb, $bp;
+
+		// The right value of this node is the left value + 1
+		$right = $left + 1;
+
+		// Get all descendants of this node
+		$descendants = BP_Activity_Activity::get_child_comments( $parent_id );
+
+		// Loop the descendants and recalculate the left and right values
+		foreach ( (array)$descendants as $descendant )
+			$right = BP_Activity_Activity::rebuild_activity_comment_tree( $descendant->id, $right );
+
+		// We've got the left value, and now that we've processed the children
+		// of this node we also know the right value
+		if ( 1 == $left )
+			$wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET mptt_left = %d, mptt_right = %d WHERE id = %d", $left, $right, $parent_id ) );
+		else
+			$wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET mptt_left = %d, mptt_right = %d WHERE type = 'activity_comment' AND id = %d", $left, $right, $parent_id ) );
+
+		// Return the right value of this node + 1
+		return $right + 1;
+	}
+
+	function get_child_comments( $parent_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} WHERE type = 'activity_comment' AND secondary_item_id = %d", $parent_id ) );
+	}
+
+	function get_recorded_components() {
+		global $wpdb, $bp;
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT component FROM {$bp->activity->table_name} ORDER BY component ASC" ) );
+	}
+
+	function get_sitewide_items_for_feed( $limit = 35 ) {
+		global $wpdb, $bp;
+
+		$activities = bp_activity_get_sitewide( array( 'max' => $limit ) );
+
+		for ( $i = 0, $count = count( $activities ); $i < $count; ++$i ) {
+				$title = explode( '<span', $activities[$i]['content'] );
+
+				$activity_feed[$i]['title'] = trim( strip_tags( $title[0] ) );
+				$activity_feed[$i]['link'] = $activities[$i]['primary_link'];
+				$activity_feed[$i]['description'] = @sprintf( $activities[$i]['content'], '' );
+				$activity_feed[$i]['pubdate'] = $activities[$i]['date_recorded'];
+		}
+
+		return $activity_feed;
+	}
+
+	function get_in_operator_sql( $field, $items ) {
+		global $wpdb;
+
+		// split items at the comma
+		$items_dirty = explode( ',', $items );
+
+		// array of prepared integers or quoted strings
+		$items_prepared = array();
+
+		// clean up and format each item
+		foreach ( $items_dirty as $item ) {
+			// clean up the string
+			$item = trim( $item );
+			// pass everything through prepare for security and to safely quote strings
+			$items_prepared[] = ( is_numeric( $item ) ) ? $wpdb->prepare( '%d', $item ) : $wpdb->prepare( '%s', $item );
+		}
+
+		// build IN operator sql syntax
+		if ( count( $items_prepared ) )
+			return sprintf( '%s IN ( %s )', trim( $field ), implode( ',', $items_prepared ) );
+		else
+			return false;
+	}
+
+	function get_filter_sql( $filter_array ) {
+		global $wpdb;
+
+		if ( !empty( $filter_array['user_id'] ) ) {
+			$user_sql = BP_Activity_Activity::get_in_operator_sql( 'a.user_id', $filter_array['user_id'] );
+			if ( !empty( $user_sql ) )
+				$filter_sql[] = $user_sql;
+		}
+
+		if ( !empty( $filter_array['object'] ) ) {
+			$object_sql = BP_Activity_Activity::get_in_operator_sql( 'a.component', $filter_array['object'] );
+			if ( !empty( $object_sql ) )
+				$filter_sql[] = $object_sql;
+		}
+
+		if ( !empty( $filter_array['action'] ) ) {
+			$action_sql = BP_Activity_Activity::get_in_operator_sql( 'a.type', $filter_array['action'] );
+			if ( !empty( $action_sql ) )
+				$filter_sql[] = $action_sql;
+		}
+
+		if ( !empty( $filter_array['primary_id'] ) ) {
+			$pid_sql = BP_Activity_Activity::get_in_operator_sql( 'a.item_id', $filter_array['primary_id'] );
+			if ( !empty( $pid_sql ) )
+				$filter_sql[] = $pid_sql;
+		}
+
+		if ( !empty( $filter_array['secondary_id'] ) ) {
+			$sid_sql = BP_Activity_Activity::get_in_operator_sql( 'a.secondary_item_id', $filter_array['secondary_id'] );
+			if ( !empty( $sid_sql ) )
+				$filter_sql[] = $sid_sql;
+		}
+
+		if ( empty($filter_sql) )
+			return false;
+
+		return join( ' AND ', $filter_sql );
+	}
+
+	function get_last_updated() {
+		global $bp, $wpdb;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT date_recorded FROM {$bp->activity->table_name} ORDER BY date_recorded DESC LIMIT 1" ) );
+	}
+
+	function total_favorite_count( $user_id ) {
+		if ( !$favorite_activity_entries = bp_get_user_meta( $user_id, 'bp_favorite_activities', true ) )
+			return 0;
+
+		return count( maybe_unserialize( $favorite_activity_entries ) );
+	}
+
+	function check_exists_by_content( $content ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} WHERE content = %s", $content ) );
+	}
+
+	function hide_all_for_user( $user_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET hide_sitewide = 1 WHERE user_id = %d", $user_id ) );
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-filters.php	(revision 5057)
@@ -0,0 +1,208 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// Apply WordPress defined filters
+add_filter( 'bp_get_activity_action',                'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_content_body',          'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_content',               'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_parent_content',        'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_latest_update',         'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'bp_activity_filter_kses', 1 );
+add_filter( 'bp_get_activity_feed_item_description', 'bp_activity_filter_kses', 1 );
+add_filter( 'bp_activity_content_before_save',       'bp_activity_filter_kses', 1 );
+add_filter( 'bp_activity_action_before_save',        'bp_activity_filter_kses', 1 );
+
+add_filter( 'bp_get_activity_action',                'force_balance_tags' );
+add_filter( 'bp_get_activity_content_body',          'force_balance_tags' );
+add_filter( 'bp_get_activity_content',               'force_balance_tags' );
+add_filter( 'bp_get_activity_latest_update',         'force_balance_tags' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'force_balance_tags' );
+add_filter( 'bp_get_activity_feed_item_description', 'force_balance_tags' );
+add_filter( 'bp_activity_content_before_save',       'force_balance_tags' );
+add_filter( 'bp_activity_action_before_save',        'force_balance_tags' );
+
+add_filter( 'bp_get_activity_action',                'wptexturize' );
+add_filter( 'bp_get_activity_content_body',          'wptexturize' );
+add_filter( 'bp_get_activity_content',               'wptexturize' );
+add_filter( 'bp_get_activity_parent_content',        'wptexturize' );
+add_filter( 'bp_get_activity_latest_update',         'wptexturize' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'wptexturize' );
+
+add_filter( 'bp_get_activity_action',                'convert_smilies' );
+add_filter( 'bp_get_activity_content_body',          'convert_smilies' );
+add_filter( 'bp_get_activity_content',               'convert_smilies' );
+add_filter( 'bp_get_activity_parent_content',        'convert_smilies' );
+add_filter( 'bp_get_activity_latest_update',         'convert_smilies' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'convert_smilies' );
+
+add_filter( 'bp_get_activity_action',                'convert_chars' );
+add_filter( 'bp_get_activity_content_body',          'convert_chars' );
+add_filter( 'bp_get_activity_content',               'convert_chars' );
+add_filter( 'bp_get_activity_parent_content',        'convert_chars' );
+add_filter( 'bp_get_activity_latest_update',         'convert_chars' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'convert_chars' );
+
+add_filter( 'bp_get_activity_action',                'wpautop' );
+add_filter( 'bp_get_activity_content_body',          'wpautop' );
+add_filter( 'bp_get_activity_content',               'wpautop' );
+add_filter( 'bp_get_activity_feed_item_description', 'wpautop' );
+
+add_filter( 'bp_get_activity_action',                'make_clickable', 9 );
+add_filter( 'bp_get_activity_content_body',          'make_clickable', 9 );
+add_filter( 'bp_get_activity_content',               'make_clickable', 9 );
+add_filter( 'bp_get_activity_parent_content',        'make_clickable', 9 );
+add_filter( 'bp_get_activity_latest_update',         'make_clickable', 9 );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'make_clickable', 9 );
+add_filter( 'bp_get_activity_feed_item_description', 'make_clickable', 9 );
+
+add_filter( 'bp_acomment_name',                      'stripslashes_deep' );
+add_filter( 'bp_get_activity_action',                'stripslashes_deep' );
+add_filter( 'bp_get_activity_content',               'stripslashes_deep' );
+add_filter( 'bp_get_activity_content_body',          'stripslashes_deep' );
+add_filter( 'bp_get_activity_parent_content',        'stripslashes_deep' );
+add_filter( 'bp_get_activity_latest_update',         'stripslashes_deep' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'stripslashes_deep' );
+add_filter( 'bp_get_activity_feed_item_description', 'stripslashes_deep' );
+
+// Apply BuddyPress defined filters
+add_filter( 'bp_get_activity_content',               'bp_activity_make_nofollow_filter' );
+add_filter( 'bp_get_activity_content_body',          'bp_activity_make_nofollow_filter' );
+add_filter( 'bp_get_activity_parent_content',        'bp_activity_make_nofollow_filter' );
+add_filter( 'bp_get_activity_latest_update',         'bp_activity_make_nofollow_filter' );
+add_filter( 'bp_get_activity_latest_update_excerpt', 'bp_activity_make_nofollow_filter' );
+add_filter( 'bp_get_activity_feed_item_description', 'bp_activity_make_nofollow_filter' );
+
+add_filter( 'pre_comment_content',                   'bp_activity_at_name_filter' );
+add_filter( 'group_forum_topic_text_before_save',    'bp_activity_at_name_filter' );
+add_filter( 'group_forum_post_text_before_save',     'bp_activity_at_name_filter' );
+
+add_filter( 'bp_get_activity_parent_content',        'bp_create_excerpt' );
+
+function bp_activity_filter_kses( $content ) {
+	global $allowedtags;
+
+	$activity_allowedtags = $allowedtags;
+	$activity_allowedtags['span']          = array();
+	$activity_allowedtags['span']['class'] = array();
+	$activity_allowedtags['div']           = array();
+	$activity_allowedtags['div']['class']  = array();
+	$activity_allowedtags['div']['id']     = array();
+	$activity_allowedtags['a']['class']    = array();
+	$activity_allowedtags['a']['id']       = array();
+	$activity_allowedtags['a']['rel']      = array();
+	$activity_allowedtags['img']           = array();
+	$activity_allowedtags['img']['src']    = array();
+	$activity_allowedtags['img']['alt']    = array();
+	$activity_allowedtags['img']['class']  = array();
+	$activity_allowedtags['img']['width']  = array();
+	$activity_allowedtags['img']['height'] = array();
+	$activity_allowedtags['img']['class']  = array();
+	$activity_allowedtags['img']['id']     = array();
+	$activity_allowedtags['img']['title']  = array();
+	$activity_allowedtags['code']          = array();
+
+	$activity_allowedtags = apply_filters( 'bp_activity_allowed_tags', $activity_allowedtags );
+	return wp_kses( $content, $activity_allowedtags );
+}
+
+/**
+ * Finds and links @-mentioned users in the contents of activity items
+ *
+ * @package BuddyPress Activity
+ *
+ * @param string $content The activity content
+ * @param int $activity_id When $adjust_mention_count is true, you must provide an $activity_id,
+ *   which will be added to the list of the user's unread mentions
+ */
+function bp_activity_at_name_filter( $content, $activity_id = 0 ) {
+	$usernames = bp_activity_find_mentions( $content );
+
+	foreach( (array)$usernames as $username ) {
+		if ( bp_is_username_compatibility_mode() )
+			$user_id = username_exists( $username );
+		else
+			$user_id = bp_core_get_userid_from_nicename( $username );
+
+		if ( empty( $user_id ) )
+			continue;
+
+		// If an activity_id is provided, we can send email and BP notifications
+		if ( $activity_id ) {
+			bp_activity_at_message_notification( $activity_id, $user_id );
+		}
+
+		$content = preg_replace( '/(@' . $username . '\b)/', "<a href='" . bp_core_get_user_domain( $user_id ) . "' rel='nofollow'>@$username</a>", $content );
+	}
+
+	// Adjust the activity count for this item
+	if ( $activity_id )
+		bp_activity_adjust_mention_count( $activity_id, 'add' );
+
+	return $content;
+}
+
+/**
+ * Catch mentions in saved activity items
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param obj $activity
+ */
+function bp_activity_at_name_filter_updates( $activity ) {
+	// Only run this function once for a given activity item
+	remove_filter( 'bp_activity_after_save', 'bp_activity_at_name_filter_updates' );
+
+	// Run the content through the linking filter, making sure to increment mention count
+	$activity->content = bp_activity_at_name_filter( $activity->content, $activity->id );
+
+	// Resave the activity with the new content
+	$activity->save();
+}
+add_filter( 'bp_activity_after_save', 'bp_activity_at_name_filter_updates' );
+
+function bp_activity_make_nofollow_filter( $text ) {
+	return preg_replace_callback( '|<a (.+?)>|i', 'bp_activity_make_nofollow_filter_callback', $text );
+}
+	function bp_activity_make_nofollow_filter_callback( $matches ) {
+		$text = $matches[1];
+		$text = str_replace( array( ' rel="nofollow"', " rel='nofollow'"), '', $text );
+		return "<a $text rel=\"nofollow\">";
+	}
+
+/**
+ * Truncates long activity entries when viewed in activity streams
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ * @param $text The original activity entry text
+ * @return $excerpt The truncated text
+ */
+function bp_activity_truncate_entry( $text ) {
+	global $activities_template;
+
+	// The full text of the activity update should always show on the single activity screen
+	if ( bp_is_single_activity() )
+		return $text;
+
+	$append_text    = apply_filters( 'bp_activity_excerpt_append_text', __( '[Read more]', 'buddypress' ) );
+	$excerpt_length = apply_filters( 'bp_activity_excerpt_length', 358 );
+
+	// Run the text through the excerpt function. If it's too short, the original text will be
+	// returned.
+	$excerpt        = bp_create_excerpt( $text, $excerpt_length, array( 'ending' => __( '&hellip;', 'buddypress' ) ) );
+
+	// If the text returned by bp_create_excerpt() is different from the original text (ie it's
+	// been truncated), add the "Read More" link.
+	if ( $excerpt != $text ) {
+		$id = !empty( $activities_template->activity->current_comment->id ) ? 'acomment-read-more-' . $activities_template->activity->current_comment->id : 'activity-read-more-' . bp_get_activity_id();
+
+		$excerpt = sprintf( '%1$s<span class="activity-read-more" id="%2$s"><a href="%3$s" rel="nofollow">%4$s</a></span>', $excerpt, $id, bp_get_activity_thread_permalink(), $append_text );
+	}
+
+	return apply_filters( 'bp_activity_truncate_entry', $excerpt, $text, $append_text );
+}
+add_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 );
+add_filter( 'bp_get_activity_content', 'bp_activity_truncate_entry', 5 );
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-functions.php	(revision 5057)
@@ -0,0 +1,1096 @@
+<?php
+/**
+ * BuddyPress Activity Functions
+ *
+ * Functions for the Activity Streams component
+ *
+ * @package BuddyPress
+ * @subpackage Activity Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Checks $bp pages global and looks for directory page
+ *
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @return bool True if set, False if empty
+ */
+function bp_activity_has_directory() {
+	global $bp;
+
+	return (bool) !empty( $bp->pages->activity->id );
+}
+
+/**
+ * Searches through the content of an activity item to locate usernames, designated by an @ sign
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ *
+ * @param str $content The content of the activity, usually found in $activity->content
+ * @return array $usernames Array of the found usernames that match existing users
+ */
+function bp_activity_find_mentions( $content ) {
+	$pattern = '/[@]+([A-Za-z0-9-_\.@]+)/';
+	preg_match_all( $pattern, $content, $usernames );
+
+	// Make sure there's only one instance of each username
+	if ( !$usernames = array_unique( $usernames[1] ) )
+		return false;
+
+	return $usernames;
+}
+
+/**
+ * Resets a user's unread mentions list and count
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ *
+ * @param int $user_id The id of the user whose unread mentions are being reset
+ */
+function bp_activity_clear_new_mentions( $user_id ) {
+	bp_delete_user_meta( $user_id, 'bp_new_mention_count' );
+	bp_delete_user_meta( $user_id, 'bp_new_mentions' );
+}
+
+/**
+ * Adjusts new mention count for mentioned users when activity items are deleted or created
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ *
+ * @param int $activity_id The unique id for the activity item
+ */
+function bp_activity_adjust_mention_count( $activity_id, $action = 'add' ) {
+	$activity = new BP_Activity_Activity( $activity_id );
+
+	if ( $usernames = bp_activity_find_mentions( strip_tags( $activity->content ) ) ) {
+		foreach( (array)$usernames as $username ) {
+			if ( bp_is_username_compatibility_mode() )
+				$user_id = username_exists( $username );
+			else
+				$user_id = bp_core_get_userid_from_nicename( $username );
+
+			if ( empty( $user_id ) )
+				continue;
+
+			// Adjust the mention list and count for the member
+			$new_mention_count = (int)bp_get_user_meta( $user_id, 'bp_new_mention_count', true );
+			if ( !$new_mentions = bp_get_user_meta( $user_id, 'bp_new_mentions', true ) )
+				$new_mentions = array();
+
+			switch ( $action ) {
+				case 'delete' :
+					$key = array_search( $activity_id, $new_mentions );
+					if ( $key !== false ) {
+						unset( $new_mentions[$key] );
+					}
+					break;
+
+				case 'add' :
+				default :
+					if ( !in_array( $activity_id, $new_mentions ) ) {
+						$new_mentions[] = (int)$activity_id;
+					}
+					break;
+			}
+
+			// Get an updated mention count
+			$new_mention_count = count( $new_mentions );
+
+			// Resave the user_meta
+			bp_update_user_meta( $user_id, 'bp_new_mention_count', $new_mention_count );
+			bp_update_user_meta( $user_id, 'bp_new_mentions', $new_mentions );
+		}
+	}
+}
+
+/**
+ * Formats notifications related to activity
+ *
+ * @package BuddyPress Activity
+ * @param str $action The type of activity item. Just 'new_at_mention' for now
+ * @param int $item_id The activity id
+ * @param int $secondary_item_id In the case of at-mentions, this is the mentioner's id
+ * @param int $total_items The total number of notifications to format
+ * @param str $format 'string' to get a BuddyBar-compatible notification, 'array' otherwise
+ */
+function bp_activity_format_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
+	global $bp;
+
+	switch ( $action ) {
+		case 'new_at_mention':
+			$activity_id      = $item_id;
+			$poster_user_id   = $secondary_item_id;
+			$at_mention_link  = bp_loggedin_user_domain() . bp_get_activity_slug() . '/mentions/';
+			$at_mention_title = sprintf( __( '@%s Mentions', 'buddypress' ), $bp->loggedin_user->userdata->user_nicename );
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( 'You have %1$d new activity mentions', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_activity_multiple_at_mentions_notification';
+			} else {
+				$user_fullname = bp_core_get_user_displayname( $poster_user_id );
+				$text =  sprintf( __( '%1$s mentioned you in an activity update', 'buddypress' ), $user_fullname );
+				$filter = 'bp_activity_single_at_mentions_notification';
+			}
+		break;
+	}
+
+	if ( 'string' == $format ) {
+		$return = apply_filters( $filter, '<a href="' . $at_mention_link . '" title="' . $at_mention_title . '">' . $text . '</a>', $at_mention_link, (int)$total_items, $activity_id, $poster_user_id );
+	} else {
+		$return = apply_filters( $filter, array(
+			'text' => $text,
+			'link' => $at_mention_link
+		), $at_mention_link, (int)$total_items, $activity_id, $poster_user_id );
+	}
+
+	do_action( 'activity_format_notifications', $action, $item_id, $secondary_item_id, $total_items );
+
+	return $return;
+}
+
+/** Actions *******************************************************************/
+
+/**
+ * Sets the current action for a given activity stream location
+ *
+ * @global obj $bp
+ * @param str $component_id
+ * @param str $key
+ * @param str $value
+ * @return bool False on error, True on success
+ */
+function bp_activity_set_action( $component_id, $key, $value ) {
+	global $bp;
+
+	// Return false if any of the above values are not set
+	if ( empty( $component_id ) || empty( $key ) || empty( $value ) )
+		return false;
+
+	// Set activity action
+	$bp->activity->actions->{$component_id}->{$key} = apply_filters( 'bp_activity_set_action', array(
+		'key'   => $key,
+		'value' => $value
+	), $component_id, $key, $value );
+
+	return true;
+}
+
+/**
+ * Retreives the current action from a component and key
+ *
+ * @global obj $bp
+ * @param str $component_id
+ * @param str $key
+ * @return mixed False on error, action on success
+ */
+function bp_activity_get_action( $component_id, $key ) {
+	global $bp;
+
+	// Return false if any of the above values are not set
+	if ( empty( $component_id ) || empty( $key ) )
+		return false;
+
+	return apply_filters( 'bp_activity_get_action', $bp->activity->actions->{$component_id}->{$key}, $component_id, $key );
+}
+
+/** Favorites *****************************************************************/
+
+/**
+ * Get a users favorite activity stream items
+ *
+ * @global obj $bp
+ * @param int $user_id
+ * @return array Array of users favorite activity stream ID's
+ */
+function bp_activity_get_user_favorites( $user_id = 0 ) {
+	global $bp;
+
+	// Fallback to logged in user if no user_id is passed
+	if ( empty( $user_id ) )
+		$user_id = $bp->displayed_user->id;
+
+	// Get favorites for user
+	$favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true );
+
+	return apply_filters( 'bp_activity_get_user_favorites', $favs );
+}
+
+/**
+ * Add an activity stream item as a favorite for a user
+ *
+ * @global obj $bp
+ * @param int $activity_id
+ * @param int $user_id
+ * @return bool
+ */
+function bp_activity_add_user_favorite( $activity_id, $user_id = 0 ) {
+	global $bp;
+
+	// Favorite activity stream items are for logged in users only
+	if ( !is_user_logged_in() )
+		return false;
+
+	// Fallback to logged in user if no user_id is passed
+	if ( empty( $user_id ) )
+		$user_id = $bp->loggedin_user->id;
+
+	// Update the user's personal favorites
+	$my_favs   = bp_get_user_meta( $bp->loggedin_user->id, 'bp_favorite_activities', true );
+	$my_favs[] = $activity_id;
+
+	// Update the total number of users who have favorited this activity
+	$fav_count = bp_activity_get_meta( $activity_id, 'favorite_count' );
+	$fav_count = !empty( $fav_count ) ? (int)$fav_count + 1 : 1;
+
+	// Update user meta
+	bp_update_user_meta( $bp->loggedin_user->id, 'bp_favorite_activities', $my_favs );
+
+	// Update activity meta counts
+	if ( true === bp_activity_update_meta( $activity_id, 'favorite_count', $fav_count ) ) {
+
+		// Execute additional code
+		do_action( 'bp_activity_add_user_favorite', $activity_id, $user_id );
+
+		// Success
+		return true;
+
+	// Saving meta was unsuccessful for an unknown reason
+	} else {
+		// Execute additional code
+		do_action( 'bp_activity_add_user_favorite_fail', $activity_id, $user_id );
+
+		return false;
+	}
+}
+
+function bp_activity_remove_user_favorite( $activity_id, $user_id = 0 ) {
+	global $bp;
+
+	// Favorite activity stream items are for logged in users only
+	if ( !is_user_logged_in() )
+		return false;
+
+	// Fallback to logged in user if no user_id is passed
+	if ( empty( $user_id ) )
+		$user_id = $bp->loggedin_user->id;
+
+	// Remove the fav from the user's favs
+	$my_favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true );
+	$my_favs = array_flip( (array) $my_favs );
+	unset( $my_favs[$activity_id] );
+	$my_favs = array_unique( array_flip( $my_favs ) );
+
+	// Update the total number of users who have favorited this activity
+	if ( $fav_count = bp_activity_get_meta( $activity_id, 'favorite_count' ) ) {
+
+		// Deduct from total favorites
+		if ( bp_activity_update_meta( $activity_id, 'favorite_count', (int)$fav_count - 1 ) ) {
+
+			// Update users favorites
+			if ( bp_update_user_meta( $user_id, 'bp_favorite_activities', $my_favs ) ) {
+
+				// Execute additional code
+				do_action( 'bp_activity_remove_user_favorite', $activity_id, $user_id );
+
+				// Success
+				return true;
+
+			// Error updating
+			} else {
+				return false;
+			}
+
+		// Error updating favorite count
+		} else {
+			return false;
+		}
+
+	// Error getting favorite count
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Check if activity exists by scanning content
+ *
+ * @param str $content
+ * @return bool
+ */
+function bp_activity_check_exists_by_content( $content ) {
+	return apply_filters( 'bp_activity_check_exists_by_content', BP_Activity_Activity::check_exists_by_content( $content ) );
+}
+
+/**
+ * Retreive the last time activity was updated
+ *
+ * @return str
+ */
+function bp_activity_get_last_updated() {
+	return apply_filters( 'bp_activity_get_last_updated', BP_Activity_Activity::get_last_updated() );
+}
+
+/**
+ * Retreive the number of favorite activity stream items a user has
+ *
+ * @global obj $bp
+ * @param int $user_id
+ * @return int
+ */
+function bp_activity_total_favorites_for_user( $user_id = 0 ) {
+	global $bp;
+
+	// Fallback on displayed user, and then logged in user
+	if ( empty( $user_id ) )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	return BP_Activity_Activity::total_favorite_count( $user_id );
+}
+
+/** Meta **********************************************************************/
+
+/**
+ * Delete a meta entry from the DB for an activity stream item
+ *
+ * @global DB $wpdb
+ * @global obj $bp
+ * @param int $activity_id
+ * @param str $meta_key
+ * @param str $meta_value
+ * @return bool
+ */
+function bp_activity_delete_meta( $activity_id, $meta_key = '', $meta_value = '' ) {
+	global $wpdb, $bp;
+
+	// Return false if any of the above values are not set
+	if ( !is_numeric( $activity_id ) )
+		return false;
+
+	// Sanitize key
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_array( $meta_value ) || is_object( $meta_value ) )
+		$meta_value = serialize( $meta_value );
+
+	// Trim off whitespace
+	$meta_value = trim( $meta_value );
+
+	// Delete all for activity_id
+	if ( empty( $meta_key ) )
+		$retval = $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name_meta} WHERE activity_id = %d", $activity_id ) );
+
+	// Delete only when all match
+	else if ( $meta_value )
+		$retval = $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name_meta} WHERE activity_id = %d AND meta_key = %s AND meta_value = %s", $activity_id, $meta_key, $meta_value ) );
+
+	// Delete only when activity_id and meta_key match
+	else
+		$retval = $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->activity->table_name_meta} WHERE activity_id = %d AND meta_key = %s", $activity_id, $meta_key ) );
+
+	// Delete cache entry
+	wp_cache_delete( 'bp_activity_meta_' . $meta_key . '_' . $activity_id, 'bp' );
+
+	// Success
+	if ( !is_wp_error( $retval ) )
+		return true;
+
+	// Fail
+	else
+		return false;
+}
+
+/**
+ * Get activity meta
+ *
+ * @global DB $wpdb
+ * @global obj $bp
+ * @param int $activity_id
+ * @param str $meta_key
+ * @return bool
+ */
+function bp_activity_get_meta( $activity_id = 0, $meta_key = '' ) {
+	global $wpdb, $bp;
+
+	// Make sure activity_id is valid
+	if ( empty( $activity_id ) || !is_numeric( $activity_id ) )
+		return false;
+
+	// We have a key to look for
+	if ( !empty( $meta_key ) ) {
+
+		// Sanitize key
+		$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+		// Check cache
+		if ( !$metas = wp_cache_get( 'bp_activity_meta_' . $meta_key . '_' . $activity_id, 'bp' ) ) {
+
+			// No cache so hit the DB
+			$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM {$bp->activity->table_name_meta} WHERE activity_id = %d AND meta_key = %s", $activity_id, $meta_key ) );
+
+			// Set cache
+			wp_cache_set( 'bp_activity_meta_' . $meta_key . '_' . $activity_id, $metas, 'bp' );
+		}
+
+	// No key so get all for activity_id
+	} else {
+		$metas = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM {$bp->activity->table_name_meta} WHERE activity_id = %d", $activity_id ) );
+	}
+
+	// No result so return false
+	if ( empty( $metas ) )
+		return false;
+
+	// Maybe, just maybe... unserialize
+	$metas = array_map( 'maybe_unserialize', (array)$metas );
+
+	// Return first item in array if only 1, else return all metas found
+	$retval = ( 1 == count( $metas ) ? $metas[0] : $metas );
+
+	// Filter result before returning
+	return apply_filters( 'bp_activity_get_meta', $retval, $activity_id, $meta_key );
+}
+
+/**
+ * Update activity meta
+ *
+ * @global DB $wpdb
+ * @global obj $bp
+ * @param int $activity_id
+ * @param str $meta_key
+ * @param str $meta_value
+ * @return bool
+ */
+function bp_activity_update_meta( $activity_id, $meta_key, $meta_value ) {
+	global $wpdb, $bp;
+
+	// Make sure activity_id is valid
+	if ( !is_numeric( $activity_id ) )
+		return false;
+
+	// Sanitize key
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	// Sanitize value
+	if ( is_string( $meta_value ) )
+		$meta_value = stripslashes( $wpdb->escape( $meta_value ) );
+
+	// Maybe, just maybe... serialize
+	$meta_value = maybe_serialize( $meta_value );
+
+	// If value is empty, delete the meta key
+	if ( empty( $meta_value ) )
+		return bp_activity_delete_meta( $activity_id, $meta_key );
+
+	// See if meta key exists for activity_id
+	$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->activity->table_name_meta} WHERE activity_id = %d AND meta_key = %s", $activity_id, $meta_key ) );
+
+	// Meta key does not exist so INSERT
+	if ( empty( $cur ) )
+		$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->activity->table_name_meta} ( activity_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", $activity_id, $meta_key, $meta_value ) );
+
+	// Meta key exists, so UPDATE
+	else if ( $cur->meta_value != $meta_value )
+		$wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name_meta} SET meta_value = %s WHERE activity_id = %d AND meta_key = %s", $meta_value, $activity_id, $meta_key ) );
+
+	// Weirdness, so return false
+	else
+		return false;
+
+	// Set cache
+	wp_cache_set( 'bp_activity_meta_' . $meta_key . '_' . $activity_id, $meta_value, 'bp' );
+
+	// Victory is ours!
+	return true;
+}
+
+/** Clean up ******************************************************************/
+
+/**
+ * Completely remove
+ * @param int $user_id
+ */
+function bp_activity_remove_all_user_data( $user_id = 0 ) {
+
+	// Do not delete user data unless a logged in user says so
+	if ( empty( $user_id ) || !is_user_logged_in() )
+		return false;
+
+	// Clear the user's activity from the sitewide stream and clear their activity tables
+	bp_activity_delete( array( 'user_id' => $user_id ) );
+
+	// Remove any usermeta
+	bp_delete_user_meta( $user_id, 'bp_latest_update' );
+	bp_delete_user_meta( $user_id, 'bp_favorite_activities' );
+
+	// Execute additional code
+	do_action( 'bp_activity_remove_data', $user_id ); // Deprecated! Do not use!
+
+	// Use this going forward
+	do_action( 'bp_activity_remove_all_user_data', $user_id );
+}
+add_action( 'wpmu_delete_user',  'bp_activity_remove_all_user_data' );
+add_action( 'delete_user',       'bp_activity_remove_all_user_data' );
+add_action( 'bp_make_spam_user', 'bp_activity_remove_all_user_data' );
+
+/**
+ * Register the activity stream actions for updates
+ *
+ * @global obj $bp
+ */
+function updates_register_activity_actions() {
+	global $bp;
+
+	bp_activity_set_action( $bp->activity->id, 'activity_update', __( 'Posted an update', 'buddypress' ) );
+
+	do_action( 'updates_register_activity_actions' );
+}
+add_action( 'bp_register_activity_actions', 'updates_register_activity_actions' );
+
+/*******************************************************************************
+ * Business functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+function bp_activity_get( $args = '' ) {
+	$defaults = array(
+		'max'              => false,  // Maximum number of results to return
+		'page'             => 1,      // page 1 without a per_page will result in no pagination.
+		'per_page'         => false,  // results per page
+		'sort'             => 'DESC', // sort ASC or DESC
+		'display_comments' => false,  // false for no comments. 'stream' for within stream display, 'threaded' for below each activity item
+
+		'search_terms'     => false,  // Pass search terms as a string
+		'show_hidden'      => false,  // Show activity items that are hidden site-wide?
+		'exclude'          => false,  // Comma-separated list of activity IDs to exclude
+		'in'               => false,  // Comma-separated list or array of activity IDs to which you want to limit the query
+
+		/**
+		 * Pass filters as an array -- all filter items can be multiple values comma separated:
+		 * array(
+		 * 	'user_id'      => false, // user_id to filter on
+		 *	'object'       => false, // object to filter on e.g. groups, profile, status, friends
+		 *	'action'       => false, // action to filter on e.g. activity_update, profile_updated
+		 *	'primary_id'   => false, // object ID to filter on e.g. a group_id or forum_id or blog_id etc.
+		 *	'secondary_id' => false, // secondary object ID to filter on e.g. a post_id
+		 * );
+		 */
+		'filter' => array()
+	);
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// Attempt to return a cached copy of the first page of sitewide activity.
+	if ( 1 == (int)$page && empty( $max ) && empty( $search_terms ) && empty( $filter ) && 'DESC' == $sort && empty( $exclude ) ) {
+		if ( !$activity = wp_cache_get( 'bp_activity_sitewide_front', 'bp' ) ) {
+			$activity = BP_Activity_Activity::get( $max, $page, $per_page, $sort, $search_terms, $filter, $display_comments, $show_hidden );
+			wp_cache_set( 'bp_activity_sitewide_front', $activity, 'bp' );
+		}
+	} else
+		$activity = BP_Activity_Activity::get( $max, $page, $per_page, $sort, $search_terms, $filter, $display_comments, $show_hidden, $exclude, $in );
+
+	return apply_filters_ref_array( 'bp_activity_get', array( &$activity, &$r ) );
+}
+
+/**
+ * Fetch specific activity items
+ *
+ * @package BuddyPress
+ *
+ * @param array $args See docs for $defaults for details
+ * @return array The array returned by BP_Activity_Activity::get()
+ */
+function bp_activity_get_specific( $args = '' ) {
+	$defaults = array(
+		'activity_ids'     => false,  // A single activity_id or array of IDs.
+		'page'             => 1,      // page 1 without a per_page will result in no pagination.
+		'per_page'         => false,  // results per page
+		'max'              => false,  // Maximum number of results to return
+		'sort'             => 'DESC', // sort ASC or DESC
+		'display_comments' => false,  // true or false to display threaded comments for these specific activity items
+		'show_hidden'      => true    // When fetching specific items, show all
+	);
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return apply_filters( 'bp_activity_get_specific', BP_Activity_Activity::get( $max, $page, $per_page, $sort, false, false, $display_comments, $show_hidden, false, $activity_ids ) );
+}
+
+function bp_activity_add( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'id'                => false, // Pass an existing activity ID to update an existing entry.
+
+		'action'            => '',    // The activity action - e.g. "Jon Doe posted an update"
+		'content'           => '',    // Optional: The content of the activity item e.g. "BuddyPress is awesome guys!"
+
+		'component'         => false, // The name/ID of the component e.g. groups, profile, mycomponent
+		'type'              => false, // The activity type e.g. activity_update, profile_updated
+		'primary_link'      => '',    // Optional: The primary URL for this item in RSS feeds (defaults to activity permalink)
+
+		'user_id'           => $bp->loggedin_user->id, // Optional: The user to record the activity for, can be false if this activity is not for a user.
+		'item_id'           => false, // Optional: The ID of the specific item being recorded, e.g. a blog_id
+		'secondary_item_id' => false, // Optional: A second ID used to further filter e.g. a comment_id
+		'recorded_time'     => bp_core_current_time(), // The GMT time that this activity was recorded
+		'hide_sitewide'     => false  // Should this be hidden on the sitewide activity stream?
+	);
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	// Make sure we are backwards compatible
+	if ( empty( $component ) && !empty( $component_name ) )
+		$component = $component_name;
+
+	if ( empty( $type ) && !empty( $component_action ) )
+		$type = $component_action;
+
+	// Setup activity to be added
+	$activity                    = new BP_Activity_Activity( $id );
+	$activity->user_id           = $user_id;
+	$activity->component         = $component;
+	$activity->type              = $type;
+	$activity->action            = $action;
+	$activity->content           = $content;
+	$activity->primary_link      = $primary_link;
+	$activity->item_id           = $item_id;
+	$activity->secondary_item_id = $secondary_item_id;
+	$activity->date_recorded     = $recorded_time;
+	$activity->hide_sitewide     = $hide_sitewide;
+
+	if ( !$activity->save() )
+		return false;
+
+	// If this is an activity comment, rebuild the tree
+	if ( 'activity_comment' == $activity->type )
+		BP_Activity_Activity::rebuild_activity_comment_tree( $activity->item_id );
+
+	wp_cache_delete( 'bp_activity_sitewide_front', 'bp' );
+	do_action( 'bp_activity_add', $params );
+
+	return $activity->id;
+}
+
+function bp_activity_post_update( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'content' => false,
+		'user_id' => $bp->loggedin_user->id
+	);
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( empty( $content ) || !strlen( trim( $content ) ) )
+		return false;
+
+	if ( bp_core_is_user_spammer( $user_id ) || bp_core_is_user_deleted( $user_id ) )
+		return false;
+
+	// Record this on the user's profile
+	$from_user_link   = bp_core_get_userlink( $user_id );
+	$activity_action  = sprintf( __( '%s posted an update', 'buddypress' ), $from_user_link );
+	$activity_content = $content;
+	$primary_link     = bp_core_get_userlink( $user_id, false, true );
+
+	// Now write the values
+	$activity_id = bp_activity_add( array(
+		'user_id'      => $user_id,
+		'action'       => apply_filters( 'bp_activity_new_update_action', $activity_action ),
+		'content'      => apply_filters( 'bp_activity_new_update_content', $activity_content ),
+		'primary_link' => apply_filters( 'bp_activity_new_update_primary_link', $primary_link ),
+		'component'    => $bp->activity->id,
+		'type'         => 'activity_update'
+	) );
+
+	// Add this update to the "latest update" usermeta so it can be fetched anywhere.
+	bp_update_user_meta( $bp->loggedin_user->id, 'bp_latest_update', array( 'id' => $activity_id, 'content' => wp_filter_kses( $content ) ) );
+
+	do_action( 'bp_activity_posted_update', $content, $user_id, $activity_id );
+
+	return $activity_id;
+}
+
+function bp_activity_new_comment( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'id'          => false,
+		'content'     => false,
+		'user_id'     => $bp->loggedin_user->id,
+		'activity_id' => false, // ID of the root activity item
+		'parent_id'   => false  // ID of a parent comment (optional)
+	);
+
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	if ( empty($content) || empty($user_id) || empty($activity_id) )
+		return false;
+
+	if ( empty($parent_id) )
+		$parent_id = $activity_id;
+
+	// Check to see if the parent activity is hidden, and if so, hide this comment publically.
+	$activity = new BP_Activity_Activity( $activity_id );
+	$is_hidden = ( (int)$activity->hide_sitewide ) ? 1 : 0;
+
+	// Insert the activity comment
+	$comment_id = bp_activity_add( array(
+		'id' => $id,
+		'action' => apply_filters( 'bp_activity_comment_action', sprintf( __( '%s posted a new activity comment', 'buddypress' ), bp_core_get_userlink( $user_id ) ) ),
+		'content' => apply_filters( 'bp_activity_comment_content', $content ),
+		'component' => $bp->activity->id,
+		'type' => 'activity_comment',
+		'user_id' => $user_id,
+		'item_id' => $activity_id,
+		'secondary_item_id' => $parent_id,
+		'hide_sitewide' => $is_hidden
+	) );
+
+	// Send an email notification if settings allow
+	bp_activity_new_comment_notification( $comment_id, $user_id, $params );
+
+	// Clear the comment cache for this activity
+	wp_cache_delete( 'bp_activity_comments_' . $parent_id );
+
+	do_action( 'bp_activity_comment_posted', $comment_id, $params );
+
+	return $comment_id;
+}
+
+/**
+ * bp_activity_get_activity_id()
+ *
+ * Fetch the activity_id for an existing activity entry in the DB.
+ *
+ * @package BuddyPress Activity
+ */
+function bp_activity_get_activity_id( $args = '' ) {
+	$defaults = array(
+		'user_id'           => false,
+		'component'         => false,
+		'type'              => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'action'            => false,
+		'content'           => false,
+		'date_recorded'     => false,
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+ 	return apply_filters( 'bp_activity_get_activity_id', BP_Activity_Activity::get_id( $user_id, $component, $type, $item_id, $secondary_item_id, $action, $content, $date_recorded ) );
+}
+
+/***
+ * Deleting Activity
+ *
+ * If you're looking to hook into one action that provides the ID(s) of
+ * the activity/activities deleted, then use:
+ *
+ * add_action( 'bp_activity_deleted_activities', 'my_function' );
+ *
+ * The action passes one parameter that is a single activity ID or an
+ * array of activity IDs depending on the number deleted.
+ *
+ * If you are deleting an activity comment please use bp_activity_delete_comment();
+*/
+
+function bp_activity_delete( $args = '' ) {
+	global $bp;
+
+	// Pass one or more the of following variables to delete by those variables
+	$defaults = array(
+		'id'                => false,
+		'action'            => false,
+		'content'           => false,
+		'component'         => false,
+		'type'              => false,
+		'primary_link'      => false,
+		'user_id'           => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'date_recorded'     => false,
+		'hide_sitewide'     => false
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+
+	// Adjust the new mention count of any mentioned member
+	bp_activity_adjust_mention_count( $args['id'], 'delete' );
+
+	if ( !$activity_ids_deleted = BP_Activity_Activity::delete( $args ) )
+		return false;
+
+	// Check if the user's latest update has been deleted
+	if ( empty( $args['user_id'] ) )
+		$user_id = $bp->loggedin_user->id;
+	else
+		$user_id = $args['user_id'];
+
+	do_action( 'bp_before_activity_delete', $args );
+
+	$latest_update = bp_get_user_meta( $user_id, 'bp_latest_update', true );
+	if ( !empty( $latest_update ) ) {
+		if ( in_array( (int)$latest_update['id'], (array)$activity_ids_deleted ) )
+			bp_delete_user_meta( $user_id, 'bp_latest_update' );
+	}
+
+	do_action( 'bp_activity_delete', $args );
+	do_action( 'bp_activity_deleted_activities', $activity_ids_deleted );
+
+	wp_cache_delete( 'bp_activity_sitewide_front', 'bp' );
+
+	return true;
+}
+	// The following functions have been deprecated in place of bp_activity_delete()
+	function bp_activity_delete_by_item_id( $args = '' ) {
+		global $bp;
+
+		$defaults = array( 'item_id' => false, 'component' => false, 'type' => false, 'user_id' => false, 'secondary_item_id' => false );
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return bp_activity_delete( array( 'item_id' => $item_id, 'component' => $component, 'type' => $type, 'user_id' => $user_id, 'secondary_item_id' => $secondary_item_id ) );
+	}
+
+	function bp_activity_delete_by_activity_id( $activity_id ) {
+		return bp_activity_delete( array( 'id' => $activity_id ) );
+	}
+
+	function bp_activity_delete_by_content( $user_id, $content, $component, $type ) {
+		return bp_activity_delete( array( 'user_id' => $user_id, 'content' => $content, 'component' => $component, 'type' => $type ) );
+	}
+
+	function bp_activity_delete_for_user_by_component( $user_id, $component ) {
+		return bp_activity_delete( array( 'user_id' => $user_id, 'component' => $component ) );
+	}
+	// End deprecation
+
+function bp_activity_delete_comment( $activity_id, $comment_id ) {
+	/***
+	 * You may want to hook into this filter if you want to override this function and
+	 * handle the deletion of child comments differently. Make sure you return false.
+	 */
+	if ( !apply_filters( 'bp_activity_delete_comment_pre', true, $activity_id, $comment_id ) )
+		return false;
+
+	// Delete any children of this comment.
+	bp_activity_delete_children( $activity_id, $comment_id );
+
+	// Delete the actual comment
+	if ( !bp_activity_delete( array( 'id' => $comment_id, 'type' => 'activity_comment' ) ) )
+		return false;
+
+	// Recalculate the comment tree
+	BP_Activity_Activity::rebuild_activity_comment_tree( $activity_id );
+
+	do_action( 'bp_activity_delete_comment', $activity_id, $comment_id );
+
+	return true;
+}
+	function bp_activity_delete_children( $activity_id, $comment_id) {
+		// Recursively delete all children of this comment.
+		if ( $children = BP_Activity_Activity::get_child_comments( $comment_id ) ) {
+			foreach( (array)$children as $child )
+				bp_activity_delete_children( $activity_id, $child->id );
+		}
+		bp_activity_delete( array( 'secondary_item_id' => $comment_id, 'type' => 'activity_comment', 'item_id' => $activity_id ) );
+	}
+
+/**
+ * Get the permalink for a single activity item
+ *
+ * When only the $activity_id param is passed, BP has to instantiate a new BP_Activity_Activity
+ * object. To save yourself some processing overhead, be sure to pass the full $activity_obj param
+ * as well, if you already have it available.
+ *
+ * @package BuddyPress
+ *
+ * @uses apply_filters_ref_array() Filter 'bp_activity_get_permalink' to modify the function output
+ * @param int $activity_id The unique id of the activity object
+ * @param obj $activity_obj (optional) The activity object
+ * @return str $link Permalink for the activity item
+ */
+function bp_activity_get_permalink( $activity_id, $activity_obj = false ) {
+	global $bp;
+
+	if ( !$activity_obj )
+		$activity_obj = new BP_Activity_Activity( $activity_id );
+
+	if ( isset( $activity_obj->current_comment ) ) {
+		$activity_obj = $activity_obj->current_comment;
+	}
+
+	if ( 'new_blog_post' == $activity_obj->type || 'new_blog_comment' == $activity_obj->type || 'new_forum_topic' == $activity_obj->type || 'new_forum_post' == $activity_obj->type )
+		$link = $activity_obj->primary_link;
+	else {
+		if ( 'activity_comment' == $activity_obj->type )
+			$link = bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $activity_obj->item_id . '/';
+		else
+			$link = bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $activity_obj->id . '/';
+	}
+
+	return apply_filters_ref_array( 'bp_activity_get_permalink', array( $link, &$activity_obj ) );
+}
+
+function bp_activity_hide_user_activity( $user_id ) {
+	return BP_Activity_Activity::hide_all_for_user( $user_id );
+}
+
+/**
+ * bp_activity_thumbnail_content_images()
+ *
+ * Take content, remove all images and replace them with one thumbnail image.
+ *
+ * @package BuddyPress Activity
+ * @param $content str - The content to work with
+ * @param $link str - Optional. The URL that the image should link to
+ * @return $content str - The content with images stripped and replaced with a single thumb.
+ */
+function bp_activity_thumbnail_content_images( $content, $link = false ) {
+	global $post;
+
+	preg_match_all( '/<img[^>]*>/Ui', $content, $matches );
+	$content = preg_replace('/<img[^>]*>/Ui', '', $content );
+
+	if ( !empty( $matches ) && !empty( $matches[0] ) ) {
+		// Get the SRC value
+		preg_match( '/<img.*?(src\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $src );
+
+		// Get the width and height
+		preg_match( '/<img.*?(height\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $height );
+		preg_match( '/<img.*?(width\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $width );
+
+		if ( !empty( $src ) ) {
+			$src = substr( substr( str_replace( 'src=', '', $src[1] ), 0, -1 ), 1 );
+			$height = substr( substr( str_replace( 'height=', '', $height[1] ), 0, -1 ), 1 );
+			$width = substr( substr( str_replace( 'width=', '', $width[1] ), 0, -1 ), 1 );
+
+			if ( empty( $width ) || empty( $height ) ) {
+				$width = 100;
+				$height = 100;
+			}
+
+			$ratio = (int)$width / (int)$height;
+			$new_height = (int)$height >= 100 ? 100 : $height;
+			$new_width = $new_height * $ratio;
+
+			$image = '<img src="' . esc_attr( $src ) . '" width="' . $new_width . '" height="' . $new_height . '" alt="' . __( 'Thumbnail', 'buddypress' ) . '" class="align-left thumbnail" />';
+
+			if ( !empty( $link ) ) {
+				$image = '<a href="' . $link . '">' . $image . '</a>';
+			}
+
+			$content = $image . $content;
+		}
+	}
+
+	return apply_filters( 'bp_activity_thumbnail_content_images', $content, $matches );
+}
+
+/** Embeds *******************************************************************/
+
+/**
+ * Grabs the activity update ID and attempts to retrieve the oEmbed cache (if it exists)
+ * during the activity loop.  If no cache and link is embeddable, cache it.
+ *
+ * This does not cover recursive activity comments, as they do not use a real loop.
+ * For that, see {@link bp_activity_comment_embed()}.
+ *
+ * @see BP_Embed
+ * @see bp_embed_activity_cache()
+ * @see bp_embed_activity_save_cache()
+ * @package BuddyPress Activity
+ * @since 1.5
+ */
+function bp_activity_embed() {
+	add_filter( 'embed_post_id',         'bp_get_activity_id'                  );
+	add_filter( 'bp_embed_get_cache',    'bp_embed_activity_cache',      10, 3 );
+	add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 );
+}
+add_action( 'activity_loop_start', 'bp_activity_embed' );
+
+/**
+ * Grabs the activity comment ID and attempts to retrieve the oEmbed cache (if it exists)
+ * when BP is recursing through activity comments {@link bp_activity_recurse_comments()}.
+ * If no cache and link is embeddable, cache it.
+ *
+ * @see BP_Embed
+ * @see bp_embed_activity_cache()
+ * @see bp_embed_activity_save_cache()
+ * @package BuddyPress Activity
+ * @since 1.5
+ */
+function bp_activity_comment_embed() {
+	add_filter( 'embed_post_id',         'bp_get_activity_comment_id'          );
+	add_filter( 'bp_embed_get_cache',    'bp_embed_activity_cache',      10, 3 );
+	add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 );
+}
+add_action( 'bp_before_activity_comment', 'bp_activity_comment_embed' );
+
+/**
+ * When a user clicks on a "Read More" item, make sure embeds are correctly parsed and shown for the expanded content.
+ *
+ * @global object $bp BuddyPress global settings
+ * @param BP_Activity_Activity $activity The activity that is being expanded
+ * @see BP_Embed
+ * @since 1.5
+ */
+function bp_dtheme_embed_read_more( $activity ) {
+	global $bp;
+
+	$bp->activity->read_more_id = $activity->id;
+
+	add_filter( 'embed_post_id',            create_function( '', 'global $bp; return $bp->activity->read_more_id;' ) );
+	add_filter( 'bp_embed_get_cache',       'bp_embed_activity_cache',      10, 3 );
+	add_action( 'bp_embed_update_cache',    'bp_embed_activity_save_cache', 10, 3 );
+}
+add_action( 'bp_dtheme_get_single_activity_content', 'bp_dtheme_embed_read_more' );
+
+/**
+ * Removes the 'embed_post_id' filter after {@link bp_activity_recurse_comments()}
+ * is rendered to avoid conflict with the 'embed_post_id' filter in
+ * {@link bp_activity_embed()} or any other component embeds.
+ *
+ * @see bp_activity_comment_embed()
+ * @package BuddyPress Activity
+ * @since 1.5
+ */
+function bp_activity_comment_embed_after_recurse() {
+	remove_filter( 'embed_post_id', 'bp_get_activity_comment_id' );
+}
+add_action( 'bp_after_activity_comment', 'bp_activity_comment_embed_after_recurse' );
+
+/**
+ * Wrapper function for {@link bp_activity_get_meta()}.
+ * Used during {@link BP_Embed::parse_oembed()} via {@link bp_activity_embed()}.
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ */
+function bp_embed_activity_cache( $cache, $id, $cachekey ) {
+	return bp_activity_get_meta( $id, $cachekey );
+}
+
+/**
+ * Wrapper function for {@link bp_activity_update_meta()}.
+ * Used during {@link BP_Embed::parse_oembed()} via {@link bp_activity_embed()}.
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ */
+function bp_embed_activity_save_cache( $cache, $cachekey, $id ) {
+	bp_activity_update_meta( $id, $cachekey, $cache );
+}
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-loader.php	(revision 5057)
@@ -0,0 +1,285 @@
+<?php
+/**
+ * BuddyPress Activity Streams Loader
+ *
+ * An activity stream component, for users, groups, and blog tracking.
+ *
+ * @package BuddyPress
+ * @subpackage Activity Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Activity_Component extends BP_Component {
+
+	/**
+	 * Start the activity component creation process
+	 *
+	 * @since 1.5
+	 */
+	function BP_Activity_Component() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		parent::start(
+			'activity',
+			__( 'Activity Streams', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		// Files to include
+		$includes = array(
+			'actions',
+			'screens',
+			'filters',
+			'classes',
+			'template',
+			'functions',
+			'notifications',
+		);
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_ACTIVITY_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_ACTIVITY_SLUG' ) )
+			define( 'BP_ACTIVITY_SLUG', $this->id );
+
+		// Global tables for activity component
+		$global_tables = array(
+			'table_name'      => $bp->table_prefix . 'bp_activity',
+			'table_name_meta' => $bp->table_prefix . 'bp_activity_meta',
+		);
+
+		// All globals for activity component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_ACTIVITY_SLUG,
+			'root_slug'             => isset( $bp->pages->activity->slug ) ? $bp->pages->activity->slug : BP_ACTIVITY_SLUG,
+			'has_directory'         => true,
+			'search_string'         => __( 'Search Activity...', 'buddypress' ),
+			'global_tables'         => $global_tables,
+			'notification_callback' => 'bp_activity_format_notifications',
+		);
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add 'Activity' to the main navigation
+		$main_nav = array(
+			'name'                => __( 'Activity', 'buddypress' ),
+			'slug'                => $this->slug,
+			'position'            => 10,
+			'screen_function'     => 'bp_activity_screen_my_activity',
+			'default_subnav_slug' => 'just-me',
+			'item_css_id'         => $this->id
+		);
+
+		// Stop if there is no user displayed or logged in
+		if ( !is_user_logged_in() && !isset( $bp->displayed_user->id ) )
+			return;
+
+		// Determine user to use
+		if ( isset( $bp->displayed_user->domain ) )
+			$user_domain = $bp->displayed_user->domain;
+		elseif ( isset( $bp->loggedin_user->domain ) )
+			$user_domain = $bp->loggedin_user->domain;
+		else
+			return;
+
+		// User link
+		$activity_link = trailingslashit( $user_domain . $this->slug );
+
+		// Add the subnav items to the activity nav item if we are using a theme that supports this
+		$sub_nav[] = array(
+			'name'            => __( 'Personal', 'buddypress' ),
+			'slug'            => 'just-me',
+			'parent_url'      => $activity_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_activity_screen_my_activity',
+			'position'        => 10
+		);
+
+		// @ mentions
+		$sub_nav[] = array(
+			'name'            => __( 'Mentions', 'buddypress' ),
+			'slug'            => 'mentions',
+			'parent_url'      => $activity_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_activity_screen_mentions',
+			'position'        => 20,
+			'item_css_id'     => 'activity-mentions'
+		);
+
+		// Favorite activity items
+		$sub_nav[] = array(
+			'name'            => __( 'Favorites', 'buddypress' ),
+			'slug'            => 'favorites',
+			'parent_url'      => $activity_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_activity_screen_favorites',
+			'position'        => 30,
+			'item_css_id'     => 'activity-favs'
+		);
+
+		// Additional menu if friends is active
+		if ( bp_is_active( 'friends' ) ) {
+			$sub_nav[] = array(
+				'name'            => __( 'Friends', 'buddypress' ),
+				'slug'            => bp_get_friends_slug(),
+				'parent_url'      => $activity_link,
+				'parent_slug'     => $this->slug,
+				'screen_function' => 'bp_activity_screen_friends',
+				'position'        => 40,
+				'item_css_id'     => 'activity-friends'
+			) ;
+		}
+
+		// Additional menu if groups is active
+		if ( bp_is_active( 'groups' ) ) {
+			$sub_nav[] = array(
+				'name'            => __( 'Groups', 'buddypress' ),
+				'slug'            => bp_get_groups_slug(),
+				'parent_url'      => $activity_link,
+				'parent_slug'     => $this->slug,
+				'screen_function' => 'bp_activity_screen_groups',
+				'position'        => 50,
+				'item_css_id'     => 'activity-groups'
+			);
+		}
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain   = $bp->loggedin_user->domain;
+			$activity_link = trailingslashit( $user_domain . $this->slug );
+
+			// Unread message count
+			if ( $count = bp_get_total_mention_count_for_user( bp_loggedin_user_id() ) ) {
+				$title = sprintf( __( 'Mentions <span class="count">%s</span>', 'buddypress' ), $count );
+			} else {
+				$title = __( 'Mentions', 'buddypress' );
+			}
+
+			// Add the "Activity" sub menu
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => __( 'Activity', 'buddypress' ),
+				'href'   => trailingslashit( $activity_link )
+			);
+
+			// Mentions
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => $title,
+				'href'   => trailingslashit( $activity_link . 'mentions' )
+			);
+
+			// Personal
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Personal', 'buddypress' ),
+				'href'   => trailingslashit( $activity_link )
+			);
+
+			// Favorites
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Favorites', 'buddypress' ),
+				'href'   => trailingslashit( $activity_link . 'favorites' )
+			);
+
+			// Friends?
+			if ( bp_is_active( 'friends' ) ) {
+				$wp_admin_nav[] = array(
+					'parent' => 'my-account-' . $this->id,
+					'title'  => __( 'Friends', 'buddypress' ),
+					'href'   => trailingslashit( $activity_link . bp_get_friends_slug() )
+				);
+			}
+
+			// Groups?
+			if ( bp_is_active( 'groups' ) ) {
+				$wp_admin_nav[] = array(
+					'parent' => 'my-account-' . $this->id,
+					'title'  => __( 'Groups', 'buddypress' ),
+					'href'   => trailingslashit( $activity_link . bp_get_groups_slug() )
+				);
+			}
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		// Adjust title based on view
+		if ( bp_is_activity_component() ) {
+			if ( bp_is_my_profile() ) {
+				$bp->bp_options_title = __( 'My Activity', 'buddypress' );
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title  = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the activity component
+$bp->activity = new BP_Activity_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-notifications.php	(revision 5057)
@@ -0,0 +1,164 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Sends an email notification and a BP notification when someone mentions you in an update
+ *
+ * @package BuddyPress Activity
+ * @param str $content The content of the activity update
+ * @param int $poster_user_id The unique user_id of the user who sent the update
+ * @param int $activity_id The id of the activity update
+ */
+function bp_activity_at_message_notification( $activity_id, $receiver_user_id ) {
+	global $bp;
+
+	$activity = new BP_Activity_Activity( $activity_id );
+
+	$subject = '';
+	$message = '';
+
+	// Add the BP notification
+	bp_core_add_notification( $activity_id, $receiver_user_id, 'activity', 'new_at_mention', $activity->user_id );
+
+	// Now email the user with the contents of the message (if they have enabled email notifications)
+	if ( 'no' != bp_get_user_meta( $receiver_user_id, 'notification_activity_new_mention', true ) ) {
+		$poster_name = bp_core_get_user_displayname( $activity->user_id );
+
+		$message_link = bp_activity_get_permalink( $activity_id );
+		$settings_link = bp_core_get_user_domain( $receiver_user_id ) . bp_get_settings_slug() . '/notifications/';
+
+		$poster_name = stripslashes( $poster_name );
+		$content = bp_activity_filter_kses( strip_tags( stripslashes( $activity->content ) ) );
+
+		// Set up and send the message
+		$ud       = bp_core_get_core_userdata( $receiver_user_id );
+		$to       = $ud->user_email;
+		$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+		$subject  = '[' . $sitename . '] ' . sprintf( __( '%s mentioned you in an update', 'buddypress' ), $poster_name );
+
+		if ( bp_is_active( 'groups' ) && bp_is_group() ) {
+			$message = sprintf( __(
+'%1$s mentioned you in the group "%2$s":
+
+"%3$s"
+
+To view and respond to the message, log in and visit: %4$s
+
+---------------------
+', 'buddypress' ), $poster_name, bp_get_current_group_name(), $content, $message_link );
+		} else {
+			$message = sprintf( __(
+'%1$s mentioned you in an update:
+
+"%2$s"
+
+To view and respond to the message, log in and visit: %3$s
+
+---------------------
+', 'buddypress' ), $poster_name, $content, $message_link );
+		}
+
+		$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		/* Send the message */
+		$to 	 = apply_filters( 'bp_activity_at_message_notification_to', $to );
+		$subject = apply_filters( 'bp_activity_at_message_notification_subject', $subject, $poster_name );
+		$message = apply_filters( 'bp_activity_at_message_notification_message', $message, $poster_name, $content, $message_link, $settings_link );
+
+		wp_mail( $to, $subject, $message );
+	}
+
+	do_action( 'bp_activity_sent_mention_email', $activity, $subject, $message, $content );
+}
+
+function bp_activity_new_comment_notification( $comment_id, $commenter_id, $params ) {
+	global $bp;
+
+	extract( $params );
+
+	$original_activity = new BP_Activity_Activity( $activity_id );
+
+	if ( $original_activity->user_id != $commenter_id && 'no' != bp_get_user_meta( $original_activity->user_id, 'notification_activity_new_reply', true ) ) {
+		$poster_name = bp_core_get_user_displayname( $commenter_id );
+		$thread_link = bp_activity_get_permalink( $activity_id );
+		$settings_link = bp_core_get_user_domain( $original_activity->user_id ) . bp_get_settings_slug() . '/notifications/';
+
+		$poster_name = stripslashes( $poster_name );
+		$content = bp_activity_filter_kses( stripslashes($content) );
+
+		// Set up and send the message
+		$ud       = bp_core_get_core_userdata( $original_activity->user_id );
+		$to       = $ud->user_email;
+		$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+		$subject = '[' . $sitename . '] ' . sprintf( __( '%s replied to one of your updates', 'buddypress' ), $poster_name );
+
+$message = sprintf( __(
+'%1$s replied to one of your updates:
+
+"%2$s"
+
+To view your original update and all comments, log in and visit: %3$s
+
+---------------------
+', 'buddypress' ), $poster_name, $content, $thread_link );
+
+		$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		/* Send the message */
+		$to = apply_filters( 'bp_activity_new_comment_notification_to', $to );
+		$subject = apply_filters( 'bp_activity_new_comment_notification_subject', $subject, $poster_name );
+		$message = apply_filters( 'bp_activity_new_comment_notification_message', $message, $poster_name, $content, $thread_link, $settings_link );
+
+		wp_mail( $to, $subject, $message );
+
+		do_action( 'bp_activity_sent_reply_to_update_email', $original_activity->user_id, $subject, $message, $comment_id, $commenter_id, $params );
+	}
+
+	/***
+	 * If this is a reply to another comment, send an email notification to the
+	 * author of the immediate parent comment.
+	 */
+	if ( $activity_id == $parent_id )
+		return false;
+
+	$parent_comment = new BP_Activity_Activity( $parent_id );
+
+	if ( $parent_comment->user_id != $commenter_id && $original_activity->user_id != $parent_comment->user_id && 'no' != bp_get_user_meta( $parent_comment->user_id, 'notification_activity_new_reply', true ) ) {
+		$poster_name = bp_core_get_user_displayname( $commenter_id );
+		$thread_link = bp_activity_get_permalink( $activity_id );
+		$settings_link = bp_core_get_user_domain( $parent_comment->user_id ) . bp_get_settings_slug() . '/notifications/';
+
+		// Set up and send the message
+		$ud       = bp_core_get_core_userdata( $parent_comment->user_id );
+		$to       = $ud->user_email;
+		$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+		$subject = '[' . $sitename . '] ' . sprintf( __( '%s replied to one of your comments', 'buddypress' ), $poster_name );
+
+		$poster_name = stripslashes( $poster_name );
+		$content = bp_activity_filter_kses( stripslashes( $content ) );
+
+$message = sprintf( __(
+'%1$s replied to one of your comments:
+
+"%2$s"
+
+To view the original activity, your comment and all replies, log in and visit: %3$s
+
+---------------------
+', 'buddypress' ), $poster_name, $content, $thread_link );
+
+		$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		/* Send the message */
+		$to = apply_filters( 'bp_activity_new_comment_notification_comment_author_to', $to );
+		$subject = apply_filters( 'bp_activity_new_comment_notification_comment_author_subject', $subject, $poster_name );
+		$message = apply_filters( 'bp_activity_new_comment_notification_comment_author_message', $message, $poster_name, $content, $settings_link );
+
+		wp_mail( $to, $subject, $message );
+
+		do_action( 'bp_activity_sent_reply_to_reply_email', $original_activity->user_id, $subject, $message, $comment_id, $commenter_id, $params );
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-screens.php	(revision 5057)
@@ -0,0 +1,199 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Activity index
+ *
+ * @global obj $bp
+ */
+function bp_activity_screen_index() {
+	if ( !bp_displayed_user_id() && bp_is_activity_component() && !bp_current_action() ) {
+		bp_update_is_directory( true, 'activity' );
+
+		do_action( 'bp_activity_screen_index' );
+
+		bp_core_load_template( apply_filters( 'bp_activity_screen_index', 'activity/index' ) );
+	}
+}
+add_action( 'bp_screens', 'bp_activity_screen_index' );
+
+function bp_activity_screen_my_activity() {
+	do_action( 'bp_activity_screen_my_activity' );
+	bp_core_load_template( apply_filters( 'bp_activity_template_my_activity', 'members/single/home' ) );
+}
+
+function bp_activity_screen_friends() {
+	if ( !bp_is_active( 'friends' ) )
+		return false;
+
+	bp_update_is_item_admin( is_super_admin(), 'activity' );
+	do_action( 'bp_activity_screen_friends' );
+	bp_core_load_template( apply_filters( 'bp_activity_template_friends_activity', 'members/single/home' ) );
+}
+
+function bp_activity_screen_groups() {
+	if ( !bp_is_active( 'groups' ) )
+		return false;
+
+	bp_update_is_item_admin( is_super_admin(), 'activity' );
+	do_action( 'bp_activity_screen_groups' );
+	bp_core_load_template( apply_filters( 'bp_activity_template_groups_activity', 'members/single/home' ) );
+}
+
+function bp_activity_screen_favorites() {
+	bp_update_is_item_admin( is_super_admin(), 'activity' );
+	do_action( 'bp_activity_screen_favorites' );
+	bp_core_load_template( apply_filters( 'bp_activity_template_favorite_activity', 'members/single/home' ) );
+}
+
+function bp_activity_screen_mentions() {
+	bp_update_is_item_admin( is_super_admin(), 'activity' );
+	do_action( 'bp_activity_screen_mentions' );
+	bp_core_load_template( apply_filters( 'bp_activity_template_mention_activity', 'members/single/home' ) );
+}
+
+/**
+ * bp_activity_remove_screen_notifications()
+ *
+ * Removes activity notifications from the notification menu when a user clicks on them and
+ * is taken to a specific screen.
+ *
+ * @package BuddyPress Activity
+ */
+function bp_activity_remove_screen_notifications() {
+	global $bp;
+
+	bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->activity->id, 'new_at_mention' );
+}
+add_action( 'bp_activity_screen_my_activity',               'bp_activity_remove_screen_notifications' );
+add_action( 'bp_activity_screen_single_activity_permalink', 'bp_activity_remove_screen_notifications' );
+add_action( 'bp_activity_screen_mentions',                  'bp_activity_remove_screen_notifications' );
+
+/**
+ * Reset the logged-in user's new mentions data when he visits his mentions screen
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ * @uses bp_activity_clear_new_mentions()
+ * @uses bp_is_my_profile()
+ */
+function bp_activity_reset_my_new_mentions() {
+	if ( bp_is_my_profile() )
+		bp_activity_clear_new_mentions( bp_loggedin_user_id() );
+}
+add_action( 'bp_activity_screen_mentions', 'bp_activity_reset_my_new_mentions' );
+
+function bp_activity_screen_single_activity_permalink() {
+	global $bp;
+
+	// No displayed user or not viewing activity component
+	if ( !bp_is_activity_component() )
+		return false;
+
+	if ( empty( $bp->current_action ) || !is_numeric( $bp->current_action ) )
+		return false;
+
+	// Get the activity details
+	$activity = bp_activity_get_specific( array( 'activity_ids' => bp_current_action(), 'show_hidden' => true ) );
+
+	// 404 if activity does not exist
+	if ( empty( $activity['activities'][0] ) || bp_action_variables() ) {
+		bp_do_404();
+		return;
+
+	} else {
+		$activity = $activity['activities'][0];
+	}
+
+	// Default access is true
+	$has_access = true;
+
+	// If activity is from a group, do an extra cap check
+	if ( isset( $bp->groups->id ) && $activity->component == $bp->groups->id ) {
+
+		// Activity is from a group, but groups is currently disabled
+		if ( !bp_is_active( 'groups') ) {
+			bp_do_404();
+			return;
+		}
+
+		// Check to see if the group is not public, if so, check the
+		// user has access to see this activity
+		if ( $group = groups_get_group( array( 'group_id' => $activity->item_id ) ) ) {
+
+			// Group is not public
+			if ( 'public' != $group->status ) {
+
+				// User is not a member of group
+				if ( !groups_is_user_member( $bp->loggedin_user->id, $group->id ) ) {
+					$has_access = false;
+				}
+			}
+		}
+	}
+
+	// Allow access to be filtered
+	$has_access = apply_filters_ref_array( 'bp_activity_permalink_access', array( $has_access, &$activity ) );
+
+	// Allow additional code execution
+	do_action( 'bp_activity_screen_single_activity_permalink', $activity, $has_access );
+
+	// Access is specifically disallowed
+	if ( false === $has_access ) {
+
+		// User feedback
+		bp_core_add_message( __( 'You do not have access to this activity.', 'buddypress' ), 'error' );
+
+		// Redirect based on logged in status
+		is_user_logged_in() ?
+			bp_core_redirect( $bp->loggedin_user->domain ) :
+			bp_core_redirect( site_url( 'wp-login.php?redirect_to=' . esc_url( bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $bp->current_action ) ) );
+	}
+
+	bp_core_load_template( apply_filters( 'bp_activity_template_profile_activity_permalink', 'members/single/activity/permalink' ) );
+}
+add_action( 'bp_screens', 'bp_activity_screen_single_activity_permalink' );
+
+function bp_activity_screen_notification_settings() {
+	global $bp;
+
+	if ( !$mention = bp_get_user_meta( $bp->displayed_user->id, 'notification_activity_new_mention', true ) )
+		$mention = 'yes';
+
+	if ( !$reply = bp_get_user_meta( $bp->displayed_user->id, 'notification_activity_new_reply', true ) )
+		$reply = 'yes'; ?>
+
+	<table class="notification-settings" id="activity-notification-settings">
+		<thead>
+			<tr>
+				<th class="icon">&nbsp;</th>
+				<th class="title"><?php _e( 'Activity', 'buddypress' ) ?></th>
+				<th class="yes"><?php _e( 'Yes', 'buddypress' ) ?></th>
+				<th class="no"><?php _e( 'No', 'buddypress' )?></th>
+			</tr>
+		</thead>
+
+		<tbody>
+			<tr id="activity-notification-settings-mentions">
+				<td>&nbsp;</td>
+				<td><?php printf( __( 'A member mentions you in an update using "@%s"', 'buddypress' ), bp_core_get_username( $bp->displayed_user->id, $bp->displayed_user->userdata->user_nicename, $bp->displayed_user->userdata->user_login ) ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_activity_new_mention]" value="yes" <?php checked( $mention, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_activity_new_mention]" value="no" <?php checked( $mention, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="activity-notification-settings-replies">
+				<td>&nbsp;</td>
+				<td><?php _e( "A member replies to an update or comment you've posted", 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_activity_new_reply]" value="yes" <?php checked( $reply, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_activity_new_reply]" value="no" <?php checked( $reply, 'no', true ) ?>/></td>
+			</tr>
+
+			<?php do_action( 'bp_activity_screen_notification_settings' ) ?>
+		</tbody>
+	</table>
+
+<?php
+}
+add_action( 'bp_notification_settings', 'bp_activity_screen_notification_settings', 1 );
+
+?>
Index: /tags/1.5-beta-3/bp-activity/bp-activity-template.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/bp-activity-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/bp-activity-template.php	(revision 5057)
@@ -0,0 +1,1592 @@
+<?php
+/**
+ * BuddyPress Activity Template Functions
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the activity component slug
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ *
+ * @uses bp_get_activity_slug()
+ */
+function bp_activity_slug() {
+	echo bp_get_activity_slug();
+}
+	/**
+	 * Return the activity component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 */
+	function bp_get_activity_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_activity_slug', $bp->activity->slug );
+	}
+
+/**
+ * Output the activity component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ *
+ * @uses bp_get_activity_root_slug()
+ */
+function bp_activity_root_slug() {
+	echo bp_get_activity_root_slug();
+}
+	/**
+	 * Return the activity component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 */
+	function bp_get_activity_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_activity_root_slug', $bp->activity->root_slug );
+	}
+
+/**
+ * Output member directory permalink
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ * @uses bp_get_activity_directory_permalink()
+ */
+function bp_activity_directory_permalink() {
+	echo bp_get_activity_directory_permalink();
+}
+	/**
+	 * Return member directory permalink
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 * @uses apply_filters()
+	 * @uses traisingslashit()
+	 * @uses bp_get_root_domain()
+	 * @uses bp_get_activity_root_slug()
+	 * @return string
+	 */
+	function bp_get_activity_directory_permalink() {
+		return apply_filters( 'bp_get_activity_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_activity_root_slug() ) );
+	}
+
+/**
+ * The main activity template loop
+ *
+ * This is responsible for loading a group of activity items and displaying them
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+class BP_Activity_Template {
+	var $current_activity = -1;
+	var $activity_count;
+	var $total_activity_count;
+	var $activities;
+	var $activity;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+
+	var $full_name;
+
+	function bp_activity_template( $page, $per_page, $max, $include, $sort, $filter, $search_terms, $display_comments, $show_hidden, $exclude = false, $in = false ) {
+		$this->__construct( $page, $per_page, $max, $include, $sort, $filter, $search_terms, $display_comments, $show_hidden, $exclude, $in );
+	}
+
+	function __construct( $page, $per_page, $max, $include, $sort, $filter, $search_terms, $display_comments, $show_hidden, $exclude = false, $in = false ) {
+		global $bp;
+
+		$this->pag_page = isset( $_REQUEST['acpage'] ) ? intval( $_REQUEST['acpage'] ) : $page;
+		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		// Check if blog/forum replies are disabled
+		$this->disable_blogforum_replies = isset( $bp->site_options['bp-disable-blogforum-comments'] ) ? $bp->site_options['bp-disable-blogforum-comments'] : false;
+
+		// Get an array of the logged in user's favorite activities
+		$this->my_favs = maybe_unserialize( bp_get_user_meta( $bp->loggedin_user->id, 'bp_favorite_activities', true ) );
+
+		// Fetch specific activity items based on ID's
+		if ( !empty( $include ) )
+			$this->activities = bp_activity_get_specific( array( 'activity_ids' => explode( ',', $include ), 'max' => $max, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'sort' => $sort, 'display_comments' => $display_comments, 'show_hidden' => $show_hidden ) );
+		// Fetch all activity items
+		else
+			$this->activities = bp_activity_get( array( 'display_comments' => $display_comments, 'max' => $max, 'per_page' => $this->pag_num, 'page' => $this->pag_page, 'sort' => $sort, 'search_terms' => $search_terms, 'filter' => $filter, 'show_hidden' => $show_hidden, 'exclude' => $exclude, 'in' => $in ) );
+
+		if ( !$max || $max >= (int)$this->activities['total'] )
+			$this->total_activity_count = (int)$this->activities['total'];
+		else
+			$this->total_activity_count = (int)$max;
+
+		$this->activities = $this->activities['activities'];
+
+		if ( $max ) {
+			if ( $max >= count($this->activities) ) {
+				$this->activity_count = count( $this->activities );
+			} else {
+				$this->activity_count = (int)$max;
+			}
+		} else {
+			$this->activity_count = count( $this->activities );
+		}
+
+		$this->full_name = $bp->displayed_user->fullname;
+
+		// Fetch parent content for activity comments so we do not have to query in the loop
+		foreach ( (array)$this->activities as $activity ) {
+			if ( 'activity_comment' != $activity->type )
+				continue;
+
+			$parent_ids[] = $activity->item_id;
+		}
+
+		if ( !empty( $parent_ids ) )
+			$activity_parents = bp_activity_get_specific( array( 'activity_ids' => $parent_ids ) );
+
+		if ( !empty( $activity_parents['activities'] ) ) {
+			foreach( $activity_parents['activities'] as $parent )
+				$this->activity_parents[$parent->id] = $parent;
+
+			unset( $activity_parents );
+		}
+
+		if ( (int)$this->total_activity_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( 'acpage', '%#%' ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_activity_count / (int)$this->pag_num ),
+				'current'   => (int)$this->pag_page,
+				'prev_text' => _x( '&larr;', 'Activity pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Activity pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+		}
+	}
+
+	function has_activities() {
+		if ( $this->activity_count )
+			return true;
+
+		return false;
+	}
+
+	function next_activity() {
+		$this->current_activity++;
+		$this->activity = $this->activities[$this->current_activity];
+
+		return $this->activity;
+	}
+
+	function rewind_activities() {
+		$this->current_activity = -1;
+		if ( $this->activity_count > 0 ) {
+			$this->activity = $this->activities[0];
+		}
+	}
+
+	function user_activities() {
+		if ( $this->current_activity + 1 < $this->activity_count ) {
+			return true;
+		} elseif ( $this->current_activity + 1 == $this->activity_count ) {
+			do_action('activity_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_activities();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_activity() {
+		global $activity;
+
+		$this->in_the_loop = true;
+		$this->activity = $this->next_activity();
+
+		if ( is_array( $this->activity ) )
+			$this->activity = (object) $this->activity;
+
+		if ( $this->current_activity == 0 ) // loop has just started
+			do_action('activity_loop_start');
+	}
+}
+
+/**
+ * bp_has_activities()
+ *
+ * Initializes the activity loop.
+ *
+ * Based on the $args passed, bp_has_activities() populates the $activities_template global.
+ *
+ * @global BP_Activity_Template $activities_template
+ * @global object $bp Global BuddyPress settings object
+ * @param mixed $args Arguments for limiting the contents of the activity loop. Can be passed as an associative array or as a URL argument string
+ * @return bool Returns true when activities are found
+ */
+function bp_has_activities( $args = '' ) {
+	global $activities_template, $bp;
+
+	/***
+	 * Set the defaults based on the current page. Any of these will be overridden
+	 * if arguments are directly passed into the loop. Custom plugins should always
+	 * pass their parameters directly to the loop.
+	 */
+	$user_id     = false;
+	$include     = false;
+	$exclude     = false;
+	$in          = false;
+	$show_hidden = false;
+	$object      = false;
+	$primary_id  = false;
+
+	// User filtering
+	if ( !empty( $bp->displayed_user->id ) )
+		$user_id = $bp->displayed_user->id;
+
+	// Group filtering
+	if ( !empty( $bp->groups->current_group ) ) {
+		$object = $bp->groups->id;
+		$primary_id = $bp->groups->current_group->id;
+
+		if ( 'public' != $bp->groups->current_group->status && ( groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) || $bp->loggedin_user->is_super_admin ) )
+			$show_hidden = true;
+	}
+
+	// The default scope should recognize custom slugs
+	if ( array_key_exists( $bp->current_action, (array)$bp->loaded_components ) ) {
+		$scope = $bp->loaded_components[$bp->current_action];
+	}
+	else
+		$scope = bp_current_action();
+
+	// Support for permalinks on single item pages: /groups/my-group/activity/124/
+	if ( bp_is_current_action( bp_get_activity_slug() ) )
+		$include = bp_action_variable( 0 );
+
+	// Note: any params used for filtering can be a single value, or multiple values comma separated.
+	$defaults = array(
+		'display_comments' => 'threaded',   // false for none, stream/threaded - show comments in the stream or threaded under items
+		'include'          => $include,     // pass an activity_id or string of IDs comma-separated
+		'exclude'          => $exclude,     // pass an activity_id or string of IDs comma-separated
+		'in'               => $in,          // comma-separated list or array of activity IDs among which to search
+		'sort'             => 'DESC',       // sort DESC or ASC
+		'page'             => 1,            // which page to load
+		'per_page'         => 20,           // number of items per page
+		'max'              => false,        // max number to return
+		'show_hidden'      => $show_hidden, // Show activity items that are hidden site-wide?
+
+		// Scope - pre-built activity filters for a user (friends/groups/favorites/mentions)
+		'scope'            => $scope,
+
+		// Filtering
+		'user_id'          => $user_id,     // user_id to filter on
+		'object'           => $object,      // object to filter on e.g. groups, profile, status, friends
+		'action'           => false,        // action to filter on e.g. activity_update, new_forum_post, profile_updated
+		'primary_id'       => $primary_id,  // object ID to filter on e.g. a group_id or forum_id or blog_id etc.
+		'secondary_id'     => false,        // secondary object ID to filter on e.g. a post_id
+
+		// Searching
+		'search_terms'     => false         // specify terms to search on
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	// If you have passed a "scope" then this will override any filters you have passed.
+	if ( 'just-me' == $scope || 'friends' == $scope || 'groups' == $scope || 'favorites' == $scope || 'mentions' == $scope ) {
+		if ( 'just-me' == $scope )
+			$display_comments = 'stream';
+
+		// determine which user_id applies
+		if ( empty( $user_id ) )
+			$user_id = ( !empty( $bp->displayed_user->id ) ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+		// are we displaying user specific activity?
+		if ( is_numeric( $user_id ) ) {
+			$show_hidden = ( $user_id == $bp->loggedin_user->id && $scope != 'friends' ) ? 1 : 0;
+
+			switch ( $scope ) {
+				case 'friends':
+					if ( bp_is_active( 'friends' ) )
+						$friends = friends_get_friend_user_ids( $user_id );
+						if ( empty( $friends ) )
+							return false;
+
+						$user_id = implode( ',', (array)$friends );
+					break;
+				case 'groups':
+					if ( bp_is_active( 'groups' ) ) {
+						$groups = groups_get_user_groups( $user_id );
+						if ( empty( $groups['groups'] ) )
+							return false;
+
+						$object = $bp->groups->id;
+						$primary_id = implode( ',', (array)$groups['groups'] );
+
+						$user_id = 0;
+					}
+					break;
+				case 'favorites':
+					$favs = bp_activity_get_user_favorites( $user_id );
+					if ( empty( $favs ) )
+						return false;
+
+					$include          = implode( ',', (array)$favs );
+					$display_comments = true;
+					break;
+				case 'mentions':
+					$user_nicename    = ( !empty( $bp->displayed_user->id ) ) ? $bp->displayed_user->userdata->user_nicename : $bp->loggedin_user->userdata->user_nicename;
+					$user_login       = ( !empty( $bp->displayed_user->id ) ) ? $bp->displayed_user->userdata->user_login : $bp->loggedin_user->userdata->user_login;
+					$search_terms     = '@' . bp_core_get_username( $user_id, $user_nicename, $user_login ) . '<'; // Start search at @ symbol and stop search at closing tag delimiter.
+					$display_comments = 'stream';
+					$user_id = 0;
+					break;
+			}
+		}
+	}
+
+	// Do not exceed the maximum per page
+	if ( !empty( $max ) && ( (int)$per_page > (int)$max ) )
+		$per_page = $max;
+
+	// Support for basic filters in earlier BP versions.
+	if ( isset( $_GET['afilter'] ) )
+		$filter = array( 'object' => $_GET['afilter'] );
+	else if ( !empty( $user_id ) || !empty( $object ) || !empty( $action ) || !empty( $primary_id ) || !empty( $secondary_id ) )
+		$filter = array( 'user_id' => $user_id, 'object' => $object, 'action' => $action, 'primary_id' => $primary_id, 'secondary_id' => $secondary_id );
+	else
+		$filter = false;
+
+	$activities_template = new BP_Activity_Template( $page, $per_page, $max, $include, $sort, $filter, $search_terms, $display_comments, $show_hidden, $exclude, $in );
+
+	return apply_filters( 'bp_has_activities', $activities_template->has_activities(), $activities_template );
+}
+
+function bp_activities() {
+	global $activities_template;
+	return $activities_template->user_activities();
+}
+
+function bp_the_activity() {
+	global $activities_template;
+	return $activities_template->the_activity();
+}
+
+function bp_activity_pagination_count() {
+	echo bp_get_activity_pagination_count();
+}
+	function bp_get_activity_pagination_count() {
+		global $bp, $activities_template;
+
+		$start_num = intval( ( $activities_template->pag_page - 1 ) * $activities_template->pag_num ) + 1;
+		$from_num  = bp_core_number_format( $start_num );
+		$to_num    = bp_core_number_format( ( $start_num + ( $activities_template->pag_num - 1 ) > $activities_template->total_activity_count ) ? $activities_template->total_activity_count : $start_num + ( $activities_template->pag_num - 1 ) );
+		$total     = bp_core_number_format( $activities_template->total_activity_count );
+
+		return sprintf( __( 'Viewing item %1$s to %2$s (of %3$s items)', 'buddypress' ), $from_num, $to_num, $total );
+	}
+
+function bp_activity_pagination_links() {
+	echo bp_get_activity_pagination_links();
+}
+	function bp_get_activity_pagination_links() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_pagination_links', $activities_template->pag_links );
+	}
+
+/**
+ * Returns true when there are more activity items to be shown than currently appear
+ *
+ * @package BuddyPress Activity
+ * @since 1.5
+ *
+ * @global $activities_template The activity data loop object created in bp_has_activities()
+ */
+function bp_activity_has_more_items() {
+	global $activities_template;
+
+	$remaining_pages = floor( ( $activities_template->total_activity_count - 1 ) / ( $activities_template->pag_num * $activities_template->pag_page ) );
+	$has_more_items  = (int)$remaining_pages ? true : false;
+
+	return apply_filters( 'bp_activity_has_more_items', $has_more_items );
+}
+
+function bp_activity_count() {
+	echo bp_get_activity_count();
+}
+	function bp_get_activity_count() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_count', (int)$activities_template->activity_count );
+	}
+
+function bp_activity_per_page() {
+	echo bp_get_activity_per_page();
+}
+	function bp_get_activity_per_page() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_per_page', (int)$activities_template->pag_num );
+	}
+
+function bp_activities_title() {
+	global $bp_activity_title;
+
+	echo bp_get_activities_title();
+}
+	function bp_get_activities_title() {
+		global $bp_activity_title;
+
+		return apply_filters( 'bp_get_activities_title', $bp_activity_title );
+	}
+
+function bp_activities_no_activity() {
+	global $bp_activity_no_activity;
+
+	echo bp_get_activities_no_activity();
+}
+	function bp_get_activities_no_activity() {
+		global $bp_activity_no_activity;
+
+		return apply_filters( 'bp_get_activities_no_activity', $bp_activity_no_activity );
+	}
+
+function bp_activity_id() {
+	echo bp_get_activity_id();
+}
+	function bp_get_activity_id() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_id', $activities_template->activity->id );
+	}
+
+function bp_activity_item_id() {
+	echo bp_get_activity_item_id();
+}
+	function bp_get_activity_item_id() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_item_id', $activities_template->activity->item_id );
+	}
+
+function bp_activity_secondary_item_id() {
+	echo bp_get_activity_secondary_item_id();
+}
+	function bp_get_activity_secondary_item_id() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_secondary_item_id', $activities_template->activity->secondary_item_id );
+	}
+
+function bp_activity_date_recorded() {
+	echo bp_get_activity_date_recorded();
+}
+	function bp_get_activity_date_recorded() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_date_recorded', $activities_template->activity->date_recorded );
+	}
+
+function bp_activity_object_name() {
+	echo bp_get_activity_object_name();
+}
+	function bp_get_activity_object_name() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_object_name', $activities_template->activity->component );
+	}
+
+function bp_activity_type() {
+	echo bp_get_activity_type();
+}
+	function bp_get_activity_type() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_type', $activities_template->activity->type );
+	}
+	function bp_activity_action_name() { echo bp_activity_type(); }
+	function bp_get_activity_action_name() { return bp_get_activity_type(); }
+
+function bp_activity_user_id() {
+	echo bp_get_activity_user_id();
+}
+	function bp_get_activity_user_id() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_user_id', $activities_template->activity->user_id );
+	}
+
+function bp_activity_user_link() {
+	echo bp_get_activity_user_link();
+}
+	function bp_get_activity_user_link() {
+		global $activities_template;
+
+		if ( empty( $activities_template->activity->user_id ) )
+			$link = $activities_template->activity->primary_link;
+		else
+			$link = bp_core_get_user_domain( $activities_template->activity->user_id, $activities_template->activity->user_nicename, $activities_template->activity->user_login );
+
+		return apply_filters( 'bp_get_activity_user_link', $link );
+	}
+
+/**
+ * bp_activity_avatar( $args )
+ *
+ * Output the avatar of the user that performed the action
+ *
+ * @param array $args
+ */
+function bp_activity_avatar( $args = '' ) {
+	echo bp_get_activity_avatar( $args );
+}
+	/**
+	 * bp_get_activity_avatar( $args )
+	 *
+	 * Return the avatar of the user that performed the action
+	 *
+	 * @global array $bp
+	 * @global object $activities_template
+	 * @param array $args optional
+	 * @return string
+	 */
+	function bp_get_activity_avatar( $args = '' ) {
+		global $activities_template, $bp;
+
+		// On activity permalink pages, default to the full-size avatar
+		$type_default = bp_is_single_activity() ? 'full' : 'thumb';
+
+		$defaults = array(
+			'alt'     => __( 'Profile picture of %s', 'buddypress' ),
+			'class'   => 'avatar',
+			'email'   => false,
+			'type'    => $type_default,
+			'user_id' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		if ( !isset( $height ) && !isset( $width ) ) {
+			// Backpat
+			if ( isset( $bp->avatar->full->height ) || isset( $bp->avatar->thumb->height ) ) {
+				$height = ( 'full' == $type ) ? $bp->avatar->full->height : $bp->avatar->thumb->height;
+			} else {
+				$height = 20;
+			}
+
+			// Backpat
+			if ( isset( $bp->avatar->full->width ) || isset( $bp->avatar->thumb->width ) ) {
+				$width = ( 'full' == $type ) ? $bp->avatar->full->width : $bp->avatar->thumb->width;
+			} else {
+				$width = 20;
+			}
+
+		}
+
+		// Within the loop, we the current activity should be set first to the
+		// current_comment, if available
+		$current_activity_item = isset( $activities_template->activity->current_comment ) ? $activities_template->activity->current_comment : $activities_template->activity;
+
+		// Primary activity avatar is always a user, but can be modified via a filter
+		$object  = apply_filters( 'bp_get_activity_avatar_object_' . $current_activity_item->component, 'user' );
+		$item_id = $user_id ? $user_id : $current_activity_item->user_id;
+		$item_id = apply_filters( 'bp_get_activity_avatar_item_id', $item_id );
+
+		// If this is a user object pass the users' email address for Gravatar so we don't have to refetch it.
+		if ( 'user' == $object && empty( $user_id ) && empty( $email ) && isset( $activities_template->activity->user_email ) )
+			$email = $current_activity_item->user_email;
+
+		return apply_filters( 'bp_get_activity_avatar', bp_core_fetch_avatar( array( 'item_id' => $item_id, 'object' => $object, 'type' => $type, 'alt' => $alt, 'class' => $class, 'width' => $width, 'height' => $height, 'email' => $email ) ) );
+	}
+
+/**
+ * bp_activity_secondary_avatar( $args )
+ *
+ * Output the avatar of the object that action was performed on
+ *
+ * @param array $args optional
+ */
+function bp_activity_secondary_avatar( $args = '' ) {
+	echo bp_get_activity_secondary_avatar( $args );
+}
+	/**
+	 * bp_get_activity_secondary_avatar( $args )
+	 *
+	 * Return the avatar of the object that action was performed on
+	 *
+	 * @global array $bp
+	 * @global object $activities_template
+	 * @param array $args optional
+	 * @return string
+	 */
+	function bp_get_activity_secondary_avatar( $args = '' ) {
+		global $bp, $activities_template;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => 20,
+			'height' => 20,
+			'class'  => 'avatar',
+			'email'  => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Set item_id and object (default to user)
+		switch ( $activities_template->activity->component ) {
+			case 'groups' :
+				$object = 'group';
+				$item_id = $activities_template->activity->item_id;
+
+				if ( empty( $alt ) )
+					$alt = __( 'Group logo of %s', 'buddypress' );
+
+				break;
+			case 'blogs' :
+				$object = 'blog';
+				$item_id = $activities_template->activity->item_id;
+
+				if ( !$alt )
+					$alt = sprintf( __( 'Site authored by %s', 'buddypress' ), get_blog_option( $item_id, 'blogname' ) );
+
+				break;
+			case 'friends' :
+				$object  = 'user';
+				$item_id = $activities_template->activity->secondary_item_id;
+
+				if ( empty( $alt ) )
+					$alt = __( 'Profile picture of %s', 'buddypress' );
+
+				break;
+			default :
+				$object  = 'user';
+				$item_id = $activities_template->activity->user_id;
+				$email = $activities_template->activity->user_email;
+
+				if ( !$alt )
+					$alt = __( 'Profile picture of %s', 'buddypress' );
+
+				break;
+		}
+
+		// Allow object and item_id to be filtered
+		$object  = apply_filters( 'bp_get_activity_secondary_avatar_object_' . $activities_template->activity->component, $object );
+		$item_id = apply_filters( 'bp_get_activity_secondary_avatar_item_id', $item_id );
+
+		// If we have no item_id or object, there is no avatar to display
+		if ( empty( $item_id ) || empty( $object ) )
+			return false;
+
+		return apply_filters( 'bp_get_activity_secondary_avatar', bp_core_fetch_avatar( array( 'item_id' => $item_id, 'object' => $object, 'type' => $type, 'alt' => $alt, 'class' => $class, 'width' => $width, 'height' => $height, 'email' => $email ) ) );
+	}
+
+function bp_activity_action() {
+	echo bp_get_activity_action();
+}
+	function bp_get_activity_action() {
+		global $activities_template;
+
+		$action = $activities_template->activity->action;
+		$action = apply_filters_ref_array( 'bp_get_activity_action_pre_meta', array( $action, &$activities_template->activity ) );
+
+		if ( !empty( $action ) )
+			$action = bp_insert_activity_meta( $action );
+
+		return apply_filters_ref_array( 'bp_get_activity_action', array( $action, &$activities_template->activity ) );
+	}
+
+function bp_activity_content_body() {
+	echo bp_get_activity_content_body();
+}
+	function bp_get_activity_content_body() {
+		global $activities_template;
+
+		// Backwards compatibility if action is not being used
+		if ( empty( $activities_template->activity->action ) && !empty( $activities_template->activity->content ) )
+			$activities_template->activity->content = bp_insert_activity_meta( $activities_template->activity->content );
+
+		return apply_filters_ref_array( 'bp_get_activity_content_body', array( $activities_template->activity->content, &$activities_template->activity ) );
+	}
+
+	function bp_activity_has_content() {
+		global $activities_template;
+
+		if ( !empty( $activities_template->activity->content ) )
+			return true;
+
+		return false;
+	}
+
+function bp_activity_content() {
+	echo bp_get_activity_content();
+}
+	function bp_get_activity_content() {
+		global $activities_template;
+
+		/***
+		 * If you want to filter activity update content, please use
+		 * the filter 'bp_get_activity_content_body'
+		 *
+		 * This function is mainly for backwards comptibility.
+		 */
+
+		$content = bp_get_activity_action() . ' ' . bp_get_activity_content_body();
+		return apply_filters( 'bp_get_activity_content', $content );
+	}
+
+	function bp_insert_activity_meta( $content ) {
+		global $activities_template, $bp;
+
+		// Strip any legacy time since placeholders from BP 1.0-1.1
+		$content = str_replace( '<span class="time-since">%s</span>', '', $content );
+
+		// Insert the time since.
+		$time_since = apply_filters_ref_array( 'bp_activity_time_since', array( '<span class="time-since">' . bp_core_time_since( $activities_template->activity->date_recorded ) . '</span>', &$activities_template->activity ) );
+
+		// Insert the permalink
+		if ( !bp_is_single_activity() )
+			$content = apply_filters_ref_array( 'bp_activity_permalink', array( sprintf( '%1$s <a href="%2$s" class="view activity-time-since" title="%3$s">%4$s</a>', $content, bp_activity_get_permalink( $activities_template->activity->id, $activities_template->activity ), esc_attr__( 'View Discussion', 'buddypress' ), $time_since ), &$activities_template->activity ) );
+		else
+			$content .= str_pad( $time_since, strlen( $time_since ) + 2, ' ', STR_PAD_BOTH );
+
+		return apply_filters( 'bp_insert_activity_meta', $content );
+	}
+
+function bp_activity_user_can_delete( $activity = false ) {
+	global $activities_template, $bp;
+
+	if ( !$activity )
+		$activity = $activities_template->activity;
+
+	if ( isset( $activity->current_comment ) )
+		$activity = $activity->current_comment;
+
+	$can_delete = false;
+
+	if ( $bp->loggedin_user->is_super_admin )
+		$can_delete = true;
+
+	if ( $activity->user_id == $bp->loggedin_user->id )
+		$can_delete = true;
+
+	if ( $bp->is_item_admin && $bp->is_single_item )
+		$can_delete = true;
+
+	return apply_filters( 'bp_activity_user_can_delete', $can_delete );
+}
+
+function bp_activity_parent_content( $args = '' ) {
+	echo bp_get_activity_parent_content($args);
+}
+	function bp_get_activity_parent_content( $args = '' ) {
+		global $bp, $activities_template;
+
+		$defaults = array(
+			'hide_user' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Get the ID of the parent activity content
+		if ( !$parent_id = $activities_template->activity->item_id )
+			return false;
+
+		// Get the content of the parent
+		if ( empty( $activities_template->activity_parents[$parent_id] ) )
+			return false;
+
+		if ( empty( $activities_template->activity_parents[$parent_id]->content ) )
+			$content = $activities_template->activity_parents[$parent_id]->action;
+		else
+			$content = $activities_template->activity_parents[$parent_id]->action . ' ' . $activities_template->activity_parents[$parent_id]->content;
+
+		// Remove the time since content for backwards compatibility
+		$content = str_replace( '<span class="time-since">%s</span>', '', $content );
+
+		// Remove images
+		$content = preg_replace( '/<img[^>]*>/Ui', '', $content );
+
+		return apply_filters( 'bp_get_activity_parent_content', $content );
+	}
+
+function bp_activity_is_favorite() {
+	echo bp_get_activity_is_favorite();
+}
+	function bp_get_activity_is_favorite() {
+		global $bp, $activities_template;
+
+ 		return apply_filters( 'bp_get_activity_is_favorite', in_array( $activities_template->activity->id, (array)$activities_template->my_favs ) );
+	}
+
+/**
+ * Echoes the comment markup for an activity item
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ *
+ * @param str $args Unused
+ */
+function bp_activity_comments( $args = '' ) {
+	echo bp_activity_get_comments( $args );
+}
+	/**
+	 * Gets the comment markup for an activity item
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 *
+	 * @param str $args Unused. Appears to be left over from an earlier implementation.
+	 * @todo Given that checks for children already happen in bp_activity_recurse_comments(),
+	 *    this function can probably be streamlined or removed.
+	 */
+	function bp_activity_get_comments( $args = '' ) {
+		global $activities_template, $bp;
+
+		if ( !isset( $activities_template->activity->children ) || !$activities_template->activity->children )
+			return false;
+
+		bp_activity_recurse_comments( $activities_template->activity );
+	}
+
+		/**
+		 * Loops through a level of activity comments and loads the template for each
+		 *
+		 * Note: The recursion itself used to happen entirely in this function. Now it is
+		 * split between here and the comment.php template.
+		 *
+		 * @package BuddyPress
+		 * @subpackage Activity Template
+		 *
+		 * @param obj $comment The activity object currently being recursed
+		 */
+		function bp_activity_recurse_comments( $comment ) {
+			global $activities_template, $bp, $counter;
+
+			if ( !$comment )
+				return false;
+
+			if ( empty( $comment->children ) )
+				return false;
+
+			echo '<ul>';
+			foreach ( (array)$comment->children as $comment_child ) {
+				// Put the comment into the global so it's available to filters
+				$activities_template->activity->current_comment = $comment_child;
+
+				$template = locate_template( 'activity/comment.php', false, false );
+
+				// Backward compatibility. In older versions of BP, the markup was
+				// generated in the PHP instead of a template. This ensures that
+				// older themes (which are not children of bp-default and won't
+				// have the new template) will still work.
+				if ( !$template ) {
+					$template = BP_PLUGIN_DIR . '/bp-themes/bp-default/activity/comment.php';
+				}
+
+				load_template( $template, false );
+
+				unset( $activities_template->activity->current_comment );
+			}
+			echo '</ul>';
+		}
+
+/**
+ * Utility function that returns the comment currently being recursed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ *
+ * @return obj $current_comment The activity comment currently being displayed
+ */
+function bp_activity_current_comment() {
+	global $activities_template;
+
+	$current_comment = !empty( $activities_template->activity->current_comment ) ? $activities_template->activity->current_comment : false;
+
+	return apply_filters( 'bp_activity_current_comment', $current_comment );
+}
+
+
+/**
+ * Echoes the id of the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_id() {
+	echo bp_get_activity_comment_id();
+}
+	/**
+	 * Gets the id of the activity comment currently being displayed
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return int $comment_id The id of the activity comment currently being displayed
+	 */
+	function bp_get_activity_comment_id() {
+		global $activities_template;
+
+		$comment_id = isset( $activities_template->activity->current_comment->id ) ? $activities_template->activity->current_comment->id : false;
+
+		return apply_filters( 'bp_activity_comment_id', $comment_id );
+	}
+
+/**
+ * Echoes the user_id of the author of the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_user_id() {
+	echo bp_get_activity_comment_user_id();
+}
+	/**
+	 * Gets the user_id of the author of the activity comment currently being displayed
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return int $user_id The user_id of the author of the displayed activity comment
+	 */
+	function bp_get_activity_comment_user_id() {
+		global $activities_template;
+
+		$user_id = isset( $activities_template->activity->current_comment->user_id ) ? $activities_template->activity->current_comment->user_id : false;
+
+		return apply_filters( 'bp_activity_comment_user_id', $user_id );
+	}
+
+/**
+ * Echoes the author link for the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_user_link() {
+	echo bp_get_activity_comment_user_link();
+}
+	/**
+	 * Gets the author link for the activity comment currently being displayed
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return str $user_link The URL of the activity comment author's profile
+	 */
+	function bp_get_activity_comment_user_link() {
+		$user_link = bp_core_get_user_domain( bp_get_activity_comment_user_id() );
+
+		return apply_filters( 'bp_activity_comment_user_link', $user_link );
+	}
+
+/**
+ * Echoes the author name for the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_name() {
+	echo bp_get_activity_comment_name();
+}
+	/**
+	 * Gets the author name for the activity comment currently being displayed
+	 *
+	 * The use of the bp_acomment_name filter is deprecated. Please use bp_activity_comment_name
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return str $name The full name of the activity comment author
+	 */
+	function bp_get_activity_comment_name() {
+		global $activities_template;
+
+		$name = apply_filters( 'bp_acomment_name', $activities_template->activity->current_comment->user_fullname, $activities_template->activity->current_comment ); // backward compatibility
+
+		return apply_filters( 'bp_activity_comment_name', $name );
+	}
+
+/**
+ * Echoes the date_recorded of the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_date_recorded() {
+	echo bp_get_activity_comment_date_recorded();
+}
+	/**
+	 * Gets the date_recorded for the activity comment currently being displayed
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return str $date_recorded Time since the activity was recorded, of the form "%s ago"
+	 */
+	function bp_get_activity_comment_date_recorded() {
+		global $activities_template;
+
+		if ( empty( $activities_template->activity->current_comment->date_recorded ) )
+			return false;
+
+		$date_recorded = bp_core_time_since( $activities_template->activity->current_comment->date_recorded );
+
+		return apply_filters( 'bp_activity_comment_date_recorded', $date_recorded );
+	}
+
+/**
+ * Echoes the 'delete' URL for the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_delete_link() {
+	echo bp_get_activity_comment_delete_link();
+}
+	/**
+	 * Gets the 'delete' URL for the activity comment currently being displayed
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return str $link The nonced URL for deleting the current activity comment
+	 */
+	function bp_get_activity_comment_delete_link() {
+		global $bp;
+
+		$link = wp_nonce_url( bp_get_root_domain() . '/' . bp_get_activity_slug() . '/delete/?cid=' . bp_get_activity_comment_id(), 'bp_activity_delete_link' );
+
+		return apply_filters( 'bp_activity_comment_delete_link', $link );
+	}
+
+/**
+ * Echoes the content of the activity comment currently being displayed
+ *
+ * @package BuddyPress
+ * @subpackage Activity Template
+ * @since 1.5
+ */
+function bp_activity_comment_content() {
+	echo bp_get_activity_comment_content();
+}
+	/**
+	 * Gets the content of the activity comment currently being displayed
+	 *
+	 * The content is run through two filters. bp_get_activity_content will apply all filters
+	 * applied to activity items in general. Use bp_activity_comment_content to modify the
+	 * content of activity comments only.
+	 *
+	 * @package BuddyPress
+	 * @subpackage Activity Template
+	 * @since 1.5
+	 *
+	 * @return str $content The content of the current activity comment
+	 */
+	function bp_get_activity_comment_content() {
+		global $activities_template;
+
+		$content = apply_filters( 'bp_get_activity_content', $activities_template->activity->current_comment->content );
+
+		return apply_filters( 'bp_activity_comment_content', $content );
+	}
+
+function bp_activity_comment_count() {
+	echo bp_activity_get_comment_count();
+}
+	function bp_activity_get_comment_count( $args = '' ) {
+		global $activities_template, $bp;
+
+		if ( !isset( $activities_template->activity->children ) || !$activities_template->activity->children )
+			return 0;
+
+		$count = bp_activity_recurse_comment_count( $activities_template->activity );
+
+		return apply_filters( 'bp_activity_get_comment_count', (int)$count );
+	}
+		function bp_activity_recurse_comment_count( $comment, $count = 0 ) {
+			global $activities_template, $bp;
+
+			if ( !$comment->children )
+				return $count;
+
+			foreach ( (array)$comment->children as $comment ) {
+				$count++;
+				$count = bp_activity_recurse_comment_count( $comment, $count );
+			}
+
+			return $count;
+		}
+
+function bp_activity_comment_link() {
+	echo bp_get_activity_comment_link();
+}
+	function bp_get_activity_comment_link() {
+		global $activities_template;
+		return apply_filters( 'bp_get_activity_comment_link', '?ac=' . $activities_template->activity->id . '/#ac-form-' . $activities_template->activity->id );
+	}
+
+function bp_activity_comment_form_nojs_display() {
+	echo bp_get_activity_comment_form_nojs_display();
+}
+	function bp_get_activity_comment_form_nojs_display() {
+		global $activities_template;
+		if ( isset( $_GET['ac'] ) && $_GET['ac'] == $activities_template->activity->id . '/' )
+			return 'style="display: block"';
+
+		return false;
+	}
+
+function bp_activity_comment_form_action() {
+	echo bp_get_activity_comment_form_action();
+}
+	function bp_get_activity_comment_form_action() {
+		global $bp;
+
+		return apply_filters( 'bp_get_activity_comment_form_action', home_url( bp_get_activity_root_slug() . '/reply/' ) );
+	}
+
+function bp_activity_permalink_id() {
+	echo bp_get_activity_permalink_id();
+}
+	function bp_get_activity_permalink_id() {
+		global $bp;
+
+		return apply_filters( 'bp_get_activity_permalink_id', $bp->current_action );
+	}
+
+function bp_activity_thread_permalink() {
+	echo bp_get_activity_thread_permalink();
+}
+	function bp_get_activity_thread_permalink() {
+		global $bp, $activities_template;
+
+		$link = bp_activity_get_permalink( $activities_template->activity->id, $activities_template->activity );
+
+	 	return apply_filters( 'bp_get_activity_thread_permalink', $link );
+	}
+
+function bp_activity_favorite_link() {
+	echo bp_get_activity_favorite_link();
+}
+	function bp_get_activity_favorite_link() {
+		global $bp, $activities_template;
+		return apply_filters( 'bp_get_activity_favorite_link', wp_nonce_url( home_url( bp_get_activity_root_slug() . '/favorite/' . $activities_template->activity->id . '/' ), 'mark_favorite' ) );
+	}
+
+function bp_activity_unfavorite_link() {
+	echo bp_get_activity_unfavorite_link();
+}
+	function bp_get_activity_unfavorite_link() {
+		global $bp, $activities_template;
+		return apply_filters( 'bp_get_activity_unfavorite_link', wp_nonce_url( home_url( bp_get_activity_root_slug() . '/unfavorite/' . $activities_template->activity->id . '/' ), 'unmark_favorite' ) );
+	}
+
+function bp_activity_css_class() {
+	echo bp_get_activity_css_class();
+}
+	function bp_get_activity_css_class() {
+		global $activities_template;
+
+		$mini_activity_actions = apply_filters( 'bp_activity_mini_activity_types', array(
+			'friendship_accepted',
+			'friendship_created',
+			'new_blog',
+			'joined_group',
+			'created_group',
+			'new_member'
+		) );
+
+		$class = '';
+		if ( in_array( $activities_template->activity->type, (array)$mini_activity_actions ) || empty( $activities_template->activity->content ) )
+			$class = ' mini';
+
+		if ( bp_activity_get_comment_count() && bp_activity_can_comment() )
+			$class .= ' has-comments';
+
+		return apply_filters( 'bp_get_activity_css_class', $activities_template->activity->component . ' ' . $activities_template->activity->type . $class );
+	}
+
+/**
+ * bp_activity_delete_link()
+ *
+ * Display the activity delete link.
+ *
+ * @since 1.1
+ * @uses bp_get_activity_delete_link()
+ */
+function bp_activity_delete_link() {
+	echo bp_get_activity_delete_link();
+}
+
+	/**
+	 * bp_get_activity_delete_link()
+	 *
+	 * Return the activity delete link.
+	 *
+	 * @global object $activities_template BuddyPress Activities Template
+	 * @global object $bp BuddyPress global settings
+	 * @return string $link Activity delete link. Contains $redirect_to arg if on single activity page.
+	 * @since 1.1
+	 */
+	function bp_get_activity_delete_link() {
+		global $activities_template, $bp;
+
+		$url   = bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/delete/' . $activities_template->activity->id;
+		$class = 'delete-activity';
+
+		// Determine if we're on a single activity page, and customize accordingly
+		if ( bp_is_activity_component() && is_numeric( bp_current_action() ) ) {
+			$url   = add_query_arg( array( 'redirect_to' => wp_get_referer() ), $url );
+			$class = 'delete-activity-single';
+		}
+
+		$link = '<a href="' . wp_nonce_url( $url, 'bp_activity_delete_link' ) . '" class="button item-button bp-secondary-action ' . $class . ' confirm" rel="nofollow">' . __( 'Delete', 'buddypress' ) . '</a>';
+		return apply_filters( 'bp_get_activity_delete_link', $link );
+	}
+
+function bp_activity_latest_update( $user_id = 0 ) {
+	echo bp_get_activity_latest_update( $user_id );
+}
+	function bp_get_activity_latest_update( $user_id = 0 ) {
+		global $bp;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		if ( bp_core_is_user_spammer( $user_id ) || bp_core_is_user_deleted( $user_id ) )
+			return false;
+
+		if ( !$update = bp_get_user_meta( $user_id, 'bp_latest_update', true ) )
+			return false;
+
+		$latest_update = apply_filters( 'bp_get_activity_latest_update_excerpt', trim( strip_tags( bp_create_excerpt( $update['content'], 358 ) ) ) );
+		$latest_update .= ' <a href="' . bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $update['id'] . '/"> ' . __( 'View', 'buddypress' ) . '</a>';
+
+		return apply_filters( 'bp_get_activity_latest_update', $latest_update  );
+	}
+
+function bp_activity_filter_links( $args = false ) {
+	echo bp_get_activity_filter_links( $args );
+}
+	function bp_get_activity_filter_links( $args = false ) {
+		global $activities_template, $bp;
+
+		$defaults = array(
+			'style' => 'list'
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Fetch the names of components that have activity recorded in the DB
+		$components = BP_Activity_Activity::get_recorded_components();
+
+		if ( !$components )
+			return false;
+
+		foreach ( (array) $components as $component ) {
+			/* Skip the activity comment filter */
+			if ( 'activity' == $component )
+				continue;
+
+			if ( isset( $_GET['afilter'] ) && $component == $_GET['afilter'] )
+				$selected = ' class="selected"';
+			else
+				unset($selected);
+
+			$component = esc_attr( $component );
+
+			switch ( $style ) {
+				case 'list':
+					$tag = 'li';
+					$before = '<li id="afilter-' . $component . '"' . $selected . '>';
+					$after = '</li>';
+				break;
+				case 'paragraph':
+					$tag = 'p';
+					$before = '<p id="afilter-' . $component . '"' . $selected . '>';
+					$after = '</p>';
+				break;
+				case 'span':
+					$tag = 'span';
+					$before = '<span id="afilter-' . $component . '"' . $selected . '>';
+					$after = '</span>';
+				break;
+			}
+
+			$link = add_query_arg( 'afilter', $component );
+			$link = remove_query_arg( 'acpage' , $link );
+
+			$link = apply_filters( 'bp_get_activity_filter_link_href', $link, $component );
+
+			// Make sure all core internal component names are translatable
+			$translatable_components = array( __( 'xprofile', 'buddypress'), __( 'friends', 'buddypress' ), __( 'groups', 'buddypress' ), __( 'status', 'buddypress' ), __( 'sites', 'buddypress' ) );
+
+			$component_links[] = $before . '<a href="' . esc_attr( $link ) . '">' . ucwords( __( $component, 'buddypress' ) ) . '</a>' . $after;
+		}
+
+		$link = remove_query_arg( 'afilter' , $link );
+
+		if ( isset( $_GET['afilter'] ) )
+			$component_links[] = '<' . $tag . ' id="afilter-clear"><a href="' . esc_attr( $link ) . '">' . __( 'Clear Filter', 'buddypress' ) . '</a></' . $tag . '>';
+
+ 		return apply_filters( 'bp_get_activity_filter_links', implode( "\n", $component_links ) );
+	}
+
+function bp_activity_can_comment() {
+	global $activities_template, $bp;
+
+	$can_comment = true;
+
+	if ( false === $activities_template->disable_blogforum_replies || (int)$activities_template->disable_blogforum_replies ) {
+		if ( 'new_blog_post' == bp_get_activity_action_name() || 'new_blog_comment' == bp_get_activity_action_name() || 'new_forum_topic' == bp_get_activity_action_name() || 'new_forum_post' == bp_get_activity_action_name() )
+			$can_comment = false;
+	}
+
+	if ( 'activity_comment' == bp_get_activity_action_name() )
+		$can_comment = false;
+
+	return apply_filters( 'bp_activity_can_comment', $can_comment );
+}
+
+function bp_activity_can_comment_reply( $comment ) {
+	$can_comment = true;
+
+	return apply_filters( 'bp_activity_can_comment_reply', $can_comment, $comment );
+}
+
+function bp_activity_can_favorite() {
+	$can_favorite = true;
+
+	return apply_filters( 'bp_activity_can_favorite', $can_favorite );
+}
+
+function bp_total_favorite_count_for_user( $user_id = 0 ) {
+	echo bp_get_total_favorite_count_for_user( $user_id );
+}
+	function bp_get_total_favorite_count_for_user( $user_id = 0 ) {
+		return apply_filters( 'bp_get_total_favorite_count_for_user', bp_activity_total_favorites_for_user( $user_id ) );
+	}
+
+function bp_total_mention_count_for_user( $user_id = 0 ) {
+	echo bp_get_total_favorite_count_for_user( $user_id );
+}
+	function bp_get_total_mention_count_for_user( $user_id = 0 ) {
+		global $bp;
+
+		return apply_filters( 'bp_get_total_mention_count_for_user', bp_get_user_meta( $user_id, 'bp_new_mention_count', true ) );
+	}
+
+function bp_send_public_message_link() {
+	echo bp_get_send_public_message_link();
+}
+	function bp_get_send_public_message_link() {
+		global $bp;
+
+		if ( bp_is_my_profile() || !is_user_logged_in() )
+			return false;
+
+		return apply_filters( 'bp_get_send_public_message_link', wp_nonce_url( bp_loggedin_user_domain() . bp_get_activity_slug() . '/?r=' . bp_core_get_username( $bp->displayed_user->id, $bp->displayed_user->userdata->user_nicename, $bp->displayed_user->userdata->user_login ) ) );
+	}
+
+function bp_mentioned_user_display_name( $user_id_or_username ) {
+	echo bp_get_mentioned_user_display_name( $user_id_or_username );
+}
+	function bp_get_mentioned_user_display_name( $user_id_or_username ) {
+		if ( !$name = bp_core_get_user_displayname( $user_id_or_username ) )
+			$name = __( 'a user', 'buddypress' );
+
+		return apply_filters( 'bp_get_mentioned_user_display_name', $name, $user_id_or_username );
+	}
+
+/**
+ * bp_send_public_message_button( $args )
+ *
+ * Output button for sending a public message
+ *
+ * @param array $args
+ */
+function bp_send_public_message_button( $args = '' ) {
+	echo bp_get_send_public_message_button( $args );
+}
+	/**
+	 * bp_get_send_public_message_button( $args )
+	 *
+	 * Return button for sending a public message
+	 *
+	 * @param array $args
+	 * @return string
+	 */
+	function bp_get_send_public_message_button( $args = '' ) {
+		$defaults = array(
+			'id'                => 'public_message',
+			'component'         => 'activity',
+			'must_be_logged_in' => true,
+			'block_self'        => true,
+			'wrapper_id'        => 'post-mention',
+			'link_href'         => bp_get_send_public_message_link(),
+			'link_title'        => __( 'Send a public message on your activity stream.', 'buddypress' ),
+			'link_text'         => __( 'Public Message', 'buddypress' ),
+			'link_class'        => 'activity-button mention'
+		);
+
+		$button = wp_parse_args( $args, $defaults );
+
+		// Filter and return the HTML button
+		return bp_get_button( apply_filters( 'bp_get_send_public_message_button', $button ) );
+	}
+
+function bp_activity_post_form_action() {
+	echo bp_get_activity_post_form_action();
+}
+	function bp_get_activity_post_form_action() {
+		return apply_filters( 'bp_get_activity_post_form_action', home_url( bp_get_activity_root_slug() . '/post/' ) );
+	}
+
+/* RSS Feed Template Tags ***************************/
+
+function bp_sitewide_activity_feed_link() {
+	echo bp_get_sitewide_activity_feed_link();
+}
+	function bp_get_sitewide_activity_feed_link() {
+		return apply_filters( 'bp_get_sitewide_activity_feed_link', home_url( bp_get_activity_root_slug() . '/feed/' ) );
+	}
+
+function bp_member_activity_feed_link() {
+	echo bp_get_member_activity_feed_link();
+}
+function bp_activities_member_rss_link() {
+	echo bp_get_member_activity_feed_link();
+}
+
+	function bp_get_member_activity_feed_link() {
+		global $bp;
+
+		if ( bp_is_profile_component() || bp_is_current_action( 'just-me' ) )
+			$link = bp_displayed_user_domain() . bp_get_activity_slug() . '/feed/';
+		elseif ( bp_is_active( 'friends' ) && bp_is_current_action( bp_get_friends_slug() ) )
+			$link = bp_displayed_user_domain() . bp_get_activity_slug() . '/' . bp_get_friends_slug() . '/feed/';
+		elseif ( bp_is_active( 'groups'  ) && bp_is_current_action( bp_get_groups_slug()  ) )
+			$link = bp_displayed_user_domain() . bp_get_activity_slug() . '/' . bp_get_groups_slug() . '/feed/';
+		elseif ( 'favorites' == $bp->current_action )
+			$link = bp_displayed_user_domain() . bp_get_activity_slug() . '/favorites/feed/';
+		elseif ( 'mentions' == $bp->current_action )
+			$link = bp_displayed_user_domain() . bp_get_activity_slug() . '/mentions/feed/';
+		else
+			$link = '';
+
+		return apply_filters( 'bp_get_activities_member_rss_link', $link );
+	}
+	function bp_get_activities_member_rss_link() { return bp_get_member_activity_feed_link(); }
+
+
+/** Template tags for RSS feed output *****************************************/
+
+function bp_activity_feed_item_guid() {
+	echo bp_get_activity_feed_item_guid();
+}
+	function bp_get_activity_feed_item_guid() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_feed_item_guid', md5( $activities_template->activity->date_recorded . '-' . $activities_template->activity->content ) );
+	}
+
+function bp_activity_feed_item_title() {
+	echo bp_get_activity_feed_item_title();
+}
+	function bp_get_activity_feed_item_title() {
+		global $activities_template;
+
+		if ( !empty( $activities_template->activity->action ) )
+			$content = $activities_template->activity->action;
+		else
+			$content = $activities_template->activity->content;
+
+		$content = explode( '<span', $content );
+		$title = strip_tags( ent2ncr( trim( convert_chars( $content[0] ) ) ) );
+
+		if ( ':' == substr( $title, -1 ) )
+			$title = substr( $title, 0, -1 );
+
+		if ( 'activity_update' == $activities_template->activity->type )
+			$title .= ': ' . strip_tags( ent2ncr( trim( convert_chars( bp_create_excerpt( $activities_template->activity->content, 70, array( 'ending' => " [&#133;]" ) ) ) ) ) );
+
+		return apply_filters( 'bp_get_activity_feed_item_title', $title );
+	}
+
+function bp_activity_feed_item_link() {
+	echo bp_get_activity_feed_item_link();
+}
+	function bp_get_activity_feed_item_link() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_feed_item_link', $activities_template->activity->primary_link );
+	}
+
+function bp_activity_feed_item_date() {
+	echo bp_get_activity_feed_item_date();
+}
+	function bp_get_activity_feed_item_date() {
+		global $activities_template;
+
+		return apply_filters( 'bp_get_activity_feed_item_date', $activities_template->activity->date_recorded );
+	}
+
+function bp_activity_feed_item_description() {
+	echo bp_get_activity_feed_item_description();
+}
+	function bp_get_activity_feed_item_description() {
+		global $activities_template;
+
+		if ( empty( $activities_template->activity->action ) )
+			$content = $activities_template->activity->content;
+		else
+			$content = $activities_template->activity->action . ' ' . $activities_template->activity->content;
+
+		return apply_filters( 'bp_get_activity_feed_item_description', ent2ncr( convert_chars( str_replace( '%s', '', $content ) ) ) );
+	}
+
+/**
+ * Template tag so we can hook activity feed to <head>
+ *
+ * @since 1.5
+ */
+function bp_activity_sitewide_feed() {
+?>
+
+	<link rel="alternate" type="application/rss+xml" title="<?php bloginfo( 'name' ) ?> | <?php _e( 'Site Wide Activity RSS Feed', 'buddypress' ) ?>" href="<?php bp_sitewide_activity_feed_link() ?>" />
+
+<?php
+}
+add_action( 'bp_head', 'bp_activity_sitewide_feed' );
+
+?>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-favorites-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-favorites-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-favorites-feed.php	(revision 5057)
@@ -0,0 +1,72 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a member's favorite activity
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true );
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_favorites_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->displayed_user->fullname; ?> | <?php _e( 'Favorite Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo home_url( bp_get_activity_root_slug() . '/#my-favorites/' ) ?></link>
+	<description><?php echo $bp->displayed_user->fullname; ?> - <?php _e( 'Favorite Activity', 'buddypress' ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+
+	<?php do_action('bp_activity_favorites_feed_head'); ?>
+
+	<?php
+		$favs = bp_activity_get_user_favorites( $bp->displayed_user->id );
+		$fav_ids = implode( ',', (array)$favs );
+	?>
+
+	<?php if ( bp_has_activities( 'include=' . $fav_ids . '&max=50&display_comments=stream' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+					<?php bp_activity_feed_item_description() ?>
+
+					<?php if ( bp_activity_can_comment() ) : ?>
+						<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+					<?php endif; ?>
+
+					<?php if ( 'activity_comment' == bp_get_activity_action_name() ) : ?>
+						<br /><strong><?php _e( 'In reply to', 'buddypress' ) ?></strong> -
+						<?php bp_activity_parent_content() ?>
+					<?php endif; ?>
+					]]>
+				</description>
+
+				<?php do_action('bp_activity_favorites_feed_item'); ?>
+
+			</item>
+
+		<?php endwhile; ?>
+	<?php endif; ?>
+
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-friends-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-friends-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-friends-feed.php	(revision 5057)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a member's friends activity stream.
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_friends_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->displayed_user->fullname; ?> | <?php _e( 'Friends Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo bp_displayed_user_domain() . bp_get_activity_slug() . '/my-friends/feed' ?></link>
+	<description><?php printf( __( '%s - Friends Activity Feed', 'buddypress' ), $bp->displayed_user->fullname ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_friends_feed_head'); ?>
+
+	<?php $friend_ids = implode( ',', friends_get_friend_user_ids( $bp->displayed_user->id ) ); ?>
+	<?php if ( bp_has_activities( 'user_id=' . $friend_ids . '&max=50&display_comments=stream' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+					<?php bp_activity_feed_item_description() ?>
+
+					<?php if ( bp_activity_can_comment() ) : ?>
+						<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+					<?php endif; ?>
+
+					<?php if ( 'activity_comment' == bp_get_activity_action_name() ) : ?>
+						<br /><strong><?php _e( 'In reply to', 'buddypress' ) ?></strong> -
+						<?php bp_activity_parent_content() ?>
+					<?php endif; ?>
+					]]>
+				</description>
+				<?php do_action('bp_activity_personal_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-group-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-group-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-group-feed.php	(revision 5057)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a group activity stream
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_group_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->groups->current_group->name ?> | <?php _e( 'Group Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo bp_get_group_permalink( $bp->groups->current_group ) . bp_get_activity_slug() . '/feed' ?></link>
+	<description><?php printf( __( '%s - Group Activity Feed', 'buddypress' ), $bp->groups->current_group->name  ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_group_feed_head'); ?>
+
+	<?php if ( bp_has_activities( 'object=' . $bp->groups->id . '&primary_id=' . $bp->groups->current_group->id . '&max=50&display_comments=threaded' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+						<?php bp_activity_feed_item_description() ?>
+
+						<?php if ( bp_activity_can_comment() ) : ?>
+							<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+						<?php endif; ?>
+					]]>
+				</description>
+				<?php do_action('bp_activity_group_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mentions-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mentions-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mentions-feed.php	(revision 5057)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a member's group's activity
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_mentions_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->displayed_user->fullname; ?> | <?php _e( 'Mentions', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo home_url( bp_get_activity_root_slug() . '/#mentions/' ) ?></link>
+	<description><?php echo $bp->displayed_user->fullname; ?> - <?php _e( 'Mentions', 'buddypress' ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_mentions_feed_head'); ?>
+
+	<?php if ( bp_has_activities( 'max=50&display_comments=stream&search_terms=@' . bp_core_get_username( $bp->displayed_user->id, $bp->displayed_user->userdata->user_nicename, $bp->displayed_user->userdata->user_login ) ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+					<?php bp_activity_feed_item_description() ?>
+
+					<?php if ( bp_activity_can_comment() ) : ?>
+						<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+					<?php endif; ?>
+
+					<?php if ( 'activity_comment' == bp_get_activity_action_name() ) : ?>
+						<br /><strong><?php _e( 'In reply to', 'buddypress' ) ?></strong> -
+						<?php bp_activity_parent_content() ?>
+					<?php endif; ?>
+					]]>
+				</description>
+				<?php do_action('bp_activity_mentions_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mygroups-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mygroups-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-mygroups-feed.php	(revision 5057)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a member's group's activity
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_mygroups_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->displayed_user->fullname; ?> | <?php _e( 'My Groups - Public Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo home_url( bp_get_activity_root_slug() . '/#my-groups/' ) ?></link>
+	<description><?php echo $bp->displayed_user->fullname; ?> - <?php _e( 'My Groups - Public Activity', 'buddypress' ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_mygroups_feed_head'); ?>
+
+	<?php
+		$groups = groups_get_user_groups( $bp->loggedin_user->id );
+		$group_ids = implode( ',', $groups['groups'] );
+	?>
+
+	<?php if ( bp_has_activities( 'object=' . $bp->groups->id . '&primary_id=' . $group_ids . '&max=50&display_comments=threaded' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+						<?php bp_activity_feed_item_description() ?>
+
+						<?php if ( bp_activity_can_comment() ) : ?>
+							<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+						<?php endif; ?>
+					]]>
+				</description>
+				<?php do_action('bp_activity_mygroups_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-personal-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-personal-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-personal-feed.php	(revision 5057)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying a member's activity stream.
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_personal_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php echo $bp->displayed_user->fullname; ?> | <?php _e( 'Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo bp_displayed_user_domain() . bp_get_activity_slug() . '/feed' ?></link>
+	<description><?php printf( __( '%s - Activity Feed', 'buddypress' ), $bp->displayed_user->fullname ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_personal_feed_head'); ?>
+
+	<?php if ( bp_has_activities( 'user_id=' . $bp->displayed_user->id . '&max=50&display_comments=stream' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php echo bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+					<?php bp_activity_feed_item_description() ?>
+
+					<?php if ( bp_activity_can_comment() ) : ?>
+						<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+					<?php endif; ?>
+
+					<?php if ( 'activity_comment' == bp_get_activity_action_name() ) : ?>
+						<br /><strong><?php _e( 'In reply to', 'buddypress' ) ?></strong> -
+						<?php bp_activity_parent_content() ?>
+					<?php endif; ?>
+					]]>
+				</description>
+				<?php do_action('bp_activity_personal_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-activity/feeds/bp-activity-sitewide-feed.php
===================================================================
--- /tags/1.5-beta-3/bp-activity/feeds/bp-activity-sitewide-feed.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-activity/feeds/bp-activity-sitewide-feed.php	(revision 5057)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * RSS2 Feed Template for displaying the site wide activity stream.
+ *
+ * @package BuddyPress
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
+header('Status: 200 OK');
+?>
+<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
+
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action('bp_activity_sitewide_feed'); ?>
+>
+
+<channel>
+	<title><?php bp_site_name() ?> | <?php _e( 'Site Wide Activity', 'buddypress' ) ?></title>
+	<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
+	<link><?php echo bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/feed' ?></link>
+	<description><?php _e( 'Site Wide Activity Feed', 'buddypress' ) ?></description>
+	<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_activity_get_last_updated(), false); ?></pubDate>
+	<generator>http://buddypress.org/?v=<?php echo BP_VERSION ?></generator>
+	<language><?php echo get_option('rss_language'); ?></language>
+	<?php do_action('bp_activity_sitewide_feed_head'); ?>
+
+	<?php if ( bp_has_activities( 'type=sitewide&max=50&display_comments=threaded' ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<item>
+				<guid><?php bp_activity_thread_permalink() ?></guid>
+				<title><?php bp_activity_feed_item_title() ?></title>
+				<link><?php bp_activity_thread_permalink() ?></link>
+				<pubDate><?php echo mysql2date('D, d M Y H:i:s O', bp_get_activity_feed_item_date(), false); ?></pubDate>
+
+				<description>
+					<![CDATA[
+					<?php bp_activity_feed_item_description() ?>
+
+					<?php if ( bp_activity_can_comment() ) : ?>
+						<p><?php printf( __( 'Comments: %s', 'buddypress' ), bp_activity_get_comment_count() ); ?></p>
+					<?php endif; ?>
+					]]>
+				</description>
+			<?php do_action('bp_activity_personal_feed_item'); ?>
+			</item>
+		<?php endwhile; ?>
+
+	<?php endif; ?>
+</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-actions.php	(revision 5057)
@@ -0,0 +1,16 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_blogs_redirect_to_random_blog() {
+	global $bp, $wpdb;
+
+	if ( bp_is_blogs_component() && isset( $_GET['random-blog'] ) ) {
+		$blog = bp_blogs_get_random_blogs( 1, 1 );
+
+		bp_core_redirect( get_site_url( $blog['blogs'][0]->blog_id ) );
+	}
+}
+add_action( 'bp_actions', 'bp_blogs_redirect_to_random_blog' );
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-activity.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-activity.php	(revision 5057)
@@ -0,0 +1,94 @@
+<?php
+/******************************************************************************
+ * These functions handle the recording, deleting and formatting of activity and
+ * notifications for the user and for this specific component.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+
+function bp_blogs_register_activity_actions() {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	bp_activity_set_action( $bp->blogs->id, 'new_blog',         __( 'New site created',        'buddypress' ) );
+	bp_activity_set_action( $bp->blogs->id, 'new_blog_post',    __( 'New post published',      'buddypress' ) );
+	bp_activity_set_action( $bp->blogs->id, 'new_blog_comment', __( 'New post comment posted', 'buddypress' ) );
+
+	do_action( 'bp_blogs_register_activity_actions' );
+}
+add_action( 'bp_register_activity_actions', 'bp_blogs_register_activity_actions' );
+
+function bp_blogs_record_activity( $args = '' ) {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	$defaults = array(
+		'user_id'           => $bp->loggedin_user->id,
+		'action'            => '',
+		'content'           => '',
+		'primary_link'      => '',
+		'component'         => $bp->blogs->id,
+		'type'              => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'recorded_time'     => bp_core_current_time(),
+		'hide_sitewide'     => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// Remove large images and replace them with just one image thumbnail
+ 	if ( bp_is_active( 'activity' ) && !empty( $content ) )
+		$content = bp_activity_thumbnail_content_images( $content, $primary_link );
+
+	if ( !empty( $action ) )
+		$action = apply_filters( 'bp_blogs_record_activity_action', $action );
+
+	if ( !empty( $content ) )
+		$content = apply_filters( 'bp_blogs_record_activity_content', bp_create_excerpt( $content ), $content );
+
+	// Check for an existing entry and update if one exists.
+	$id = bp_activity_get_activity_id( array(
+		'user_id'           => $user_id,
+		'component'         => $component,
+		'type'              => $type,
+		'item_id'           => $item_id,
+		'secondary_item_id' => $secondary_item_id
+	) );
+
+	return bp_activity_add( array( 'id' => $id, 'user_id' => $user_id, 'action' => $action, 'content' => $content, 'primary_link' => $primary_link, 'component' => $component, 'type' => $type, 'item_id' => $item_id, 'secondary_item_id' => $secondary_item_id, 'recorded_time' => $recorded_time, 'hide_sitewide' => $hide_sitewide ) );
+}
+
+function bp_blogs_delete_activity( $args = true ) {
+	global $bp;
+
+	if ( bp_is_active( 'activity' ) ) {
+		$defaults = array(
+			'item_id'           => false,
+			'component'         => $bp->blogs->id,
+			'type'              => false,
+			'user_id'           => false,
+			'secondary_item_id' => false
+		);
+
+		$params = wp_parse_args( $args, $defaults );
+		extract( $params, EXTR_SKIP );
+
+		bp_activity_delete_by_item_id( array(
+			'item_id'           => $item_id,
+			'component'         => $component,
+			'type'              => $type,
+			'user_id'           => $user_id,
+			'secondary_item_id' => $secondary_item_id
+		) );
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-buddybar.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-buddybar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-buddybar.php	(revision 5057)
@@ -0,0 +1,63 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// *** "My Blogs" Menu ********
+function bp_adminbar_blogs_menu() {
+	global $bp;
+
+	if ( !is_user_logged_in() || !bp_is_active( 'blogs' ) )
+		return false;
+
+	if ( !is_multisite() )
+		return false;
+
+	if ( !$blogs = wp_cache_get( 'bp_blogs_of_user_' . $bp->loggedin_user->id . '_inc_hidden', 'bp' ) ) {
+		$blogs = bp_blogs_get_blogs_for_user( $bp->loggedin_user->id, true );
+		wp_cache_set( 'bp_blogs_of_user_' . $bp->loggedin_user->id . '_inc_hidden', $blogs, 'bp' );
+	}
+
+	$counter = 0;
+	if ( is_array( $blogs['blogs'] ) && (int)$blogs['count'] ) {
+
+		echo '<li id="bp-adminbar-blogs-menu"><a href="' . trailingslashit( $bp->loggedin_user->domain . bp_get_blogs_slug() ) . '">';
+
+		_e( 'My Sites', 'buddypress' );
+
+		echo '</a>';
+		echo '<ul>';
+
+		foreach ( (array)$blogs['blogs'] as $blog ) {
+			$alt      = ( 0 == $counter % 2 ) ? ' class="alt"' : '';
+			$site_url = esc_attr( $blog->siteurl );
+
+			echo '<li' . $alt . '>';
+			echo '<a href="' . $site_url . '">' . esc_html( $blog->name ) . '</a>';
+			echo '<ul>';
+			echo '<li class="alt"><a href="' . $site_url . 'wp-admin/">' . __( 'Dashboard', 'buddypress' ) . '</a></li>';
+			echo '<li><a href="' . $site_url . 'wp-admin/post-new.php">' . __( 'New Post', 'buddypress' ) . '</a></li>';
+			echo '<li class="alt"><a href="' . $site_url . 'wp-admin/edit.php">' . __( 'Manage Posts', 'buddypress' ) . '</a></li>';
+			echo '<li><a href="' . $site_url . 'wp-admin/edit-comments.php">' . __( 'Manage Comments', 'buddypress' ) . '</a></li>';
+			echo '</ul>';
+
+			do_action( 'bp_adminbar_blog_items', $blog );
+
+			echo '</li>';
+			$counter++;
+		}
+
+		$alt = ( 0 == $counter % 2 ) ? ' class="alt"' : '';
+
+		if ( bp_blog_signup_enabled() ) {
+			echo '<li' . $alt . '>';
+			echo '<a href="' . bp_get_root_domain() . '/' . bp_get_blogs_root_slug() . '/create/">' . __( 'Create a Site!', 'buddypress' ) . '</a>';
+			echo '</li>';
+		}
+
+		echo '</ul>';
+		echo '</li>';
+	}
+}
+add_action( 'bp_adminbar_menus', 'bp_adminbar_blogs_menu', 6 );
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-cache.php	(revision 5057)
@@ -0,0 +1,37 @@
+<?php
+/*******************************************************************************
+ * Caching
+ *
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_blogs_clear_blog_object_cache( $blog_id, $user_id ) {
+	wp_cache_delete( 'bp_blogs_of_user_' . $user_id, 'bp' );
+	wp_cache_delete( 'bp_total_blogs_for_user_' . $user_id, 'bp' );
+}
+
+function bp_blogs_format_clear_blog_cache( $recorded_blog_obj ) {
+	bp_blogs_clear_blog_object_cache( false, $recorded_blog_obj->user_id );
+	wp_cache_delete( 'bp_total_blogs', 'bp' );
+}
+
+// List actions to clear object caches on
+add_action( 'bp_blogs_remove_blog_for_user', 'bp_blogs_clear_blog_object_cache', 10, 2 );
+add_action( 'bp_blogs_new_blog',             'bp_blogs_format_clear_blog_cache', 10, 2 );
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'bp_blogs_remove_data_for_blog', 'bp_core_clear_cache' );
+add_action( 'bp_blogs_remove_comment',       'bp_core_clear_cache' );
+add_action( 'bp_blogs_remove_post',          'bp_core_clear_cache' );
+add_action( 'bp_blogs_remove_blog_for_user', 'bp_core_clear_cache' );
+add_action( 'bp_blogs_remove_blog',          'bp_core_clear_cache' );
+add_action( 'bp_blogs_new_blog_comment',     'bp_core_clear_cache' );
+add_action( 'bp_blogs_new_blog_post',        'bp_core_clear_cache' );
+add_action( 'bp_blogs_new_blog',             'bp_core_clear_cache' );
+add_action( 'bp_blogs_remove_data',          'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-classes.php	(revision 5057)
@@ -0,0 +1,282 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_Blogs_Blog {
+	var $id;
+	var $user_id;
+	var $blog_id;
+
+	function bp_blogs_blog( $id = null ) {
+		$this->__construct( $id );
+	}
+
+	function __construct( $id = null ) {
+		global $bp, $wpdb;
+
+		$user_id = $bp->displayed_user->id;
+
+		if ( $id ) {
+			$this->id = $id;
+			$this->populate();
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp;
+
+		$blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->blogs->table_name} WHERE id = %d", $this->id ) );
+
+		$this->user_id = $blog->user_id;
+		$this->blog_id = $blog->blog_id;
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->user_id = apply_filters( 'bp_blogs_blog_user_id_before_save', $this->user_id, $this->id );
+		$this->blog_id = apply_filters( 'bp_blogs_blog_id_before_save', $this->blog_id, $this->id );
+
+		do_action_ref_array( 'bp_blogs_blog_before_save', array( &$this ) );
+
+		// Don't try and save if there is no user ID or blog ID set.
+		if ( !$this->user_id || !$this->blog_id )
+			return false;
+
+		// Don't save if this blog has already been recorded for the user.
+		if ( !$this->id && $this->exists() )
+			return false;
+
+		if ( $this->id ) {
+			// Update
+			$sql = $wpdb->prepare( "UPDATE {$bp->blogs->table_name} SET user_id = %d, blog_id = %d WHERE id = %d", $this->user_id, $this->blog_id, $this->id );
+		} else {
+			// Save
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->blogs->table_name} ( user_id, blog_id ) VALUES ( %d, %d )", $this->user_id, $this->blog_id );
+		}
+
+		if ( !$wpdb->query($sql) )
+			return false;
+
+		do_action_ref_array( 'bp_blogs_blog_after_save', array( &$this ) );
+
+		if ( $this->id )
+			return $this->id;
+		else
+			return $wpdb->insert_id;
+	}
+
+	function exists() {
+		global $bp, $wpdb;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->blogs->table_name} WHERE user_id = %d AND blog_id = %d", $this->user_id, $this->blog_id ) );
+	}
+
+	/* Static Functions */
+
+	function get( $type, $limit = false, $page = false, $user_id = 0, $search_terms = false ) {
+		global $bp, $wpdb;
+
+		if ( !is_user_logged_in() || ( !is_super_admin() && ( $user_id != $bp->loggedin_user->id ) ) )
+			$hidden_sql = "AND wb.public = 1";
+		else
+			$hidden_sql = '';
+
+		$pag_sql = ( $limit && $page ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : '';
+
+		$user_sql = !empty( $user_id ) ? $wpdb->prepare( " AND b.user_id = %d", $user_id ) : '';
+
+		switch ( $type ) {
+			case 'active': default:
+				$order_sql = "ORDER BY bm.meta_value DESC";
+				break;
+			case 'alphabetical':
+				$order_sql = "ORDER BY bm2.meta_value ASC";
+				break;
+			case 'newest':
+				$order_sql = "ORDER BY wb.registered DESC";
+				break;
+			case 'random':
+				$order_sql = "ORDER BY RAND()";
+				break;
+		}
+
+		if ( !empty( $search_terms ) ) {
+			$filter = like_escape( $wpdb->escape( $search_terms ) );
+			$paged_blogs = $wpdb->get_results( "SELECT b.blog_id, b.user_id as admin_user_id, u.user_email as admin_user_email, wb.domain, wb.path, bm.meta_value as last_activity, bm2.meta_value as name FROM {$bp->blogs->table_name} b, {$bp->blogs->table_name_blogmeta} bm, {$bp->blogs->table_name_blogmeta} bm2, {$wpdb->base_prefix}blogs wb, {$wpdb->users} u WHERE b.blog_id = wb.blog_id AND b.user_id = u.ID AND b.blog_id = bm.blog_id AND b.blog_id = bm2.blog_id AND wb.archived = '0' AND wb.spam = 0 AND wb.mature = 0 AND wb.deleted = 0 {$hidden_sql} AND bm.meta_key = 'last_activity' AND bm2.meta_key = 'name' AND bm2.meta_value LIKE '%%$filter%%' {$user_sql} GROUP BY b.blog_id {$order_sql} {$pag_sql}" );
+			$total_blogs = $wpdb->get_var( "SELECT COUNT(DISTINCT b.blog_id) FROM {$bp->blogs->table_name} b, {$wpdb->base_prefix}blogs wb, {$bp->blogs->table_name_blogmeta} bm, {$bp->blogs->table_name_blogmeta} bm2 WHERE b.blog_id = wb.blog_id AND bm.blog_id = b.blog_id AND bm2.blog_id = b.blog_id AND wb.archived = '0' AND wb.spam = 0 AND wb.mature = 0 AND wb.deleted = 0 {$hidden_sql} AND bm.meta_key = 'name' AND bm2.meta_key = 'description' AND ( bm.meta_value LIKE '%%$filter%%' || bm2.meta_value LIKE '%%$filter%%' ) {$user_sql}" );
+		} else {
+			$paged_blogs = $wpdb->get_results( $wpdb->prepare( "SELECT b.blog_id, b.user_id as admin_user_id, u.user_email as admin_user_email, wb.domain, wb.path, bm.meta_value as last_activity, bm2.meta_value as name FROM {$bp->blogs->table_name} b, {$bp->blogs->table_name_blogmeta} bm, {$bp->blogs->table_name_blogmeta} bm2, {$wpdb->base_prefix}blogs wb, {$wpdb->users} u WHERE b.blog_id = wb.blog_id AND b.user_id = u.ID AND b.blog_id = bm.blog_id AND b.blog_id = bm2.blog_id {$user_sql} AND wb.archived = '0' AND wb.spam = 0 AND wb.mature = 0 AND wb.deleted = 0 {$hidden_sql} AND bm.meta_key = 'last_activity' AND bm2.meta_key = 'name' GROUP BY b.blog_id {$order_sql} {$pag_sql}" ) );
+			$total_blogs = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT b.blog_id) FROM {$bp->blogs->table_name} b, {$wpdb->base_prefix}blogs wb WHERE b.blog_id = wb.blog_id {$user_sql} AND wb.archived = '0' AND wb.spam = 0 AND wb.mature = 0 AND wb.deleted = 0 {$hidden_sql}" ) );
+		}
+
+		$blog_ids = array();
+		foreach ( (array)$paged_blogs as $blog ) {
+			$blog_ids[] = $blog->blog_id;
+		}
+
+		$blog_ids = $wpdb->escape( join( ',', (array)$blog_ids ) );
+		$paged_blogs = BP_Blogs_Blog::get_blog_extras( $paged_blogs, $blog_ids, $type );
+
+		return array( 'blogs' => $paged_blogs, 'total' => $total_blogs );
+	}
+
+	function delete_blog_for_all( $blog_id ) {
+		global $wpdb, $bp;
+
+		bp_blogs_delete_blogmeta( $blog_id );
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name} WHERE blog_id = %d", $blog_id ) );
+	}
+
+	function delete_blog_for_user( $blog_id, $user_id = null ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			$user_id = $bp->loggedin_user->id;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name} WHERE user_id = %d AND blog_id = %d", $user_id, $blog_id ) );
+	}
+
+	function delete_blogs_for_user( $user_id = null ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			$user_id = $bp->loggedin_user->id;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name} WHERE user_id = %d", $user_id ) );
+	}
+
+	function get_blogs_for_user( $user_id = 0, $show_hidden = false ) {
+		global $bp, $wpdb;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		// Show logged in users their hidden blogs.
+		if ( !bp_is_my_profile() && !$show_hidden )
+			$blogs = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT b.blog_id, b.id, bm1.meta_value as name, wb.domain, wb.path FROM {$bp->blogs->table_name} b, {$wpdb->base_prefix}blogs wb, {$bp->blogs->table_name_blogmeta} bm1 WHERE b.blog_id = wb.blog_id AND b.blog_id = bm1.blog_id AND bm1.meta_key = 'name' AND wb.public = 1 AND wb.deleted = 0 AND wb.spam = 0 AND wb.mature = 0 AND wb.archived = '0' AND b.user_id = %d ORDER BY b.blog_id", $user_id ) );
+		else
+			$blogs = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT b.blog_id, b.id, bm1.meta_value as name, wb.domain, wb.path FROM {$bp->blogs->table_name} b, {$wpdb->base_prefix}blogs wb, {$bp->blogs->table_name_blogmeta} bm1 WHERE b.blog_id = wb.blog_id AND b.blog_id = bm1.blog_id AND bm1.meta_key = 'name' AND wb.deleted = 0 AND wb.spam = 0 AND wb.mature = 0 AND wb.archived = '0' AND b.user_id = %d ORDER BY b.blog_id", $user_id ) );
+
+		$total_blog_count = BP_Blogs_Blog::total_blog_count_for_user( $user_id );
+
+		$user_blogs = array();
+		foreach ( (array)$blogs as $blog ) {
+			$user_blogs[$blog->blog_id] = new stdClass;
+			$user_blogs[$blog->blog_id]->id = $blog->id;
+			$user_blogs[$blog->blog_id]->blog_id = $blog->blog_id;
+			$user_blogs[$blog->blog_id]->siteurl = ( is_ssl() ) ? 'https://' . $blog->domain . $blog->path : 'http://' . $blog->domain . $blog->path;
+			$user_blogs[$blog->blog_id]->name = $blog->name;
+		}
+
+		return array( 'blogs' => $user_blogs, 'count' => $total_blog_count );
+	}
+
+	function get_blog_ids_for_user( $user_id = 0 ) {
+		global $bp, $wpdb;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM {$bp->blogs->table_name} WHERE user_id = %d", $user_id ) );
+	}
+
+	function is_recorded( $blog_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->blogs->table_name} WHERE blog_id = %d", $blog_id ) );
+	}
+
+	function total_blog_count_for_user( $user_id = null ) {
+		global $bp, $wpdb;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		// If the user is logged in return the blog count including their hidden blogs.
+		if ( ( is_user_logged_in() && $user_id == $bp->loggedin_user->id ) || is_super_admin() )
+			return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT b.blog_id) FROM {$bp->blogs->table_name} b LEFT JOIN {$wpdb->base_prefix}blogs wb ON b.blog_id = wb.blog_id WHERE wb.deleted = 0 AND wb.spam = 0 AND wb.mature = 0 AND wb.archived = '0' AND user_id = %d", $user_id) );
+		else
+			return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT b.blog_id) FROM {$bp->blogs->table_name} b LEFT JOIN {$wpdb->base_prefix}blogs wb ON b.blog_id = wb.blog_id WHERE wb.public = 1 AND wb.deleted = 0 AND wb.spam = 0 AND wb.mature = 0 AND wb.archived = '0' AND user_id = %d", $user_id) );
+	}
+
+	function search_blogs( $filter, $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		if ( !is_super_admin() )
+			$hidden_sql = "AND wb.public = 1";
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		$paged_blogs = $wpdb->get_results( "SELECT DISTINCT bm.blog_id FROM {$bp->blogs->table_name_blogmeta} bm LEFT JOIN {$wpdb->base_prefix}blogs wb ON bm.blog_id = wb.blog_id WHERE ( ( bm.meta_key = 'name' OR bm.meta_key = 'description' ) AND bm.meta_value LIKE '%%$filter%%' ) {$hidden_sql} AND wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 ORDER BY meta_value ASC{$pag_sql}" );
+		$total_blogs = $wpdb->get_var( "SELECT COUNT(DISTINCT bm.blog_id) FROM {$bp->blogs->table_name_blogmeta} bm LEFT JOIN {$wpdb->base_prefix}blogs wb ON bm.blog_id = wb.blog_id WHERE ( ( bm.meta_key = 'name' OR bm.meta_key = 'description' ) AND bm.meta_value LIKE '%%$filter%%' ) {$hidden_sql} AND wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 ORDER BY meta_value ASC" );
+
+		return array( 'blogs' => $paged_blogs, 'total' => $total_blogs );
+	}
+
+	function get_all( $limit = null, $page = null ) {
+		global $bp, $wpdb;
+
+		$hidden_sql = !is_super_admin() ? "AND wb.public = 1" : '';
+		$pag_sql = ( $limit && $page ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : '';
+
+		$paged_blogs = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT b.blog_id FROM {$bp->blogs->table_name} b LEFT JOIN {$wpdb->base_prefix}blogs wb ON b.blog_id = wb.blog_id WHERE wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 {$hidden_sql} {$pag_sql}" ) );
+		$total_blogs = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT b.blog_id) FROM {$bp->blogs->table_name} b LEFT JOIN {$wpdb->base_prefix}blogs wb ON b.blog_id = wb.blog_id WHERE wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 {$hidden_sql}" ) );
+
+		return array( 'blogs' => $paged_blogs, 'total' => $total_blogs );
+	}
+
+	function get_by_letter( $letter, $limit = null, $page = null ) {
+		global $bp, $wpdb;
+
+		$letter = like_escape( $wpdb->escape( $letter ) );
+
+		if ( !is_super_admin() )
+			$hidden_sql = "AND wb.public = 1";
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		$paged_blogs = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT bm.blog_id FROM {$bp->blogs->table_name_blogmeta} bm LEFT JOIN {$wpdb->base_prefix}blogs wb ON bm.blog_id = wb.blog_id WHERE bm.meta_key = 'name' AND bm.meta_value LIKE '$letter%%' {$hidden_sql} AND wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 ORDER BY bm.meta_value ASC{$pag_sql}" ) );
+		$total_blogs = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT bm.blog_id) FROM {$bp->blogs->table_name_blogmeta} bm LEFT JOIN {$wpdb->base_prefix}blogs wb ON bm.blog_id = wb.blog_id WHERE bm.meta_key = 'name' AND bm.meta_value LIKE '$letter%%' {$hidden_sql} AND wb.mature = 0 AND wb.spam = 0 AND wb.archived = '0' AND wb.deleted = 0 ORDER BY bm.meta_value ASC" ) );
+
+		return array( 'blogs' => $paged_blogs, 'total' => $total_blogs );
+	}
+
+	function get_blog_extras( &$paged_blogs, &$blog_ids, $type = false ) {
+		global $bp, $wpdb;
+
+		if ( empty( $blog_ids ) )
+			return $paged_blogs;
+
+		for ( $i = 0, $count = count( $paged_blogs ); $i < $count; ++$i ) {
+			$blog_prefix = $wpdb->get_blog_prefix( $paged_blogs[$i]->blog_id );
+			$paged_blogs[$i]->latest_post = $wpdb->get_row( "SELECT post_title, guid FROM {$blog_prefix}posts WHERE post_status = 'publish' AND post_type = 'post' AND id != 1 ORDER BY id DESC LIMIT 1" );
+		}
+
+		/* Fetch the blog description for each blog (as it may be empty we can't fetch it in the main query). */
+		$blog_descs = $wpdb->get_results( $wpdb->prepare( "SELECT blog_id, meta_value as description FROM {$bp->blogs->table_name_blogmeta} WHERE meta_key = 'description' AND blog_id IN ( {$blog_ids} )" ) );
+
+		for ( $i = 0, $count = count( $paged_blogs ); $i < $count; ++$i ) {
+			foreach ( (array)$blog_descs as $desc ) {
+				if ( $desc->blog_id == $paged_blogs[$i]->blog_id )
+					$paged_blogs[$i]->description = $desc->description;
+			}
+		}
+
+		return $paged_blogs;
+	}
+
+	function is_hidden( $blog_id ) {
+		global $wpdb;
+
+		if ( !(int)$wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT public FROM {$wpdb->base_prefix}blogs WHERE blog_id = %d", $blog_id ) ) )
+			return true;
+
+		return false;
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-functions.php	(revision 5057)
@@ -0,0 +1,566 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Checks $bp pages global and looks for directory page
+ *
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @return bool True if set, False if empty
+ */
+function bp_blogs_has_directory() {
+	global $bp;
+
+	return (bool) !empty( $bp->pages->blogs->id );
+}
+
+function bp_blogs_get_blogs( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'type'         => 'active', // active, alphabetical, newest, or random
+		'user_id'      => false,    // Pass a user_id to limit to only blogs that this user has privilages higher than subscriber on
+		'search_terms' => false,    // Limit to blogs that match these search terms
+		'per_page'     => 20,       // The number of results to return per page
+		'page'         => 1,        // The page to return if limiting per page
+	);
+
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	return apply_filters( 'bp_blogs_get_blogs', BP_Blogs_Blog::get( $type, $per_page, $page, $user_id, $search_terms ), $params );
+}
+
+/**
+ * Populates the BP blogs table with existing blogs.
+ *
+ * @package BuddyPress Blogs
+ *
+ * @global object $bp BuddyPress global settings
+ * @global object $wpdb WordPress database object
+ * @uses get_users()
+ * @uses bp_blogs_record_blog()
+ */
+function bp_blogs_record_existing_blogs() {
+	global $bp, $wpdb;
+
+	// Truncate user blogs table and re-record.
+	$wpdb->query( "TRUNCATE TABLE {$bp->blogs->table_name}" );
+
+	if ( is_multisite() )
+		$blog_ids = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM {$wpdb->base_prefix}blogs WHERE mature = 0 AND spam = 0 AND deleted = 0" ) );
+	else
+		$blog_ids = 1;
+
+	if ( $blog_ids ) {
+		foreach( (array)$blog_ids as $blog_id ) {
+			$users 		= get_users( array( 'blog_id' => $blog_id ) );
+			$subscribers 	= get_users( array( 'blog_id' => $blog_id, 'role' => 'subscriber' ) );
+
+			if ( !empty( $users ) ) {
+				foreach ( (array)$users as $user ) {
+					// Don't record blogs for subscribers
+					if ( !in_array( $user, $subscribers ) )
+						bp_blogs_record_blog( $blog_id, $user->ID, true );
+				}
+			}
+		}
+	}
+}
+
+/**
+ * Makes BuddyPress aware of a new site so that it can track its activity.
+ *
+ * @global object $bp BuddyPress global settings
+ * @param int $blog_id
+ * @param int $user_id
+ * @param $bool $no_activity ; optional.
+ * @since 1.0
+ * @uses BP_Blogs_Blog
+ */
+function bp_blogs_record_blog( $blog_id, $user_id, $no_activity = false ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	$name = get_blog_option( $blog_id, 'blogname' );
+	$description = get_blog_option( $blog_id, 'blogdescription' );
+
+	if ( empty( $name ) )
+		return false;
+
+	$recorded_blog          = new BP_Blogs_Blog;
+	$recorded_blog->user_id = $user_id;
+	$recorded_blog->blog_id = $blog_id;
+
+	$recorded_blog_id = $recorded_blog->save();
+
+	$is_recorded = !empty( $recorded_blog_id ) ? true : false;
+
+	bp_blogs_update_blogmeta( $recorded_blog->blog_id, 'name', $name );
+	bp_blogs_update_blogmeta( $recorded_blog->blog_id, 'description', $description );
+	bp_blogs_update_blogmeta( $recorded_blog->blog_id, 'last_activity', bp_core_current_time() );
+
+	$is_private = !empty( $_POST['blog_public'] ) && (int)$_POST['blog_public'] ? false : true;
+	$is_private = !apply_filters( 'bp_is_new_blog_public', !$is_private );
+
+	// Only record this activity if the blog is public
+	if ( !$is_private && !$no_activity ) {
+		// Record this in activity streams
+		bp_blogs_record_activity( array(
+			'user_id'      => $recorded_blog->user_id,
+			'action'       => apply_filters( 'bp_blogs_activity_created_blog_action', sprintf( __( '%s created the site %s', 'buddypress'), bp_core_get_userlink( $recorded_blog->user_id ), '<a href="' . get_site_url( $recorded_blog->blog_id ) . '">' . esc_attr( $name ) . '</a>' ), $recorded_blog, $name, $description ),
+			'primary_link' => apply_filters( 'bp_blogs_activity_created_blog_primary_link', get_site_url( $recorded_blog->blog_id ), $recorded_blog->blog_id ),
+			'type'         => 'new_blog',
+			'item_id'      => $recorded_blog->blog_id
+		) );
+	}
+
+	do_action_ref_array( 'bp_blogs_new_blog', array( &$recorded_blog, $is_private, $is_recorded ) );
+}
+add_action( 'wpmu_new_blog', 'bp_blogs_record_blog', 10, 2 );
+
+/**
+ * Updates blogname in BuddyPress blogmeta table
+ *
+ * @global object $wpdb DB Layer
+ * @param string $oldvalue Value before save (not used)
+ * @param string $newvalue Value to change meta to
+ */
+function bp_blogs_update_option_blogname( $oldvalue, $newvalue ) {
+	global $wpdb;
+
+	bp_blogs_update_blogmeta( $wpdb->blogid, 'name', $newvalue );
+}
+add_action( 'update_option_blogname', 'bp_blogs_update_option_blogname', 10, 2 );
+
+/**
+ * Updates blogdescription in BuddyPress blogmeta table
+ *
+ * @global object $wpdb DB Layer
+ * @param string $oldvalue Value before save (not used)
+ * @param string $newvalue Value to change meta to
+ */
+function bp_blogs_update_option_blogdescription( $oldvalue, $newvalue ) {
+	global $wpdb;
+
+	bp_blogs_update_blogmeta( $wpdb->blogid, 'description', $newvalue );
+}
+add_action( 'update_option_blogdescription', 'bp_blogs_update_option_blogdescription', 10, 2 );
+
+function bp_blogs_record_post( $post_id, $post, $user_id = 0 ) {
+	global $bp, $wpdb;
+
+	$post_id = (int)$post_id;
+	$blog_id = (int)$wpdb->blogid;
+
+	if ( !$user_id )
+		$user_id = (int)$post->post_author;
+
+	// This is to stop infinite loops with Donncha's sitewide tags plugin
+	if ( !empty( $bp->site_options['tags_blog_id'] ) && (int)$blog_id == (int)$bp->site_options['tags_blog_id'] )
+		return false;
+
+	// Don't record this if it's not a post
+	if ( !in_array( $post->post_type, apply_filters( 'bp_blogs_record_post_post_types', array( 'post' ) ) ) )
+		return false;
+
+	$is_blog_public = apply_filters( 'bp_is_blog_public', (int)get_blog_option( $blog_id, 'blog_public' ) );
+
+	if ( 'publish' == $post->post_status && empty( $post->post_password ) ) {
+		if ( $is_blog_public || !is_multisite() ) {
+			// Record this in activity streams
+			$post_permalink   = get_permalink( $post_id );
+
+			if ( is_multisite() )
+				$activity_action  = sprintf( __( '%1$s wrote a new post, %2$s, on the site %3$s', 'buddypress' ), bp_core_get_userlink( (int)$post->post_author ), '<a href="' . $post_permalink . '">' . $post->post_title . '</a>', '<a href="' . get_blog_option( $blog_id, 'home' ) . '">' . get_blog_option( $blog_id, 'blogname' ) . '</a>' );
+			else
+				$activity_action  = sprintf( __( '%1$s wrote a new post, %2$s', 'buddypress' ), bp_core_get_userlink( (int)$post->post_author ), '<a href="' . $post_permalink . '">' . $post->post_title . '</a>' );
+
+			$activity_content = $post->post_content;
+
+			bp_blogs_record_activity( array(
+				'user_id'           => (int)$post->post_author,
+				'action'            => apply_filters( 'bp_blogs_activity_new_post_action',       $activity_action,  $post, $post_permalink ),
+				'content'           => apply_filters( 'bp_blogs_activity_new_post_content',      $activity_content, $post, $post_permalink ),
+				'primary_link'      => apply_filters( 'bp_blogs_activity_new_post_primary_link', $post_permalink,   $post_id               ),
+				'type'              => 'new_blog_post',
+				'item_id'           => $blog_id,
+				'secondary_item_id' => $post_id,
+				'recorded_time'     => $post->post_modified_gmt
+			));
+		}
+
+		// Update the blogs last activity
+		bp_blogs_update_blogmeta( $blog_id, 'last_activity', bp_core_current_time() );
+	} else {
+		bp_blogs_remove_post( $post_id, $blog_id, $user_id );
+	}
+
+	do_action( 'bp_blogs_new_blog_post', $post_id, $post, $user_id );
+}
+add_action( 'save_post', 'bp_blogs_record_post', 10, 2 );
+
+/**
+ * Record blog comment activity. Checks if blog is public and post is not
+ * password protected.
+ *
+ * @global $bp $bp
+ * @param int $comment_id
+ * @param bool $is_approved
+ * @return mixed
+ */
+function bp_blogs_record_comment( $comment_id, $is_approved = true ) {
+	global $bp;
+
+	// Get the users comment
+	$recorded_comment = get_comment( $comment_id );
+
+	// Don't record activity if the comment hasn't been approved
+	if ( empty( $is_approved ) )
+		return false;
+
+	// Don't record activity if no email address has been included
+	if ( empty( $recorded_comment->comment_author_email ) )
+		return false;
+
+	// Get the user_id from the comment author email.
+	$user    = get_user_by_email( $recorded_comment->comment_author_email );
+	$user_id = (int)$user->ID;
+
+	// If there's no registered user id, don't record activity
+	if ( empty( $user_id ) )
+		return false;
+
+	// Get blog and post data
+	$blog_id                = get_current_blog_id();
+	$recorded_comment->post = get_post( $recorded_comment->comment_post_ID );
+
+	if ( empty( $recorded_comment->post ) || is_wp_error( $recorded_comment->post ) )
+		return false;
+
+	// If this is a password protected post, don't record the comment
+	if ( !empty( $recorded_comment->post->post_password ) )
+		return false;
+
+	// Don't record activity if the comment's associated post isn't a WordPress Post
+	if ( !in_array( $recorded_comment->post->post_type, apply_filters( 'bp_blogs_record_comment_post_types', array( 'post' ) ) ) )
+		return false;
+
+	$is_blog_public = apply_filters( 'bp_is_blog_public', (int)get_blog_option( $blog_id, 'blog_public' ) );
+
+	// If blog is public allow activity to be posted
+	if ( $is_blog_public ) {
+
+		// Get activity related links
+		$post_permalink = get_permalink( $recorded_comment->comment_post_ID );
+		$comment_link   = htmlspecialchars( get_comment_link( $recorded_comment->comment_ID ) );
+
+		// Prepare to record in activity streams
+		if ( is_multisite() )
+			$activity_action = sprintf( __( '%1$s commented on the post, %2$s, on the site %3$s', 'buddypress' ), bp_core_get_userlink( $user_id ), '<a href="' . $post_permalink . '">' . apply_filters( 'the_title', $recorded_comment->post->post_title ) . '</a>', '<a href="' . get_blog_option( $blog_id, 'home' ) . '">' . get_blog_option( $blog_id, 'blogname' ) . '</a>' );
+		else
+			$activity_action = sprintf( __( '%1$s commented on the post, %2$s', 'buddypress' ), bp_core_get_userlink( $user_id ), '<a href="' . $post_permalink . '">' . apply_filters( 'the_title', $recorded_comment->post->post_title ) . '</a>' );
+
+		$activity_content	= $recorded_comment->comment_content;
+
+		// Record in activity streams
+		bp_blogs_record_activity( array(
+			'user_id'           => $user_id,
+			'action'            => apply_filters_ref_array( 'bp_blogs_activity_new_comment_action',       array( $activity_action,  &$recorded_comment, $comment_link ) ),
+			'content'           => apply_filters_ref_array( 'bp_blogs_activity_new_comment_content',      array( $activity_content, &$recorded_comment, $comment_link ) ),
+			'primary_link'      => apply_filters_ref_array( 'bp_blogs_activity_new_comment_primary_link', array( $comment_link,     &$recorded_comment                ) ),
+			'type'              => 'new_blog_comment',
+			'item_id'           => $blog_id,
+			'secondary_item_id' => $comment_id,
+			'recorded_time'     => $recorded_comment->comment_date_gmt
+		) );
+
+		// Update the blogs last active date
+		bp_blogs_update_blogmeta( $blog_id, 'last_activity', bp_core_current_time() );
+	}
+
+	return $recorded_comment;
+}
+add_action( 'comment_post', 'bp_blogs_record_comment', 10, 2 );
+add_action( 'edit_comment', 'bp_blogs_record_comment', 10    );
+
+function bp_blogs_manage_comment( $comment_id, $comment_status ) {
+	if ( 'spam' == $comment_status || 'hold' == $comment_status || 'delete' == $comment_status || 'trash' == $comment_status )
+		return bp_blogs_remove_comment( $comment_id );
+
+	return bp_blogs_record_comment( $comment_id, true );
+}
+add_action( 'wp_set_comment_status', 'bp_blogs_manage_comment', 10, 2 );
+
+function bp_blogs_add_user_to_blog( $user_id, $role = false, $blog_id = 0 ) {
+	global $wpdb;
+
+	if ( empty( $blog_id ) && isset( $wpdb->blogid ) )
+		$blog_id = $wpdb->blogid;
+	else
+		$blog_id = bp_get_root_blog_id();
+
+	if ( empty( $role ) ) {
+		$key = $wpdb->get_blog_prefix( $blog_id ). 'capabilities';
+
+		$roles = get_user_meta( $user_id, $key, true );
+
+		if ( is_array( $roles ) )
+			$role = array_search( 1, $roles );
+		else
+			return false;
+	}
+
+	if ( $role != 'subscriber' )
+		bp_blogs_record_blog( $blog_id, $user_id, true );
+}
+add_action( 'add_user_to_blog', 'bp_blogs_add_user_to_blog', 10, 3 );
+add_action( 'profile_update',   'bp_blogs_add_user_to_blog'        );
+add_action( 'user_register',    'bp_blogs_add_user_to_blog'        );
+
+function bp_blogs_remove_user_from_blog( $user_id, $blog_id = 0 ) {
+	global $wpdb;
+
+	if ( empty( $blog_id ) )
+		$blog_id = $wpdb->blogid;
+
+	bp_blogs_remove_blog_for_user( $user_id, $blog_id );
+}
+add_action( 'remove_user_from_blog', 'bp_blogs_remove_user_from_blog', 10, 2 );
+
+function bp_blogs_remove_blog( $blog_id ) {
+	global $bp;
+
+	$blog_id = (int)$blog_id;
+	do_action( 'bp_blogs_before_remove_blog', $blog_id );
+
+	BP_Blogs_Blog::delete_blog_for_all( $blog_id );
+
+	// Delete activity stream item
+	bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'component' => $bp->blogs->id, 'type' => 'new_blog' ) );
+
+	do_action( 'bp_blogs_remove_blog', $blog_id );
+}
+add_action( 'delete_blog', 'bp_blogs_remove_blog' );
+
+function bp_blogs_remove_blog_for_user( $user_id, $blog_id ) {
+	global $bp, $current_user;
+
+	$blog_id = (int)$blog_id;
+	$user_id = (int)$user_id;
+
+	do_action( 'bp_blogs_before_remove_blog_for_user', $blog_id, $user_id );
+
+	BP_Blogs_Blog::delete_blog_for_user( $blog_id, $user_id );
+
+	// Delete activity stream item
+	bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'component' => $bp->blogs->id, 'type' => 'new_blog' ) );
+
+	do_action( 'bp_blogs_remove_blog_for_user', $blog_id, $user_id );
+}
+add_action( 'remove_user_from_blog', 'bp_blogs_remove_blog_for_user', 10, 2 );
+
+function bp_blogs_remove_post( $post_id, $blog_id = 0, $user_id = 0 ) {
+	global $wpdb, $bp;
+
+	if ( empty( $wpdb->blogid ) )
+		return false;
+
+	$post_id = (int)$post_id;
+
+	if ( !$blog_id )
+		$blog_id = (int)$wpdb->blogid;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	do_action( 'bp_blogs_before_remove_post', $blog_id, $post_id, $user_id );
+
+	// Delete activity stream item
+	bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'secondary_item_id' => $post_id, 'component' => $bp->blogs->id, 'type' => 'new_blog_post' ) );
+
+	do_action( 'bp_blogs_remove_post', $blog_id, $post_id, $user_id );
+}
+add_action( 'delete_post', 'bp_blogs_remove_post' );
+
+function bp_blogs_remove_comment( $comment_id ) {
+	global $wpdb, $bp;
+
+	// Delete activity stream item
+	bp_blogs_delete_activity( array( 'item_id' => $wpdb->blogid , 'secondary_item_id' => $comment_id, 'type' => 'new_blog_comment' ) );
+
+	do_action( 'bp_blogs_remove_comment', $blog_id, $comment_id, $bp->loggedin_user->id );
+}
+add_action( 'delete_comment', 'bp_blogs_remove_comment' );
+
+function bp_blogs_total_blogs() {
+	if ( !$count = wp_cache_get( 'bp_total_blogs', 'bp' ) ) {
+		$blogs = BP_Blogs_Blog::get_all();
+		$count = $blogs['total'];
+		wp_cache_set( 'bp_total_blogs', $count, 'bp' );
+	}
+	return $count;
+}
+
+function bp_blogs_total_blogs_for_user( $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	if ( !$count = wp_cache_get( 'bp_total_blogs_for_user_' . $user_id, 'bp' ) ) {
+		$count = BP_Blogs_Blog::total_blog_count_for_user( $user_id );
+		wp_cache_set( 'bp_total_blogs_for_user_' . $user_id, $count, 'bp' );
+	}
+
+	return $count;
+}
+
+function bp_blogs_remove_data_for_blog( $blog_id ) {
+	global $bp;
+
+	do_action( 'bp_blogs_before_remove_data_for_blog', $blog_id );
+
+	// If this is regular blog, delete all data for that blog.
+	BP_Blogs_Blog::delete_blog_for_all( $blog_id );
+
+	// Delete activity stream item
+	bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'component' => $bp->blogs->id, 'type' => false ) );
+
+	do_action( 'bp_blogs_remove_data_for_blog', $blog_id );
+}
+add_action( 'delete_blog', 'bp_blogs_remove_data_for_blog', 1 );
+
+function bp_blogs_get_blogs_for_user( $user_id, $show_hidden = false ) {
+	return BP_Blogs_Blog::get_blogs_for_user( $user_id, $show_hidden );
+}
+
+function bp_blogs_get_all_blogs( $limit = null, $page = null ) {
+	return BP_Blogs_Blog::get_all( $limit, $page );
+}
+
+function bp_blogs_get_random_blogs( $limit = null, $page = null ) {
+	return BP_Blogs_Blog::get( 'random', $limit, $page );
+}
+
+function bp_blogs_is_blog_hidden( $blog_id ) {
+	return BP_Blogs_Blog::is_hidden( $blog_id );
+}
+
+/*******************************************************************************
+ * Blog meta functions
+ *
+ * These functions are used to store specific blogmeta in one global table,
+ * rather than in each blog's options table. Significantly speeds up global blog
+ * queries. By default each blog's name, description and last updated time are
+ * stored and synced here.
+ */
+
+function bp_blogs_delete_blogmeta( $blog_id, $meta_key = false, $meta_value = false ) {
+	global $wpdb, $bp;
+
+	if ( !is_numeric( $blog_id ) )
+		return false;
+
+	$meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key);
+
+	if ( is_array($meta_value) || is_object($meta_value) )
+		$meta_value = serialize($meta_value);
+
+	$meta_value = trim( $meta_value );
+
+	if ( !$meta_key )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d", $blog_id ) );
+	else if ( $meta_value )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d AND meta_key = %s AND meta_value = %s", $blog_id, $meta_key, $meta_value ) );
+	else
+		$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d AND meta_key = %s", $blog_id, $meta_key ) );
+
+	wp_cache_delete( 'bp_blogs_blogmeta_' . $blog_id . '_' . $meta_key, 'bp' );
+
+	return true;
+}
+
+function bp_blogs_get_blogmeta( $blog_id, $meta_key = '') {
+	global $wpdb, $bp;
+
+	$blog_id = (int) $blog_id;
+
+	if ( !$blog_id )
+		return false;
+
+	if ( !empty($meta_key) ) {
+		$meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key);
+
+		if ( !$metas = wp_cache_get( 'bp_blogs_blogmeta_' . $blog_id . '_' . $meta_key, 'bp' ) ) {
+			$metas = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d AND meta_key = %s", $blog_id, $meta_key ) );
+			wp_cache_set( 'bp_blogs_blogmeta_' . $blog_id . '_' . $meta_key, $metas, 'bp' );
+		}
+	} else {
+		$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d", $blog_id) );
+	}
+
+	if ( empty($metas) ) {
+		if ( empty($meta_key) )
+			return array();
+		else
+			return '';
+	}
+
+	$metas = array_map('maybe_unserialize', (array)$metas);
+
+	if ( 1 == count($metas) )
+		return $metas[0];
+	else
+		return $metas;
+}
+
+function bp_blogs_update_blogmeta( $blog_id, $meta_key, $meta_value ) {
+	global $wpdb, $bp;
+
+	if ( !is_numeric( $blog_id ) )
+		return false;
+
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_string($meta_value) )
+		$meta_value = stripslashes($wpdb->escape($meta_value));
+
+	$meta_value = maybe_serialize($meta_value);
+
+	if (empty( $meta_value ) )
+		return bp_blogs_delete_blogmeta( $blog_id, $meta_key );
+
+	$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->blogs->table_name_blogmeta} WHERE blog_id = %d AND meta_key = %s", $blog_id, $meta_key ) );
+
+	if ( !$cur )
+		$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->blogs->table_name_blogmeta} ( blog_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", $blog_id, $meta_key, $meta_value ) );
+	else if ( $cur->meta_value != $meta_value )
+		$wpdb->query( $wpdb->prepare( "UPDATE {$bp->blogs->table_name_blogmeta} SET meta_value = %s WHERE blog_id = %d AND meta_key = %s", $meta_value, $blog_id, $meta_key ) );
+	else
+		return false;
+
+	wp_cache_set( 'bp_blogs_blogmeta_' . $blog_id . '_' . $meta_key, $meta_value, 'bp' );
+
+	return true;
+}
+
+function bp_blogs_remove_data( $user_id ) {
+	if ( !is_multisite() )
+		return false;
+
+	do_action( 'bp_blogs_before_remove_data', $user_id );
+
+	// If this is regular blog, delete all data for that blog.
+	BP_Blogs_Blog::delete_blogs_for_user( $user_id );
+
+	do_action( 'bp_blogs_remove_data', $user_id );
+}
+add_action( 'wpmu_delete_user',  'bp_blogs_remove_data' );
+add_action( 'delete_user',       'bp_blogs_remove_data' );
+add_action( 'bp_make_spam_user', 'bp_blogs_remove_data' );
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-loader.php	(revision 5057)
@@ -0,0 +1,196 @@
+<?php
+/**
+ * BuddyPress Blogs Streams Loader
+ *
+ * An blogs stream component, for users, groups, and blog tracking.
+ *
+ * @package BuddyPress
+ * @subpackage Blogs Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Blogs_Component extends BP_Component {
+
+	/**
+	 * Start the blogs component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'blogs',
+			__( 'Site Tracking', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_BLOGS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		if ( !defined( 'BP_BLOGS_SLUG' ) )
+			define ( 'BP_BLOGS_SLUG', $this->id );
+
+		// Global tables for messaging component
+		$global_tables = array(
+			'table_name'          => $bp->table_prefix . 'bp_user_blogs',
+			'table_name_blogmeta' => $bp->table_prefix . 'bp_user_blogs_blogmeta',
+		);
+
+		// All globals for messaging component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_BLOGS_SLUG,
+			'root_slug'             => isset( $bp->pages->blogs->slug ) ? $bp->pages->blogs->slug : BP_BLOGS_SLUG,
+			'has_directory'         => is_multisite(), // Non-multisite installs don't need a top-level Sites directory, since there's only one site
+			'notification_callback' => 'bp_blogs_format_notifications',
+			'search_string'         => __( 'Search sites...', 'buddypress' ),
+			'autocomplete_all'      => defined( 'BP_MESSAGES_AUTOCOMPLETE_ALL' ),
+			'global_tables'         => $global_tables,
+		);
+
+		// Setup the globals
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		// Files to include
+		$includes = array(
+			'cache',
+			'actions',
+			'screens',
+			'classes',
+			'template',
+			'activity',
+			'functions',
+			'buddybar'
+		);
+
+		if ( is_multisite() )
+			$includes[] = 'widgets';
+
+		// Include the files
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		/**
+		 * Blog/post/comment menus should not appear on single WordPress setups.
+		 * Although comments and posts made by users will still show on their
+		 * activity stream.
+		 */
+		if ( !is_multisite() )
+			return false;
+
+		// Add 'Sites' to the main navigation
+		$main_nav =  array(
+			'name'                => sprintf( __( 'Sites <span>%d</span>', 'buddypress' ), bp_blogs_total_blogs_for_user() ),
+			'slug'                => $this->slug,
+			'position'            => 30,
+			'screen_function'     => 'bp_blogs_screen_my_blogs',
+			'default_subnav_slug' => 'my-blogs',
+			'item_css_id'         => $this->id
+		);
+
+		// Setup navigation
+		parent::setup_nav( $main_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		/**
+		 * Blog/post/comment menus should not appear on single WordPress setups.
+		 * Although comments and posts made by users will still show on their
+		 * activity stream.
+		 */
+		if ( !is_multisite() )
+			return false;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			$blogs_link = trailingslashit( $bp->loggedin_user->domain . $this->slug );
+
+			// Add the "Blogs" sub menu
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => __( 'Sites', 'buddypress' ),
+				'href'   => trailingslashit( $blogs_link )
+			);
+
+			// My Blogs
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'My Sites', 'buddypress' ),
+				'href'   => trailingslashit( $blogs_link . 'my-blogs' )
+			);
+
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		// Set up the component options navigation for Blog
+		if ( bp_is_blogs_component() ) {
+			if ( bp_is_my_profile() ) {
+				if ( bp_is_active( 'xprofile' ) ) {
+					$bp->bp_options_title = __( 'My Sites', 'buddypress' );
+				}
+
+			// If we are not viewing the logged in user, set up the current
+			// users avatar and name
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the blogs component
+$bp->blogs = new BP_Blogs_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-screens.php	(revision 5057)
@@ -0,0 +1,38 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_blogs_screen_my_blogs() {
+	if ( !is_multisite() )
+		return false;
+
+	do_action( 'bp_blogs_screen_my_blogs' );
+
+	bp_core_load_template( apply_filters( 'bp_blogs_template_my_blogs', 'members/single/home' ) );
+}
+
+function bp_blogs_screen_create_a_blog() {
+	if ( !is_multisite() ||  !bp_is_blogs_component() || !bp_is_current_action( 'create' ) )
+		return false;
+
+	if ( !is_user_logged_in() || !bp_blog_signup_enabled() )
+		return false;
+
+	do_action( 'bp_blogs_screen_create_a_blog' );
+
+	bp_core_load_template( apply_filters( 'bp_blogs_template_create_a_blog', 'blogs/create' ) );
+}
+add_action( 'bp_screens', 'bp_blogs_screen_create_a_blog', 3 );
+
+function bp_blogs_screen_index() {
+	if ( is_multisite() && bp_is_blogs_component() && !bp_current_action() ) {
+		bp_update_is_directory( true, 'blogs' );
+
+		do_action( 'bp_blogs_screen_index' );
+
+		bp_core_load_template( apply_filters( 'bp_blogs_screen_index', 'blogs/index' ) );
+	}
+}
+add_action( 'bp_screens', 'bp_blogs_screen_index', 2 );
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-template.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-template.php	(revision 5057)
@@ -0,0 +1,639 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the blogs component slug
+ *
+ * @package BuddyPress
+ * @subpackage Blogs Template
+ * @since BuddyPress (r4100)
+ *
+ * @uses bp_get_blogs_slug()
+ */
+function bp_blogs_slug() {
+	echo bp_get_blogs_slug();
+}
+	/**
+	 * Return the blogs component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Blogs Template
+	 * @since BuddyPress (r4100)
+	 */
+	function bp_get_blogs_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_blogs_slug', $bp->blogs->slug );
+	}
+
+/**
+ * Output the blogs component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Blogs Template
+ * @since BuddyPress (r4100)
+ *
+ * @uses bp_get_blogs_root_slug()
+ */
+function bp_blogs_root_slug() {
+	echo bp_get_blogs_root_slug();
+}
+	/**
+	 * Return the blogs component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Blogs Template
+	 * @since BuddyPress (r4100)
+	 */
+	function bp_get_blogs_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_blogs_root_slug', $bp->blogs->root_slug );
+	}
+
+/**
+ * Output blog directory permalink
+ *
+ * @package BuddyPress
+ * @subpackage Blogs Template
+ * @since 1.5
+ * @uses bp_get_blogs_directory_permalink()
+ */
+function bp_blogs_directory_permalink() {
+	echo bp_get_blogs_directory_permalink();
+}
+	/**
+	 * Return blog directory permalink
+	 *
+	 * @package BuddyPress
+	 * @subpackage Blogs Template
+	 * @since 1.5
+	 * @uses apply_filters()
+	 * @uses traisingslashit()
+	 * @uses bp_get_root_domain()
+	 * @uses bp_get_blogs_root_slug()
+	 * @return string
+	 */
+	function bp_get_blogs_directory_permalink() {
+		return apply_filters( 'bp_get_blogs_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_blogs_root_slug() ) );
+	}
+
+/**********************************************************************
+ * Blog listing template class.
+ */
+
+class BP_Blogs_Template {
+	var $current_blog = -1;
+	var $blog_count;
+	var $blogs;
+	var $blog;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_blog_count;
+
+	function bp_blogs_template( $type, $page, $per_page, $max, $user_id, $search_terms ) {
+		$this->__construct( $type, $page, $per_page, $max, $user_id, $search_terms );
+	}
+
+	function __construct( $type, $page, $per_page, $max, $user_id, $search_terms ) {
+		global $bp;
+
+		$this->pag_page = isset( $_REQUEST['bpage'] ) ? intval( $_REQUEST['bpage'] ) : $page;
+		$this->pag_num = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		if ( isset( $_REQUEST['letter'] ) && '' != $_REQUEST['letter'] )
+			$this->blogs = BP_Blogs_Blog::get_by_letter( $_REQUEST['letter'], $this->pag_num, $this->pag_page );
+		else
+			$this->blogs = bp_blogs_get_blogs( array( 'type' => $type, 'per_page' => $this->pag_num, 'page' => $this->pag_page, 'user_id' => $user_id, 'search_terms' => $search_terms ) );
+
+		if ( !$max || $max >= (int)$this->blogs['total'] )
+			$this->total_blog_count = (int)$this->blogs['total'];
+		else
+			$this->total_blog_count = (int)$max;
+
+		$this->blogs = $this->blogs['blogs'];
+
+		if ( $max ) {
+			if ( $max >= count($this->blogs) ) {
+				$this->blog_count = count( $this->blogs );
+			} else {
+				$this->blog_count = (int)$max;
+			}
+		} else {
+			$this->blog_count = count( $this->blogs );
+		}
+
+		if ( (int)$this->total_blog_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( 'bpage', '%#%' ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_blog_count / (int)$this->pag_num ),
+				'current'   => (int)$this->pag_page,
+				'prev_text' => _x( '&larr;', 'Blog pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Blog pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+		}
+	}
+
+	function has_blogs() {
+		if ( $this->blog_count )
+			return true;
+
+		return false;
+	}
+
+	function next_blog() {
+		$this->current_blog++;
+		$this->blog = $this->blogs[$this->current_blog];
+
+		return $this->blog;
+	}
+
+	function rewind_blogs() {
+		$this->current_blog = -1;
+		if ( $this->blog_count > 0 ) {
+			$this->blog = $this->blogs[0];
+		}
+	}
+
+	function blogs() {
+		if ( $this->current_blog + 1 < $this->blog_count ) {
+			return true;
+		} elseif ( $this->current_blog + 1 == $this->blog_count ) {
+			do_action('blog_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_blogs();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_blog() {
+		global $blog;
+
+		$this->in_the_loop = true;
+		$this->blog        = $this->next_blog();
+
+		if ( 0 == $this->current_blog ) // loop has just started
+			do_action('blog_loop_start');
+	}
+}
+
+function bp_rewind_blogs() {
+	global $blogs_template;
+
+	$blogs_template->rewind_blogs();
+}
+
+function bp_has_blogs( $args = '' ) {
+	global $bp, $blogs_template;
+
+	/***
+	 * Set the defaults based on the current page. Any of these will be overridden
+	 * if arguments are directly passed into the loop. Custom plugins should always
+	 * pass their parameters directly to the loop.
+	 */
+	$type         = 'active';
+	$user_id      = 0;
+	$search_terms = null;
+
+	/* User filtering */
+	if ( !empty( $bp->displayed_user->id ) )
+		$user_id = $bp->displayed_user->id;
+
+	$defaults = array(
+		'type'         => $type,
+		'page'         => 1,
+		'per_page'     => 20,
+		'max'          => false,
+
+		'user_id'      => $user_id, // Pass a user_id to limit to only blogs this user has higher than subscriber access to
+		'search_terms' => $search_terms // Pass search terms to filter on the blog title or description.
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	if ( is_null( $search_terms ) ) {
+		if ( isset( $_REQUEST['s'] ) && !empty( $_REQUEST['s'] ) )
+			$search_terms = $_REQUEST['s'];
+		else
+			$search_terms = false;
+	}
+
+	if ( $max ) {
+		if ( $per_page > $max )
+			$per_page = $max;
+	}
+
+	$blogs_template = new BP_Blogs_Template( $type, $page, $per_page, $max, $user_id, $search_terms );
+	return apply_filters( 'bp_has_blogs', $blogs_template->has_blogs(), $blogs_template );
+}
+
+function bp_blogs() {
+	global $blogs_template;
+
+	return $blogs_template->blogs();
+}
+
+function bp_the_blog() {
+	global $blogs_template;
+
+	return $blogs_template->the_blog();
+}
+
+function bp_blogs_pagination_count() {
+	global $bp, $blogs_template;
+
+	$start_num = intval( ( $blogs_template->pag_page - 1 ) * $blogs_template->pag_num ) + 1;
+	$from_num  = bp_core_number_format( $start_num );
+	$to_num    = bp_core_number_format( ( $start_num + ( $blogs_template->pag_num - 1 ) > $blogs_template->total_blog_count ) ? $blogs_template->total_blog_count : $start_num + ( $blogs_template->pag_num - 1 ) );
+	$total     = bp_core_number_format( $blogs_template->total_blog_count );
+
+	echo sprintf( __( 'Viewing site %1$s to %2$s (of %3$s sites)', 'buddypress' ), $from_num, $to_num, $total );
+}
+
+function bp_blogs_pagination_links() {
+	echo bp_get_blogs_pagination_links();
+}
+	function bp_get_blogs_pagination_links() {
+		global $blogs_template;
+
+		return apply_filters( 'bp_get_blogs_pagination_links', $blogs_template->pag_links );
+	}
+
+function bp_blog_avatar( $args = '' ) {
+	echo bp_get_blog_avatar( $args );
+}
+	function bp_get_blog_avatar( $args = '' ) {
+		global $blogs_template, $bp;
+
+		$defaults = array(
+			'type'    => 'full',
+			'width'   => false,
+			'height'  => false,
+			'class'   => 'avatar',
+			'id'      => false,
+			'alt'     => __( 'Site authored by %s', 'buddypress' ),
+			'no_grav' => true
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		/***
+		 * In future BuddyPress versions you will be able to set the avatar for a blog.
+		 * Right now you can use a filter with the ID of the blog to change it if you wish.
+		 * By default it will return the avatar for the primary blog admin.
+		 *
+		 * This filter is deprecated as of BuddyPress 1.5 and may be removed in a future version.
+		 * Use the 'bp_get_blog_avatar' filter instead.
+		 */
+		$avatar = apply_filters( 'bp_get_blog_avatar_' . $blogs_template->blog->blog_id, bp_core_fetch_avatar( array( 'item_id' => $blogs_template->blog->admin_user_id, 'type' => $type, 'alt' => $alt, 'width' => $width, 'height' => $height, 'class' => $class, 'email' => $blogs_template->blog->admin_user_email ) ) );
+
+		return apply_filters( 'bp_get_blog_avatar', $avatar, $blogs_template->blog->blog_id, array( 'item_id' => $blogs_template->blog->admin_user_id, 'type' => $type, 'alt' => $alt, 'width' => $width, 'height' => $height, 'class' => $class, 'email' => $blogs_template->blog->admin_user_email ) );
+	}
+
+function bp_blog_permalink() {
+	echo bp_get_blog_permalink();
+}
+	function bp_get_blog_permalink() {
+		global $blogs_template;
+
+		if ( empty( $blogs_template->blog->domain ) )
+			$permalink = bp_get_root_domain() . $blogs_template->blog->path;
+		else {
+			$protocol = 'http://';
+			if ( is_ssl() )
+				$protocol = 'https://';
+
+			$permalink = $protocol . $blogs_template->blog->domain . $blogs_template->blog->path;
+		}
+
+		return apply_filters( 'bp_get_blog_permalink', $permalink );
+	}
+
+function bp_blog_name() {
+	echo bp_get_blog_name();
+}
+	function bp_get_blog_name() {
+		global $blogs_template;
+
+		return apply_filters( 'bp_get_blog_name', $blogs_template->blog->name );
+	}
+
+function bp_blog_description() {
+	echo apply_filters( 'bp_blog_description', bp_get_blog_description() );
+}
+	function bp_get_blog_description() {
+		global $blogs_template;
+
+		return apply_filters( 'bp_get_blog_description', $blogs_template->blog->description );
+	}
+
+function bp_blog_last_active() {
+	echo bp_get_blog_last_active();
+}
+	function bp_get_blog_last_active() {
+		global $blogs_template;
+
+		return apply_filters( 'bp_blog_last_active', bp_core_get_last_activity( $blogs_template->blog->last_activity, __( 'active %s', 'buddypress' ) ) );
+	}
+
+function bp_blog_latest_post() {
+	echo bp_get_blog_latest_post();
+}
+	function bp_get_blog_latest_post() {
+		global $blogs_template;
+
+		if ( null == $blogs_template->blog->latest_post )
+			return false;
+
+		return apply_filters( 'bp_get_blog_latest_post', sprintf( __( 'Latest Post: %s', 'buddypress' ), '<a href="' . $blogs_template->blog->latest_post->guid . '">' . apply_filters( 'the_title', $blogs_template->blog->latest_post->post_title ) . '</a>' ) );
+	}
+
+function bp_blog_hidden_fields() {
+	if ( isset( $_REQUEST['s'] ) )
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['s'] ). '" name="search_terms" />';
+
+	if ( isset( $_REQUEST['letter'] ) )
+		echo '<input type="hidden" id="selected_letter" value="' . esc_attr( $_REQUEST['letter'] ) . '" name="selected_letter" />';
+
+	if ( isset( $_REQUEST['blogs_search'] ) )
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['blogs_search'] ) . '" name="search_terms" />';
+}
+
+function bp_total_blog_count() {
+	echo bp_get_total_blog_count();
+}
+	function bp_get_total_blog_count() {
+		return apply_filters( 'bp_get_total_blog_count', bp_blogs_total_blogs() );
+	}
+	add_filter( 'bp_get_total_blog_count', 'bp_core_number_format' );
+
+function bp_total_blog_count_for_user( $user_id = 0 ) {
+	echo bp_get_total_blog_count_for_user( $user_id );
+}
+	function bp_get_total_blog_count_for_user( $user_id = 0 ) {
+		return apply_filters( 'bp_get_total_blog_count_for_user', bp_blogs_total_blogs_for_user( $user_id ) );
+	}
+	add_filter( 'bp_get_total_blog_count_for_user', 'bp_core_number_format' );
+
+
+/* Blog registration template tags */
+
+function bp_blog_signup_enabled() {
+	global $bp;
+
+	$active_signup = isset( $bp->site_options['registration'] ) ? $bp->site_options['registration'] : 'all';
+
+	$active_signup = apply_filters( 'wpmu_active_signup', $active_signup ); // return "all", "none", "blog" or "user"
+
+	if ( 'none' == $active_signup || 'user' == $active_signup )
+		return false;
+
+	return true;
+}
+
+function bp_show_blog_signup_form($blogname = '', $blog_title = '', $errors = '') {
+	global $current_user, $current_site;
+	global $bp;
+
+	if ( isset($_POST['submit']) ) {
+		bp_blogs_validate_blog_signup();
+	} else {
+		if ( ! is_wp_error($errors) ) {
+			$errors = new WP_Error();
+		}
+
+		// allow definition of default variables
+		$filtered_results = apply_filters('signup_another_blog_init', array('blogname' => $blogname, 'blog_title' => $blog_title, 'errors' => $errors ));
+		$blogname = $filtered_results['blogname'];
+		$blog_title = $filtered_results['blog_title'];
+		$errors = $filtered_results['errors'];
+
+		if ( $errors->get_error_code() ) {
+			echo "<p>" . __('There was a problem, please correct the form below and try again.', 'buddypress') . "</p>";
+		}
+		?>
+		<p><?php printf(__("By filling out the form below, you can <strong>add a site to your account</strong>. There is no limit to the number of sites that you can have, so create to your heart's content, but blog responsibly!", 'buddypress'), $current_user->display_name) ?></p>
+
+		<p><?php _e("If you&#8217;re not going to use a great domain, leave it for a new user. Now have at it!", 'buddypress') ?></p>
+
+		<form class="standard-form" id="setupform" method="post" action="">
+
+			<input type="hidden" name="stage" value="gimmeanotherblog" />
+			<?php do_action( 'signup_hidden_fields' ); ?>
+
+			<?php bp_blogs_signup_blog($blogname, $blog_title, $errors); ?>
+			<p>
+				<input id="submit" type="submit" name="submit" class="submit" value="<?php _e('Create Site', 'buddypress') ?>" />
+			</p>
+
+			<?php wp_nonce_field( 'bp_blog_signup_form' ) ?>
+		</form>
+		<?php
+	}
+}
+
+function bp_blogs_signup_blog( $blogname = '', $blog_title = '', $errors = '' ) {
+	global $current_site;
+
+	// Blog name
+	if( !is_subdomain_install() )
+		echo '<label for="blogname">' . __('Site Name:', 'buddypress') . '</label>';
+	else
+		echo '<label for="blogname">' . __('Site Domain:', 'buddypress') . '</label>';
+
+	if ( $errmsg = $errors->get_error_message('blogname') ) { ?>
+
+		<p class="error"><?php echo $errmsg ?></p>
+
+	<?php }
+
+	if ( !is_subdomain_install() )
+		echo '<span class="prefix_address">' . $current_site->domain . $current_site->path . '</span> <input name="blogname" type="text" id="blogname" value="'.$blogname.'" maxlength="50" /><br />';
+	else
+		echo '<input name="blogname" type="text" id="blogname" value="'.$blogname.'" maxlength="50" /> <span class="suffix_address">.' . preg_replace( '|^www\.|', '', $current_site->domain ) . $current_site->path . '</span><br />';
+
+	if ( !is_user_logged_in() ) {
+		print '(<strong>' . __( 'Your address will be ' , 'buddypress');
+
+		if ( !is_subdomain_install() ) {
+			print $current_site->domain . $current_site->path . __( 'blogname' , 'buddypress');
+		} else {
+			print __( 'domain.' , 'buddypress') . $current_site->domain . $current_site->path;
+		}
+
+		echo '.</strong> ' . __( 'Must be at least 4 characters, letters and numbers only. It cannot be changed so choose carefully!)' , 'buddypress') . '</p>';
+	}
+
+	// Blog Title
+	?>
+
+	<label for="blog_title"><?php _e('Site Title:', 'buddypress') ?></label>
+
+	<?php if ( $errmsg = $errors->get_error_message('blog_title') ) { ?>
+
+		<p class="error"><?php echo $errmsg ?></p>
+
+	<?php }
+	echo '<input name="blog_title" type="text" id="blog_title" value="'.esc_html($blog_title, 1).'" /></p>';
+	?>
+
+	<p>
+		<label for="blog_public_on"><?php _e('Privacy:', 'buddypress') ?></label>
+		<?php _e( 'I would like my site to appear in search engines, and in public listings around this network.', 'buddypress' ); ?>
+
+		<label class="checkbox" for="blog_public_on">
+			<input type="radio" id="blog_public_on" name="blog_public" value="1" <?php if( !isset( $_POST['blog_public'] ) || '1' == $_POST['blog_public'] ) { ?>checked="checked"<?php } ?> />
+			<strong><?php _e( 'Yes' , 'buddypress'); ?></strong>
+		</label>
+		<label class="checkbox" for="blog_public_off">
+			<input type="radio" id="blog_public_off" name="blog_public" value="0" <?php if( isset( $_POST['blog_public'] ) && '0' == $_POST['blog_public'] ) { ?>checked="checked"<?php } ?> />
+			<strong><?php _e( 'No' , 'buddypress'); ?></strong>
+		</label>
+	</p>
+
+	<?php
+	do_action('signup_blogform', $errors);
+}
+
+function bp_blogs_validate_blog_signup() {
+	global $wpdb, $current_user, $blogname, $blog_title, $errors, $domain, $path, $current_site;
+
+	if ( !check_admin_referer( 'bp_blog_signup_form' ) )
+		return false;
+
+	$current_user = wp_get_current_user();
+
+	if( !is_user_logged_in() )
+		die();
+
+	$result = bp_blogs_validate_blog_form();
+	extract($result);
+
+	if ( $errors->get_error_code() ) {
+		unset($_POST['submit']);
+		bp_show_blog_signup_form( $blogname, $blog_title, $errors );
+		return false;
+	}
+
+	$public = (int) $_POST['blog_public'];
+
+	$meta = apply_filters( 'signup_create_blog_meta', array( 'lang_id' => 1, 'public' => $public ) ); // depreciated
+	$meta = apply_filters( 'add_signup_meta', $meta );
+
+	// If this is a subdomain install, set up the site inside the root domain.
+	if ( is_subdomain_install() )
+		$domain = $blogname . '.' . preg_replace( '|^www\.|', '', $current_site->domain );
+
+	wpmu_create_blog( $domain, $path, $blog_title, $current_user->id, $meta, $wpdb->siteid );
+	bp_blogs_confirm_blog_signup($domain, $path, $blog_title, $current_user->user_login, $current_user->user_email, $meta);
+	return true;
+}
+
+function bp_blogs_validate_blog_form() {
+	$user = '';
+	if ( is_user_logged_in() )
+		$user = wp_get_current_user();
+
+	return wpmu_validate_blog_signup($_POST['blogname'], $_POST['blog_title'], $user);
+}
+
+function bp_blogs_confirm_blog_signup( $domain, $path, $blog_title, $user_name, $user_email = '', $meta = '' ) {
+	$protocol = is_ssl() ? 'https://' : 'http://';
+	$blog_url = $protocol . $domain . $path; ?>
+
+	<p><?php _e( 'Congratulations! You have successfully registered a new site.', 'buddypress' ) ?></p>
+	<p>
+		<?php printf(__( '<a href="%1$s">%2$s</a> is your new site.  <a href="%3$s">Login</a> as "%4$s" using your existing password.', 'buddypress' ), $blog_url, $blog_url, $blog_url . "wp-login.php", $user_name ); ?>
+	</p>
+
+<?php
+	do_action('signup_finished');
+}
+
+function bp_create_blog_link() {
+	global $bp;
+
+	if ( bp_is_my_profile() )
+		echo apply_filters( 'bp_create_blog_link', '<a href="' . bp_get_root_domain() . '/' . bp_get_blogs_root_slug() . '/create/">' . __( 'Create a Site', 'buddypress' ) . '</a>' );
+}
+
+function bp_blogs_blog_tabs() {
+	global $bp, $groups_template;
+
+	// Don't show these tabs on a user's own profile
+	if ( bp_is_my_profile() )
+		return false;
+
+	?>
+
+	<ul class="content-header-nav">
+		<li<?php if ( bp_is_current_action( 'my-blogs'        ) || !bp_current_action() ) : ?> class="current"<?php endif; ?>><a href="<?php echo trailingslashit( bp_displayed_user_domain() . bp_get_blogs_slug() . '/my-blogs'        ); ?>"><?php printf( __( "%s's Sites", 'buddypress' ),           bp_get_displayed_user_fullname() ); ?></a></li>
+		<li<?php if ( bp_is_current_action( 'recent-posts'    )                         ) : ?> class="current"<?php endif; ?>><a href="<?php echo trailingslashit( bp_displayed_user_domain() . bp_get_blogs_slug() . '/recent-posts'    ); ?>"><?php printf( __( "%s's Recent Posts", 'buddypress' ),    bp_get_displayed_user_fullname() ); ?></a></li>
+		<li<?php if ( bp_is_current_action( 'recent-comments' )                         ) : ?> class="current"<?php endif; ?>><a href="<?php echo trailingslashit( bp_displayed_user_domain() . bp_get_blogs_slug() . '/recent-comments' ); ?>"><?php printf( __( "%s's Recent Comments", 'buddypress' ), bp_get_displayed_user_fullname() ); ?></a></li>
+	</ul>
+
+<?php
+	do_action( 'bp_blogs_blog_tabs', $current_tab );
+}
+
+function bp_directory_blogs_search_form() {
+	global $bp;
+
+	$default_search_value = bp_get_search_default_text();
+	$search_value = !empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : $default_search_value; ?>
+
+	<form action="" method="get" id="search-blogs-form">
+		<label><input type="text" name="s" id="blogs_search" value="<?php echo esc_attr( $search_value ) ?>"  onfocus="if (this.value == '<?php echo $default_search_value ?>') {this.value = '';}" onblur="if (this.value == '') {this.value = '<?php echo $default_search_value ?>';}" /></label>
+		<input type="submit" id="blogs_search_submit" name="blogs_search_submit" value="<?php _e( 'Search', 'buddypress' ) ?>" />
+	</form>
+
+<?php
+}
+
+/**
+ * bp_blogs_visit_blog_button()
+ *
+ * Output button for visiting a blog in a loop
+ *
+ * @param array $args Custom button properties
+ */
+function bp_blogs_visit_blog_button( $args = '' ) {
+	echo bp_get_blogs_visit_blog_button( $args );
+}
+	/**
+	 * bp_get_blogs_visit_blog_button()
+	 *
+	 * Return button for visiting a blog in a loop
+	 *
+	 * @param array $args Custom button properties
+	 * @return string
+	 */
+	function bp_get_blogs_visit_blog_button( $args = '' ) {
+		$defaults = array(
+			'id'                => 'visit_blog',
+			'component'         => 'blogs',
+			'must_be_logged_in' => false,
+			'block_self'        => false,
+			'wrapper_class'     => 'blog-button visit',
+			'link_href'         => bp_get_blog_permalink(),
+			'link_class'        => 'blog-button visit',
+			'link_text'         => __( 'Visit Site', 'buddypress' ),
+			'link_title'        => __( 'Visit Site', 'buddypress' ),
+		);
+
+		$button = wp_parse_args( $args, $defaults );
+
+		// Filter and return the HTML button
+		return bp_get_button( apply_filters( 'bp_get_blogs_visit_blog_button', $button ) );
+	}
+
+?>
Index: /tags/1.5-beta-3/bp-blogs/bp-blogs-widgets.php
===================================================================
--- /tags/1.5-beta-3/bp-blogs/bp-blogs-widgets.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-blogs/bp-blogs-widgets.php	(revision 5057)
@@ -0,0 +1,92 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/***
+ * The recent blogs widget is actually just the activity feed filtered on "new_blog_post".
+ * Why not make some of your own widgets using a filtered activity stream?
+ */
+
+function bp_blogs_register_widgets() {
+	global $wpdb, $bp;
+
+	if ( bp_is_active( 'activity' ) && (int)$wpdb->blogid == bp_get_root_blog_id() )
+		add_action('widgets_init', create_function('', 'return register_widget("BP_Blogs_Recent_Posts_Widget");') );
+}
+add_action( 'bp_register_widgets', 'bp_blogs_register_widgets' );
+
+class BP_Blogs_Recent_Posts_Widget extends WP_Widget {
+
+	function bp_blogs_recent_posts_widget() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		parent::__construct( false, $name = __( 'Recent Networkwide Posts', 'buddypress' ) );
+	}
+
+	function widget($args, $instance) {
+		global $bp;
+
+		extract( $args );
+
+		echo $before_widget;
+		echo $before_title . $widget_name . $after_title;
+
+		if ( empty( $instance['max_posts'] ) || !$instance['max_posts'] )
+			$instance['max_posts'] = 10; ?>
+
+		<?php /* Override some of the contextually set parameters for bp_has_activities() */ ?>
+		<?php if ( bp_has_activities( array( 'action' => 'new_blog_post', 'max' => $instance['max_posts'], 'per_page' => $instance['max_posts'], 'user_id' => 0, 'scope' => false, 'object' => false, 'primary_id' => false ) ) ) : ?>
+
+			<ul id="blog-post-list" class="activity-list item-list">
+
+				<?php while ( bp_activities() ) : bp_the_activity(); ?>
+
+					<li>
+						<div class="activity-content" style="margin: 0">
+
+							<div class="activity-header">
+								<?php bp_activity_action() ?>
+							</div>
+
+							<?php if ( bp_get_activity_content_body() ) : ?>
+								<div class="activity-inner">
+									<?php bp_activity_content_body() ?>
+								</div>
+							<?php endif; ?>
+
+						</div>
+					</li>
+
+				<?php endwhile; ?>
+
+			</ul>
+
+		<?php else : ?>
+			<div id="message" class="info">
+				<p><?php _e( 'Sorry, there were no posts found. Why not write one?', 'buddypress' ) ?></p>
+			</div>
+		<?php endif; ?>
+
+		<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['max_posts'] = strip_tags( $new_instance['max_posts'] );
+
+		return $instance;
+	}
+
+	function form( $instance ) {
+		$instance = wp_parse_args( (array) $instance, array( 'max_posts' => 10 ) );
+		$max_posts = strip_tags( $instance['max_posts'] );
+		?>
+
+		<p><label for="bp-blogs-widget-posts-max"><?php _e('Max posts to show:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'max_posts' ); ?>" name="<?php echo $this->get_field_name( 'max_posts' ); ?>" type="text" value="<?php echo esc_attr( $max_posts ); ?>" style="width: 30%" /></label></p>
+	<?php
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-core/admin/bp-core-admin.php
===================================================================
--- /tags/1.5-beta-3/bp-core/admin/bp-core-admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/admin/bp-core-admin.php	(revision 5057)
@@ -0,0 +1,607 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Add an extra update message to the update plugin notification.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_update_message() {
+	echo '<p style="color: red; margin: 3px 0 0 0; border-top: 1px solid #ddd; padding-top: 3px">' . __( 'IMPORTANT: <a href="http://codex.buddypress.org/buddypress-site-administration/upgrading-buddypress/">Read this before attempting to update BuddyPress</a>', 'buddypress' ) . '</p>';
+}
+add_action( 'in_plugin_update_message-buddypress/bp-loader.php', 'bp_core_update_message' );
+
+/**
+ * Renders the Settings admin panel.
+ *
+ * @package BuddyPress Core
+ * @since {@internal Unknown}}
+ */
+function bp_core_admin_settings() {
+	global $wpdb, $bp;
+
+	$ud = get_userdata( $bp->loggedin_user->id );
+
+	if ( isset( $_POST['bp-admin-submit'] ) && isset( $_POST['bp-admin'] ) ) {
+		if ( !check_admin_referer('bp-admin') )
+			return false;
+
+		// Settings form submitted, now save the settings.
+		foreach ( (array)$_POST['bp-admin'] as $key => $value )
+			bp_update_option( $key, $value );
+
+	} ?>
+
+	<div class="wrap">
+
+		<?php screen_icon( 'buddypress' ); ?>
+
+		<h2 class="nav-tab-wrapper">
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-general-settings'                 ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Components', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-page-settings'                    ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Pages', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-settings'                         ), 'admin.php' ) ); ?>" class="nav-tab nav-tab-active"><?php _e( 'Settings', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bb-forums-setup' ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Forum Setup', 'buddypress' ); ?></a>
+
+			<?php do_action( 'bp_admin_tabs' ); ?>
+		</h2>
+
+		<?php if ( isset( $_POST['bp-admin'] ) ) : ?>
+
+			<div id="message" class="updated fade">
+				<p><?php _e( 'Settings Saved', 'buddypress' ); ?></p>
+			</div>
+
+		<?php endif; ?>
+
+		<form action="" method="post" id="bp-admin-form">
+
+			<table class="form-table">
+				<tbody>
+
+					<?php if ( bp_is_active( 'xprofile' ) ) : ?>
+
+						<tr>
+							<th scope="row"><?php _e( 'Disable BuddyPress to WordPress profile syncing?', 'buddypress' ) ?></th>
+							<td>
+								<input type="radio" name="bp-admin[bp-disable-profile-sync]"<?php if ( (int)bp_get_option( 'bp-disable-profile-sync' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-profile-sync" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+								<input type="radio" name="bp-admin[bp-disable-profile-sync]"<?php if ( !(int)bp_get_option( 'bp-disable-profile-sync' ) || '' == bp_get_option( 'bp-disable-profile-sync' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-profile-sync" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+							</td>
+						</tr>
+
+					<?php endif; ?>
+
+					<tr>
+						<th scope="row"><?php _e( 'Hide admin bar for logged out users?', 'buddypress' ) ?></th>
+						<td>
+							<input type="radio" name="bp-admin[hide-loggedout-adminbar]"<?php if ( (int)bp_get_option( 'hide-loggedout-adminbar' ) ) : ?> checked="checked"<?php endif; ?> id="bp-admin-hide-loggedout-adminbar-yes" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+							<input type="radio" name="bp-admin[hide-loggedout-adminbar]"<?php if ( !(int)bp_get_option( 'hide-loggedout-adminbar' ) ) : ?> checked="checked"<?php endif; ?> id="bp-admin-hide-loggedout-adminbar-no" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+						</td>
+					</tr>
+
+					<tr>
+						<th scope="row"><?php _e( 'Disable avatar uploads? (Gravatars will still work)', 'buddypress' ) ?></th>
+						<td>
+							<input type="radio" name="bp-admin[bp-disable-avatar-uploads]"<?php if ( (int)bp_get_option( 'bp-disable-avatar-uploads' ) ) : ?> checked="checked"<?php endif; ?> id="bp-admin-disable-avatar-uploads-yes" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+							<input type="radio" name="bp-admin[bp-disable-avatar-uploads]"<?php if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) : ?> checked="checked"<?php endif; ?> id="bp-admin-disable-avatar-uploads-no" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+						</td>
+					</tr>
+
+					<tr>
+						<th scope="row"><?php _e( 'Disable user account deletion?', 'buddypress' ) ?></th>
+						<td>
+							<input type="radio" name="bp-admin[bp-disable-account-deletion]"<?php if ( (int)bp_get_option( 'bp-disable-account-deletion' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-account-deletion" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+							<input type="radio" name="bp-admin[bp-disable-account-deletion]"<?php if ( !(int)bp_get_option( 'bp-disable-account-deletion' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-account-deletion" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+						</td>
+					</tr>
+
+					<?php if ( bp_is_active( 'activity' ) ) : ?>
+
+						<tr>
+							<th scope="row"><?php _e( 'Disable activity stream commenting on blog and forum posts?', 'buddypress' ) ?></th>
+							<td>
+								<input type="radio" name="bp-admin[bp-disable-blogforum-comments]"<?php if ( (int)bp_get_option( 'bp-disable-blogforum-comments' ) || false === bp_get_option( 'bp-disable-blogforum-comments' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-blogforum-comments" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+								<input type="radio" name="bp-admin[bp-disable-blogforum-comments]"<?php if ( !(int)bp_get_option( 'bp-disable-blogforum-comments' ) ) : ?> checked="checked"<?php endif; ?> id="bp-disable-blogforum-comments" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+							</td>
+						</tr>
+
+					<?php endif; ?>
+
+					<?php if ( bp_is_active( 'groups' ) ) : ?>
+
+						<tr>
+							<th scope="row"><?php _e( 'Restrict group creation to Site Admins?', 'buddypress' ) ?></th>
+							<td>
+								<input type="radio" name="bp-admin[bp_restrict_group_creation]"<?php checked( '1', bp_get_option( 'bp_restrict_group_creation', '0' ) ); ?>id="bp-restrict-group-creation" value="1" /> <?php _e( 'Yes', 'buddypress' ) ?> &nbsp;
+								<input type="radio" name="bp-admin[bp_restrict_group_creation]"<?php checked( '0', bp_get_option( 'bp_restrict_group_creation', '0' ) ); ?>id="bp-restrict-group-creation" value="0" /> <?php _e( 'No', 'buddypress' ) ?>
+							</td>
+						</tr>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_core_admin_screen_fields' ) ?>
+
+				</tbody>
+			</table>
+
+			<?php do_action( 'bp_core_admin_screen' ); ?>
+
+			<p class="submit">
+				<input class="button-primary" type="submit" name="bp-admin-submit" id="bp-admin-submit" value="<?php _e( 'Save Settings', 'buddypress' ); ?>" />
+			</p>
+
+			<?php wp_nonce_field( 'bp-admin' ); ?>
+
+		</form>
+
+	</div>
+
+<?php
+}
+
+function bp_core_admin_component_setup_handler() {
+	global $wpdb, $bp;
+
+	if ( isset( $_POST['bp-admin-component-submit'] ) ) {
+		if ( !check_admin_referer('bp-admin-component-setup') )
+			return false;
+
+		// Settings form submitted, now save the settings. First, set active components
+		if ( isset( $_POST['bp_components'] ) ) {
+			// Save settings and upgrade schema
+			require( BP_PLUGIN_DIR . '/bp-core/admin/bp-core-update.php' );
+			$bp->active_components = stripslashes_deep( $_POST['bp_components'] );
+			bp_core_install( $bp->active_components );
+
+			bp_update_option( 'bp-active-components', $bp->active_components );
+		}
+
+		$base_url = bp_get_admin_url(  add_query_arg( array( 'page' => 'bp-general-settings', 'updated' => 'true' ), 'admin.php' ) );
+
+		wp_redirect( $base_url );
+	}
+}
+add_action( 'admin_init', 'bp_core_admin_component_setup_handler' );
+
+function bp_core_admin_pages_setup_handler() {
+	global $wpdb, $bp;
+
+	if ( isset( $_POST['bp-admin-pages-submit'] ) || isset( $_POST['bp-admin-pages-single'] ) ) {
+		if ( !check_admin_referer( 'bp-admin-pages-setup' ) )
+			return false;
+
+		// Then, update the directory pages
+		if ( isset( $_POST['bp_pages'] ) ) {
+
+			$directory_pages = array();
+
+			foreach ( (array)$_POST['bp_pages'] as $key => $value ) {
+				if ( !empty( $value ) ) {
+					$directory_pages[$key] = (int)$value;
+				}
+			}
+			bp_core_update_directory_page_ids( $directory_pages );
+		}
+
+		$base_url = bp_get_admin_url( add_query_arg( array( 'page' => 'bp-page-settings', 'updated' => 'true' ), 'admin.php' ) );
+
+		wp_redirect( $base_url );
+	}
+}
+add_action( 'admin_init', 'bp_core_admin_pages_setup_handler' );
+
+/**
+ * Renders the Component Setup admin panel.
+ *
+ * @package BuddyPress Core
+ * @since {@internal Unknown}}
+ * @uses bp_core_admin_component_options()
+ */
+function bp_core_admin_component_setup() {
+?>
+
+	<div class="wrap">
+
+		<?php screen_icon( 'buddypress'); ?>
+
+		<h2 class="nav-tab-wrapper">
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-general-settings'                 ), 'admin.php' ) ); ?>" class="nav-tab nav-tab-active"><?php _e( 'Components', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-page-settings'                    ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Pages', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-settings'                         ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Settings', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bb-forums-setup' ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Forum Setup', 'buddypress' ); ?></a>
+
+			<?php do_action( 'bp_admin_tabs' ); ?>
+		</h2>
+
+		<?php if ( isset( $_GET['updated'] ) && 'true' === $_GET['updated'] ) : ?>
+
+			<div id="message" class="updated fade">
+
+				<p><?php _e( 'Settings Saved', 'buddypress' ); ?></p>
+
+			</div>
+
+		<?php endif; ?>
+
+		<form action="" method="post" id="bp-admin-component-form">
+
+			<?php bp_core_admin_component_options(); ?>
+
+			<p class="submit clear">
+				<input class="button-primary" type="submit" name="bp-admin-component-submit" id="bp-admin-component-submit" value="<?php _e( 'Save Settings', 'buddypress' ) ?>"/>
+			</p>
+
+			<?php wp_nonce_field( 'bp-admin-component-setup' ); ?>
+
+		</form>
+	</div>
+
+<?php
+}
+
+/**
+ * Renders the Component Setup admin panel.
+ *
+ * @package BuddyPress Core
+ * @since {@internal Unknown}}
+ * @uses bp_core_admin_component_options()
+ */
+function bp_core_admin_page_setup() {
+?>
+
+	<div class="wrap">
+
+		<?php screen_icon( 'buddypress'); ?>
+
+		<h2 class="nav-tab-wrapper">
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-general-settings'                 ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Components', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-page-settings'                    ), 'admin.php' ) ); ?>" class="nav-tab nav-tab-active"><?php _e( 'Pages', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-settings'                         ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Settings', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bb-forums-setup' ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Forum Setup', 'buddypress' ); ?></a>
+
+			<?php do_action( 'bp_admin_tabs' ); ?>
+		</h2>
+
+		<?php if ( isset( $_GET['updated'] ) && 'true' === $_GET['updated'] ) : ?>
+
+			<div id="message" class="updated fade">
+
+				<p><?php _e( 'Settings Saved', 'buddypress' ); ?></p>
+
+			</div>
+
+		<?php endif; ?>
+
+		<form action="" method="post" id="bp-admin-page-form">
+
+			<?php bp_core_admin_page_options(); ?>
+
+			<p class="submit clear">
+				<input class="button-primary" type="submit" name="bp-admin-pages-submit" id="bp-admin-pages-submit" value="<?php _e( 'Save All', 'buddypress' ) ?>"/>
+			</p>
+
+			<?php wp_nonce_field( 'bp-admin-pages-setup' ); ?>
+
+		</form>
+	</div>
+
+<?php
+}
+
+/**
+ * Creates reusable markup for component setup on the Components and Pages dashboard panel.
+ *
+ * This markup has been abstracted so that it can be used both during the setup wizard as well as
+ * when BP has been fully installed.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_core_admin_component_options() {
+	global $bp_wizard;
+
+	// Load core functions, if needed
+	if ( !function_exists( 'bp_get_option' ) )
+		require( BP_PLUGIN_DIR . '/bp-core/bp-core-functions.php' );
+
+	$active_components = apply_filters( 'bp_active_components', bp_get_option( 'bp-active-components' ) );
+
+	// An array of strings looped over to create component setup markup
+	$optional_components = array(
+		'xprofile' => array(
+			'title'       => __( 'Extended Profiles', 'buddypress' ),
+			'description' => __( 'Customize your community with fully editable profile fields that allow your users to describe themselves.', 'buddypress' )
+		),
+		'settings' => array(
+			'title'       => __( 'Account Settings', 'buddypress' ),
+			'description' => __( 'Allow your users to modify their account and notification settings directly from within their profiles.', 'buddypress' )
+		),
+		'friends'  => array(
+			'title'       => __( 'Friend Connections', 'buddypress' ),
+			'description' => __( 'Let your users make connections so they can track the activity of others and focus on the people they care about the most.', 'buddypress' )
+		),
+		'messages' => array(
+			'title'       => __( 'Private Messaging', 'buddypress' ),
+			'description' => __( 'Allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, messages can be sent between any number of members.', 'buddypress' )
+		),
+		'activity' => array(
+			'title'       => __( 'Activity Streams', 'buddypress' ),
+			'description' => __( 'Global, personal, and group activity streams with threaded commenting, direct posting, favoriting and @mentions, all with full RSS feed and email notification support.', 'buddypress' )
+		),
+		'groups'   => array(
+			'title'       => __( 'User Groups', 'buddypress' ),
+			'description' => __( 'Groups allow your users to organize themselves into specific public, private or hidden sections with separate activity streams and member listings.', 'buddypress' )
+		),
+		'forums'   => array(
+			'title'       => __( 'Discussion Forums', 'buddypress' ),
+			'description' => __( 'Full-powered discussion forums built directly into groups allow for more conventional in-depth conversations. NOTE: This will require an extra (but easy) setup step.', 'buddypress' )
+		),
+		'blogs'    => array(
+			'title'       => __( 'Site Tracking', 'buddypress' ),
+			'description' => __( 'Make BuddyPress aware of new posts and new comments from your site.', 'buddypress' )
+		)
+	);
+
+	if ( is_multisite() )
+		$optional_components['blogs']['description'] = __( 'Make BuddyPress aware of new sites, new posts and new comments from across your entire network.', 'buddypress' );
+
+	// If this is an upgrade from before BuddyPress 1.5, we'll have to convert deactivated
+	// components into activated ones
+	if ( empty( $active_components ) ) {
+		$deactivated_components = bp_get_option( 'bp-deactivated-components' );
+
+		// Trim off namespace and filename
+		$trimmed = array();
+		foreach ( (array) $deactivated_components as $component => $value ) {
+			$trimmed[] = str_replace( '.php', '', str_replace( 'bp-', '', $component ) );
+		}
+
+		// Loop through the optional components to create an active component array
+		foreach ( (array) $optional_components as $ocomponent => $ovalue ) {
+			if ( !in_array( $ocomponent, $trimmed ) ) {
+				$active_components[$ocomponent] = 1;
+			}
+		}
+	}
+
+	// Required components
+	$required_components = array(
+		'core' => array(
+			'title'       => __( 'BuddyPress Core', 'buddypress' ),
+			'description' => __( 'It&#8216;s what makes <del>time travel</del> BuddyPress possible!', 'buddypress' )
+		),
+		'members' => array(
+			'title'       => __( 'Community Members', 'buddypress' ),
+			'description' => __( 'Everything in a BuddyPress community revolves around its members.', 'buddypress' )
+		),
+	);
+
+	// On new install, set all components to be active by default
+	if ( !empty( $bp_wizard ) && 'install' == $bp_wizard->setup_type && empty( $active_components ) )
+		$active_components = $optional_components;
+
+	?>
+
+	<?php /* The setup wizard uses different, more descriptive text here */ ?>
+	<?php if ( empty( $bp_wizard ) ) : ?>
+
+		<h3><?php _e( 'Available Components', 'buddypress' ); ?></h3>
+
+		<p><?php _e( 'Each component has a unique purpose, and your community may not need each one.', 'buddypress' ); ?></p>
+
+	<?php endif ?>
+
+	<table class="form-table">
+		<tbody>
+
+			<?php foreach ( $optional_components as $name => $labels ) : ?>
+
+				<tr valign="top">
+					<th scope="row"><?php echo esc_html( $labels['title'] ); ?></th>
+
+					<td>
+						<label for="bp_components[<?php echo esc_attr( $name ); ?>]">
+							<input type="checkbox" id="bp_components[<?php echo esc_attr( $name ); ?>]" name="bp_components[<?php echo esc_attr( $name ); ?>]" value="1"<?php checked( isset( $active_components[esc_attr( $name )] ) ); ?> />
+
+							<?php echo $labels['description']; ?>
+
+						</label>
+
+					</td>
+				</tr>
+
+			<?php endforeach ?>
+
+		</tbody>
+	</table>
+
+	<?php if ( empty( $bp_wizard ) ) : ?>
+
+		<h3><?php _e( 'Required Components', 'buddypress' ); ?></h3>
+
+		<p><?php _e( 'The following components are required by BuddyPress and cannot be turned off.', 'buddypress' ); ?></p>
+
+	<?php endif ?>
+
+	<table class="form-table">
+		<tbody>
+
+			<?php foreach ( $required_components as $name => $labels ) : ?>
+
+				<tr valign="top">
+					<th scope="row"><?php echo esc_html( $labels['title'] ); ?></th>
+
+					<td>
+						<label for="bp_components[<?php echo esc_attr( $name ); ?>]">
+							<input type="checkbox" id="bp_components[<?php echo esc_attr( $name ); ?>]" name="" disabled="disabled" value="1"<?php checked( true ); ?> />
+
+							<?php echo $labels['description']; ?>
+
+						</label>
+
+					</td>
+				</tr>
+
+			<?php endforeach ?>
+
+		</tbody>
+	</table>
+
+	<input type="hidden" name="bp_components[members]" value="1" />
+
+	<?php
+}
+
+/**
+ * Creates reusable markup for page setup on the Components and Pages dashboard panel.
+ *
+ * This markup has been abstracted so that it can be used both during the setup wizard as well as
+ * when BP has been fully installed.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_core_admin_page_options() {
+	global $bp;
+
+	// Get the existing WP pages
+	$existing_pages = bp_core_get_directory_page_ids();
+
+	// Set up an array of components (along with component names) that have
+	// directory pages.
+	$directory_pages = array();
+
+	foreach( $bp->loaded_components as $component_slug => $component_id ) {
+
+		// Only components that need directories should be listed here
+		if ( isset( $bp->{$component_id} ) && !empty( $bp->{$component_id}->has_directory ) ) {
+
+			// component->name was introduced in BP 1.5, so we must provide a fallback
+			$component_name = !empty( $bp->{$component_id}->name ) ? $bp->{$component_id}->name : ucwords( $component_id );
+
+			$directory_pages[$component_id] = $component_name;
+		}
+	}
+
+	$directory_pages = apply_filters( 'bp_directory_pages', $directory_pages );
+
+	?>
+
+	<h3><?php _e( 'Directories', 'buddypress' ); ?></h3>
+
+	<p><?php _e( 'Associate a WordPress Page with each BuddyPress component directory.', 'buddypress' ); ?></p>
+
+	<table class="form-table">
+		<tbody>
+
+			<?php foreach ( $directory_pages as $name => $label ) : ?>
+				<?php $disabled = !bp_is_active( $name ) ? ' disabled="disabled"' : ''; ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<label for="bp_pages[<?php echo esc_attr( $name ) ?>]"><?php echo esc_html( $label ) ?></label>
+					</th>
+
+					<td>
+						<?php if ( !bp_is_root_blog() )
+							switch_to_blog( bp_get_root_blog_id() ) ?>
+
+						<?php echo wp_dropdown_pages( array(
+							'name'             => 'bp_pages[' . esc_attr( $name ) . ']',
+							'echo'             => false,
+							'show_option_none' => __( '- None -', 'buddypress' ),
+							'selected'         => !empty( $existing_pages[$name] ) ? $existing_pages[$name] : false
+						) ); ?>
+
+						<a href="<?php echo admin_url( add_query_arg( array( 'post_type' => 'page' ), 'post-new.php' ) ); ?>" class="button-secondary"><?php _e( 'New Page' ); ?></a>
+						<input class="button-primary" type="submit" name="bp-admin-pages-single" value="<?php _e( 'Save', 'buddypress' ) ?>" />
+
+						<?php if ( !empty( $existing_pages[$name] ) ) : ?>
+
+							<a href="<?php echo get_permalink( $existing_pages[$name] ); ?>" class="button-secondary" target="_bp"><?php _e( 'View' ); ?></a>
+
+						<?php endif; ?>
+
+						<?php if ( !bp_is_root_blog() )
+							restore_current_blog() ?>
+
+					</td>
+				</tr>
+
+
+			<?php endforeach ?>
+
+			<?php do_action( 'bp_active_external_directories' ); ?>
+
+		</tbody>
+	</table>
+
+	<?php
+
+	// Static pages
+	$static_pages = array(
+		'register' => __( 'Sign-up',    'buddypress' ),
+		'activate' => __( 'Activation', 'buddypress' ),
+	); ?>
+
+	<h3><?php _e( 'Registration', 'buddypress' ); ?></h3>
+
+	<p><?php _e( 'Associate WordPress Pages with the following BuddyPress Registration pages.', 'buddypress' ); ?></p>
+
+	<table class="form-table">
+		<tbody>
+
+			<?php foreach ( $static_pages as $name => $label ) : ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<label for="bp_pages[<?php echo esc_attr( $name ) ?>]"><?php echo esc_html( $label ) ?></label>
+					</th>
+
+					<td>
+						<?php echo wp_dropdown_pages( array(
+							'name'             => 'bp_pages[' . esc_attr( $name ) . ']',
+							'echo'             => false,
+							'show_option_none' => __( '- None -', 'buddypress' ),
+							'selected'         => !empty( $existing_pages[$name] ) ? $existing_pages[$name] : false
+						) ) ?>
+
+						<a href="<?php echo admin_url( add_query_arg( array( 'post_type' => 'page' ), 'post-new.php' ) ); ?>" class="button-secondary"><?php _e( 'New Page' ); ?></a>
+						<input class="button-primary" type="submit" name="bp-admin-pages-single" value="<?php _e( 'Save', 'buddypress' ) ?>" />
+
+						<?php if ( !empty( $existing_pages[$name] ) ) : ?>
+
+							<a href="<?php echo get_permalink( $existing_pages[$name] ); ?>" class="button-secondary" target="_bp"><?php _e( 'View' ); ?></a>
+
+						<?php endif; ?>
+
+					</td>
+				</tr>
+
+			<?php endforeach ?>
+
+			<?php do_action( 'bp_active_external_pages' ); ?>
+
+		</tbody>
+	</table>
+
+	<?php
+}
+
+/**
+ * Loads admin panel styles and scripts.
+ *
+ * @package BuddyPress Core
+ * @since {@internal Unknown}}
+ */
+function bp_core_add_admin_menu_styles() {
+	if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+		wp_enqueue_style( 'bp-admin-css', apply_filters( 'bp_core_admin_css', BP_PLUGIN_URL . '/bp-core/css/admin.dev.css' ), array(), '20110723' );
+	else
+		wp_enqueue_style( 'bp-admin-css', apply_filters( 'bp_core_admin_css', BP_PLUGIN_URL . '/bp-core/css/admin.css' ), array(), '20110723' );
+
+	wp_enqueue_script( 'thickbox' );
+	wp_enqueue_style( 'thickbox' );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-core/admin/bp-core-schema.php
===================================================================
--- /tags/1.5-beta-3/bp-core/admin/bp-core-schema.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/admin/bp-core-schema.php	(revision 5057)
@@ -0,0 +1,300 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_core_set_charset() {
+	global $wpdb;
+
+	require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+
+	/* BuddyPress component DB schema */
+	if ( !empty($wpdb->charset) )
+		return "DEFAULT CHARACTER SET $wpdb->charset";
+
+	return '';
+}
+
+function bp_core_install_notifications() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_notifications (
+	  		    id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+			    user_id bigint(20) NOT NULL,
+			    item_id bigint(20) NOT NULL,
+			    secondary_item_id bigint(20),
+	  		    component_name varchar(75) NOT NULL,
+			    component_action varchar(75) NOT NULL,
+	  		    date_notified datetime NOT NULL,
+			    is_new bool NOT NULL DEFAULT 0,
+		        KEY item_id (item_id),
+			    KEY secondary_item_id (secondary_item_id),
+			    KEY user_id (user_id),
+			    KEY is_new (is_new),
+			    KEY component_name (component_name),
+	 	   	    KEY component_action (component_action),
+			    KEY useritem (user_id,is_new)
+		       ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+function bp_core_install_activity_streams() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_activity (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				user_id bigint(20) NOT NULL,
+				component varchar(75) NOT NULL,
+				type varchar(75) NOT NULL,
+				action text NOT NULL,
+				content longtext NOT NULL,
+				primary_link varchar(150) NOT NULL,
+				item_id varchar(75) NOT NULL,
+				secondary_item_id varchar(75) DEFAULT NULL,
+				date_recorded datetime NOT NULL,
+				hide_sitewide bool DEFAULT 0,
+				mptt_left int(11) NOT NULL DEFAULT 0,
+				mptt_right int(11) NOT NULL DEFAULT 0,
+				KEY date_recorded (date_recorded),
+				KEY user_id (user_id),
+				KEY item_id (item_id),
+				KEY secondary_item_id (secondary_item_id),
+				KEY component (component),
+				KEY type (type),
+				KEY mptt_left (mptt_left),
+				KEY mptt_right (mptt_right),
+				KEY hide_sitewide (hide_sitewide)
+		 	   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_activity_meta (
+				id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				activity_id bigint(20) NOT NULL,
+				meta_key varchar(255) DEFAULT NULL,
+				meta_value longtext DEFAULT NULL,
+				KEY activity_id (activity_id),
+				KEY meta_key (meta_key)
+		   	   ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+function bp_core_install_friends() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_friends (
+	  		    id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+	  		    initiator_user_id bigint(20) NOT NULL,
+	  		    friend_user_id bigint(20) NOT NULL,
+	  		    is_confirmed bool DEFAULT 0,
+			    is_limited bool DEFAULT 0,
+	  		    date_created datetime NOT NULL,
+		        KEY initiator_user_id (initiator_user_id),
+		        KEY friend_user_id (friend_user_id)
+	 	       ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+function bp_core_install_groups() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_groups (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				creator_id bigint(20) NOT NULL,
+		  		name varchar(100) NOT NULL,
+		  		slug varchar(200) NOT NULL,
+		  		description longtext NOT NULL,
+				status varchar(10) NOT NULL DEFAULT 'public',
+				enable_forum tinyint(1) NOT NULL DEFAULT '1',
+				date_created datetime NOT NULL,
+			    KEY creator_id (creator_id),
+			    KEY status (status)
+		 	   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_groups_members (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				group_id bigint(20) NOT NULL,
+				user_id bigint(20) NOT NULL,
+				inviter_id bigint(20) NOT NULL,
+				is_admin tinyint(1) NOT NULL DEFAULT '0',
+				is_mod tinyint(1) NOT NULL DEFAULT '0',
+				user_title varchar(100) NOT NULL,
+				date_modified datetime NOT NULL,
+				comments longtext NOT NULL,
+				is_confirmed tinyint(1) NOT NULL DEFAULT '0',
+				is_banned tinyint(1) NOT NULL DEFAULT '0',
+				invite_sent tinyint(1) NOT NULL DEFAULT '0',
+				KEY group_id (group_id),
+				KEY is_admin (is_admin),
+				KEY is_mod (is_mod),
+			 	KEY user_id (user_id),
+				KEY inviter_id (inviter_id),
+				KEY is_confirmed (is_confirmed)
+		 	   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_groups_groupmeta (
+				id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				group_id bigint(20) NOT NULL,
+				meta_key varchar(255) DEFAULT NULL,
+				meta_value longtext DEFAULT NULL,
+				KEY group_id (group_id),
+				KEY meta_key (meta_key)
+			   ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+function bp_core_install_private_messaging() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_messages_messages (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+		  		thread_id bigint(20) NOT NULL,
+		  		sender_id bigint(20) NOT NULL,
+		  		subject varchar(200) NOT NULL,
+		  		message longtext NOT NULL,
+		  		date_sent datetime NOT NULL,
+			    KEY sender_id (sender_id),
+			    KEY thread_id (thread_id)
+		 	   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_messages_recipients (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+		  		user_id bigint(20) NOT NULL,
+		  		thread_id bigint(20) NOT NULL,
+		  		unread_count int(10) NOT NULL DEFAULT '0',
+				sender_only tinyint(1) NOT NULL DEFAULT '0',
+				is_deleted tinyint(1) NOT NULL DEFAULT '0',
+			    KEY user_id (user_id),
+			    KEY thread_id (thread_id),
+				KEY is_deleted (is_deleted),
+				KEY sender_only (sender_only),
+			    KEY unread_count (unread_count)
+		 	   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_messages_notices (
+		  		id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+		  		subject varchar(200) NOT NULL,
+		  		message longtext NOT NULL,
+		  		date_sent datetime NOT NULL,
+				is_active tinyint(1) NOT NULL DEFAULT '0',
+			    KEY is_active (is_active)
+		 	   ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+function bp_core_install_extended_profiles() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	bp_update_option( 'bp-xprofile-base-group-name', _x( 'Base', 'First XProfile group name', 'buddypress' ) );
+	bp_update_option( 'bp-xprofile-fullname-field-name', _x( 'Name', 'XProfile fullname field name', 'buddypress' ) );
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_xprofile_groups (
+			    id bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
+			    name varchar(150) NOT NULL,
+			    description mediumtext NOT NULL,
+			    group_order bigint(20) NOT NULL DEFAULT '0',
+			    can_delete tinyint(1) NOT NULL,
+			    KEY can_delete (can_delete)
+			   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_xprofile_fields (
+			    id bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
+			    group_id bigint(20) unsigned NOT NULL,
+			    parent_id bigint(20) unsigned NOT NULL,
+			    type varchar(150) NOT NULL,
+			    name varchar(150) NOT NULL,
+			    description longtext NOT NULL,
+			    is_required tinyint(1) NOT NULL DEFAULT '0',
+			    is_default_option tinyint(1) NOT NULL DEFAULT '0',
+			    field_order bigint(20) NOT NULL DEFAULT '0',
+			    option_order bigint(20) NOT NULL DEFAULT '0',
+			    order_by varchar(15) NOT NULL DEFAULT '',
+			    can_delete tinyint(1) NOT NULL DEFAULT '1',
+			    KEY group_id (group_id),
+			    KEY parent_id (parent_id),
+			    KEY field_order (field_order),
+			    KEY can_delete (can_delete),
+			    KEY is_required (is_required)
+			   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_xprofile_data (
+			    id bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
+			    field_id bigint(20) unsigned NOT NULL,
+			    user_id bigint(20) unsigned NOT NULL,
+			    value longtext NOT NULL,
+			    last_updated datetime NOT NULL,
+			    KEY field_id (field_id),
+			    KEY user_id (user_id)
+			   ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_xprofile_meta (
+				id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				object_id bigint(20) NOT NULL,
+				object_type varchar(150) NOT NULL,
+				meta_key varchar(255) DEFAULT NULL,
+				meta_value longtext DEFAULT NULL,
+				KEY object_id (object_id),
+				KEY meta_key (meta_key)
+		   	   ) {$charset_collate};";
+
+	dbDelta( $sql );
+
+	// Insert the default group and fields
+	$insert_sql = array();
+
+	if ( !$wpdb->get_var( "SELECT id FROM {$bp_prefix}bp_xprofile_groups WHERE id = 1" ) )
+		$insert_sql[] = "INSERT INTO {$bp_prefix}bp_xprofile_groups ( name, description, can_delete ) VALUES ( " . $wpdb->prepare( '%s', stripslashes( bp_get_option( 'bp-xprofile-base-group-name' ) ) ) . ", '', 0 );";
+
+	if ( !$wpdb->get_var( "SELECT id FROM {$bp_prefix}bp_xprofile_fields WHERE id = 1" ) )
+		$insert_sql[] = "INSERT INTO {$bp_prefix}bp_xprofile_fields ( group_id, parent_id, type, name, description, is_required, can_delete ) VALUES ( 1, 0, 'textbox', " . $wpdb->prepare( '%s', stripslashes( bp_get_option( 'bp-xprofile-fullname-field-name' ) ) ) . ", '', 1, 0 );";
+
+	dbDelta( $insert_sql );
+}
+
+function bp_core_install_blog_tracking() {
+	global $wpdb;
+
+	$charset_collate = bp_core_set_charset();
+	$bp_prefix = bp_core_get_table_prefix();
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_user_blogs (
+	  		    id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+			    user_id bigint(20) NOT NULL,
+			    blog_id bigint(20) NOT NULL,
+			    KEY user_id (user_id),
+			    KEY blog_id (blog_id)
+		       ) {$charset_collate};";
+
+	$sql[] = "CREATE TABLE {$bp_prefix}bp_user_blogs_blogmeta (
+				id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+				blog_id bigint(20) NOT NULL,
+				meta_key varchar(255) DEFAULT NULL,
+				meta_value longtext DEFAULT NULL,
+				KEY blog_id (blog_id),
+				KEY meta_key (meta_key)
+		       ) {$charset_collate};";
+
+	dbDelta($sql);
+}
+
+?>
Index: /tags/1.5-beta-3/bp-core/admin/bp-core-update.php
===================================================================
--- /tags/1.5-beta-3/bp-core/admin/bp-core-update.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/admin/bp-core-update.php	(revision 5057)
@@ -0,0 +1,1328 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Core_Setup_Wizard {
+	var $current_step;
+	var $steps;
+
+	var $database_version;
+	var $is_network_activate;
+	var $new_version;
+	var $setup_type;
+
+	function bp_core_setup_wizard() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		global $bp;
+
+		// Ensure that we have access to some utility functions. Must use require_once()
+		// because BP Core is loaded during incremental upgrades
+		require_once( BP_PLUGIN_DIR . '/bp-core/bp-core-functions.php' );
+
+		// Get current DB version
+		$this->database_version = !empty( $bp->database_version ) ? (int) $bp->database_version : 0;
+
+		if ( !empty( $bp->is_network_activate ) ) {
+			$this->is_network_activate = $bp->is_network_activate;
+
+		} elseif ( !$this->current_step() ) {
+			setcookie( 'bp-wizard-step', 0, time() + 60 * 60 * 24, COOKIEPATH );
+			$_COOKIE['bp-wizard-step'] = 0;
+		}
+
+		$this->new_version  = constant( 'BP_DB_VERSION' );
+		$this->setup_type   = !empty( $bp->maintenance_mode ) ? $bp->maintenance_mode : '';
+		$this->current_step = $this->current_step();
+
+		// Remove the admin menu while we update/install
+		remove_action( bp_core_admin_hook(), 'bp_core_add_admin_menu', 9 );
+
+		// Call the save method that will save data and modify $current_step
+		if ( isset( $_POST['save'] ) )
+			$this->save( $_POST['save'] );
+
+		// Build the steps needed for update or new installations
+		$this->steps = $this->add_steps();
+	}
+
+	function current_step() {
+		if ( isset( $_POST['step'] ) ) {
+			$current_step = (int)$_POST['step'] + 1;
+		} else {
+			if ( !empty( $_COOKIE['bp-wizard-step'] ) )
+				$current_step = $_COOKIE['bp-wizard-step'];
+			else
+				$current_step = 0;
+		}
+
+		return $current_step;
+	}
+
+	function add_steps() {
+		global $wp_rewrite;
+
+		// Setup wizard steps
+		$steps = array();
+
+		if ( 'install' == $this->setup_type ) {
+			$steps = array(
+				__( 'Components', 'buddypress' ),
+				__( 'Pages',      'buddypress' ),
+				__( 'Permalinks', 'buddypress' ),
+				__( 'Theme',      'buddypress' ),
+				__( 'Finish',     'buddypress' )
+			);
+
+		// Update wizard steps
+		} else {
+			if ( $this->is_network_activate )
+				$steps[] = __( 'Multisite Update', 'buddypress' );
+
+			if ( $this->database_version < (int) $this->new_version )
+				$steps[] = __( 'Database Update', 'buddypress' );
+
+			if ( $this->database_version < 1801 || !bp_core_get_directory_page_ids() ) {
+				$steps[] = __( 'Components', 'buddypress' );
+				$steps[] = __( 'Pages', 'buddypress' );
+			}
+
+			$steps[] = __( 'Finish', 'buddypress' );
+		}
+
+		return $steps;
+	}
+
+	function save( $step_name ) {
+
+		// Save any posted values
+		switch ( $step_name ) {
+			case 'db_update': default:
+				$result = $this->step_db_update_save();
+				break;
+
+			case 'ms_update': default:
+				$result = $this->step_ms_update_save();
+				break;
+
+			case 'ms_pages': default:
+				$result = $this->step_ms_update_save();
+				break;
+
+			case 'components': default:
+				$result = $this->step_components_save();
+				break;
+
+			case 'pages': default:
+				$result = $this->step_pages_save();
+				break;
+
+			case 'permalinks': default:
+				$result = $this->step_permalinks_save();
+				break;
+
+			case 'theme': default:
+				$result = $this->step_theme_save();
+				break;
+
+			case 'finish': default:
+				$result = $this->step_finish_save();
+				break;
+		}
+
+		if ( !$result && $this->current_step )
+			$this->current_step--;
+
+		if ( 'finish' != $step_name )
+			setcookie( 'bp-wizard-step', (int)$this->current_step, time() + 60 * 60 * 24, COOKIEPATH );
+	}
+
+	function html() {
+
+		// Update or Setup
+		$type = ( 'update' == $this->setup_type ) ? __( 'Update', 'buddypress' ) : __( 'Setup', 'buddypress' );
+
+		?>
+
+		<div class="wrap" id="bp-admin">
+
+			<?php screen_icon( 'buddypress' ); ?>
+
+			<h2><?php printf( __( 'BuddyPress %s', 'buddypress' ), $type ); ?></h2>
+
+			<?php
+				do_action( 'bp_admin_notices' );
+
+				$step_count  = count( $this->steps ) - 1;
+				$wiz_or_set  = $this->current_step >= $step_count ? 'bp-general-settings' : 'bp-wizard';
+				$form_action = bp_core_update_do_network_admin() ? network_admin_url( add_query_arg( array( 'page' => $wiz_or_set ), 'admin.php' ) ) : admin_url( add_query_arg( array( 'page' => $wiz_or_set ), 'admin.php' ) );
+			?>
+
+			<form action="<?php echo $form_action; ?>" method="post" id="bp-admin-form">
+				<div id="bp-admin-nav">
+					<ol>
+
+						<?php foreach( (array)$this->steps as $i => $name ) : ?>
+
+							<li<?php if ( $this->current_step == $i ) : ?> class="current"<?php endif; ?>>
+								<?php if ( $this->current_step > $i ) : ?>
+
+									<span class="complete">&nbsp;</span>
+
+								<?php else :
+
+									echo $i + 1 . '. ';
+
+								endif;
+
+								echo esc_attr( $name ) ?>
+
+							</li>
+
+						<?php endforeach; ?>
+
+					</ol>
+
+					<?php if ( __( 'Finish', 'buddypress' ) == $this->steps[$this->current_step] ) : ?>
+
+						<div class="prev-next submit clear">
+							<input type="submit" value="<?php _e( 'Finish &amp; Activate', 'buddypress' ); ?>" name="submit" />
+						</div>
+
+					<?php else : ?>
+
+						<div class="prev-next submit clear">
+							<input type="submit" value="<?php _e( 'Save &amp; Next', 'buddypress' ); ?>" name="submit" />
+						</div>
+
+					<?php endif; ?>
+
+				</div>
+
+				<div id="bp-admin-content">
+
+					<?php switch ( $this->steps[$this->current_step] ) {
+						case __( 'Database Update', 'buddypress') :
+							$this->step_db_update();
+							break;
+
+						case __( 'Multisite Update', 'buddypress') :
+							$this->step_ms_update();
+							break;
+
+						case __( 'Site Directory', 'buddypress') :
+							$this->step_ms_update();
+							break;
+
+						case __( 'Components', 'buddypress') :
+							$this->step_components();
+							break;
+
+						case __( 'Pages', 'buddypress') :
+							$this->step_pages();
+							break;
+
+						case __( 'Permalinks', 'buddypress') :
+							$this->step_permalinks();
+							break;
+
+						case __( 'Theme', 'buddypress') :
+							$this->step_theme();
+							break;
+
+						case __( 'Finish', 'buddypress') :
+							$this->step_finish();
+							break;
+
+					} ?>
+
+				</div>
+			</form>
+		</div>
+
+	<?php
+	}
+
+	/** Screen methods ********************************************************/
+
+	function step_db_update() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false; ?>
+
+		<p><?php _e( 'Before you can continue using BuddyPress, a few minor adjustments need to be made. These changes are not destructive and will not remove or change any existing settings.', 'buddypress' ); ?></p>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="db_update" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_db_update' ) ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_ms_update() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		$active_components = bp_get_option( 'bp-active-components' );
+
+		if ( defined( 'BP_BLOGS_SLUG' ) )
+			$blogs_slug = constant( 'BP_BLOGS_SLUG' );
+		else
+			$blogs_slug = 'blogs';
+
+ 		// Call up old bp-pages to see if a page has been previously linked to Blogs
+		$existing_pages = bp_get_option( 'bp-pages' );
+
+		if ( !empty( $existing_pages['blogs'] ) )
+			$existing_blog_page = '&selected=' . $existing_pages['blogs'];
+		else
+			$existing_blog_page = '';
+		?>
+
+		<script type="text/javascript">
+			jQuery( document ).ready( function() {
+				jQuery( 'select' ).change( function() {
+					jQuery( this ).siblings( 'input[@type=radio]' ).click();
+				});
+			});
+		</script>
+
+		<p><?php printf( __( 'BuddyPress has detected a recent change to WordPress Multisite, which allows members of your community to have their own WordPress sites. You can enable or disable this feature at any time at <a href="%s">Network Options</a>.', 'buddypress' ), network_admin_url( 'settings.php' ) ); ?></p>
+
+		<p><?php __( "Please select the WordPress page you would like to use to display the site directory. You can either choose an existing page or let BuddyPress auto-create a page for you. If you'd like, you can go to manually create pages now, and return to this step when you are finished.", 'buddypress' ) ?></p>
+
+		<p><strong><?php _e( 'Please Note:', 'buddypress' ) ?></strong> <?php _e( "If you have manually added BuddyPress navigation links in your theme you may need to remove these from your header.php to avoid duplicate links.", 'buddypress' ) ?></p>
+
+		<p><?php _e( 'Would you like to enable site tracking, which tracks blog posts and comments from across your network?', 'buddypress' ); ?></p>
+
+		<table class="form-table">
+
+			<tr valign="top">
+				<th scope="row"><?php _e( "Enable Site Tracking?", 'buddypress' ) ?></th>
+
+				<td>
+					<label for="bp_components[blogs]">
+						<input id="site-tracking-enabled" type="checkbox" id="bp_components[blogs]" name="bp_components[blogs]" value="1"<?php checked( isset( $active_components[blogs] ) ); ?> />
+
+						<?php _e( "Track new sites, new posts and new comments across your entire network.", 'buddypress' ) ?>
+
+					</label>
+
+				</td>
+			</tr>
+
+			<tr valign="top" id="site-tracking-page-selector">
+				<th scope="row"><?php _e( 'Select a WordPress page for the Sites directory.', 'buddypress' ); ?></th>
+
+				<td>
+					<p><input type="radio" name="bp_pages[blogs]" checked="checked" value="<?php echo $blogs_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo site_url( $blogs_slug ); ?>/</p>
+					<p><input type="radio" name="bp_pages[blogs]" value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-blogs-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . $existing_blog_page ); ?></p>
+				</td>
+			</tr>
+
+		</table>
+
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="ms_update" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_ms_update' ); ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_components() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		if ( !function_exists( 'bp_core_admin_component_options' ) )
+			require ( WP_PLUGIN_DIR . '/buddypress/bp-core/admin/bp-core-admin.php' ); ?>
+
+		<p><?php _e( "BuddyPress bundles several individual social components together, each one adding a distinct feature. This first step decides which features are enabled on your site; all features are enabled by default. Don't worry, you can change your mind at any point in the future.", 'buddypress' ); ?></p>
+
+		<?php bp_core_admin_component_options(); ?>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="components" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_components' ); ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_pages() {
+		global $bp;
+
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		$existing_pages = bp_core_update_get_page_meta();
+
+		// Provide empty indexes to avoid PHP errors with wp_dropdown_pages()
+		$indexes = array( 'members', 'activity', 'groups', 'forums', 'blogs', 'register', 'activate' );
+		foreach ( $indexes as $index ) {
+			if ( !isset( $existing_pages[$index] ) )
+				$existing_pages[$index] = '';
+		}
+
+		if ( !empty( $existing_pages['blogs'] ) )
+			$existing_blog_page = '&selected=' . $existing_pages['blogs'];
+		else
+			$existing_blog_page = '';
+
+		// Get active components
+		$active_components = apply_filters( 'bp_active_components', bp_get_option( 'bp-active-components' ) );
+
+		// Check for defined slugs
+		$members_slug    = !empty( $bp->members->slug    ) ? $bp->members->slug    : __( 'members',  'buddypress' );
+
+		// Groups
+		$groups_slug     = !empty( $bp->groups->slug     ) ? $bp->groups->slug     : __( 'groups',   'buddypress' );
+
+		// Activity
+		$activity_slug   = !empty( $bp->activity->slug   ) ? $bp->activity->slug   : __( 'activity', 'buddypress' );
+
+		// Forums
+		$forums_slug     = !empty( $bp->forums->slug     ) ? $bp->forums->slug     : __( 'forums',   'buddypress' );
+
+		// Blogs
+		$blogs_slug      = !empty( $bp->blogs->slug      ) ? $bp->blogs->slug      : __( 'blogs',    'buddypress' );
+
+		// Register
+		$register_slug   = !empty( $bp->register->slug   ) ? $bp->register->slug   : __( 'register', 'buddypress' );
+
+		// Activation
+		$activation_slug = !empty( $bp->activation->slug ) ? $bp->activation->slug : __( 'activate', 'buddypress' );
+
+		?>
+
+		<script type="text/javascript">
+			jQuery( document ).ready( function() {
+				jQuery( 'select' ).change( function() {
+					jQuery( this ).siblings( 'input[@type=radio]' ).click();
+				});
+			});
+		</script>
+
+		<p><?php _e( 'BuddyPress now uses WordPress pages to display content. This allows you to easily change the names of pages or move them to a sub page.', 'buddypress' ); ?></p>
+
+		<p><?php _e( 'Either choose an existing page or let BuddyPress auto-create pages for you. To manually create custom pages, come back to this step once you are finished.', 'buddypress' ); ?></p>
+
+		<p><strong><?php _e( 'Please Note:', 'buddypress' ); ?></strong> <?php _e( 'If you have manually added BuddyPress navigation links in your theme you may need to remove these from your header.php to avoid duplicate links.', 'buddypress' ); ?></p>
+
+		<table class="form-table">
+
+			<tr valign="top">
+				<th scope="row">
+					<h5><?php _e( 'Members', 'buddypress' ); ?></h5>
+					<p><?php _e( 'Displays member profiles, and a directory of all site members.', 'buddypress' ); ?></p>
+				</th>
+				<td>
+					<p><label><input type="radio" name="bp_pages[members]" <?php checked( empty( $existing_pages['members'] ) ); ?>  value="<?php echo $members_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ) ?> <?php echo home_url( $members_slug ); ?>/</label></p>
+					<p><label><input type="radio" name="bp_pages[members]" <?php checked( !empty( $existing_pages['members'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-members-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['members'] ); ?></label></p>
+				</td>
+			</tr>
+
+			<?php if ( isset( $active_components['activity'] ) ) : ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<h5><?php _e( 'Site Activity', 'buddypress' ); ?></h5>
+						<p><?php _e( "Displays the activity for the entire site, a member's friends, groups and @mentions.", 'buddypress' ); ?></p>
+					</th>
+					<td>
+						<p><label><input type="radio" name="bp_pages[activity]" <?php checked( empty( $existing_pages['activity'] ) ); ?>  value="<?php echo $activity_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo home_url( $activity_slug ); ?>/</label></p>
+						<p><label><input type="radio" name="bp_pages[activity]" <?php checked( !empty( $existing_pages['activity'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-activity-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['activity'] ); ?></label></p>
+					</td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( isset( $active_components['groups'] ) ) : ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<h5><?php _e( 'Groups', 'buddypress' ); ?></h5>
+						<p><?php _e( 'Displays individual groups as well as a directory of groups.', 'buddypress' ); ?></p>
+					</th>
+					<td>
+						<p><label><input type="radio" name="bp_pages[groups]" <?php checked( empty( $existing_pages['groups'] ) ); ?>  value="<?php echo $groups_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo home_url( $groups_slug ); ?>/</label></p>
+						<p><label><input type="radio" name="bp_pages[groups]" <?php checked( !empty( $existing_pages['groups'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-groups-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['groups'] ); ?></label></p>
+					</td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( isset( $active_components['forums'] ) ) : ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<h5><?php _e( 'Forums', 'buddypress' ); ?></h5>
+						<p><?php _e( 'Displays a directory of public forum topics.', 'buddypress' ); ?></p>
+					</th>
+					<td>
+						<p><label><input type="radio" name="bp_pages[forums]" <?php checked( empty( $existing_pages['forums'] ) ); ?>  value="<?php echo $forums_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo home_url( $forums_slug ); ?>/</label></p>
+						<p><label><input type="radio" name="bp_pages[forums]" <?php checked( !empty( $existing_pages['forums'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-forums-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['forums'] ); ?></label></p>
+					</td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php /* The Blogs component only needs a directory page when Multisite is enabled */ ?>
+			<?php if ( is_multisite() && isset( $active_components['blogs'] ) ) : ?>
+
+				<tr valign="top">
+					<th scope="row">
+						<h5><?php _e( 'Sites', 'buddypress' ); ?></h5>
+						<p><?php _e( 'Displays a directory of the sites in your network.', 'buddypress' ); ?></p>
+					</th>
+					<td>
+						<p><label><input type="radio" name="bp_pages[blogs]" <?php checked( empty( $existing_pages['blogs'] ) ); ?>  value="<?php echo $blogs_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo home_url( $blogs_slug ); ?>/</label></p>
+						<p><label><input type="radio" name="bp_pages[blogs]" <?php checked( !empty( $existing_pages['blogs'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-blogs-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['blogs'] ); ?></label></p>
+					</td>
+				</tr>
+
+			<?php endif; ?>
+
+			<tr valign="top">
+				<th scope="row">
+					<h5><?php _e( 'Register', 'buddypress' ); ?></h5>
+					<p><?php _e( 'Displays a site registration page where users can create new accounts.', 'buddypress' ); ?></p>
+				</th>
+				<td>
+					<p><label><input type="radio" name="bp_pages[register]" <?php checked( empty( $existing_pages['register'] ) ); ?>  value="<?php echo $register_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ) ?> <?php echo home_url( $register_slug ) ?>/</label></p>
+					<p><label><input type="radio" name="bp_pages[register]" <?php checked( !empty( $existing_pages['register'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ) ?> <?php echo wp_dropdown_pages( "name=bp-register-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['register'] ); ?></label></p>
+				</td>
+			</tr>
+
+			<tr valign="top">
+				<th scope="row">
+					<h5><?php _e( 'Activate', 'buddypress' ); ?></h5>
+					<p><?php _e( 'The page users will visit to activate their account once they have registered.', 'buddypress' ); ?></p>
+				</th>
+				<td>
+					<p><label><input type="radio" name="bp_pages[activate]" <?php checked( empty( $existing_pages['activate'] ) ); ?>  value="<?php echo $activation_slug; ?>" /> <?php _e( 'Automatically create a page at:', 'buddypress' ); ?> <?php echo home_url( $activation_slug ); ?>/</label></p>
+					<p><label><input type="radio" name="bp_pages[activate]" <?php checked( !empty( $existing_pages['activate'] ) ); ?> value="page" /> <?php _e( 'Use an existing page:', 'buddypress' ); ?> <?php echo wp_dropdown_pages( "name=bp-activate-page&echo=0&show_option_none=" . __( '- Select -', 'buddypress' ) . "&selected=" . $existing_pages['activate'] ); ?></label></p>
+				</td>
+			</tr>
+		</table>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="pages" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_pages' ); ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_permalinks() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		$prefix              = '';
+		$permalink_structure = get_option( 'permalink_structure' );
+		$using_permalinks    = ( !empty( $permalink_structure ) ) ? true : false;
+		$structures          = array( '', $prefix . '/%year%/%monthnum%/%day%/%postname%/', $prefix . '/%year%/%monthnum%/%postname%/', $prefix . '/archives/%post_id%' );
+
+		// If we're using permalinks already, adjust text accordingly
+		if ( $permalink_structure )
+			$permalink_setup_text = __( 'Congratulations! You are already using pretty permalinks, which BuddyPress requires. If you\'d like to change your settings, you may do so now. If you\'re happy with your current settings, click Save &amp; Next to continue.', 'buddypress' );
+		else
+			$permalink_setup_text = __( 'To make sure the pages created in the previous step work correctly, pretty permalinks must be active on your site.', 'buddypress' );
+
+		if ( !got_mod_rewrite() && !iis7_supports_permalinks() )
+			$prefix = '/index.php'; ?>
+
+		<p><?php echo $permalink_setup_text; ?></p>
+		<p><?php printf( __( 'Please select the permalink setting you would like to use. For more advanced options please visit the <a href="%s">permalink settings page</a> first, and complete this setup wizard later.', 'buddypress' ), admin_url( 'options-permalink.php' ) ); ?>
+
+		<table class="form-table">
+			<tr>
+				<th><label><input name="permalink_structure" type="radio"<?php if ( empty( $permalink_structure ) || false != strpos( $permalink_structure, $structures[1] ) ) : ?> checked="checked" <?php endif; ?>value="<?php echo esc_attr( $structures[1] ); ?>" class="tog" <?php checked( $structures[1], $permalink_structure ); ?> />&nbsp;<?php _e( 'Day and name' ); ?></label></th>
+				<td><code><?php echo get_home_url() . $prefix . '/' . date('Y') . '/' . date('m') . '/' . date('d') . '/sample-post/'; ?></code></td>
+			</tr>
+			<tr>
+				<th><label><input name="permalink_structure" type="radio"<?php if ( empty( $permalink_structure ) || false != strpos( $permalink_structure, $structures[2] ) ) : ?> checked="checked" <?php endif; ?> value="<?php echo esc_attr( $structures[2] ); ?>" class="tog" <?php checked( $structures[2], $permalink_structure ); ?> />&nbsp;<?php _e( 'Month and name' ); ?></label></th>
+				<td><code><?php echo get_home_url() . $prefix . '/' . date('Y') . '/' . date('m') . '/sample-post/'; ?></code></td>
+			</tr>
+			<tr>
+				<th><label><input name="permalink_structure" type="radio"<?php if ( empty( $permalink_structure ) || false != strpos( $permalink_structure, $structures[3] ) ) : ?> checked="checked" <?php endif; ?> value="<?php echo esc_attr( $structures[3] ); ?>" class="tog" <?php checked( $structures[3], $permalink_structure ); ?> />&nbsp;<?php _e( 'Numeric' ); ?></label></th>
+				<td><code><?php echo get_home_url() . $prefix ?>/archives/123</code></td>
+			</tr>
+		</table>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="permalinks" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && empty( $_POST['skip-htaccess'] ) ) : ?>
+
+				<input type="hidden" name="skip-htaccess" value="skip-htaccess" />
+
+			<?php endif; ?>
+
+			<?php wp_nonce_field( 'bpwizard_permalinks' ); ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_theme() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		$installed_plugins = get_plugins();
+		$installed_themes  = get_themes();
+
+		$template_pack_installed = false;
+		$bp_autotheme_installed  = false;
+		$bp_theme_installed      = false;
+
+		foreach ( (array)$installed_plugins as $plugin ) {
+			if ( 'BuddyPress Template Pack' == $plugin['Name'] ) {
+				$template_pack_installed = true;
+			}
+		}
+
+		foreach ( (array)$installed_themes as $theme ) {
+			foreach ( (array)$theme['Tags'] as $tag ) {
+				if ( ( 'BuddyPress Default' != $theme['Name'] ) && ( 'buddypress' == $tag ) ) {
+					$bp_theme_installed = true;
+					$bp_themes[] = $theme;
+				}
+			}
+		}
+
+		// Get theme screenshot
+		$current_theme = get_current_theme();
+		$screenshot    = '';
+		$themes        = get_themes();
+
+		if ( !empty( $themes[$current_theme]['Screenshot'] ) )
+			$screenshot = trailingslashit( get_stylesheet_directory_uri() ) . $themes[$current_theme]['Screenshot'];
+	?>
+
+		<script type="text/javascript">
+			jQuery( document ).ready( function() {
+				jQuery( 'select' ).change( function() {
+					jQuery( this ).siblings( 'input[@type=radio]' ).click();
+				});
+			});
+		</script>
+
+		<p><?php _e( "BuddyPress introduces a whole range of new screens to display content. To display these screens, you need to decide how you want to handle them in your current theme.", 'buddypress' ); ?></p>
+
+		<table class="form-table">
+			<tr>
+				<th>
+					<h5><?php _e( 'Use BuddyPress Default', 'buddypress' ); ?></h5>
+					<img src="<?php echo plugins_url( '/buddypress/bp-themes/bp-default/screenshot.png' ); ?>" alt="<?php _e( 'BuddyPress Default', 'buddypress' ); ?>" />
+				</th>
+				<td>
+					<p><?php _e( 'BuddyPress Default contains everything you need to get up and running out of the box. It supports all features and is highly customizable.', 'buddypress' ); ?></p>
+					<p><strong><?php _e( 'This is the best choice if you do not have an existing WordPress theme, or want to start using BuddyPress immediately.', 'buddypress' ); ?></strong></p>
+					<p><label><input type="radio" name="theme" value="bp_default" checked="checked" /> <?php _e( 'Yes, please!', 'buddypress' ); ?></label></p>
+				</td>
+			</tr>
+
+			<?php if ( $bp_theme_installed ) : ?>
+				<tr>
+					<th>
+						<h5><?php _e( 'Other themes', 'buddypress' ); ?></h5>
+						<img src="<?php echo plugins_url( '/buddypress/bp-core/images/find.png' ); ?>" alt="<?php _e( 'A BuddyPress theme', 'buddypress' ); ?>" />
+					</th>
+					<td>
+						<p><?php _e( "We've found that you already have some other BuddyPress-compatible themes available. To use one of those, pick it from this list.", 'buddypress' ); ?></p>
+						<p>
+							<label>
+								<input type="radio" name="theme" value="3rd_party" /> <?php _e( 'Use this theme', 'buddypress' ); ?>
+							</label>
+							<select name="3rd_party_theme">
+
+								<?php foreach( (array) $bp_themes as $theme ) : ?>
+									<option value="<?php echo $theme['Template'] . ',' . $theme['Stylesheet']; ?>"><?php echo $theme['Name']; ?></option>
+								<?php endforeach; ?>
+
+							</select>
+						</p>
+					</td>
+				</tr>
+			<?php endif; ?>
+
+			<tr>
+				<th>
+					<h5><?php _e( 'Manually update current theme', 'buddypress' ); ?></h5>
+					<?php if ( !empty( $screenshot ) ) : ?>
+						<img src="<?php echo esc_url( $screenshot ); ?>" alt="<?php _e( 'Your existing theme', 'buddypress' ); ?>" />
+					<?php endif; ?>
+				</th>
+				<td>
+					<p><?php _e( 'The BuddyPress Template Pack plugin will guide you through the process of manually upgrading your existing WordPress theme. This usually involves following the step-by-step instructions and copying the BuddyPress template files into your theme. This option requires a working knowledge of CSS and HTML, as you will need to tweak the new templates to match your existing theme.', 'buddypress' ); ?></p>
+
+					<?php if ( empty( $template_pack_installed ) ) : ?>
+
+						<p><a id="bp-template-pack" class="thickbox onclick button" href="<?php echo network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=bp-template-pack&TB_iframe=true&width=640&height=500' ); ?>"><?php _e( 'Install BuddyPress Template Pack', 'buddypress' ); ?></a></p>
+
+					<?php else : ?>
+
+						<p><label><input type="radio" name="theme" value="manual_wp" /> <?php _e( 'Choose this option (go to Appearance &rarr; BP Compatibility after setup is complete)', 'buddypress' ); ?></label></p>
+						<p><a id="bp-template-pack" class="button installed disabled" href="javascript:void();"><span></span><?php _e( 'Plugin Installed', 'buddypress' ); ?></a></p>
+
+					<?php endif; ?>
+
+				</td>
+			</tr>
+
+			<tr>
+				<th>
+					<h5><?php _e( 'Do not change theme', 'buddypress' ) ?></h5>
+				</th>
+				<td>
+					<p><?php _e( "You are happy with your current theme and plan on changing it later.", 'buddypress' ); ?></p>
+					<p><strong><?php _e( 'This is the best choice if you have a highly customized theme on your site already, and want to later manually integrate BuddyPress into your site.', 'buddypress' ); ?></strong></p>
+
+					<p><label><input type="radio" name="theme" value="do_not_change" /> <?php _e( "Don't change my current theme", 'buddypress' ); ?></label></p>
+
+				</td>
+			</tr>
+		</table>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="theme" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ) ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_theme' ) ?>
+
+		</div>
+
+	<?php
+	}
+
+	function step_finish() {
+		if ( !current_user_can( 'activate_plugins' ) )
+			return false;
+
+		// What type of action is happening here?
+		$type = ( 'install' == $this->setup_type ) ? __( 'setup', 'buddypress' ) : __( 'update', 'buddypress' ); ?>
+
+		<p><?php printf( __( "The BuddyPress %1\$s is complete, and your site is ready to go!", 'buddypress' ), $type, $type ); ?></p>
+
+		<div class="submit clear">
+			<input type="hidden" name="save" value="finish" />
+			<input type="hidden" name="step" value="<?php echo esc_attr( $this->current_step ); ?>" />
+
+			<?php wp_nonce_field( 'bpwizard_finish' ); ?>
+
+		</div>
+
+	<?php
+	}
+
+	/** Save Step Methods *****************************************************/
+
+	function step_db_update_save() {
+		global $bp;
+
+		if ( isset( $_POST['submit'] ) ) {
+			check_admin_referer( 'bpwizard_db_update' );
+
+			// Run the schema install to update tables
+			bp_core_install();
+
+			if ( $this->database_version < 1801 )
+				$this->update_1_5();
+
+			// Update the active components option early if we're updating
+			if ( 'update' == $this->setup_type )
+				bp_update_option( 'bp-active-components', $bp->active_components );
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function step_ms_update_save() {
+		global $wpdb;
+
+		if ( isset( $_POST['submit'] ) ) {
+			check_admin_referer( 'bpwizard_ms_update' );
+
+			if ( !$active_components = bp_get_option( 'bp-active-components' ) )
+				$active_components = array();
+
+			// Transfer important settings from blog options to site options
+			$options = array(
+				'bp-db-version'        => $this->database_version,
+				'bp-active-components' => $active_components,
+				'avatar-default'       => get_option( 'avatar-default' )
+			);
+			bp_core_activate_site_options( $options );
+
+			if ( isset( $_POST['bp_components']['blogs'] ) ) {
+				$active_components['blogs'] = 1;
+
+				// Make sure that the pages are created on the bp_get_root_blog_id(), no matter which Dashboard the setup is being run on
+				if ( !empty( $wpdb->blogid ) && ( $wpdb->blogid != bp_get_root_blog_id() ) && ( !defined( 'BP_ENABLE_MULTIBLOG' ) ) )
+					switch_to_blog( bp_get_root_blog_id() );
+
+				// Move bp-pages data from the blog options table to site options
+				$existing_pages	= bp_get_option( 'bp-pages' );
+
+				$bp_pages       = $this->setup_pages( (array)$_POST['bp_pages'] );
+				$bp_pages       = array_merge( (array)$existing_pages, (array)$bp_pages );
+
+				bp_update_option( 'bp-pages', $bp_pages );
+
+				if ( !empty( $wpdb->blogid ) && ( $wpdb->blogid != bp_get_root_blog_id() ) && ( !defined( 'BP_ENABLE_MULTIBLOG' ) ) )
+					restore_current_blog();
+
+				bp_core_install( $active_components );
+			}
+
+			bp_update_option( 'bp-active-components', $active_components );
+
+			return true;
+		}
+
+		return false;
+	}
+
+
+	function step_components_save() {
+		if ( isset( $_POST['submit'] ) && isset( $_POST['bp_components'] ) ) {
+			check_admin_referer( 'bpwizard_components' );
+
+			$active_components = array();
+
+			// Settings form submitted, now save the settings.
+			foreach ( (array)$_POST['bp_components'] as $key => $value )
+				$active_components[$key] = 1;
+
+			bp_update_option( 'bp-active-components', $active_components );
+
+			wp_cache_flush();
+			bp_core_install();
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function step_pages_save() {
+		global $wpdb;
+
+		if ( isset( $_POST['submit'] ) && isset( $_POST['bp_pages'] ) ) {
+			check_admin_referer( 'bpwizard_pages' );
+
+			// Make sure that the pages are created on the bp_get_root_blog_id(), no matter which Dashboard the setup is being run on
+			if ( !empty( $wpdb->blogid ) && ( $wpdb->blogid != bp_get_root_blog_id() ) && ( !defined( 'BP_ENABLE_MULTIBLOG' ) ) )
+				switch_to_blog( bp_get_root_blog_id() );
+
+			// Delete any existing pages
+			$existing_pages = bp_core_update_get_page_meta( 'bp-pages' );
+
+			foreach ( (array)$existing_pages as $page_id )
+				wp_delete_post( $page_id, true );
+
+			$blog_pages   = $this->setup_pages( (array)$_POST['bp_pages'] );
+			bp_update_option( 'bp-pages', $blog_pages );
+
+			if ( !empty( $wpdb->blogid ) && ( $wpdb->blogid != bp_get_root_blog_id() ) && ( !defined( 'BP_ENABLE_MULTIBLOG' ) ) )
+				restore_current_blog();
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function step_permalinks_save() {
+		global $wp_rewrite, $current_site, $current_blog;
+
+		// Prevent debug notices
+		$iis7_permalinks = $usingpi = $writable = false;
+
+		if ( isset( $_POST['submit'] ) ) {
+			check_admin_referer( 'bpwizard_permalinks' );
+
+			$home_path       = get_home_path();
+			$iis7_permalinks = iis7_supports_permalinks();
+
+			if ( isset( $_POST['permalink_structure'] ) ) {
+				$permalink_structure = $_POST['permalink_structure'];
+
+				if ( !empty( $permalink_structure ) )
+					$permalink_structure = preg_replace( '#/+#', '/', '/' . $_POST['permalink_structure'] );
+
+				if ( ( defined( 'VHOST' ) && constant( 'VHOST' ) == 'no' ) && $permalink_structure != '' && $current_site->domain . $current_site->path == $current_blog->domain . $current_blog->path )
+					$permalink_structure = '/blog' . $permalink_structure;
+
+				$wp_rewrite->set_permalink_structure( $permalink_structure );
+			}
+
+			if ( !empty( $iis7_permalinks ) ) {
+				if ( ( !file_exists( $home_path . 'web.config' ) && win_is_writable( $home_path ) ) || win_is_writable( $home_path . 'web.config' ) ) {
+					$writable = true;
+				}
+			} else {
+				if ( ( !file_exists( $home_path . '.htaccess' ) && is_writable( $home_path ) ) || is_writable( $home_path . '.htaccess' ) ) {
+					$writable = true;
+				}
+			}
+
+			if ( $wp_rewrite->using_index_permalinks() )
+				$usingpi = true;
+
+			$wp_rewrite->flush_rules();
+
+			if ( !empty( $iis7_permalinks ) || ( empty( $usingpi ) && empty( $writable ) ) ) {
+
+				function _bp_core_wizard_step_permalinks_message() {
+					global $wp_rewrite; ?>
+
+					<div id="message" class="updated fade"><p>
+
+						<?php
+							_e( 'Oops, there was a problem creating a configuration file. ', 'buddypress' );
+
+							if ( !empty( $iis7_permalinks ) ) {
+
+								if ( !empty( $permalink_structure ) && empty( $usingpi ) && empty( $writable ) ) {
+
+									_e( 'If your <code>web.config</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so this is the url rewrite rule you should have in your <code>web.config</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all. Then insert this rule inside of the <code>/&lt;configuration&gt;/&lt;system.webServer&gt;/&lt;rewrite&gt;/&lt;rules&gt;</code> element in <code>web.config</code> file.' ); ?>
+
+									<br /><br />
+
+									<textarea rows="9" class="large-text readonly" style="background: #fff;" name="rules" id="rules" readonly="readonly"><?php echo esc_html( $wp_rewrite->iis7_url_rewrite_rules() ); ?></textarea>
+
+								<?php
+
+								} else if ( !empty( $permalink_structure ) && empty( $usingpi ) && !empty( $writable ) ); {
+									_e( 'Permalink structure updated. Remove write access on web.config file now!' );
+								}
+
+							} else {
+
+								_e( 'If your <code>.htaccess</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so these are the mod_rewrite rules you should have in your <code>.htaccess</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all.' ); ?>
+
+								<br /><br />
+
+								<textarea rows="6" class="large-text readonly" style="background: #fff;" name="rules" id="rules" readonly="readonly"><?php echo esc_html( $wp_rewrite->mod_rewrite_rules() ); ?></textarea>
+
+							<?php } ?>
+
+						<br /><br />
+
+						<?php
+							if ( empty( $iis7_permalinks ) )
+								_e( 'Paste all these rules into a new <code>.htaccess</code> file in the root of your WordPress installation and save the file. Once you\'re done, please hit the "Save and Next" button to continue.', 'buddypress' );
+						?>
+
+					</p></div>
+
+				<?php
+				}
+
+				if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && !empty( $_POST['skip-htaccess'] ) ) {
+					return true;
+				} else {
+					add_action( 'bp_admin_notices', '_bp_core_wizard_step_permalinks_message' );
+					return false;
+				}
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function step_theme_save() {
+		if ( isset( $_POST['submit'] ) && isset( $_POST['theme'] ) ) {
+			check_admin_referer( 'bpwizard_theme' );
+
+			if ( is_multisite() && bp_get_root_blog_id() != get_current_blog_id() )
+				switch_to_blog( bp_get_root_blog_id() );
+
+			switch ( $_POST['theme'] ) {
+
+				// Activate the bp-default theme
+				case 'bp_default' :
+					switch_theme( 'bp-default', 'bp-default' );
+					break;
+
+				// Activate Template Pack plugin
+				case 'manual_wp' :
+
+					// Include
+					require_once( ABSPATH . WPINC . '/plugin.php' );
+					$installed_plugins = get_plugins();
+
+					foreach ( $installed_plugins as $key => $plugin ) {
+						if ( 'BuddyPress Template Pack' == $plugin['Name'] ) {
+							activate_plugin( $key );
+						}
+					}
+					break;
+
+				// Pick a theme from the repo
+				case '3rd_party' :
+					if ( empty( $_POST['3rd_party_theme'] ) )
+						return false;
+
+					$theme = explode( ',', $_POST['3rd_party_theme'] );
+					switch_theme( $theme[0], $theme[1] );
+					break;
+
+				// Keep existing theme
+				case 'do_not_change' :
+					return true;
+					break;
+			}
+
+			if ( is_multisite() )
+				restore_current_blog();
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function step_finish_save() {
+		if ( isset( $_POST['submit'] ) ) {
+			check_admin_referer( 'bpwizard_finish' );
+
+			// Update the DB version in the database
+			// Stored in sitemeta. Do not use bp_update_option()
+			update_site_option( 'bp-db-version', $this->new_version );
+			delete_site_option( 'bp-core-db-version' );
+
+			// Delete the setup cookie
+			@setcookie( 'bp-wizard-step', '', time() - 3600, COOKIEPATH );
+
+			// Load BP and hook the admin menu, so that the redirect is successful
+			if ( !function_exists( 'bp_core_update_message' ) )
+				require( WP_PLUGIN_DIR . '/buddypress/bp-core/admin/bp-core-admin.php' );
+
+			bp_core_add_admin_menu();
+
+			// Redirect to the BuddyPress dashboard
+			$redirect = bp_core_update_do_network_admin() ? add_query_arg( array( 'page' => 'bp-general-settings' ), network_admin_url( 'admin.php' ) ) : add_query_arg( array( 'page' => 'bp-general-settings' ), admin_url( 'admin.php' ) );
+
+			wp_redirect( $redirect );
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function setup_pages( $pages ) {
+		foreach ( $pages as $key => $value ) {
+			if ( 'page' == $value ) {
+				// Check for the selected page
+				if ( !empty( $_POST['bp-' . $key . '-page'] ) )
+					$bp_pages[$key] = (int)$_POST['bp-' . $key . '-page'];
+				else
+					$bp_pages[$key] = wp_insert_post( array( 'comment_status' => 'closed', 'ping_status' => 'closed', 'post_title' => ucwords( $key ), 'post_status' => 'publish', 'post_type' => 'page' ) );
+			} else {
+				// Create a new page
+				$bp_pages[$key] = wp_insert_post( array( 'comment_status' => 'closed', 'ping_status' => 'closed', 'post_title' => ucwords( $value ), 'post_status' => 'publish', 'post_type' => 'page' ) );
+			}
+		}
+
+		return $bp_pages;
+	}
+
+	// Database update methods based on version numbers
+	function update_1_5() {
+		// Delete old database version options
+		delete_site_option( 'bp-activity-db-version' );
+		delete_site_option( 'bp-blogs-db-version'    );
+		delete_site_option( 'bp-friends-db-version'  );
+		delete_site_option( 'bp-groups-db-version'   );
+		delete_site_option( 'bp-messages-db-version' );
+		delete_site_option( 'bp-xprofile-db-version' );
+	}
+
+	/**
+	 * Reset the cookie so the install script starts over
+	 */
+	function reset_cookie() {
+		@setcookie( 'bp-wizard-step', '', time() - 3600, COOKIEPATH );
+	}
+}
+
+function bp_core_setup_wizard_init() {
+	global $bp_wizard;
+
+	$bp_wizard = new BP_Core_Setup_Wizard;
+}
+add_action( bp_core_update_admin_hook(), 'bp_core_setup_wizard_init', 7 );
+
+function bp_core_install( $active_components = false ) {
+	global $wpdb;
+
+	if ( empty( $active_components ) )
+		$active_components = apply_filters( 'bp_active_components', bp_get_option( 'bp-active-components' ) );
+
+	require( dirname( __FILE__ ) . '/bp-core-schema.php' );
+
+	// Core DB Tables
+	bp_core_install_notifications();
+
+	// Activity Streams
+	if ( !empty( $active_components['activity'] ) )
+		bp_core_install_activity_streams();
+
+	// Friend Connections
+	if ( !empty( $active_components['friends'] ) )
+		bp_core_install_friends();
+
+	// Extensible Groups
+	if ( !empty( $active_components['groups'] ) )
+		bp_core_install_groups();
+
+	// Private Messaging
+	if ( !empty( $active_components['messages'] ) )
+		bp_core_install_private_messaging();
+
+	// Extended Profiles
+	if ( !empty( $active_components['xprofile'] ) )
+		bp_core_install_extended_profiles();
+
+	// Blog tracking
+	if ( !empty( $active_components['blogs'] ) )
+		bp_core_install_blog_tracking();
+}
+
+function bp_core_update( $disabled ) {
+	global $wpdb;
+
+	require( dirname( __FILE__ ) . '/bp-core-schema.php' );
+}
+
+function bp_update_db_stuff() {
+	$bp_prefix = bp_core_get_table_prefix();
+	// Rename the old user activity cached table if needed.
+	if ( $wpdb->get_var( "SHOW TABLES LIKE '%{$bp_prefix}bp_activity_user_activity_cached%'" ) )
+		$wpdb->query( "RENAME TABLE {$bp_prefix}bp_activity_user_activity_cached TO {$bp->activity->table_name}" );
+
+	// Rename fields from pre BP 1.2
+	if ( $wpdb->get_var( "SHOW TABLES LIKE '%{$bp->activity->table_name}%'" ) ) {
+		if ( $wpdb->get_var( "SHOW COLUMNS FROM {$bp->activity->table_name} LIKE 'component_action'" ) )
+			$wpdb->query( "ALTER TABLE {$bp->activity->table_name} CHANGE component_action type varchar(75) NOT NULL" );
+
+		if ( $wpdb->get_var( "SHOW COLUMNS FROM {$bp->activity->table_name} LIKE 'component_name'" ) )
+			$wpdb->query( "ALTER TABLE {$bp->activity->table_name} CHANGE component_name component varchar(75) NOT NULL" );
+	}
+
+	// On first installation - record all existing blogs in the system.
+	if ( !(int)$bp->site_options['bp-blogs-first-install'] ) {
+		bp_blogs_record_existing_blogs();
+		bp_update_option( 'bp-blogs-first-install', 1 );
+	}
+
+	if ( is_multisite() )
+		bp_core_add_illegal_names();
+
+	// Update and remove the message threads table if it exists
+	if ( $wpdb->get_var( "SHOW TABLES LIKE '%{$bp_prefix}bp_messages_threads%'" ) ) {
+		$update = BP_Messages_Thread::update_tables();
+
+		if ( $update )
+			$wpdb->query( "DROP TABLE {$bp_prefix}bp_messages_threads" );
+	}
+
+}
+
+function bp_core_wizard_message() {
+	if ( isset( $_GET['updated'] ) )
+		$message = __( 'Installation was successful. The available options have now been updated, please continue with your selection.', 'buddypress' );
+	else
+		return false; ?>
+
+	<div id="message" class="updated">
+		<p><?php echo esc_attr( $message ) ?></p>
+	</div>
+
+<?php
+}
+add_action( 'bp_admin_notices', 'bp_core_wizard_message' );
+
+// Alter thickbox screens so the entire plugin download and install
+// interface is contained within.
+function bp_core_wizard_thickbox() {
+	$form_action = bp_core_update_do_network_admin() ? network_admin_url( add_query_arg( array( 'page' => 'bp-wizard', 'updated' => '1' ), 'admin.php' ) ) : admin_url( add_query_arg( array( 'page' => 'bp-wizard', 'updated' => '1' ), 'admin.php' ) ); ?>
+
+	<script type="text/javascript">
+		jQuery('p.action-button a').attr( 'target', '' );
+
+		if ( window.location != window.parent.location ) {
+			jQuery('#adminmenu, #wphead, #footer, #update-nag, #screen-meta').hide();
+			jQuery('#wpbody').css( 'margin', '15px' );
+			jQuery('body').css( 'min-width', '30px' );
+			jQuery('#wpwrap').css( 'min-height', '30px' );
+			jQuery('a').removeClass( 'thickbox thickbox-preview onclick' );
+			jQuery('body.update-php div.wrap p:last').hide();
+			jQuery('body.update-php div.wrap p:last').after( '<p><a class="button" target="_parent" href="<?php echo $form_action; ?>"><?php _e( 'Finish', 'buddypress' ) ?></a></p>' );
+		}
+	</script>
+
+<?php
+}
+add_action( 'admin_footer', 'bp_core_wizard_thickbox' );
+
+/**
+ * Adds the "BuddyPress" admin submenu item to the Site Admin tab.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @global $wpdb WordPress DB access object.
+ * @uses add_submenu_page() WP function to add a submenu item
+ */
+function bp_core_update_add_admin_menu() {
+	global $bp_wizard;
+
+	// Only load this version of the menu if this is an upgrade or a new installation
+	if ( empty( $bp_wizard->setup_type ) )
+		return false;
+
+	if ( !current_user_can( 'activate_plugins' ) )
+		return false;
+
+	if ( 'install' == $bp_wizard->setup_type )
+		$status = __( 'Setup', 'buddypress' );
+	else
+		$status = __( 'Update', 'buddypress' );
+
+	// Add the administration tab under the "Site Admin" tab for site administrators
+	add_menu_page( __( 'BuddyPress', 'buddypress' ), __( 'BuddyPress', 'buddypress' ), 'manage_options', 'bp-wizard', '' );
+	$hook = add_submenu_page( 'bp-wizard', $status, $status, 'manage_options', 'bp-wizard', array( $bp_wizard, 'html' ) );
+
+	// Add a hook for css/js
+	add_action( "admin_print_styles-$hook", 'bp_core_update_add_admin_menu_styles' );
+}
+add_action( bp_core_update_admin_hook(),  'bp_core_update_add_admin_menu', 9 );
+
+function bp_core_update_add_admin_menu_styles() {
+	if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
+		wp_enqueue_style( 'bp-admin-css', apply_filters( 'bp_core_admin_css', plugins_url( '/buddypress' ) . '/bp-core/css/admin.dev.css' ), array(), '20110723' );
+		wp_enqueue_script( 'bp-update-js', apply_filters( 'bp_core_update_js', plugins_url( '/buddypress' ) . '/bp-core/js/update.dev.js' ), array( 'jquery' ), '20110723' );
+	} else {
+		wp_enqueue_style( 'bp-admin-css', apply_filters( 'bp_core_admin_css', plugins_url( '/buddypress' ) . '/bp-core/css/admin.css' ), array(), '20110723' );
+		wp_enqueue_script( 'bp-update-js', apply_filters( 'bp_core_update_js', plugins_url( '/buddypress' ) . '/bp-core/js/update.js' ), array( 'jquery' ), '20110723' );
+
+	}
+
+	wp_enqueue_script( 'thickbox' );
+	wp_enqueue_style( 'thickbox' ); ?>
+
+	<style type="text/css">
+		/* Wizard Icon */
+		ul#adminmenu li.toplevel_page_bp-wizard .wp-menu-image a img { display: none; }
+		ul#adminmenu li.toplevel_page_bp-wizard .wp-menu-image a { background-image: url( <?php echo plugins_url( 'buddypress/bp-core/images/admin_menu_icon.png' ) ?> ) !important; background-position: -1px -32px; }
+		ul#adminmenu li.toplevel_page_bp-wizard:hover .wp-menu-image a,
+		ul#adminmenu li.toplevel_page_bp-wizard.wp-has-current-submenu .wp-menu-image a {
+			background-position: -1px 0;
+		}
+
+		/* Settings Icon */
+		ul#adminmenu li.toplevel_page_bp-general-settings .wp-menu-image a img { display: none; }
+		ul#adminmenu li.toplevel_page_bp-general-settings .wp-menu-image a { background-image: url( <?php echo plugins_url( 'buddypress/bp-core/images/admin_menu_icon.png' ) ?> ) !important; background-position: -1px -32px; }
+		ul#adminmenu li.toplevel_page_bp-general-settings:hover .wp-menu-image a,
+		ul#adminmenu li.toplevel_page_bp-general-settings.wp-has-current-submenu .wp-menu-image a {
+			background-position: -1px 0;
+		}
+	</style>
+
+<?php
+}
+add_action( 'admin_head', 'bp_core_update_add_admin_menu_styles' );
+
+/**
+ * Fetches BP pages from the meta table
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @return array $page_ids
+ */
+function bp_core_update_get_page_meta() {
+	if ( !$page_ids = bp_get_option( 'bp-pages' ) )
+		$page_ids = array();
+
+	return apply_filters( 'bp_core_update_get_page_meta', $page_ids );
+}
+
+function bp_core_update_do_network_admin() {
+	$do_network_admin = false;
+
+	if ( is_multisite() && ( !defined( 'BP_ENABLE_MULTIBLOG' ) || !BP_ENABLE_MULTIBLOG ) )
+		$do_network_admin = true;
+
+	return apply_filters( 'bp_core_do_network_admin', $do_network_admin );
+}
+
+function bp_core_update_admin_hook() {
+	$hook = bp_core_update_do_network_admin() ? 'network_admin_menu' : 'admin_menu';
+
+	return apply_filters( 'bp_core_admin_hook', $hook );
+}
+
+/**
+ * Adds an admin nag about running the BP upgrade/install wizard
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ * @global $pagenow The current admin page
+ */
+function bp_core_update_nag() {
+	global $bp_wizard, $pagenow;
+
+	if ( empty( $bp_wizard->setup_type ) )
+		return;
+
+	if ( !is_super_admin() )
+		return;
+
+	if ( 'admin.php' == $pagenow && ( empty( $_GET['page'] ) || 'bp-wizard' == $_GET['page'] ) )
+		return;
+
+	$url = bp_core_update_do_network_admin() ? network_admin_url( 'admin.php?page=bp-wizard' ) : admin_url( 'admin.php?page=bp-wizard' );
+
+	switch( $bp_wizard->setup_type ) {
+		case 'update':
+			$msg = sprintf( __( 'BuddyPress has been updated! Please run the <a href="%s">update wizard</a>.', 'buddypress' ), $url );
+			break;
+
+		default:
+		case 'install':
+			$msg = sprintf( __( 'BuddyPress was successfully installed! Please run the <a href="%s">installation wizard</a>.', 'buddypress' ), $url );
+			break;
+	}
+
+	echo '<div class="update-nag">' . $msg . '</div>';
+}
+add_action( 'admin_notices', 'bp_core_update_nag', 5 );
+add_action( 'network_admin_notices', 'bp_core_update_nag', 5 );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-adminbar.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-adminbar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-adminbar.php	(revision 5057)
@@ -0,0 +1,251 @@
+<?php
+/**
+ * BuddyPress Core Admin Bar
+ *
+ * Handles the core functions related to the WordPress Admin Bar
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+if ( !bp_use_wp_admin_bar() || defined( 'DOING_AJAX' ) )
+	return;
+
+/**
+ * Unhook the WordPress core menus.
+ *
+ * @since BuddyPress (r4151)
+ *
+ * @uses remove_action
+ * @uses is_network_admin()
+ * @uses is_user_admin()
+ */
+function bp_admin_bar_remove_wp_menus() {
+
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 10 );
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_dashboard_view_site_menu', 25 );
+
+	// Don't show the 'Edit Page' menu on BP pages
+	if ( !bp_is_blog_page() )
+		remove_action( 'admin_bar_menu', 'wp_admin_bar_edit_menu', 30 );
+
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_shortlink_menu', 80 );
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 70 );
+
+	if ( !is_network_admin() && !is_user_admin() ) {
+		remove_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 50 );
+		remove_action( 'admin_bar_menu', 'wp_admin_bar_appearance_menu', 60 );
+	}
+
+	remove_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 70 );
+}
+add_action( 'bp_init', 'bp_admin_bar_remove_wp_menus', 2 );
+
+/**
+ * Add a menu for the root site of this BuddyPress network
+ *
+ * @global type $bp
+ * @global type $wp_admin_bar
+ * @return If in ajax
+ */
+function bp_admin_bar_root_site() {
+	global $bp, $wp_admin_bar;
+
+	// Create the root blog menu
+	$wp_admin_bar->add_menu( array(
+		'id'    => 'bp-root-blog',
+		'title' => get_blog_option( bp_get_root_blog_id(), 'blogname' ),
+		'href'  => bp_get_root_domain()
+	) );
+
+	// Logged in user
+	if ( is_user_logged_in() ) {
+
+		// Dashboard links
+		if ( is_super_admin() ) {
+
+			// Add site admin link
+			$wp_admin_bar->add_menu( array(
+				'id' => 'dashboard',
+				'parent' => 'bp-root-blog',
+				'title' => __( 'Admin Dashboard', 'buddypress' ),
+				'href' => get_admin_url( bp_get_root_blog_id() )
+			) );
+
+			// Add network admin link
+			if ( is_multisite() ) {
+
+				// Link to the network admin dashboard
+				$wp_admin_bar->add_menu( array(
+					'id' => 'network-dashboard',
+					'parent' => 'bp-root-blog',
+					'title' => __( 'Network Dashboard', 'buddypress' ),
+					'href' => network_admin_url()
+				) );
+			}
+		}
+	}
+}
+add_action( 'bp_setup_admin_bar', 'bp_admin_bar_root_site', 3 );
+
+/**
+ * Add the "My Sites/[Site Name]" menu and all submenus.
+ */
+function bp_admin_bar_my_sites_menu() {
+	global $wpdb, $wp_admin_bar;
+
+	/* Add the 'My Sites' menu if the user has more than one site. */
+	if ( count( $wp_admin_bar->user->blogs ) <= 1 )
+		return;
+
+	$wp_admin_bar->add_menu( array( 'id' => 'my-blogs', 'title' => __( 'My Sites' ), 'href' => admin_url( 'my-sites.php' ) ) );
+
+	$default = includes_url( 'images/wpmini-blue.png' );
+
+	foreach ( (array)$wp_admin_bar->user->blogs as $blog ) {
+		// @todo Replace with some favicon lookup.
+		//$blavatar = '<img src="' . esc_url( blavatar_url( blavatar_domain( $blog->siteurl ), 'img', 16, $default ) ) . '" alt="Blavatar" width="16" height="16" />';
+		$blavatar = '<img src="' . esc_url( $default ) . '" alt="' . esc_attr__( 'Blavatar' ) . '" width="16" height="16" class="blavatar"/>';
+
+		$blogname = empty( $blog->blogname ) ? $blog->domain : $blog->blogname;
+
+		$wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-' . $blog->userblog_id, 'title' => $blavatar . $blogname, 'href' => get_admin_url( $blog->userblog_id ) ) );
+		$wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-d', 'title' => __( 'Dashboard' ), 'href' => get_admin_url( $blog->userblog_id ) ) );
+
+		if ( current_user_can_for_blog( $blog->userblog_id, 'edit_posts' ) ) {
+			$wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-n', 'title' => __( 'New Post' ), 'href' => get_admin_url( $blog->userblog_id, 'post-new.php' ) ) );
+			$wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-c', 'title' => __( 'Manage Comments' ), 'href' => get_admin_url( $blog->userblog_id, 'edit-comments.php' ) ) );
+		}
+
+		$wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-v', 'title' => __( 'Visit Site' ), 'href' => get_home_url( $blog->userblog_id ) ) );
+	}
+}
+add_action( 'bp_setup_admin_bar', 'bp_admin_bar_my_sites_menu', 3 );
+
+/**
+ * Add edit comments link with awaiting moderation count bubble
+ */
+function bp_admin_bar_comments_menu( $wp_admin_bar ) {
+	global $wp_admin_bar;
+
+	if ( !current_user_can( 'edit_posts' ) )
+		return;
+
+	$awaiting_mod = wp_count_comments();
+	$awaiting_mod = $awaiting_mod->moderated;
+
+	$awaiting_mod = $awaiting_mod ? "<span id='ab-awaiting-mod' class='pending-count'>" . number_format_i18n( $awaiting_mod ) . "</span>" : '';
+	$wp_admin_bar->add_menu( array( 'parent' => 'dashboard', 'id' => 'comments', 'title' => sprintf( __( 'Comments %s' ), $awaiting_mod ), 'href' => admin_url( 'edit-comments.php' ) ) );
+}
+add_action( 'bp_setup_admin_bar', 'bp_admin_bar_comments_menu', 3 );
+
+/**
+ * Add "Appearance" menu with widget and nav menu submenu
+ */
+function bp_admin_bar_appearance_menu() {
+	global $wp_admin_bar;
+
+	// You can have edit_theme_options but not switch_themes.
+	if ( !current_user_can( 'switch_themes' ) && !current_user_can( 'edit_theme_options' ) )
+		return;
+
+	$wp_admin_bar->add_menu( array( 'parent' => 'dashboard', 'id' => 'appearance', 'title' => __( 'Appearance' ), 'href' => admin_url( 'themes.php' ) ) );
+
+	if ( !current_user_can( 'edit_theme_options' ) )
+		return;
+
+	if ( current_user_can( 'switch_themes' ) )
+		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'themes', 'title' => __( 'Themes' ), 'href' => admin_url( 'themes.php' ) ) );
+
+	if ( current_theme_supports( 'widgets' ) )
+		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'widgets', 'title' => __( 'Widgets' ), 'href' => admin_url( 'widgets.php' ) ) );
+
+	if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) )
+		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'menus', 'title' => __( 'Menus' ), 'href' => admin_url( 'nav-menus.php' ) ) );
+
+	if ( current_theme_supports( 'custom-background' ) )
+		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'background', 'title' => __( 'Background' ), 'href' => admin_url( 'themes.php?page=custom-background' ) ) );
+
+	if ( current_theme_supports( 'custom-header' ) )
+		$wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'header', 'title' => __( 'Header' ), 'href' => admin_url( 'themes.php?page=custom-header' ) ) );
+}
+add_action( 'bp_setup_admin_bar', 'bp_admin_bar_appearance_menu', 3 );
+
+/**
+ * Provide an update link if theme/plugin/core updates are available
+ */
+function bp_admin_bar_updates_menu() {
+	global $wp_admin_bar;
+
+	if ( !current_user_can( 'install_plugins' ) )
+		return;
+
+	$plugin_update_count = $theme_update_count = $wordpress_update_count = 0;
+	$update_plugins = get_site_transient( 'update_plugins' );
+	if ( !empty( $update_plugins->response ) )
+		$plugin_update_count = count( $update_plugins->response );
+	$update_themes = get_site_transient( 'update_themes' );
+	if ( !empty( $update_themes->response ) )
+		$theme_update_count = count( $update_themes->response );
+	/* @todo get_core_updates() is only available on admin page loads
+	  $update_wordpress = get_core_updates( array('dismissed' => false) );
+	  if ( !empty($update_wordpress) && !in_array( $update_wordpress[0]->response, array('development', 'latest') ) )
+	  $wordpress_update_count = 1;
+	 */
+
+	$update_count = $plugin_update_count + $theme_update_count + $wordpress_update_count;
+
+	if ( !$update_count )
+		return;
+
+	$update_title = array( );
+	if ( $wordpress_update_count )
+		$update_title[] = sprintf( __( '%d WordPress Update' ), $wordpress_update_count );
+	if ( $plugin_update_count )
+		$update_title[] = sprintf( _n( '%d Plugin Update', '%d Plugin Updates', $plugin_update_count ), $plugin_update_count );
+	if ( $theme_update_count )
+		$update_title[] = sprintf( _n( '%d Theme Update', '%d Theme Updates', $theme_update_count ), $theme_update_count );
+
+	$update_title = !empty( $update_title ) ? esc_attr( implode( ', ', $update_title ) ) : '';
+
+	$update_title = "<span title='$update_title'>";
+	$update_title .= sprintf( __( 'Updates %s' ), "<span id='ab-updates' class='update-count'>" . number_format_i18n( $update_count ) . '</span>' );
+	$update_title .= '</span>';
+
+	$wp_admin_bar->add_menu( array( 'parent' => 'dashboard', 'id' => 'updates', 'title' => $update_title, 'href' => network_admin_url( 'update-core.php' ) ) );
+}
+add_action( 'bp_setup_admin_bar', 'bp_admin_bar_updates_menu', 3 );
+
+/**
+ * Handle the Admin Bar CSS
+ */
+function bp_core_load_admin_bar_css() {
+	global $wp_version;
+
+	if ( !bp_use_wp_admin_bar() )
+		return;
+
+	// Admin bar styles
+	if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/admin-bar.dev.css';
+	else
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/admin-bar.css';
+
+	wp_enqueue_style( 'bp-admin-bar', apply_filters( 'bp_core_admin_bar_css', $stylesheet ), array( 'admin-bar' ), '20110723' );
+
+	if ( !is_rtl() )
+		return;
+
+	if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/admin-bar-rtl.dev.css';
+	else
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/admin-bar-rtl.css';
+
+	wp_enqueue_style( 'bp-admin-bar-rtl', apply_filters( 'bp_core_admin_bar_rtl_css', $stylesheet ), array( 'bp-admin-bar' ), '20110723' );
+}
+add_action( 'bp_init', 'bp_core_load_admin_bar_css' );
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-avatars.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-avatars.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-avatars.php	(revision 5057)
@@ -0,0 +1,853 @@
+<?php
+/**
+ * BuddyPress Avatars
+ *
+ * Based on contributions from: Beau Lebens - http://www.dentedreality.com.au/
+ * Modified for BuddyPress by: Andy Peatling - http://apeatling.wordpress.com/
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/***
+ * Set up the constants we need for avatar support
+ */
+function bp_core_set_avatar_constants() {
+	global $bp;
+	
+	if ( !defined( 'BP_AVATAR_THUMB_WIDTH' ) )
+		define( 'BP_AVATAR_THUMB_WIDTH', 50 );
+
+	if ( !defined( 'BP_AVATAR_THUMB_HEIGHT' ) )
+		define( 'BP_AVATAR_THUMB_HEIGHT', 50 );
+
+	if ( !defined( 'BP_AVATAR_FULL_WIDTH' ) )
+		define( 'BP_AVATAR_FULL_WIDTH', 150 );
+
+	if ( !defined( 'BP_AVATAR_FULL_HEIGHT' ) )
+		define( 'BP_AVATAR_FULL_HEIGHT', 150 );
+
+	if ( !defined( 'BP_AVATAR_ORIGINAL_MAX_WIDTH' ) )
+		define( 'BP_AVATAR_ORIGINAL_MAX_WIDTH', 450 );
+
+	if ( !defined( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE' ) ) {
+		if ( !isset( $bp->site_options['fileupload_maxk'] ) )
+			define( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE', 5120000 ); // 5mb
+		else
+			define( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE', $bp->site_options['fileupload_maxk'] * 1024 );
+	}
+
+	if ( !defined( 'BP_AVATAR_DEFAULT' ) )
+		define( 'BP_AVATAR_DEFAULT', BP_PLUGIN_URL . '/bp-core/images/mystery-man.jpg' );
+
+	if ( !defined( 'BP_AVATAR_DEFAULT_THUMB' ) )
+		define( 'BP_AVATAR_DEFAULT_THUMB', BP_PLUGIN_URL . '/bp-core/images/mystery-man-50.jpg' );
+}
+add_action( 'bp_init', 'bp_core_set_avatar_constants', 3 );
+
+function bp_core_set_avatar_globals() {
+	global $bp;
+	
+	// Dimensions
+	$bp->avatar->thumb->width  	   = BP_AVATAR_THUMB_WIDTH;
+	$bp->avatar->thumb->height 	   = BP_AVATAR_THUMB_HEIGHT;
+	$bp->avatar->full->width 	   = BP_AVATAR_FULL_WIDTH;
+	$bp->avatar->full->height 	   = BP_AVATAR_FULL_HEIGHT;
+	
+	// Upload maximums
+	$bp->avatar->original_max_width    = BP_AVATAR_ORIGINAL_MAX_WIDTH;
+	$bp->avatar->original_max_filesize = BP_AVATAR_ORIGINAL_MAX_FILESIZE;
+	
+	// Defaults
+	$bp->avatar->thumb->default 	   = BP_AVATAR_DEFAULT_THUMB;
+	$bp->avatar->full->default 	   = BP_AVATAR_DEFAULT;
+	
+	// These have to be set on page load in order to avoid infinite filter loops at runtime
+	$bp->avatar->upload_path	   = bp_core_avatar_upload_path();
+	$bp->avatar->url	   	   = bp_core_avatar_url();
+	
+	do_action( 'bp_core_set_avatar_globals' );
+}
+add_action( 'bp_setup_globals', 'bp_core_set_avatar_globals' );
+
+/**
+ * bp_core_fetch_avatar()
+ *
+ * Fetches an avatar from a BuddyPress object. Supports user/group/blog as
+ * default, but can be extended to include your own custom components too.
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @global $current_blog WordPress global containing information and settings for the current blog being viewed.
+ * @param array $args Determine the output of this function
+ * @return string Formatted HTML <img> element, or raw avatar URL based on $html arg
+ */
+function bp_core_fetch_avatar( $args = '' ) {
+	global $bp, $current_blog;
+
+	// Set a few default variables
+	$def_object = 'user';
+	$def_type   = 'thumb';
+	$def_class  = 'avatar';
+	$def_alt    = __( 'Avatar Image', 'buddypress' );
+
+	// Set the default variables array
+	$defaults = array(
+		'item_id'    => false,
+		'object'     => $def_object, // user/group/blog/custom type (if you use filters)
+		'type'       => $def_type,   // thumb or full
+		'avatar_dir' => false,       // Specify a custom avatar directory for your object
+		'width'      => false,       // Custom width (int)
+		'height'     => false,       // Custom height (int)
+		'class'      => $def_class,  // Custom <img> class (string)
+		'css_id'     => false,       // Custom <img> ID (string)
+		'alt'        => $def_alt,    // Custom <img> alt (string)
+		'email'      => false,       // Pass the user email (for gravatar) to prevent querying the DB for it
+		'no_grav'    => false,       // If there is no avatar found, return false instead of a grav?
+		'html'       => true,        // Wrap the return img URL in <img />
+		'title'      => ''           // Custom <img> title (string)
+	);
+
+	// Compare defaults to passed and extract
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	// Set item_id if not passed
+	if ( !$item_id ) {
+		if ( 'user' == $object )
+			$item_id = $bp->displayed_user->id;
+		else if ( bp_is_active( 'groups' ) && 'group' == $object )
+			$item_id = $bp->groups->current_group->id;
+		else if ( 'blog' == $object )
+			$item_id = $current_blog->id;
+
+		$item_id = apply_filters( 'bp_core_avatar_item_id', $item_id, $object );
+
+		if ( !$item_id )
+			return false;
+	}
+
+	// Set avatar_dir if not passed (uses $object)
+	if ( !$avatar_dir ) {
+		if ( 'user' == $object )
+			$avatar_dir = 'avatars';
+		else if ( bp_is_active( 'groups' ) && 'group' == $object )
+			$avatar_dir = 'group-avatars';
+		else if ( 'blog' == $object )
+			$avatar_dir = 'blog-avatars';
+
+		$avatar_dir = apply_filters( 'bp_core_avatar_dir', $avatar_dir, $object );
+
+		if ( !$avatar_dir )
+			return false;
+	}
+
+	// Add an identifying class to each item
+	$class .= ' ' . $object . '-' . $item_id . '-avatar';
+
+	// Get item name for alt/title tags
+	$item_name = '';
+
+	if ( 'user' == $object )
+		$item_name = bp_core_get_user_displayname( $item_id );
+	elseif ( 'group' == $object )
+		$item_name = bp_get_group_name( new BP_Groups_Group( $item_id ) );
+	elseif ( 'blog' == $object )
+		$item_name = get_blog_option( $item_id, 'blogname' );
+
+	$alt = sprintf( $alt, apply_filters( 'bp_core_avatar_alt', $item_name, $item_id, $object ) );
+
+	// Set title tag
+	if ( $title )
+		$title = " title='" . esc_attr( apply_filters( 'bp_core_avatar_title', $title, $item_id, $object ) ) . "'";
+	elseif ( $item_name )
+		$title = " title='" . esc_attr( apply_filters( 'bp_core_avatar_title', $item_name, $item_id, $object ) ) . "'";
+
+	// Set CSS ID if passed
+	if ( !empty( $css_id ) )
+		$css_id = " id='{$css_id}'";
+
+	// Set avatar width
+	if ( $width )
+		$html_width = " width='{$width}'";
+	else
+		$html_width = ( 'thumb' == $type ) ? ' width="' . bp_core_avatar_thumb_width() . '"' : ' width="' . bp_core_avatar_full_width() . '"';
+
+	// Set avatar height
+	if ( $height )
+		$html_height = " height='{$height}'";
+	else
+		$html_height = ( 'thumb' == $type ) ? ' height="' . bp_core_avatar_thumb_height() . '"' : ' height="' . bp_core_avatar_full_height() . '"';
+
+	// Set avatar URL and DIR based on prepopulated constants
+	$avatar_folder_url = apply_filters( 'bp_core_avatar_folder_url', bp_core_avatar_url() . '/' . $avatar_dir . '/' . $item_id, $item_id, $object, $avatar_dir );
+	$avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', bp_core_avatar_upload_path() . '/' . $avatar_dir . '/' . $item_id, $item_id, $object, $avatar_dir );
+
+	/****
+	 * Look for uploaded avatar first. Use it if it exists.
+	 * Set the file names to search for, to select the full size
+	 * or thumbnail image.
+	 */
+	$avatar_size              = ( 'full' == $type ) ? '-bpfull' : '-bpthumb';
+	$legacy_user_avatar_name  = ( 'full' == $type ) ? '-avatar2' : '-avatar1';
+	$legacy_group_avatar_name = ( 'full' == $type ) ? '-groupavatar-full' : '-groupavatar-thumb';
+
+	// Check for directory
+	if ( file_exists( $avatar_folder_dir ) ) {
+
+		// Open directory
+		if ( $av_dir = opendir( $avatar_folder_dir ) ) {
+
+			// Stash files in an array once to check for one that matches
+			$avatar_files = array();
+			while ( false !== ( $avatar_file = readdir($av_dir) ) ) {
+				// Only add files to the array (skip directories)
+				if ( 2 < strlen( $avatar_file ) )
+					$avatar_files[] = $avatar_file;
+			}
+
+			// Check for array
+			if ( 0 < count( $avatar_files ) ) {
+
+				// Check for current avatar
+				foreach( $avatar_files as $key => $value ) {
+					if ( strpos ( $value, $avatar_size )!== false )
+						$avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
+				}
+
+				// Legacy avatar check
+				if ( !isset( $avatar_url ) ) {
+					foreach( $avatar_files as $key => $value ) {
+						if ( strpos ( $value, $legacy_user_avatar_name )!== false )
+							$avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
+					}
+
+					// Legacy group avatar check
+					if ( !isset( $avatar_url ) ) {
+						foreach( $avatar_files as $key => $value ) {
+							if ( strpos ( $value, $legacy_group_avatar_name )!== false )
+								$avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
+						}
+					}
+				}
+			}
+		}
+
+		// Close the avatar directory
+		closedir( $av_dir );
+
+		// If we found a locally uploaded avatar
+		if ( isset( $avatar_url ) ) {
+
+			// Return it wrapped in an <img> element
+			if ( true === $html ) {
+				return apply_filters( 'bp_core_fetch_avatar', '<img src="' . $avatar_url . '" alt="' . esc_attr( $alt ) . '" class="' . esc_attr( $class ) . '"' . $css_id . $html_width . $html_height . $title . ' />', $params, $item_id, $avatar_dir, $css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir );
+
+			// ...or only the URL
+			} else {
+				return apply_filters( 'bp_core_fetch_avatar_url', $avatar_url );
+			}
+		}
+	}
+
+	// If no avatars could be found, try to display a gravatar
+
+	// Skips gravatar check if $no_grav is passed
+	if ( ! apply_filters( 'bp_core_fetch_avatar_no_grav', $no_grav ) ) {
+
+		// Set gravatar size
+		if ( $width )
+			$grav_size = $width;
+		else if ( 'full' == $type )
+			$grav_size = bp_core_avatar_full_width();
+		else if ( 'thumb' == $type )
+			$grav_size = bp_core_avatar_thumb_width();
+
+		// Set gravatar type
+		if ( empty( $bp->grav_default->{$object} ) )
+			$default_grav = 'wavatar';
+		else if ( 'mystery' == $bp->grav_default->{$object} )
+			$default_grav = apply_filters( 'bp_core_mysteryman_src', bp_core_avatar_default(), $grav_size );
+		else
+			$default_grav = $bp->grav_default->{$object};
+
+		// Set gravatar object
+		if ( empty( $email ) ) {
+			if ( 'user' == $object ) {
+				$email = bp_core_get_user_email( $item_id );
+			} else if ( 'group' == $object || 'blog' == $object ) {
+				$email = "{$item_id}-{$object}@{bp_get_root_domain()}";
+			}
+		}
+
+		// Set host based on if using ssl
+		if ( is_ssl() )
+			$host = 'https://secure.gravatar.com/avatar/';
+		else
+			$host = 'http://www.gravatar.com/avatar/';
+
+		// Filter gravatar vars
+		$email    = apply_filters( 'bp_core_gravatar_email', $email, $item_id, $object );
+		$gravatar = apply_filters( 'bp_gravatar_url', $host ) . md5( strtolower( $email ) ) . '?d=' . $default_grav . '&amp;s=' . $grav_size;
+
+	} else {
+		// No avatar was found, and we've been told not to use a gravatar.
+		$gravatar = apply_filters( "bp_core_default_avatar_$object", BP_PLUGIN_URL . '/bp-core/images/mystery-man.jpg', $params );
+	}
+
+	if ( true === $html )
+		return apply_filters( 'bp_core_fetch_avatar', '<img src="' . $gravatar . '" alt="' . esc_attr( $alt ) . '" class="' . esc_attr( $class ) . '"' . $css_id . $html_width . $html_height . $title . ' />', $params, $item_id, $avatar_dir, $css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir );
+	else
+		return apply_filters( 'bp_core_fetch_avatar_url', $gravatar );
+}
+
+/**
+ * Delete an existing avatar
+ *
+ * Accepted values for $args are:
+ *  item_id - item id which relates to the object type.
+ *  object - the objetc type user, group, blog, etc.
+ *  avatar_dir - The directory where the avatars to be uploaded.
+ *
+ * @global object $bp BuddyPress global settings
+ * @param mixed $args
+ * @return bool Success/failure
+ */
+function bp_core_delete_existing_avatar( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'item_id'    => false,
+		'object'     => 'user', // user OR group OR blog OR custom type (if you use filters)
+		'avatar_dir' => false
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( !$item_id ) {
+		if ( 'user' == $object )
+			$item_id = $bp->displayed_user->id;
+		else if ( 'group' == $object )
+			$item_id = $bp->groups->current_group->id;
+		else if ( 'blog' == $object )
+			$item_id = $current_blog->id;
+
+		$item_id = apply_filters( 'bp_core_avatar_item_id', $item_id, $object );
+
+		if ( !$item_id ) return false;
+	}
+
+	if ( !$avatar_dir ) {
+		if ( 'user' == $object )
+			$avatar_dir = 'avatars';
+		else if ( 'group' == $object )
+			$avatar_dir = 'group-avatars';
+		else if ( 'blog' == $object )
+			$avatar_dir = 'blog-avatars';
+
+		$avatar_dir = apply_filters( 'bp_core_avatar_dir', $avatar_dir, $object );
+
+		if ( !$avatar_dir ) return false;
+	}
+
+	$avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', bp_core_avatar_upload_path() . '/' . $avatar_dir . '/' . $item_id, $item_id, $object, $avatar_dir );
+
+	if ( !file_exists( $avatar_folder_dir ) )
+		return false;
+
+	if ( $av_dir = opendir( $avatar_folder_dir ) ) {
+		while ( false !== ( $avatar_file = readdir($av_dir) ) ) {
+			if ( ( preg_match( "/-bpfull/", $avatar_file ) || preg_match( "/-bpthumb/", $avatar_file ) ) && '.' != $avatar_file && '..' != $avatar_file )
+				@unlink( $avatar_folder_dir . '/' . $avatar_file );
+		}
+	}
+	closedir($av_dir);
+
+	@rmdir( $avatar_folder_dir );
+
+	do_action( 'bp_core_delete_existing_avatar', $args );
+
+	return true;
+}
+
+/**
+ * Handles avatar uploading.
+ *
+ * The functions starts off by checking that the file has been uploaded properly using bp_core_check_avatar_upload().
+ * It then checks that the file size is within limits, and that it has an accepted file extension (jpg, gif, png).
+ * If everything checks out, crop the image and move it to its real location.
+ *
+ * @global object $bp BuddyPress global settings
+ * @param array $file The appropriate entry the from $_FILES superglobal.
+ * @param string $upload_dir_filter A filter to be applied to upload_dir
+ * @return bool Success/failure
+ * @see bp_core_check_avatar_upload()
+ * @see bp_core_check_avatar_type()
+ */
+function bp_core_avatar_handle_upload( $file, $upload_dir_filter ) {
+	global $bp;
+
+	/***
+	 * You may want to hook into this filter if you want to override this function.
+	 * Make sure you return false.
+	 */
+	if ( !apply_filters( 'bp_core_pre_avatar_handle_upload', true, $file, $upload_dir_filter ) )
+		return true;
+
+	require_once( ABSPATH . '/wp-admin/includes/image.php' );
+	require_once( ABSPATH . '/wp-admin/includes/file.php' );
+
+	$uploadErrors = array(
+		0 => __("There is no error, the file uploaded with success", 'buddypress'),
+		1 => __("Your image was bigger than the maximum allowed file size of: ", 'buddypress') . size_format( bp_core_avatar_original_max_filesize() ),
+		2 => __("Your image was bigger than the maximum allowed file size of: ", 'buddypress') . size_format( bp_core_avatar_original_max_filesize() ),
+		3 => __("The uploaded file was only partially uploaded", 'buddypress'),
+		4 => __("No file was uploaded", 'buddypress'),
+		6 => __("Missing a temporary folder", 'buddypress')
+	);
+
+	if ( !bp_core_check_avatar_upload( $file ) ) {
+		bp_core_add_message( sprintf( __( 'Your upload failed, please try again. Error was: %s', 'buddypress' ), $uploadErrors[$file['file']['error']] ), 'error' );
+		return false;
+	}
+
+	if ( !bp_core_check_avatar_size( $file ) ) {
+		bp_core_add_message( sprintf( __( 'The file you uploaded is too big. Please upload a file under %s', 'buddypress'), size_format( bp_core_avatar_original_max_filesize() ) ), 'error' );
+		return false;
+	}
+
+	if ( !bp_core_check_avatar_type( $file ) ) {
+		bp_core_add_message( __( 'Please upload only JPG, GIF or PNG photos.', 'buddypress' ), 'error' );
+		return false;
+	}
+
+	// Filter the upload location
+	add_filter( 'upload_dir', $upload_dir_filter, 10, 0 );
+
+	$bp->avatar_admin->original = wp_handle_upload( $file['file'], array( 'action'=> 'bp_avatar_upload' ) );
+
+	// Move the file to the correct upload location.
+	if ( !empty( $bp->avatar_admin->original['error'] ) ) {
+		bp_core_add_message( sprintf( __( 'Upload Failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->original['error'] ), 'error' );
+		return false;
+	}
+
+	// Get image size
+	$size = @getimagesize( $bp->avatar_admin->original['file'] );
+
+	// Check image size and shrink if too large
+	if ( $size[0] > bp_core_avatar_original_max_width() ) {
+		$thumb = wp_create_thumbnail( $bp->avatar_admin->original['file'], bp_core_avatar_original_max_width() );
+
+		// Check for thumbnail creation errors
+		if ( is_wp_error( $thumb ) ) {
+			bp_core_add_message( sprintf( __( 'Upload Failed! Error was: %s', 'buddypress' ), $thumb->get_error_message() ), 'error' );
+			return false;
+		}
+
+		// Thumbnail is good so proceed
+		$bp->avatar_admin->resized = $thumb;
+	}
+
+	// We only want to handle one image after resize.
+	if ( empty( $bp->avatar_admin->resized ) )
+		$bp->avatar_admin->image->dir = str_replace( bp_core_avatar_upload_path(), '', $bp->avatar_admin->original['file'] );
+	else {
+		$bp->avatar_admin->image->dir = str_replace( bp_core_avatar_upload_path(), '', $bp->avatar_admin->resized );
+		@unlink( $bp->avatar_admin->original['file'] );
+	}
+
+	// Check for WP_Error on what should be an image
+	if ( is_wp_error( $bp->avatar_admin->image->dir ) ) {
+		bp_core_add_message( sprintf( __( 'Upload failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->image->dir->get_error_message() ), 'error' );
+		return false;
+	}
+
+	// Set the url value for the image
+	$bp->avatar_admin->image->url = bp_core_avatar_url() . $bp->avatar_admin->image->dir;
+
+	return true;
+}
+
+/**
+ * Crop an uploaded avatar
+ *
+ * $args has the following parameters:
+ *  object - What component the avatar is for, e.g. "user"
+ *  avatar_dir  The absolute path to the avatar
+ *  item_id - Item ID
+ *  original_file - The absolute path to the original avatar file
+ *  crop_w - Crop width
+ *  crop_h - Crop height
+ *  crop_x - The horizontal starting point of the crop
+ *  crop_y - The vertical starting point of the crop
+ *
+ * @global object $bp BuddyPress global settings
+ * @param mixed $args
+ * @return bool Success/failure
+ */
+function bp_core_avatar_handle_crop( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'object'        => 'user',
+		'avatar_dir'    => 'avatars',
+		'item_id'       => false,
+		'original_file' => false,
+		'crop_w'        => bp_core_avatar_full_width(),
+		'crop_h'        => bp_core_avatar_full_height(),
+		'crop_x'        => 0,
+		'crop_y'        => 0
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+
+	/***
+	 * You may want to hook into this filter if you want to override this function.
+	 * Make sure you return false.
+	 */
+	if ( !apply_filters( 'bp_core_pre_avatar_handle_crop', true, $r ) )
+		return true;
+
+	extract( $r, EXTR_SKIP );
+
+	if ( !$original_file )
+		return false;
+
+	$original_file = bp_core_avatar_upload_path() . $original_file;
+
+	if ( !file_exists( $original_file ) )
+		return false;
+
+	if ( !$item_id )
+		$avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', dirname( $original_file ), $item_id, $object, $avatar_dir );
+	else
+		$avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', bp_core_avatar_upload_path() . '/' . $avatar_dir . '/' . $item_id, $item_id, $object, $avatar_dir );
+
+	if ( !file_exists( $avatar_folder_dir ) )
+		return false;
+
+	require_once( ABSPATH . '/wp-admin/includes/image.php' );
+	require_once( ABSPATH . '/wp-admin/includes/file.php' );
+
+	// Delete the existing avatar files for the object
+	bp_core_delete_existing_avatar( array( 'object' => $object, 'avatar_path' => $avatar_folder_dir ) );
+
+	// Make sure we at least have a width and height for cropping
+	if ( !(int)$crop_w )
+		$crop_w = bp_core_avatar_full_width();
+
+	if ( !(int)$crop_h )
+		$crop_h = bp_core_avatar_full_height();
+
+	// Set the full and thumb filenames
+	$full_filename  = wp_hash( $original_file . time() ) . '-bpfull.jpg';
+	$thumb_filename = wp_hash( $original_file . time() ) . '-bpthumb.jpg';
+
+	// Crop the image
+	$full_cropped  = wp_crop_image( $original_file, (int)$crop_x, (int)$crop_y, (int)$crop_w, (int)$crop_h, bp_core_avatar_full_width(), bp_core_avatar_full_height(), false, $avatar_folder_dir . '/' . $full_filename );
+	$thumb_cropped = wp_crop_image( $original_file, (int)$crop_x, (int)$crop_y, (int)$crop_w, (int)$crop_h, bp_core_avatar_thumb_width(), bp_core_avatar_thumb_height(), false, $avatar_folder_dir . '/' . $thumb_filename );
+
+	// Remove the original
+	@unlink( $original_file );
+
+	return true;
+}
+
+/**
+ * bp_core_fetch_avatar_filter()
+ *
+ * Attempts to filter get_avatar function and let BuddyPress have a go
+ * at finding an avatar that may have been uploaded locally.
+ *
+ * @global array $authordata
+ * @param string $avatar The result of get_avatar from before-filter
+ * @param int|string|object $user A user ID, email address, or comment object
+ * @param int $size Size of the avatar image (thumb/full)
+ * @param string $default URL to a default image to use if no avatar is available
+ * @param string $alt Alternate text to use in image tag. Defaults to blank
+ * @return <type>
+ */
+function bp_core_fetch_avatar_filter( $avatar, $user, $size, $default, $alt = '' ) {
+	global $pagenow;
+
+	// Do not filter if inside WordPress options page
+	if ( 'options-discussion.php' == $pagenow )
+		return $avatar;
+
+	// If passed an object, assume $user->user_id
+	if ( is_object( $user ) )
+		$id = $user->user_id;
+
+	// If passed a number, assume it was a $user_id
+	else if ( is_numeric( $user ) )
+		$id = $user;
+
+	// If passed a string and that string returns a user, get the $id
+	else if ( is_string( $user ) && ( $user_by_email = get_user_by_email( $user ) ) )
+		$id = $user_by_email->ID;
+
+	// If somehow $id hasn't been assigned, return the result of get_avatar
+	if ( empty( $id ) )
+		return !empty( $avatar ) ? $avatar : $default;
+
+	if ( !$alt )
+		$alt = __( 'Avatar of %s', 'buddypress' );
+
+	// Let BuddyPress handle the fetching of the avatar
+	$bp_avatar = bp_core_fetch_avatar( array( 'item_id' => $id, 'width' => $size, 'height' => $size, 'alt' => $alt ) );
+
+	// If BuddyPress found an avatar, use it. If not, use the result of get_avatar
+	return ( !$bp_avatar ) ? $avatar : $bp_avatar;
+}
+add_filter( 'get_avatar', 'bp_core_fetch_avatar_filter', 10, 5 );
+
+/**
+ * Has the current avatar upload generated an error?
+ *
+ * @param array $file
+ * @return bool
+ */
+function bp_core_check_avatar_upload( $file ) {
+	if ( isset( $file['error'] ) && $file['error'] )
+		return false;
+
+	return true;
+}
+
+/**
+ * Is the file size of the current avatar upload permitted?
+ *
+ * @param array $file
+ * @return bool
+ */
+function bp_core_check_avatar_size( $file ) {
+	if ( $file['file']['size'] > bp_core_avatar_original_max_filesize() )
+		return false;
+
+	return true;
+}
+
+/**
+ * Does the current avatar upload have an allowed file type?
+ *
+ * Permitted file types are JPG, GIF and PNG.
+ *
+ * @param string $file
+ * @return bool
+ */
+function bp_core_check_avatar_type($file) {
+	if ( ( !empty( $file['file']['type'] ) && !preg_match('/(jpe?g|gif|png)$/i', $file['file']['type'] ) ) || !preg_match( '/(jpe?g|gif|png)$/i', $file['file']['name'] ) )
+		return false;
+
+	return true;
+}
+
+/**
+ * bp_core_avatar_upload_path()
+ *
+ * Returns the absolute upload path for the WP installation
+ *
+ * @uses wp_upload_dir To get upload directory info
+ * @return string Absolute path to WP upload directory
+ */
+function bp_core_avatar_upload_path() {
+	global $bp;
+	
+	// See if the value has already been calculated and stashed in the $bp global
+	if ( isset( $bp->avatar->upload_path ) ) {
+		$basedir = $bp->avatar->upload_path;
+	} else {
+		// If this value has been set in a constant, just use that
+		if ( defined( 'BP_AVATAR_UPLOAD_PATH' ) ) {
+			$basedir = BP_AVATAR_UPLOAD_PATH;
+		} else {
+			// Get upload directory information from current site
+			$upload_dir = wp_upload_dir();
+		
+			// Directory does not exist and cannot be created
+			if ( !empty( $upload_dir['error'] ) ) {
+				$basedir = '';
+		
+			} else {
+				$basedir = $upload_dir['basedir'];
+		
+				// If multisite, and current blog does not match root blog, make adjustments
+				if ( is_multisite() && bp_get_root_blog_id() != get_current_blog_id() )
+					$basedir = get_blog_option( bp_get_root_blog_id(), 'upload_path' );
+			}
+		}
+		
+		// Stash in $bp for later use
+		$bp->avatar->upload_path = $basedir;
+	}
+	
+	return apply_filters( 'bp_core_avatar_upload_path', $basedir );
+}
+
+/**
+ * bp_core_avatar_url()
+ *
+ * Returns the raw base URL for root site upload location
+ *
+ * @uses wp_upload_dir To get upload directory info
+ * @return string Full URL to current upload location
+ */
+function bp_core_avatar_url() {
+	global $bp;
+	
+	// See if the value has already been calculated and stashed in the $bp global
+	if ( isset( $bp->avatar->url ) ) {
+		$baseurl = $bp->avatar->url;
+	} else {
+		// If this value has been set in a constant, just use that
+		if ( defined( 'BP_AVATAR_URL' ) ) {
+			$baseurl = BP_AVATAR_URL;
+		} else {
+			// Get upload directory information from current site
+			$upload_dir = wp_upload_dir();
+		
+			// Directory does not exist and cannot be created
+			if ( !empty( $upload_dir['error'] ) ) {
+				$baseurl = '';
+		
+			} else {
+				$baseurl = $upload_dir['baseurl'];
+		
+				// If multisite, and current blog does not match root blog, make adjustments
+				if ( is_multisite() && bp_get_root_blog_id() != get_current_blog_id() )
+					$baseurl = trailingslashit( get_blog_option( bp_get_root_blog_id(), 'home' ) ) . get_blog_option( bp_get_root_blog_id(), 'upload_path' );
+			}
+		}
+		
+		// Stash in $bp for later use
+		$bp->avatar->url = $baseurl;
+	}
+
+	return apply_filters( 'bp_core_avatar_url', $baseurl );
+}
+
+/**
+ * Utility function for fetching an avatar dimension setting
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param str $type 'thumb' for thumbs, otherwise full
+ * @param str $h_or_w 'height' for height, otherwise width
+ * @return int $dim The dimension
+ */
+function bp_core_avatar_dimension( $type = 'thumb', $h_or_w = 'height' ) {
+	global $bp;
+	
+	$dim = isset( $bp->avatar->{$type}->{$h_or_w} ) ? (int)$bp->avatar->{$type}->{$h_or_w} : false;
+	
+	return apply_filters( 'bp_core_avatar_dimension', $dim, $type, $h_or_w );
+}
+
+/**
+ * Get the avatar thumb width setting
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The thumb width
+ */
+function bp_core_avatar_thumb_width() {
+	return apply_filters( 'bp_core_avatar_thumb_width', bp_core_avatar_dimension( 'thumb', 'width' ) );
+}
+
+/**
+ * Get the avatar thumb height setting
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The thumb height
+ */
+function bp_core_avatar_thumb_height() {
+	return apply_filters( 'bp_core_avatar_thumb_height', bp_core_avatar_dimension( 'thumb', 'height' ) );
+}
+
+/**
+ * Get the avatar full width setting
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The full width
+ */
+function bp_core_avatar_full_width() {
+	return apply_filters( 'bp_core_avatar_full_width', bp_core_avatar_dimension( 'full', 'width' ) );
+}
+
+/**
+ * Get the avatar full height setting
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The full height
+ */
+function bp_core_avatar_full_height() {
+	return apply_filters( 'bp_core_avatar_full_height', bp_core_avatar_dimension( 'full', 'height' ) );
+}
+
+/**
+ * Get the max width for original avatar uploads
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The width
+ */
+function bp_core_avatar_original_max_width() {
+	global $bp;
+	
+	return apply_filters( 'bp_core_avatar_original_max_width', (int)$bp->avatar->original_max_width );
+}
+
+/**
+ * Get the max filesize for original avatar uploads
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The filesize
+ */
+function bp_core_avatar_original_max_filesize() {
+	global $bp;
+	
+	return apply_filters( 'bp_core_avatar_original_max_filesize', (int)$bp->avatar->original_max_filesize );
+}
+
+/**
+ * Get the default avatar
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The URL of the default avatar
+ */
+function bp_core_avatar_default() {
+	global $bp;
+	
+	return apply_filters( 'bp_core_avatar_default', $bp->avatar->full->default );
+}
+
+/**
+ * Get the default avatar thumb
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int The URL of the default avatar thumb
+ */
+function bp_core_avatar_default_thumb() {
+	global $bp;
+	
+	return apply_filters( 'bp_core_avatar_thumb', $bp->avatar->thumb->default );
+}
+
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-buddybar.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-buddybar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-buddybar.php	(revision 5057)
@@ -0,0 +1,588 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Adds a navigation item to the main navigation array used in BuddyPress themes.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_new_nav_item( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'name'                    => false, // Display name for the nav item
+		'slug'                    => false, // URL slug for the nav item
+		'item_css_id'             => false, // The CSS ID to apply to the HTML of the nav item
+		'show_for_displayed_user' => true,  // When viewing another user does this nav item show up?
+		'site_admin_only'         => false, // Can only site admins see this nav item?
+		'position'                => 99,    // Index of where this nav item should be positioned
+		'screen_function'         => false, // The name of the function to run when clicked
+		'default_subnav_slug'     => false  // The slug of the default subnav item to select when clicked
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// If we don't have the required info we need, don't create this subnav item
+	if ( empty( $name ) || empty( $slug ) )
+		return false;
+
+	// If this is for site admins only and the user is not one, don't create the subnav item
+	if ( $site_admin_only && !is_super_admin() )
+		return false;
+
+	if ( empty( $item_css_id ) )
+		$item_css_id = $slug;
+
+	$bp->bp_nav[$slug] = array(
+		'name'                    => $name,
+		'slug'                    => $slug,
+		'link'                    => $bp->loggedin_user->domain . $slug . '/',
+		'css_id'                  => $item_css_id,
+		'show_for_displayed_user' => $show_for_displayed_user,
+		'position'                => $position,
+		'screen_function'         => &$screen_function
+	);
+
+ 	/***
+	 * If this nav item is hidden for the displayed user, and
+	 * the logged in user is not the displayed user
+	 * looking at their own profile, don't create the nav item.
+	 */
+	if ( !$show_for_displayed_user && !bp_user_has_access() )
+		return false;
+
+	/***
+ 	 * If the nav item is visible, we are not viewing a user, and this is a root
+	 * component, don't attach the default subnav function so we can display a
+	 * directory or something else.
+ 	 */
+	if ( ( -1 != $position ) && bp_is_root_component( $slug ) && !bp_displayed_user_id() )
+		return;
+
+	// Look for current component
+	if ( bp_is_current_component( $slug ) && !bp_current_action() ) {
+		if ( !is_object( $screen_function[0] ) )
+			add_action( 'bp_screens', $screen_function );
+		else
+			add_action( 'bp_screens', array( &$screen_function[0], $screen_function[1] ), 3 );
+
+		if ( !empty( $default_subnav_slug ) )
+			$bp->current_action = apply_filters( 'bp_default_component_subnav', $default_subnav_slug, $r );
+
+	// Look for current item
+	} elseif ( bp_is_current_item( $slug ) && !bp_current_action() ) {
+		if ( !is_object( $screen_function[0] ) )
+			add_action( 'bp_screens', $screen_function );
+		else
+			add_action( 'bp_screens', array( &$screen_function[0], $screen_function[1] ), 3 );
+
+		if ( !empty( $default_subnav_slug ) )
+			$bp->current_action = apply_filters( 'bp_default_component_subnav', $default_subnav_slug, $r );
+	}
+
+	do_action( 'bp_core_new_nav_item', $r, $args, $defaults );
+}
+
+/**
+ * Modify the default subnav item to load when a top level nav item is clicked.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_new_nav_default( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'parent_slug'     => false, // Slug of the parent
+		'screen_function' => false, // The name of the function to run when clicked
+		'subnav_slug'     => false  // The slug of the subnav item to select when clicked
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( $function = $bp->bp_nav[$parent_slug]['screen_function'] ) {
+		if ( !is_object( $function[0] ) )
+			remove_action( 'bp_screens', $function, 3 );
+		else
+			remove_action( 'bp_screens', array( &$function[0], $function[1] ), 3 );
+	}
+
+	$bp->bp_nav[$parent_slug]['screen_function'] = &$screen_function;
+
+	if ( $bp->current_component == $parent_slug && !$bp->current_action ) {
+		if ( !is_object( $screen_function[0] ) )
+			add_action( 'bp_screens', $screen_function );
+		else
+			add_action( 'bp_screens', array( &$screen_function[0], $screen_function[1] ) );
+
+		if ( $subnav_slug )
+			$bp->current_action = $subnav_slug;
+	}
+}
+
+/**
+ * We can only sort nav items by their position integer at a later point in time, once all
+ * plugins have registered their navigation items.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_sort_nav_items() {
+	global $bp;
+
+	if ( empty( $bp->bp_nav ) || !is_array( $bp->bp_nav ) )
+		return false;
+
+	foreach ( (array)$bp->bp_nav as $slug => $nav_item ) {
+		if ( empty( $temp[$nav_item['position']]) )
+			$temp[$nav_item['position']] = $nav_item;
+		else {
+			// increase numbers here to fit new items in.
+			do {
+				$nav_item['position']++;
+			} while ( !empty( $temp[$nav_item['position']] ) );
+
+			$temp[$nav_item['position']] = $nav_item;
+		}
+	}
+
+	ksort( $temp );
+	$bp->bp_nav = &$temp;
+}
+add_action( 'wp_head',    'bp_core_sort_nav_items' );
+add_action( 'admin_head', 'bp_core_sort_nav_items' );
+
+/**
+ * Adds a navigation item to the sub navigation array used in BuddyPress themes.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_new_subnav_item( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'name'            => false, // Display name for the nav item
+		'slug'            => false, // URL slug for the nav item
+		'parent_slug'     => false, // URL slug of the parent nav item
+		'parent_url'      => false, // URL of the parent item
+		'item_css_id'     => false, // The CSS ID to apply to the HTML of the nav item
+		'user_has_access' => true,  // Can the logged in user see this nav item?
+		'site_admin_only' => false, // Can only site admins see this nav item?
+		'position'        => 90,    // Index of where this nav item should be positioned
+		'screen_function' => false, // The name of the function to run when clicked
+		'link'            => ''     // The link for the subnav item; optional, not usually required.
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// If we don't have the required info we need, don't create this subnav item
+	if ( empty( $name ) || empty( $slug ) || empty( $parent_slug ) || empty( $parent_url ) || empty( $screen_function ) )
+		return false;
+
+	if ( empty( $link ) )
+		$link = $parent_url . $slug;
+
+	// If this is for site admins only and the user is not one, don't create the subnav item
+	if ( $site_admin_only && !is_super_admin() )
+		return false;
+
+	if ( empty( $item_css_id ) )
+		$item_css_id = $slug;
+
+	$bp->bp_options_nav[$parent_slug][$slug] = array(
+		'name'            => $name,
+		'link'            => trailingslashit( $link ),
+		'slug'            => $slug,
+		'css_id'          => $item_css_id,
+		'position'        => $position,
+		'user_has_access' => $user_has_access,
+		'screen_function' => &$screen_function
+	);
+
+	/**
+	 * The last step is to hook the screen function for the added subnav item. But this only
+	 * needs to be done if this subnav item is the current view, and the user has access to the
+	 * subnav item. We figure out whether we're currently viewing this subnav by checking the
+	 * following two conditions:
+	 *   (1) Either:
+	 *	 (a) the parent slug matches the current_component, or
+	 *	 (b) the parent slug matches the current_item
+	 *   (2) And either:
+	 * 	 (a) the current_action matches $slug, or
+	 *       (b) there is no current_action (ie, this is the default subnav for the parent nav)
+	 *	     and this subnav item is the default for the parent item (which we check by
+	 *	     comparing this subnav item's screen function with the screen function of the
+	 *	     parent nav item in $bp->bp_nav). This condition only arises when viewing a
+	 *	     user, since groups should always have an action set.
+	 */
+
+	// If we *don't* meet condition (1), return
+	if ( $bp->current_component != $parent_slug && $bp->current_item != $parent_slug )
+		return;
+
+	// If we *do* meet condition (2), then the added subnav item is currently being requested
+	if ( ( !empty( $bp->current_action ) && $slug == $bp->current_action ) || ( bp_is_user() && empty( $bp->current_action ) && $screen_function == $bp->bp_nav[$parent_slug]['screen_function'] ) ) {
+
+		// Before hooking the screen function, check user access
+		if ( $user_has_access ) {
+			if ( !is_object( $screen_function[0] ) )
+				add_action( 'bp_screens', $screen_function );
+			else
+				add_action( 'bp_screens', array( &$screen_function[0], $screen_function[1] ) );
+		} else {
+			// When the content is off-limits, we handle the situation differently
+			// depending on whether the current user is logged in
+			if ( is_user_logged_in() ) {
+				// Off-limits to this user. Throw an error and redirect to the displayed user's domain
+				bp_core_no_access( array(
+					'message'  => __( 'You do not have access to this page.', 'buddypress' ),
+					'root'     => bp_displayed_user_domain(),
+					'redirect' => false
+				) );
+			} else {
+				// Not logged in. Allow the user to log in, and attempt to redirect
+				bp_core_no_access();
+			}
+		}
+	}
+}
+
+function bp_core_sort_subnav_items() {
+	global $bp;
+
+	if ( empty( $bp->bp_options_nav ) || !is_array( $bp->bp_options_nav ) )
+		return false;
+
+	foreach ( (array)$bp->bp_options_nav as $parent_slug => $subnav_items ) {
+		if ( !is_array( $subnav_items ) )
+			continue;
+
+		foreach ( (array)$subnav_items as $subnav_item ) {
+			if ( empty( $temp[$subnav_item['position']]) )
+				$temp[$subnav_item['position']] = $subnav_item;
+			else {
+				// increase numbers here to fit new items in.
+				do {
+					$subnav_item['position']++;
+				} while ( !empty( $temp[$subnav_item['position']] ) );
+
+				$temp[$subnav_item['position']] = $subnav_item;
+			}
+		}
+		ksort( $temp );
+		$bp->bp_options_nav[$parent_slug] = &$temp;
+		unset( $temp );
+	}
+}
+add_action( 'wp_head',    'bp_core_sort_subnav_items' );
+add_action( 'admin_head', 'bp_core_sort_subnav_items' );
+
+/**
+ * Determines whether a given nav item has subnav items
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param str $nav_item The id of the top-level nav item whose nav items you're checking
+ * @return bool $has_subnav True if the nav item is found and has subnav items; false otherwise
+ */
+function bp_nav_item_has_subnav( $nav_item = '' ) {
+	global $bp;
+
+	if ( !$nav_item )
+		$nav_item = bp_current_component();
+
+	$has_subnav = isset( $bp->bp_options_nav[$nav_item] ) && count( $bp->bp_options_nav[$nav_item] ) > 0;
+
+	return apply_filters( 'bp_nav_item_has_subnav', $has_subnav, $nav_item );
+}
+
+/**
+ * Removes a navigation item from the sub navigation array used in BuddyPress themes.
+ *
+ * @package BuddyPress Core
+ * @param $parent_id The id of the parent navigation item.
+ * @param $slug The slug of the sub navigation item.
+ */
+function bp_core_remove_nav_item( $parent_id ) {
+	global $bp;
+
+	// Unset subnav items for this nav item
+	if ( isset( $bp->bp_options_nav[$parent_id] ) && is_array( $bp->bp_options_nav[$parent_id] ) ) {
+		foreach( (array)$bp->bp_options_nav[$parent_id] as $subnav_item ) {
+			bp_core_remove_subnav_item( $parent_id, $subnav_item['slug'] );
+		}
+	}
+
+	if ( $function = $bp->bp_nav[$parent_id]['screen_function'] ) {
+		if ( !is_object( $function[0] ) ) {
+			remove_action( 'bp_screens', $function );
+		} else {
+			remove_action( 'bp_screens', array( &$function[0], $function[1] ) );
+		}
+	}
+
+	unset( $bp->bp_nav[$parent_id] );
+}
+
+/**
+ * Removes a navigation item from the sub navigation array used in BuddyPress themes.
+ *
+ * @package BuddyPress Core
+ * @param $parent_id The id of the parent navigation item.
+ * @param $slug The slug of the sub navigation item.
+ */
+function bp_core_remove_subnav_item( $parent_id, $slug ) {
+	global $bp;
+
+	$screen_function = ( isset( $bp->bp_options_nav[$parent_id][$slug]['screen_function'] ) ) ? $bp->bp_options_nav[$parent_id][$slug]['screen_function'] : false;
+
+	if ( $screen_function ) {
+		if ( !is_object( $screen_function[0] ) )
+			remove_action( 'bp_screens', $screen_function );
+		else
+			remove_action( 'bp_screens', array( &$screen_function[0], $screen_function[1] ) );
+	}
+
+	unset( $bp->bp_options_nav[$parent_id][$slug] );
+
+	if ( isset( $bp->bp_options_nav[$parent_id] ) && !count( $bp->bp_options_nav[$parent_id] ) )
+		unset($bp->bp_options_nav[$parent_id]);
+}
+
+/**
+ * Clear the subnav items for a specific nav item.
+ *
+ * @package BuddyPress Core
+ * @param $parent_id The id of the parent navigation item.
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_reset_subnav_items( $parent_slug ) {
+	global $bp;
+
+	unset( $bp->bp_options_nav[$parent_slug] );
+}
+
+/** Template functions ********************************************************/
+
+function bp_core_admin_bar() {
+	global $bp;
+
+	if ( defined( 'BP_DISABLE_ADMIN_BAR' ) && BP_DISABLE_ADMIN_BAR )
+		return false;
+
+	if ( (int)bp_get_option( 'hide-loggedout-adminbar' ) && !is_user_logged_in() )
+		return false;
+
+	$bp->doing_admin_bar = true;
+
+	echo '<div id="wp-admin-bar"><div class="padder">';
+
+	// **** Do bp-adminbar-logo Actions ********
+	do_action( 'bp_adminbar_logo' );
+
+	echo '<ul class="main-nav">';
+
+	// **** Do bp-adminbar-menus Actions ********
+	do_action( 'bp_adminbar_menus' );
+
+	echo '</ul>';
+	echo "</div></div><!-- #wp-admin-bar -->\n\n";
+
+	$bp->doing_admin_bar = false;
+}
+
+// **** Default BuddyPress admin bar logo ********
+function bp_adminbar_logo() {
+	global $bp;
+
+	echo '<a href="' . bp_get_root_domain() . '" id="admin-bar-logo">' . get_blog_option( bp_get_root_blog_id(), 'blogname' ) . '</a>';
+}
+
+// **** "Log In" and "Sign Up" links (Visible when not logged in) ********
+function bp_adminbar_login_menu() {
+	global $bp;
+
+	if ( is_user_logged_in() )
+		return false;
+
+	echo '<li class="bp-login no-arrow"><a href="' . bp_get_root_domain() . '/wp-login.php?redirect_to=' . urlencode( bp_get_root_domain() ) . '">' . __( 'Log In', 'buddypress' ) . '</a></li>';
+
+	// Show "Sign Up" link if user registrations are allowed
+	if ( bp_get_signup_allowed() )
+		echo '<li class="bp-signup no-arrow"><a href="' . bp_get_signup_page(false) . '">' . __( 'Sign Up', 'buddypress' ) . '</a></li>';
+}
+
+
+// **** "My Account" Menu ******
+function bp_adminbar_account_menu() {
+	global $bp;
+
+	if ( !$bp->bp_nav || !is_user_logged_in() )
+		return false;
+
+	echo '<li id="bp-adminbar-account-menu"><a href="' . bp_loggedin_user_domain() . '">';
+	echo __( 'My Account', 'buddypress' ) . '</a>';
+	echo '<ul>';
+
+	// Loop through each navigation item
+	$counter = 0;
+	foreach( (array)$bp->bp_nav as $nav_item ) {
+		$alt = ( 0 == $counter % 2 ) ? ' class="alt"' : '';
+
+		if ( -1 == $nav_item['position'] )
+			continue;
+
+		echo '<li' . $alt . '>';
+		echo '<a id="bp-admin-' . $nav_item['css_id'] . '" href="' . $nav_item['link'] . '">' . $nav_item['name'] . '</a>';
+
+		if ( isset( $bp->bp_options_nav[$nav_item['slug']] ) && is_array( $bp->bp_options_nav[$nav_item['slug']] ) ) {
+			echo '<ul>';
+			$sub_counter = 0;
+
+			foreach( (array)$bp->bp_options_nav[$nav_item['slug']] as $subnav_item ) {
+				$link = $subnav_item['link'];
+				$name = $subnav_item['name'];
+
+				if ( isset( $bp->displayed_user->domain ) )
+					$link = str_replace( $bp->displayed_user->domain, $bp->loggedin_user->domain, $subnav_item['link'] );
+
+				if ( isset( $bp->displayed_user->userdata->user_login ) )
+					$name = str_replace( $bp->displayed_user->userdata->user_login, $bp->loggedin_user->userdata->user_login, $subnav_item['name'] );
+
+				$alt = ( 0 == $sub_counter % 2 ) ? ' class="alt"' : '';
+				echo '<li' . $alt . '><a id="bp-admin-' . $subnav_item['css_id'] . '" href="' . $link . '">' . $name . '</a></li>';
+				$sub_counter++;
+			}
+			echo '</ul>';
+		}
+
+		echo '</li>';
+
+		$counter++;
+	}
+
+	$alt = ( 0 == $counter % 2 ) ? ' class="alt"' : '';
+
+	echo '<li' . $alt . '><a id="bp-admin-logout" class="logout" href="' . wp_logout_url( home_url() ) . '">' . __( 'Log Out', 'buddypress' ) . '</a></li>';
+	echo '</ul>';
+	echo '</li>';
+}
+
+function bp_adminbar_thisblog_menu() {
+	if ( current_user_can( 'edit_posts' ) ) {
+		echo '<li id="bp-adminbar-thisblog-menu"><a href="' . admin_url() . '">';
+		_e( 'Dashboard', 'buddypress' );
+		echo '</a>';
+		echo '<ul>';
+
+		echo '<li class="alt"><a href="' . admin_url() . 'post-new.php">' . __( 'New Post', 'buddypress' ) . '</a></li>';
+		echo '<li><a href="' . admin_url() . 'edit.php">' . __( 'Manage Posts', 'buddypress' ) . '</a></li>';
+		echo '<li class="alt"><a href="' . admin_url() . 'edit-comments.php">' . __( 'Manage Comments', 'buddypress' ) . '</a></li>';
+
+		do_action( 'bp_adminbar_thisblog_items' );
+
+		echo '</ul>';
+		echo '</li>';
+	}
+}
+
+
+// **** "Random" Menu (visible when not logged in) ********
+function bp_adminbar_random_menu() {
+	global $bp; ?>
+
+	<li class="align-right" id="bp-adminbar-visitrandom-menu">
+		<a href="#"><?php _e( 'Visit', 'buddypress' ) ?></a>
+		<ul class="random-list">
+			<li><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_members_root_slug() ) . '?random-member' ?>" rel="nofollow"><?php _e( 'Random Member', 'buddypress' ) ?></a></li>
+
+			<?php if ( bp_is_active( 'groups' ) ) : ?>
+
+				<li class="alt"><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ) . '?random-group' ?>"  rel="nofollow"><?php _e( 'Random Group', 'buddypress' ) ?></a></li>
+
+			<?php endif; ?>
+
+			<?php if ( is_multisite() && bp_is_active( 'blogs' ) ) : ?>
+
+				<li><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_blogs_root_slug() ) . '?random-blog' ?>"  rel="nofollow"><?php _e( 'Random Site', 'buddypress' ) ?></a></li>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_adminbar_random_menu' ) ?>
+
+		</ul>
+	</li>
+
+	<?php
+}
+
+/**
+ * Handle the Admin Bar/BuddyBar business
+ *
+ * @global num $wp_version
+ * @todo Clean up global constants
+ */
+function bp_core_load_admin_bar() {
+	global $wp_version;
+
+	// Show the WordPress admin bar
+	if ( bp_use_wp_admin_bar() && $wp_version >= 3.1 ) {
+		show_admin_bar( true );
+
+	// Hide the WordPress admin bar
+	} elseif ( !bp_use_wp_admin_bar() ) {
+
+		// Keep the WP admin bar from loading
+		show_admin_bar( false );
+
+		if ( (int)bp_get_option( 'hide-loggedout-adminbar' ) && !is_user_logged_in() )
+			return;
+
+		// Actions used to build the BP admin bar
+		add_action( 'bp_adminbar_logo',  'bp_adminbar_logo' );
+		add_action( 'bp_adminbar_menus', 'bp_adminbar_login_menu',         2   );
+		add_action( 'bp_adminbar_menus', 'bp_adminbar_account_menu',       4   );
+		add_action( 'bp_adminbar_menus', 'bp_adminbar_thisblog_menu',      6   );
+		add_action( 'bp_adminbar_menus', 'bp_adminbar_random_menu',        100 );
+
+		// Actions used to append BP admin bar to footer
+		add_action( 'wp_footer',    'bp_core_admin_bar', 8 );
+		add_action( 'admin_footer', 'bp_core_admin_bar'    );
+	}
+}
+
+/**
+ * Handle the BuddyBar CSS
+ */
+function bp_core_load_buddybar_css() {
+	if ( bp_use_wp_admin_bar() || ( (int) bp_get_option( 'hide-loggedout-adminbar' ) && !is_user_logged_in() ) )
+		return;
+
+	if ( file_exists( get_stylesheet_directory() . '/_inc/css/adminbar.css' ) ) // Backwards compatibility
+		$stylesheet = get_stylesheet_directory_uri() . '/_inc/css/adminbar.css';
+	elseif ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/buddybar.dev.css';
+	else
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/buddybar.css';
+
+	wp_enqueue_style( 'bp-admin-bar', apply_filters( 'bp_core_admin_bar_css', $stylesheet ), array(), '20110723' );
+
+	if ( !is_rtl() )
+		return;
+
+	if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/buddybar-rtl.dev.css';
+	else
+		$stylesheet = BP_PLUGIN_URL . '/bp-core/css/buddybar-rtl.css';
+
+	wp_enqueue_style( 'bp-admin-bar-rtl', apply_filters( 'bp_core_buddybar_rtl_css', $stylesheet ), array( 'bp-admin-bar' ), '20110723' );
+}
+add_action( 'bp_init', 'bp_core_load_buddybar_css' );
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-cache.php	(revision 5057)
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * REQUIRES WP-SUPER-CACHE
+ *
+ * When wp-super-cache is installed this function will clear cached pages
+ * so that success/error messages are not cached, or time sensitive content.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_clear_cache() {
+	global $cache_path, $cache_filename;
+
+	if ( function_exists( 'prune_super_cache' ) ) {
+		do_action( 'bp_core_clear_cache' );
+		return prune_super_cache( $cache_path, true );
+	}
+}
+
+/**
+ * Add's 'bp' to global group of network wide cachable objects
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_add_global_group() {
+	wp_cache_init();
+	wp_cache_add_global_groups( array( 'bp' ) );
+}
+add_action( 'bp_loaded', 'bp_core_add_global_group' );
+
+/**
+ * Clears all cached objects for a user, or a user is part of.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_clear_user_object_cache( $user_id ) {
+	wp_cache_delete( 'bp_user_' . $user_id, 'bp' );
+}
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'wp_login',              'bp_core_clear_cache' );
+add_action( 'bp_core_render_notice', 'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-catchuri.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-catchuri.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-catchuri.php	(revision 5057)
@@ -0,0 +1,466 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/*
+Based on contributions from: Chris Taylor - http://www.stillbreathing.co.uk/
+Modified for BuddyPress by: Andy Peatling - http://apeatling.wordpress.com/
+*/
+
+/**
+ * Analyzes the URI structure and breaks it down into parts for use in code.
+ * The idea is that BuddyPress can use complete custom friendly URI's without the
+ * user having to add new re-write rules.
+ *
+ * Future custom components would then be able to use their own custom URI structure.
+ *
+ * @package BuddyPress Core
+ * @since BuddyPress (r100)
+ *
+ * The URI's are broken down as follows:
+ *   - http:// domain.com / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
+ *   - OUTSIDE ROOT: http:// domain.com / sites / buddypress / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
+ *
+ *	Example:
+ *    - http://domain.com/members/andy/profile/edit/group/5/
+ *    - $bp->current_component: string 'xprofile'
+ *    - $bp->current_action: string 'edit'
+ *    - $bp->action_variables: array ['group', 5]
+ *
+ */
+function bp_core_set_uri_globals() {
+	global $bp, $bp_unfiltered_uri, $bp_unfiltered_uri_offset;
+	global $current_blog, $wpdb;
+
+	// Create global component, action, and item variables
+	$bp->current_component = $bp->current_action = $bp->current_item ='';
+	$bp->action_variables = $bp->displayed_user->id = '';
+
+	// Don't catch URIs on non-root blogs unless multiblog mode is on
+	if ( !bp_is_root_blog() && !bp_is_multiblog_mode() )
+		return false;
+
+	// Fetch all the WP page names for each component
+	if ( empty( $bp->pages ) )
+		$bp->pages = bp_core_get_directory_pages();
+
+	// Ajax or not?
+	if ( strpos( $_SERVER['REQUEST_URI'], 'wp-load.php' ) )
+		$path = bp_core_referrer();
+	else
+		$path = esc_url( $_SERVER['REQUEST_URI'] );
+
+	// Filter the path
+	$path = apply_filters( 'bp_uri', $path );
+
+	// Take GET variables off the URL to avoid problems,
+	// they are still registered in the global $_GET variable
+	if ( $noget = substr( $path, 0, strpos( $path, '?' ) ) )
+		$path = $noget;
+
+	// Fetch the current URI and explode each part separated by '/' into an array
+	$bp_uri = explode( '/', $path );
+
+	// Loop and remove empties
+	foreach ( (array)$bp_uri as $key => $uri_chunk )
+		if ( empty( $bp_uri[$key] ) ) unset( $bp_uri[$key] );
+
+	// Running off blog other than root
+	if ( is_multisite() && !is_subdomain_install() && ( bp_is_multiblog_mode() || 1 != bp_get_root_blog_id() ) ) {
+
+		// Any subdirectory names must be removed from $bp_uri.
+		// This includes two cases: (1) when WP is installed in a subdirectory,
+		// and (2) when BP is running on secondary blog of a subdirectory
+		// multisite installation. Phew!
+		if ( $chunks = explode( '/', $current_blog->path ) ) {
+			foreach( $chunks as $key => $chunk ) {
+				$bkey = array_search( $chunk, $bp_uri );
+
+				if ( $bkey !== false ) {
+					unset( $bp_uri[$bkey] );
+				}
+
+				$bp_uri = array_values( $bp_uri );
+			}
+		}
+	}
+
+	// Set the indexes, these are incresed by one if we are not on a VHOST install
+	$component_index = 0;
+	$action_index    = $component_index + 1;
+
+	// Get site path items
+	$paths = explode( '/', bp_core_get_site_path() );
+
+	// Take empties off the end of path
+	if ( empty( $paths[count( $paths ) - 1] ) )
+		array_pop( $paths );
+
+	// Take empties off the start of path
+	if ( empty( $paths[0] ) )
+		array_shift( $paths );
+
+	// Unset URI indices if they intersect with the paths
+	foreach ( (array) $bp_uri as $key => $uri_chunk ) {
+		if ( in_array( $uri_chunk, $paths ) ) {
+			unset( $bp_uri[$key] );
+		}
+	}
+
+	// Reset the keys by merging with an empty array
+	$bp_uri = array_merge( array(), $bp_uri );
+
+	// If a component is set to the front page, force its name into $bp_uri
+	// so that $current_component is populated
+	if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) && empty( $bp_uri ) ) {
+		$post = get_post( get_option( 'page_on_front' ) );
+		if ( !empty( $post ) ) {
+			$bp_uri[0] = $post->post_name;
+		}
+	}
+
+	// Keep the unfiltered URI safe
+	$bp_unfiltered_uri = $bp_uri;
+
+	// Get slugs of pages into array
+	foreach ( (array) $bp->pages as $page_key => $bp_page )
+		$key_slugs[$page_key] = trailingslashit( '/' . $bp_page->slug );
+
+	// Bail if keyslugs are empty, as BP is not setup correct
+	if ( empty( $key_slugs ) )
+		return;
+
+	// Loop through page slugs and look for exact match to path
+	foreach ( $key_slugs as $key => $slug ) {
+		if ( $slug == $path ) {
+			$match      = $bp->pages->{$key};
+			$match->key = $key;
+			$matches[]  = 1;
+			break;
+		}
+	}
+
+	// No exact match, so look for partials
+	if ( empty( $match ) ) {
+
+		// Loop through each page in the $bp->pages global
+		foreach ( (array) $bp->pages as $page_key => $bp_page ) {
+
+			// Look for a match (check members first)
+			if ( in_array( $bp_page->name, (array) $bp_uri ) ) {
+
+				// Match found, now match the slug to make sure.
+				$uri_chunks = explode( '/', $bp_page->slug );
+
+				// Loop through uri_chunks
+				foreach ( (array) $uri_chunks as $key => $uri_chunk ) {
+
+					// Make sure chunk is in the correct position
+					if ( !empty( $bp_uri[$key] ) && ( $bp_uri[$key] == $uri_chunk ) ) {
+						$matches[] = 1;
+
+					// No match
+					} else {
+						$matches[] = 0;
+					}
+				}
+
+				// Have a match
+				if ( !in_array( 0, (array) $matches ) ) {
+					$match      = $bp_page;
+					$match->key = $page_key;
+					break;
+				};
+
+				// Unset matches
+				unset( $matches );
+			}
+
+			// Unset uri chunks
+			unset( $uri_chunks );
+		}
+	}
+
+	// URLs with BP_ENABLE_ROOT_PROFILES enabled won't be caught above
+	if ( empty( $matches ) && defined( 'BP_ENABLE_ROOT_PROFILES' ) && BP_ENABLE_ROOT_PROFILES ) {
+
+		// Make sure there's a user corresponding to $bp_uri[0]
+		if ( !empty( $bp_uri[0] ) && $root_profile = get_userdatabylogin( $bp_uri[0] ) ) {
+
+			// Force BP to recognize that this is a members page
+			$matches[]  = 1;
+			$match      = $bp->pages->members;
+			$match->key = 'members';
+
+			// Without the 'members' URL chunk, WordPress won't know which page to load
+			// This filter intercepts the WP query and tells it to load the members page
+			add_filter( 'request', create_function( '$query_args', '$query_args["pagename"] = "' . $match->name . '"; return $query_args;' ) );
+
+		}
+
+	}
+
+	// Search doesn't have an associated page, so we check for it separately
+	if ( !empty( $bp_uri[0] ) && ( bp_get_search_slug() == $bp_uri[0] ) ) {
+		$matches[]   = 1;
+		$match       = new stdClass;
+		$match->key  = 'search';
+		$match->slug = bp_get_search_slug();
+	}
+
+	// This is not a BuddyPress page, so just return.
+	if ( !isset( $matches ) )
+		return false;
+
+	// Find the offset. With $root_profile set, we fudge the offset down so later parsing works
+	$slug       = !empty ( $match ) ? explode( '/', $match->slug ) : '';
+	$uri_offset = empty( $root_profile ) ? 0 : -1;
+
+	// Rejig the offset
+	if ( !empty( $slug ) && ( 1 < count( $slug ) ) ) {
+		array_pop( $slug );
+		$uri_offset = count( $slug );
+	}
+
+	// Global the unfiltered offset to use in bp_core_load_template().
+	// To avoid PHP warnings in bp_core_load_template(), it must always be >= 0
+	$bp_unfiltered_uri_offset = $uri_offset >= 0 ? $uri_offset : 0;
+
+	// We have an exact match
+	if ( isset( $match->key ) ) {
+
+		// Set current component to matched key
+		$bp->current_component = $match->key;
+
+		// If members component, do more work to find the actual component
+		if ( 'members' == $match->key ) {
+
+			// Viewing a specific user
+			if ( !empty( $bp_uri[$uri_offset + 1] ) ) {
+
+				// Switch the displayed_user based on compatbility mode
+				if ( bp_is_username_compatibility_mode() )
+					$bp->displayed_user->id = (int) bp_core_get_userid( urldecode( $bp_uri[$uri_offset + 1] ) );
+				else
+					$bp->displayed_user->id = (int) bp_core_get_userid_from_nicename( urldecode( $bp_uri[$uri_offset + 1] ) );
+
+				if ( empty( $bp->displayed_user->id ) ) {
+					// Prevent components from loading their templates
+					$bp->current_component = '';
+
+					bp_do_404();
+					return;
+				}
+
+				// Bump the offset
+				if ( isset( $bp_uri[$uri_offset + 2] ) ) {
+					$bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
+					$bp->current_component = $bp_uri[0];
+
+				// No component, so default will be picked later
+				} else {
+					$bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
+					$bp->current_component = '';
+				}
+
+				// Reset the offset
+				$uri_offset = 0;
+			}
+		}
+	}
+
+	// Set the current action
+	$bp->current_action = isset( $bp_uri[$uri_offset + 1] ) ? $bp_uri[$uri_offset + 1] : '';
+
+	// Slice the rest of the $bp_uri array and reset offset
+	$bp_uri      = array_slice( $bp_uri, $uri_offset + 2 );
+	$uri_offset  = 0;
+
+	// Set the entire URI as the action variables, we will unset the current_component and action in a second
+	$bp->action_variables = $bp_uri;
+
+	// Remove the username from action variables if this is not a VHOST install
+	if ( defined( 'VHOST' ) && 'no' == VHOST && empty( $is_root_component ) )
+		array_shift( $bp_uri );
+
+	// Reset the keys by merging with an empty array
+	$bp->action_variables = array_merge( array(), $bp->action_variables );
+}
+
+/**
+ * bp_core_load_template()
+ *
+ * Load a specific template file with fallback support.
+ *
+ * Example:
+ *   bp_core_load_template( 'members/index' );
+ * Loads:
+ *   wp-content/themes/[activated_theme]/members/index.php
+ *
+ * @package BuddyPress Core
+ * @param $username str Username to check.
+ * @return false|int The user ID of the matched user, or false.
+ */
+function bp_core_load_template( $templates ) {
+	global $post, $bp, $wpdb, $wp_query, $bp_unfiltered_uri, $bp_unfiltered_uri_offset;
+
+	// Determine if the root object WP page exists for this request (TODO: is there an API function for this?
+	if ( !empty( $bp_unfiltered_uri_offset ) && !$page_exists = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s", $bp_unfiltered_uri[$bp_unfiltered_uri_offset] ) ) )
+		return false;
+
+	// Set the root object as the current wp_query-ied item
+	$object_id = 0;
+	foreach ( (array)$bp->pages as $page ) {
+		if ( isset( $bp_unfiltered_uri[$bp_unfiltered_uri_offset] ) && $page->name == $bp_unfiltered_uri[$bp_unfiltered_uri_offset] ) {
+			$object_id = $page->id;
+		}
+	}
+
+	// Make the queried/post object an actual valid page
+	if ( !empty( $object_id ) ) {
+		$wp_query->queried_object    = &get_post( $object_id );
+		$wp_query->queried_object_id = $object_id;
+		$post                        = $wp_query->queried_object;
+	}
+
+	// Fetch each template and add the php suffix
+	foreach ( (array)$templates as $template )
+		$filtered_templates[] = $template . '.php';
+
+	// Filter the template locations so that plugins can alter where they are located
+	if ( $located_template = apply_filters( 'bp_located_template', locate_template( (array) $filtered_templates, false ), $filtered_templates ) ) {
+		// Template was located, lets set this as a valid page and not a 404.
+		status_header( 200 );
+		$wp_query->is_page = true;
+		$wp_query->is_404 = false;
+
+		load_template( apply_filters( 'bp_load_template', $located_template ) );
+	}
+
+	// Kill any other output after this.
+	die;
+}
+
+/**
+ * bp_core_catch_profile_uri()
+ *
+ * If the extended profiles component is not installed we still need
+ * to catch the /profile URI's and display whatever we have installed.
+ *
+ */
+function bp_core_catch_profile_uri() {
+	global $bp;
+
+	if ( !bp_is_active( 'xprofile' ) )
+		bp_core_load_template( apply_filters( 'bp_core_template_display_profile', 'members/single/home' ) );
+}
+
+/**
+ * Catches invalid access to BuddyPress pages and redirects them accordingly.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_core_catch_no_access() {
+	global $bp, $bp_no_status_set, $wp_query;
+
+	// If bp_core_redirect() and $bp_no_status_set is true,
+	// we are redirecting to an accessible page, so skip this check.
+	if ( $bp_no_status_set )
+		return false;
+
+	// If the displayed user was marked as a spammer and the logged-in user is not a super admin, 404.
+	if ( isset( $bp->displayed_user->id ) && bp_core_is_user_spammer( $bp->displayed_user->id ) ) {
+		if ( !$bp->loggedin_user->is_super_admin ) {
+			bp_do_404();
+			return;
+
+		} else {
+			bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'error' );
+		}
+	}
+
+	if ( !isset( $wp_query->queried_object ) && !bp_is_blog_page() ) {
+		bp_do_404();
+	}
+}
+add_action( 'wp', 'bp_core_catch_no_access' );
+
+/**
+ * Redirects a user to login for BP pages that require access control and adds an error message (if
+ * one is provided).
+ * If authenticated, redirects user back to requested content by default.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_core_no_access( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'mode'     => '1',			    // 1 = $root, 2 = wp-login.php
+		'message'  => __( 'You must log in to access the page you requested.', 'buddypress' ),
+		'redirect' => wp_guess_url(),	// the URL you get redirected to when a user successfully logs in
+		'root'     => $bp->root_domain	// the landing page you get redirected to when a user doesn't have access
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// Apply filters to these variables
+	$mode		= apply_filters( 'bp_no_access_mode', $mode, $root, $redirect, $message );
+	$redirect	= apply_filters( 'bp_no_access_redirect', $redirect, $root, $message, $mode );
+	$root		= trailingslashit( apply_filters( 'bp_no_access_root', $root, $redirect, $message, $mode ) );
+	$message	= apply_filters( 'bp_no_access_message', $message, $root, $redirect, $mode );
+
+	switch ( $mode ) {
+		// Option to redirect to wp-login.php
+		// Error message is displayed with bp_core_no_access_wp_login_error()
+		case 2 :
+			if ( $redirect ) {
+				bp_core_redirect( wp_login_url( $redirect ) . '&action=bpnoaccess' );
+			} else {
+				bp_core_redirect( $root );
+			}
+
+			break;
+
+		// Redirect to root with "redirect_to" parameter
+		// Error message is displayed with bp_core_add_message()
+		case 1 :
+		default :
+			if ( $redirect ) {
+				$url = add_query_arg( 'redirect_to', urlencode( $redirect ), $root );
+			} else {
+				$url = $root;
+			}
+
+			if ( $message ) {
+				bp_core_add_message( $message, 'error' );
+			}
+
+			bp_core_redirect( $url );
+
+			break;
+	}
+}
+
+/**
+ * Adds an error message to wp-login.php.
+ * Hooks into the "bpnoaccess" action defined in bp_core_no_access().
+ *
+ * @package BuddyPress Core
+ * @global $error
+ * @since 1.5
+ */
+function bp_core_no_access_wp_login_error() {
+	global $error;
+
+	$error = apply_filters( 'bp_wp_login_error', __( 'You must log in to access the page you requested.', 'buddypress' ), $_REQUEST['redirect_to'] );
+
+	// shake shake shake!
+	add_action( 'login_head', 'wp_shake_js', 12 );
+}
+add_action( 'login_form_bpnoaccess', 'bp_core_no_access_wp_login_error' );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-classes.php	(revision 5057)
@@ -0,0 +1,1254 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * BP_Core_User class can be used by any component. It will fetch useful
+ * details for any user when provided with a user_id.
+ *
+ * Example:
+ *    $user = new BP_Core_User( $user_id );
+ *    $user_avatar = $user->avatar;
+ *	  $user_email = $user->email;
+ *    $user_status = $user->status;
+ *    etc.
+ *
+ * @package BuddyPress Core
+ */
+class BP_Core_User {
+
+	/**
+	 * ID of the user which the object relates to.
+	 *
+	 * @var integer
+	 */
+	var $id;
+
+	/**
+	 * The URL to the full size of the avatar for the user.
+	 *
+	 * @var string
+	 */
+	var $avatar;
+
+	/**
+	 * The URL to the thumb size of the avatar for the user.
+	 *
+	 * @var string
+	 */
+	var $avatar_thumb;
+
+	/**
+	 * The URL to the mini size of the avatar for the user.
+	 *
+	 * @var string
+	 */
+	var $avatar_mini;
+
+	/**
+	 * The full name of the user
+	 *
+	 * @var string
+	 */
+	var $fullname;
+
+	/**
+	 * The email for the user.
+	 *
+	 * @var string
+	 */
+	var $email;
+
+	/**
+	 * The absolute url for the user's profile.
+	 *
+	 * @var string
+	 */
+	var $user_url;
+
+	/**
+	 * The HTML for the user link, with the link text being the user's full name.
+	 *
+	 * @var string
+	 */
+	var $user_link;
+
+	/**
+	 * Contains a formatted string when the last time the user was active.
+	 *
+	 * Example: "active 2 hours and 50 minutes ago"
+	 *
+	 * @var string
+	 */
+	var $last_active;
+
+	/* Extras */
+
+	/**
+	 * The total number of "Friends" the user has on site.
+	 *
+	 * @var integer
+	 */
+	var $total_friends;
+
+	/**
+	 * The total number of blog posts posted by the user
+	 *
+	 * @var integer
+	 * @deprecated No longer used
+	 */
+	var $total_blogs;
+
+	/**
+	 * The total number of groups the user is a part of.
+	 *
+	 * Example: "1 group", "2 groups"
+	 *
+	 * @var string
+	 */
+	var $total_groups;
+
+	/**
+	 * PHP4 constructor.
+	 *
+	 * @see BP_Core_User::__construct()
+	 */
+	function bp_core_user( $user_id, $populate_extras = false ) {
+		$this->__construct( $user_id, $populate_extras );
+	}
+
+	/**
+	 * Class constructor.
+	 *
+	 * @param integer $user_id The ID for the user
+	 * @param boolean $populate_extras Whether to fetch extra information such as group/friendship counts or not.
+	 */
+	function __construct( $user_id, $populate_extras = false ) {
+		if ( $user_id ) {
+			$this->id = $user_id;
+			$this->populate();
+
+			if ( $populate_extras )
+				$this->populate_extras();
+		}
+	}
+
+	/**
+	 * Populate the instantiated class with data based on the User ID provided.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @uses bp_core_get_userurl() Returns the URL with no HTML markup for a user based on their user id
+	 * @uses bp_core_get_userlink() Returns a HTML formatted link for a user with the user's full name as the link text
+	 * @uses bp_core_get_user_email() Returns the email address for the user based on user ID
+	 * @uses bp_get_user_meta() BP function returns the value of passed usermeta name from usermeta table
+	 * @uses bp_core_fetch_avatar() Returns HTML formatted avatar for a user
+	 * @uses bp_profile_last_updated_date() Returns the last updated date for a user.
+	 */
+	function populate() {
+		global $bp;
+
+		$full_name_field_name = bp_xprofile_fullname_field_name();
+
+		if ( bp_is_active( 'xprofile' ) )
+			$this->profile_data = $this->get_profile_data();
+
+		if ( $this->profile_data ) {
+			$this->user_url  = bp_core_get_user_domain( $this->id, $this->profile_data['user_nicename'], $this->profile_data['user_login'] );
+			$this->fullname  = esc_attr( $this->profile_data[$full_name_field_name]['field_data'] );
+			$this->user_link = "<a href='{$this->user_url}' title='{$this->fullname}'>{$this->fullname}</a>";
+			$this->email     = esc_attr( $this->profile_data['user_email'] );
+		} else {
+			$this->user_url  = bp_core_get_user_domain( $this->id );
+			$this->user_link = bp_core_get_userlink( $this->id );
+			$this->fullname  = esc_attr( bp_core_get_user_displayname( $this->id ) );
+			$this->email     = esc_attr( bp_core_get_user_email( $this->id ) );
+		}
+
+		// Cache a few things that are fetched often
+		wp_cache_set( 'bp_user_fullname_' . $this->id, $this->fullname, 'bp' );
+		wp_cache_set( 'bp_user_email_' . $this->id, $this->email, 'bp' );
+		wp_cache_set( 'bp_user_url_' . $this->id, $this->user_url, 'bp' );
+
+		$this->avatar       = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'full'  ) );
+		$this->avatar_thumb = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'thumb' ) );
+		$this->avatar_mini  = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'thumb', 'width' => 30, 'height' => 30 ) );
+		$this->last_active  = bp_core_get_last_activity( bp_get_user_meta( $this->id, 'last_activity', true ), __( 'active %s', 'buddypress' ) );
+	}
+
+	/**
+	 * Populates extra fields such as group and friendship counts.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 */
+	function populate_extras() {
+		global $bp;
+
+		if ( bp_is_active( 'friends' ) )
+			$this->total_friends = BP_Friends_Friendship::total_friend_count( $this->id );
+
+		if ( bp_is_active( 'groups' ) ) {
+			$this->total_groups = BP_Groups_Member::total_group_count( $this->id );
+			$this->total_groups = sprintf( _n( '%d group', '%d groups', $this->total_groups ), $this->total_groups );
+		}
+	}
+
+	function get_profile_data() {
+		return BP_XProfile_ProfileData::get_all_for_user( $this->id );
+	}
+
+	/** Static Functions ******************************************************/
+
+	function get_users( $type, $limit = 0, $page = 1, $user_id = 0, $include = false, $search_terms = false, $populate_extras = true, $exclude = false, $meta_key = false, $meta_value = false ) {
+		global $wpdb, $bp;
+
+		$sql = array();
+
+		$sql['select_main'] = "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.display_name, u.user_email";
+
+		if ( 'active' == $type || 'online' == $type || 'newest' == $type  )
+			$sql['select_active'] = ", um.meta_value as last_activity";
+
+		if ( 'popular' == $type )
+			$sql['select_popular'] = ", um.meta_value as total_friend_count";
+
+		if ( 'alphabetical' == $type )
+			$sql['select_alpha'] = ", pd.value as fullname";
+
+		if ( $meta_key ) {
+			$sql['select_meta'] = ", umm.meta_key";
+
+			if ( $meta_value )
+				$sql['select_meta'] .= ", umm.meta_value";
+		}
+
+		$sql['from'] = "FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON um.user_id = u.ID";
+
+		// We search against xprofile fields, so we must join the table
+		if ( $search_terms && bp_is_active( 'xprofile' ) )
+			$sql['join_profiledata_search'] = "LEFT JOIN {$bp->profile->table_name_data} spd ON u.ID = spd.user_id";
+
+		// Alphabetical sorting is done by the xprofile Full Name field
+		if ( 'alphabetical' == $type )
+			$sql['join_profiledata_alpha'] = "LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id";
+
+		if ( $meta_key )
+			$sql['join_meta'] = "LEFT JOIN {$wpdb->usermeta} umm ON umm.user_id = u.ID";
+
+		$sql['where'] = 'WHERE ' . bp_core_get_status_sql( 'u.' );
+
+		if ( 'active' == $type || 'online' == $type || 'newest' == $type )
+			$sql['where_active'] = $wpdb->prepare( "AND um.meta_key = %s", bp_get_user_meta_key( 'last_activity' ) );
+
+		if ( 'popular' == $type )
+			$sql['where_popular'] = $wpdb->prepare( "AND um.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) );
+
+		if ( 'online' == $type )
+			$sql['where_online'] = "AND DATE_ADD( um.meta_value, INTERVAL 5 MINUTE ) >= UTC_TIMESTAMP()";
+
+		if ( 'alphabetical' == $type )
+			$sql['where_alpha'] = "AND pd.field_id = 1";
+
+		if ( !empty( $exclude ) )
+			$sql['where_exclude'] = "AND u.ID NOT IN ({$exclude})";
+
+		if ( $include ) {
+			if ( is_array( $include ) )
+				$uids = $wpdb->escape( implode( ',', (array)$include ) );
+			else
+				$uids = $wpdb->escape( $include );
+
+			if ( !empty( $uids ) )
+				$sql['where_users'] = "AND u.ID IN ({$uids})";
+		}
+
+		else if ( $user_id && bp_is_active( 'friends' ) ) {
+			$friend_ids = friends_get_friend_user_ids( $user_id );
+			$friend_ids = $wpdb->escape( implode( ',', (array)$friend_ids ) );
+
+			if ( !empty( $friend_ids ) )
+				$sql['where_friends'] = "AND u.ID IN ({$friend_ids})";
+
+			// User has no friends, return false since there will be no users to fetch.
+			else
+				return false;
+
+		}
+
+		if ( $search_terms && bp_is_active( 'xprofile' ) ) {
+			$search_terms             = like_escape( $wpdb->escape( $search_terms ) );
+			$sql['where_searchterms'] = "AND spd.value LIKE '%%$search_terms%%'";
+		}
+
+		if ( $meta_key ) {
+			$sql['where_meta'] = $wpdb->prepare( " AND umm.meta_key = %s", $meta_key );
+
+			// If a meta value is provided, match it
+			if ( $meta_value ) {
+				$sql['where_meta'] .= $wpdb->prepare( " AND umm.meta_value = %s", $meta_value );
+			}
+		}
+
+		switch ( $type ) {
+			case 'active': case 'online': default:
+				$sql[] = "ORDER BY um.meta_value DESC";
+				break;
+			case 'newest':
+				$sql[] = "ORDER BY u.ID DESC";
+				break;
+			case 'alphabetical':
+				$sql[] = "ORDER BY pd.value ASC";
+				break;
+			case 'random':
+				$sql[] = "ORDER BY rand()";
+				break;
+			case 'popular':
+				$sql[] = "ORDER BY CONVERT(um.meta_value, SIGNED) DESC";
+				break;
+		}
+
+		if ( $limit && $page )
+			$sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		// Get paginated results
+		$paged_users_sql = apply_filters( 'bp_core_get_paged_users_sql', join( ' ', (array)$sql ), $sql );
+		$paged_users     = $wpdb->get_results( $paged_users_sql );
+
+		// Re-jig the SQL so we can get the total user count
+		unset( $sql['select_main'] );
+
+		if ( !empty( $sql['select_active'] ) )
+			unset( $sql['select_active'] );
+
+		if ( !empty( $sql['select_popular'] ) )
+			unset( $sql['select_popular'] );
+
+		if ( !empty( $sql['select_alpha'] ) )
+			unset( $sql['select_alpha'] );
+
+		if ( !empty( $sql['pagination'] ) )
+			unset( $sql['pagination'] );
+
+		array_unshift( $sql, "SELECT COUNT(DISTINCT u.ID)" );
+
+		// Get total user results
+		$total_users_sql = apply_filters( 'bp_core_get_total_users_sql', join( ' ', (array)$sql ), $sql );
+		$total_users     = $wpdb->get_var( $total_users_sql );
+
+		/***
+		 * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
+		 * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
+		 */
+		if ( !empty( $populate_extras ) ) {
+			$user_ids = array();
+
+			foreach ( (array)$paged_users as $user )
+				$user_ids[] = $user->id;
+
+			$user_ids = $wpdb->escape( join( ',', (array)$user_ids ) );
+
+			// Add additional data to the returned results
+			$paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids, $type );
+		}
+
+		return array( 'users' => $paged_users, 'total' => $total_users );
+	}
+
+
+	/**
+	 * Fetches the user details for all the users who username starts with the letter given.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param string $letter The letter the users names are to start with.
+	 * @param integer $limit The number of users we wish to retrive.
+	 * @param integer $page The page number we are currently on, used in conjunction with $limit to get the start position for the limit.
+	 * @param boolean $populate_extras Populate extra user fields?
+	 * @param string $exclude Comma-separated IDs of users whose results aren't to be fetched.
+	 * @return mixed False on error, otherwise associative array of results.
+	 * @static
+	 */
+	function get_users_by_letter( $letter, $limit = null, $page = 1, $populate_extras = true, $exclude = '' ) {
+		global $bp, $wpdb;
+
+		$pag_sql = '';
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		// Multibyte compliance
+		if ( function_exists( 'mb_strlen' ) ) {
+			if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) {
+				return false;
+			}
+		} else {
+			if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) {
+				return false;
+			}
+		}
+
+		$letter     = like_escape( $wpdb->escape( $letter ) );
+		$status_sql = bp_core_get_status_sql( 'u.' );
+
+		$exclude_sql = ( !empty( $exclude ) ) ? " AND u.ID NOT IN ({$exclude})" : "";
+
+		$total_users_sql = apply_filters( 'bp_core_users_by_letter_count_sql', $wpdb->prepare( "SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id LEFT JOIN {$bp->profile->table_name_fields} pf ON pd.field_id = pf.id WHERE {$status_sql} AND pf.name = %s {$exclude_sql} AND pd.value LIKE '$letter%%'  ORDER BY pd.value ASC", bp_xprofile_fullname_field_name() ), $letter );
+		$paged_users_sql = apply_filters( 'bp_core_users_by_letter_sql', $wpdb->prepare( "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.user_email FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id LEFT JOIN {$bp->profile->table_name_fields} pf ON pd.field_id = pf.id WHERE {$status_sql} AND pf.name = %s {$exclude_sql} AND pd.value LIKE '$letter%%' ORDER BY pd.value ASC{$pag_sql}", bp_xprofile_fullname_field_name() ), $letter, $pag_sql );
+
+		$total_users = $wpdb->get_var( $total_users_sql );
+		$paged_users = $wpdb->get_results( $paged_users_sql );
+
+		/***
+		 * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
+		 * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
+		 */
+		$user_ids = array();
+		foreach ( (array)$paged_users as $user )
+			$user_ids[] = $user->id;
+
+		$user_ids = $wpdb->escape( join( ',', (array)$user_ids ) );
+
+		/* Add additional data to the returned results */
+		if ( $populate_extras )
+			$paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
+
+		return array( 'users' => $paged_users, 'total' => $total_users );
+	}
+
+	/**
+	 * Get details of specific users from the database
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param array $user_ids The user IDs of the users who we wish to fetch information on.
+	 * @param integer $limit The limit of results we want.
+	 * @param integer $page The page we are on for pagination.
+	 * @param boolean $populate_extras Populate extra user fields?
+	 * @return array Associative array
+	 * @static
+	 */
+	function get_specific_users( $user_ids, $limit = null, $page = 1, $populate_extras = true ) {
+		global $bp, $wpdb;
+
+		$pag_sql = '';
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		$user_sql   = " AND user_id IN ( " . $wpdb->escape( $user_ids ) . " ) ";
+		$status_sql = bp_core_get_status_sql();
+
+		$total_users_sql = apply_filters( 'bp_core_get_specific_users_count_sql', $wpdb->prepare( "SELECT COUNT(DISTINCT ID) FROM {$wpdb->users} WHERE {$status_sql} AND ID IN ( " . $wpdb->escape( $user_ids ) . " ) " ), $wpdb->escape( $user_ids ) );
+		$paged_users_sql = apply_filters( 'bp_core_get_specific_users_count_sql', $wpdb->prepare( "SELECT DISTINCT ID as id, user_registered, user_nicename, user_login, user_email FROM {$wpdb->users} WHERE {$status_sql} AND ID IN ( " . $wpdb->escape( $user_ids ) . " ) {$pag_sql}" ), $wpdb->escape( $user_ids ) );
+
+		$total_users = $wpdb->get_var( $total_users_sql );
+		$paged_users = $wpdb->get_results( $paged_users_sql );
+
+		/***
+		 * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
+		 * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
+		 */
+
+		/* Add additional data to the returned results */
+		if ( $populate_extras )
+			$paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
+
+		return array( 'users' => $paged_users, 'total' => $total_users );
+	}
+
+	/**
+	 * Find users who match on the value of an xprofile data.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param string $search_terms The terms to search the profile table value column for.
+	 * @param integer $limit The limit of results we want.
+	 * @param integer $page The page we are on for pagination.
+	 * @param boolean $populate_extras Populate extra user fields?
+	 * @return array Associative array
+	 * @static
+	 */
+	function search_users( $search_terms, $limit = null, $page = 1, $populate_extras = true ) {
+		global $bp, $wpdb;
+
+		$pag_sql = $limit && $page ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * intval( $limit ) ), intval( $limit ) ) : '';
+
+		$search_terms = like_escape( $wpdb->escape( $search_terms ) );
+		$status_sql   = bp_core_get_status_sql( 'u.' );
+
+		$total_users_sql = apply_filters( 'bp_core_search_users_count_sql', "SELECT COUNT(DISTINCT u.ID) as id FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id WHERE {$status_sql} AND pd.value LIKE '%%$search_terms%%' ORDER BY pd.value ASC", $search_terms );
+		$paged_users_sql = apply_filters( 'bp_core_search_users_sql', "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.user_email FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id WHERE {$status_sql} AND pd.value LIKE '%%$search_terms%%' ORDER BY pd.value ASC{$pag_sql}", $search_terms, $pag_sql );
+
+		$total_users = $wpdb->get_var( $total_users_sql );
+		$paged_users = $wpdb->get_results( $paged_users_sql );
+
+		/***
+		 * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
+		 * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
+		 */
+		foreach ( (array)$paged_users as $user )
+			$user_ids[] = $user->id;
+
+		$user_ids = $wpdb->escape( join( ',', (array)$user_ids ) );
+
+		// Add additional data to the returned results
+		if ( $populate_extras )
+			$paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
+
+		return array( 'users' => $paged_users, 'total' => $total_users );
+	}
+
+	/**
+	 * Fetch extra user information, such as friend count and last profile update message.
+	 *
+	 * Accepts multiple user IDs to fetch data for.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param array $paged_users an array of stdClass containing the users
+	 * @param string $user_ids the user ids to select information about
+	 * @param string $type the type of fields we wish to get
+	 * @return mixed False on error, otherwise associative array of results.
+	 * @static
+	 */
+	function get_user_extras( &$paged_users, &$user_ids, $type = false ) {
+		global $bp, $wpdb;
+
+		if ( empty( $user_ids ) )
+			return $paged_users;
+
+		// Fetch the user's full name
+		if ( bp_is_active( 'xprofile' ) && 'alphabetical' != $type ) {
+			$names = $wpdb->get_results( $wpdb->prepare( "SELECT pd.user_id as id, pd.value as fullname FROM {$bp->profile->table_name_fields} pf, {$bp->profile->table_name_data} pd WHERE pf.id = pd.field_id AND pf.name = %s AND pd.user_id IN ( {$user_ids} )", bp_xprofile_fullname_field_name() ) );
+			for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+				foreach ( (array)$names as $name ) {
+					if ( $name->id == $paged_users[$i]->id )
+						$paged_users[$i]->fullname = $name->fullname;
+				}
+			}
+		}
+
+		// Fetch the user's total friend count
+		if ( 'popular' != $type ) {
+			$friend_count = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as total_friend_count FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'total_friend_count' ) ) );
+			for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+				foreach ( (array)$friend_count as $fcount ) {
+					if ( $fcount->id == $paged_users[$i]->id )
+						$paged_users[$i]->total_friend_count = (int)$fcount->total_friend_count;
+				}
+			}
+		}
+
+		// Fetch whether or not the user is a friend
+		if ( bp_is_active( 'friends' ) ) {
+			$friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids} ) ) OR (initiator_user_id IN ( {$user_ids} ) AND friend_user_id = %d )", $bp->loggedin_user->id, $bp->loggedin_user->id ) );
+			for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+				foreach ( (array)$friend_status as $status ) {
+					if ( $status->initiator_user_id == $paged_users[$i]->id || $status->friend_user_id == $paged_users[$i]->id )
+						$paged_users[$i]->is_friend = $status->is_confirmed;
+				}
+			}
+		}
+
+		if ( 'active' != $type ) {
+			$user_activity = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as last_activity FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'last_activity' ) ) );
+			for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+				foreach ( (array)$user_activity as $activity ) {
+					if ( $activity->id == $paged_users[$i]->id )
+						$paged_users[$i]->last_activity = $activity->last_activity;
+				}
+			}
+		}
+
+		// Fetch the user's last_activity
+		if ( 'active' != $type ) {
+			$user_activity = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as last_activity FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'last_activity' ) ) );
+			for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+				foreach ( (array)$user_activity as $activity ) {
+					if ( $activity->id == $paged_users[$i]->id )
+						$paged_users[$i]->last_activity = $activity->last_activity;
+				}
+			}
+		}
+
+		// Fetch the user's latest update
+		$user_update = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as latest_update FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'bp_latest_update' ) ) );
+		for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
+			foreach ( (array)$user_update as $update ) {
+				if ( $update->id == $paged_users[$i]->id )
+					$paged_users[$i]->latest_update = $update->latest_update;
+			}
+		}
+
+		return $paged_users;
+	}
+
+	/**
+	 * Get WordPress user details for a specified user.
+	 *
+	 * @global wpdb $wpdb WordPress database object
+	 * @param integer $user_id User ID
+	 * @return array Associative array
+	 * @static
+	 */
+	function get_core_userdata( $user_id ) {
+		global $wpdb;
+
+		if ( !$user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE ID = %d LIMIT 1", $user_id ) ) )
+			return false;
+
+		return $user;
+	}
+}
+
+
+/**
+ * BP_Core_Notification class can be used by any component.
+ * It will handle the fetching, saving and deleting of a user notification.
+ *
+ * @package BuddyPress Core
+ */
+
+class BP_Core_Notification {
+
+	/**
+	 * The notification id
+	 *
+	 * @var integer
+	 */
+	var $id;
+
+	/**
+	 * The ID to which the notification relates to within the component.
+	 *
+	 * @var integer
+	 */
+	var $item_id;
+
+	/**
+	 * The secondary ID to which the notification relates to within the component.
+	 *
+	 * @var integer
+	 */
+	var $secondary_item_id = null;
+
+	/**
+	 * The user ID for who the notification is for.
+	 *
+	 * @var integer
+	 */
+	var $user_id;
+
+	/**
+	 * The name of the component that the notification is for.
+	 *
+	 * @var string
+	 */
+	var $component_name;
+
+	/**
+	 * The action within the component which the notification is related to.
+	 *
+	 * @var string
+	 */
+	var $component_action;
+
+	/**
+	 * The date the notification was created.
+	 *
+	 * @var string
+	 */
+	var $date_notified;
+
+	/**
+	 * Is the notification new or has it already been read.
+	 *
+	 * @var boolean
+	 */
+	var $is_new;
+
+
+	/**
+	 * PHP4 constructor
+	 *
+	 * @param integer $id
+	 */
+	function bp_core_notification( $id = 0 ) {
+		$this->__construct($id);
+	}
+
+	/**
+	 * Constructor
+	 *
+	 * @param integer $id
+	 */
+	function __construct( $id = 0 ) {
+		if ( $id ) {
+			$this->id = $id;
+			$this->populate();
+		}
+	}
+
+	/**
+	 * Fetches the notification data from the database.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 */
+	function populate() {
+		global $bp, $wpdb;
+
+		if ( $notification = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->core->table_name_notifications} WHERE id = %d", $this->id ) ) ) {
+			$this->item_id = $notification->item_id;
+			$this->secondary_item_id = $notification->secondary_item_id;
+			$this->user_id           = $notification->user_id;
+			$this->component_name    = $notification->component_name;
+			$this->component_action  = $notification->component_action;
+			$this->date_notified     = $notification->date_notified;
+			$this->is_new            = $notification->is_new;
+		}
+	}
+
+	/**
+	 * Update or insert notification details into the database.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @return bool Success or failure
+	 */
+	function save() {
+		global $bp, $wpdb;
+
+		// Update
+		if ( $this->id )
+			$sql = $wpdb->prepare( "UPDATE {$bp->core->table_name_notifications} SET item_id = %d, secondary_item_id = %d, user_id = %d, component_name = %s, component_action = %d, date_notified = %s, is_new = %d ) WHERE id = %d", $this->item_id, $this->secondary_item_id, $this->user_id, $this->component_name, $this->component_action, $this->date_notified, $this->is_new, $this->id );
+
+		// Save
+		else
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->core->table_name_notifications} ( item_id, secondary_item_id, user_id, component_name, component_action, date_notified, is_new ) VALUES ( %d, %d, %d, %s, %s, %s, %d )", $this->item_id, $this->secondary_item_id, $this->user_id, $this->component_name, $this->component_action, $this->date_notified, $this->is_new );
+
+		if ( !$result = $wpdb->query( $sql ) )
+			return false;
+
+		$this->id = $wpdb->insert_id;
+		return true;
+	}
+
+	/** Static functions ******************************************************/
+
+	function check_access( $user_id, $notification_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->core->table_name_notifications} WHERE id = %d AND user_id = %d", $notification_id, $user_id ) );
+	}
+
+	/**
+	 * Fetches all the notifications in the database for a specific user.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param integer $user_id User ID
+	 * @return array Associative array
+	 * @static
+	 */
+	function get_all_for_user( $user_id ) {
+		global $bp, $wpdb;
+
+ 		return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->core->table_name_notifications} WHERE user_id = %d AND is_new = 1", $user_id ) );
+	}
+
+	/**
+	 * Delete all the notifications for a user based on the component name and action.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param integer $user_id
+	 * @param string $component_name
+	 * @param string $component_action
+	 * @static
+	 */
+	function delete_for_user_by_type( $user_id, $component_name, $component_action ) {
+		global $bp, $wpdb;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE user_id = %d AND component_name = %s AND component_action = %s", $user_id, $component_name, $component_action ) );
+	}
+
+	/**
+	 * Delete all the notifications that have a specific item id, component name and action.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param integer $user_id The ID of the user who the notifications are for.
+	 * @param integer $item_id The item ID of the notifications we wish to delete.
+	 * @param string $component_name The name of the component that the notifications we wish to delete.
+	 * @param string $component_action The action of the component that the notifications we wish to delete.
+	 * @param integer $secondary_item_id (optional) The secondary item id of the notifications that we wish to use to delete.
+	 * @static
+	 */
+	function delete_for_user_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id = false ) {
+		global $bp, $wpdb;
+
+		$secondary_item_sql = !empty( $secondary_item_id ) ? $wpdb->prepare( " AND secondary_item_id = %d", $secondary_item_id ) : '';
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE user_id = %d AND item_id = %d AND component_name = %s AND component_action = %s{$secondary_item_sql}", $user_id, $item_id, $component_name, $component_action ) );
+	}
+
+	/**
+	 * Deletes all the notifications sent by a specific user, by component and action.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param integer $user_id The ID of the user whose sent notifications we wish to delete.
+	 * @param string $component_name The name of the component the notification was sent from.
+	 * @param string $component_action The action of the component the notification was sent from.
+	 * @static
+	 */
+	function delete_from_user_by_type( $user_id, $component_name, $component_action ) {
+		global $bp, $wpdb;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE item_id = %d AND component_name = %s AND component_action = %s", $user_id, $component_name, $component_action ) );
+	}
+
+	/**
+	 * Deletes all the notifications for all users by item id, and optional secondary item id, and component name and action.
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @global wpdb $wpdb WordPress database object
+	 * @param string $item_id The item id that they notifications are to be for.
+	 * @param string $component_name The component that the notifications are to be from.
+	 * @param string $component_action The action that the notificationsa are to be from.
+	 * @param string $secondary_item_id Optional secondary item id that the notifications are to have.
+	 * @static
+	 */
+	function delete_all_by_type( $item_id, $component_name, $component_action, $secondary_item_id ) {
+		global $bp, $wpdb;
+
+		if ( $component_action )
+			$component_action_sql = $wpdb->prepare( "AND component_action = %s", $component_action );
+		else
+			$component_action_sql = '';
+
+		if ( $secondary_item_id )
+			$secondary_item_sql = $wpdb->prepare( "AND secondary_item_id = %d", $secondary_item_id );
+		else
+			$secondary_item_sql = '';
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE item_id = %d AND component_name = %s {$component_action_sql} {$secondary_item_sql}", $item_id, $component_name ) );
+	}
+}
+
+/**
+ * BP_Button
+ *
+ * API to create BuddyPress buttons
+ *
+ * @package BuddyPress Core
+ * @since 1.2.6
+ */
+class BP_Button {
+	// Button properties
+
+	/**
+	 * The button ID
+	 *
+	 * @var integer
+	 */
+	var $id;
+
+	/**
+	 * The component name that button belongs to.
+	 *
+	 * @var string
+	 */
+	var $component;
+
+	/**
+	 * Does the user need to be logged in to see this button?
+	 *
+	 * @var boolean
+	 */
+	var $must_be_logged_in;
+
+	/**
+	 * True or false if the button should not be displayed while viewing your own profile.
+	 *
+	 * @var boolean
+	 */
+	var $block_self;
+
+
+	// Wrapper
+
+	/**
+	 * What type of DOM element to use for a wrapper.
+	 *
+	 *
+	 * @var mixed div|span|p|li, or false for no wrapper
+	 */
+	var $wrapper;
+
+	/**
+	 * The DOM class of the button wrapper
+	 *
+	 * @var string
+	 */
+	var $wrapper_class;
+
+	/**
+	 * The DOM ID of the button wrapper
+	 *
+	 * @var string
+	 */
+	var $wrapper_id;
+
+
+	// Button
+
+	/**
+	 * The destination link of the button
+	 *
+	 * @var string
+	 */
+	var $link_href;
+
+	/**
+	 * The DOM class of the button link
+	 *
+	 * @var string
+	 */
+	var $link_class;
+
+	/**
+	 * The DOM ID of the button link
+	 *
+	 * @var string
+	 */
+	var $link_id;
+
+	/**
+	 * The DOM rel value of the button link
+	 *
+	 * @var string
+	 */
+	var $link_rel;
+
+	/**
+	 * Title of the button link
+	 *
+	 * @var string
+	 */
+	var $link_title;
+
+	/**
+	 * The contents of the button link
+	 *
+	 * @var string
+	 */
+	var $link_text;
+
+
+	// HTML result
+
+	var $contents;
+
+	/**
+	 * bp_button()
+	 *
+	 * Builds the button based on passed parameters:
+	 *
+	 * component: Which component this button is for
+	 * must_be_logged_in: Button only appears for logged in users
+	 * block_self: Button will not appear when viewing your own profile.
+	 * wrapper: div|span|p|li|false for no wrapper
+	 * wrapper_id: The DOM ID of the button wrapper
+	 * wrapper_class: The DOM class of the button wrapper
+	 * link_href: The destination link of the button
+	 * link_title: Title of the button
+	 * link_id: The DOM ID of the button
+	 * link_class: The DOM class of the button
+	 * link_rel: The DOM rel of the button
+	 * link_text: The contents of the button
+	 *
+	 * @param array $args
+	 * @return bool False if not allowed
+	 */
+	function bp_button( $args = '' ) {
+		$this->__construct($args);
+	}
+
+	function __construct( $args = '' ) {
+
+		// Default arguments
+		$defaults = array(
+			'id'                => '',
+			'component'         => 'core',
+			'must_be_logged_in' => true,
+			'block_self'        => true,
+
+			'wrapper'           => 'div',
+			'wrapper_id'        => '',
+			'wrapper_class'     => '',
+
+			'link_href'         => '',
+			'link_title'        => '',
+			'link_id'           => '',
+			'link_class'        => '',
+			'link_rel'          => '',
+			'link_text'         => '',
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Required button properties
+		$this->id                = $id;
+		$this->component         = $component;
+		$this->must_be_logged_in = (bool)$must_be_logged_in;
+		$this->block_self        = (bool)$block_self;
+		$this->wrapper           = $wrapper;
+
+		// $id and $component are required
+		if ( empty( $id ) || empty( $component ) )
+			return false;
+
+		// No button if component is not active
+		if ( !bp_is_active( $this->component ) )
+			return false;
+
+		// No button for guests if must be logged in
+		if ( true == $this->must_be_logged_in && !is_user_logged_in() )
+			return false;
+
+		// No button if viewing your own profile
+		if ( true == $this->block_self && bp_is_my_profile() )
+			return false;
+
+		// Wrapper properties
+		if ( false !== $this->wrapper ) {
+
+			// Wrapper ID
+			if ( !empty( $wrapper_id ) )
+				$this->wrapper_id    = ' id="' . $wrapper_id . '"';
+
+			// Wrapper class
+			if ( !empty( $wrapper_class ) )
+				$this->wrapper_class = ' class="generic-button ' . $wrapper_class . '"';
+			else
+				$this->wrapper_class = ' class="generic-button"';
+
+			// Set before and after
+			$before = '<' . $wrapper . $this->wrapper_class . $this->wrapper_id . '>';
+			$after  = '</' . $wrapper . '>';
+
+		// No wrapper
+		} else {
+			$before = $after = '';
+		}
+
+		// Link properties
+		if ( !empty( $link_id ) )
+			$this->link_id    = ' id="' . $link_id . '"';
+
+		if ( !empty( $link_href ) )
+			$this->link_href  = ' href="' . $link_href . '"';
+
+		if ( !empty( $link_title ) )
+			$this->link_title = ' title="' . $link_title . '"';
+
+		if ( !empty( $link_rel ) )
+			$this->link_rel   = ' rel="' . $link_rel . '"';
+
+		if ( !empty( $link_class ) )
+			$this->link_class = ' class="' . $link_class . '"';
+
+		if ( !empty( $link_text ) )
+			$this->link_text  = $link_text;
+
+		// Build the button
+		$this->contents = $before . '<a'. $this->link_href . $this->link_title . $this->link_id . $this->link_rel . $this->link_class . '>' . $this->link_text . '</a>' . $after;
+
+		// Allow button to be manipulated externally
+		$this->contents = apply_filters( 'bp_button_' . $component . '_' . $id, $this->contents, $this, $before, $after );
+	}
+
+	/**
+	 * contents()
+	 *
+	 * Return contents of button
+	 *
+	 * @return string
+	 */
+	function contents() {
+		return $this->contents;
+	}
+
+	/**
+	 * display()
+	 *
+	 * Output contents of button
+	 */
+	function display() {
+		if ( !empty( $this->contents ) )
+			echo $this->contents;
+	}
+}
+
+/**
+ * BP_Embed
+ *
+ * Extends WP_Embed class for use with BuddyPress.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ * @see WP_Embed
+ */
+class BP_Embed extends WP_Embed {
+	/**
+	 * Constructor
+	 *
+	 * @global unknown $wp_embed
+	 */
+	function __construct() {
+		global $wp_embed;
+
+		// Make sure we populate the WP_Embed handlers array.
+		// These are providers that use a regex callback on the URL in question.
+		// Do not confuse with oEmbed providers, which require an external ping.
+		// Used in WP_Embed::shortcode()
+		$this->handlers = $wp_embed->handlers;
+
+		if ( bp_use_embed_in_activity() ) {
+			add_filter( 'bp_get_activity_content_body', array( &$this, 'autoembed' ), 8 );
+			add_filter( 'bp_get_activity_content_body', array( &$this, 'run_shortcode' ), 7 );
+		}
+
+		if ( bp_use_embed_in_activity_replies() ) {
+			add_filter( 'bp_get_activity_content', array( &$this, 'autoembed' ), 8 );
+			add_filter( 'bp_get_activity_content', array( &$this, 'run_shortcode' ), 7 );
+		}
+
+		if ( bp_use_embed_in_forum_posts() ) {
+			add_filter( 'bp_get_the_topic_post_content', array( &$this, 'autoembed' ), 8 );
+			add_filter( 'bp_get_the_topic_post_content', array( &$this, 'run_shortcode' ), 7 );
+		}
+
+		if ( bp_use_embed_in_private_messages() ) {
+			add_filter( 'bp_get_the_thread_message_content', array( &$this, 'autoembed' ), 8 );
+			add_filter( 'bp_get_the_thread_message_content', array( &$this, 'run_shortcode' ), 7 );
+		}
+
+		do_action_ref_array( 'bp_core_setup_oembed', array( &$this ) );
+	}
+
+	/**
+	 * The {@link do_shortcode()} callback function.
+	 *
+	 * Attempts to convert a URL into embed HTML. Starts by checking the URL against the regex of the registered embed handlers.
+	 * Next, checks the URL against the regex of registered {@link WP_oEmbed} providers if oEmbed discovery is false.
+	 * If none of the regex matches and it's enabled, then the URL will be passed to {@link BP_Embed::parse_oembed()} for oEmbed parsing.
+	 *
+	 * @uses wp_parse_args()
+	 * @uses wp_embed_defaults()
+	 * @uses current_user_can()
+	 * @uses _wp_oembed_get_object()
+	 * @uses WP_Embed::maybe_make_link()
+	 *
+	 * @param array $attr Shortcode attributes.
+	 * @param string $url The URL attempting to be embeded.
+	 * @return string The embed HTML on success, otherwise the original URL.
+	 */
+	function shortcode( $attr, $url = '' ) {
+		if ( empty( $url ) )
+			return '';
+
+		$rawattr = $attr;
+		$attr = wp_parse_args( $attr, wp_embed_defaults() );
+
+		// kses converts & into &amp; and we need to undo this
+		// See http://core.trac.wordpress.org/ticket/11311
+		$url = str_replace( '&amp;', '&', $url );
+
+		// Look for known internal handlers
+		ksort( $this->handlers );
+		foreach ( $this->handlers as $priority => $handlers ) {
+			foreach ( $handlers as $hid => $handler ) {
+				if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) {
+					if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) )
+						return apply_filters( 'embed_handler_html', $return, $url, $attr );
+				}
+			}
+		}
+
+		// Get object ID
+		$id = apply_filters( 'embed_post_id', 0 );
+
+		// Is oEmbed discovery on?
+		$attr['discover'] = ( apply_filters( 'bp_embed_oembed_discover', false ) && current_user_can( 'unfiltered_html' ) );
+
+		// Set up a new WP oEmbed object to check URL with registered oEmbed providers
+		require_once( ABSPATH . WPINC . '/class-oembed.php' );
+		$oembed_obj = _wp_oembed_get_object();
+
+		// If oEmbed discovery is true, skip oEmbed provider check
+		$is_oembed_link = false;
+		if ( !$attr['discover'] ) {
+			foreach ( (array)$oembed_obj->providers as $provider_matchmask => $provider ) {
+				$regex = ( $is_regex = $provider[1] ) ? $provider_matchmask : '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $provider_matchmask ), '#' ) ) . '#i';
+
+				if ( preg_match( $regex, $url ) )
+					$is_oembed_link = true;
+			}
+
+			// If url doesn't match a WP oEmbed provider, stop parsing
+			if ( !$is_oembed_link )
+				return $this->maybe_make_link( $url );
+		}
+
+		return $this->parse_oembed( $id, $url, $attr, $rawattr );
+	}
+
+	/**
+	 * Base function so BP components / plugins can parse links to be embedded.
+	 * View an example to add support in {@link bp_activity_embed()}.
+	 *
+	 * @uses apply_filters() Filters cache.
+	 * @uses do_action() To save cache.
+	 * @uses wp_oembed_get() Connects to oEmbed provider and returns HTML on success.
+	 * @uses WP_Embed::maybe_make_link() Process URL for hyperlinking on oEmbed failure.
+	 * @param int $id ID to do the caching for.
+	 * @param string $url The URL attempting to be embedded.
+	 * @param array $attr Shortcode attributes from {@link WP_Embed::shortcode()}.
+	 * @param array $rawattr Untouched shortcode attributes from {@link WP_Embed::shortcode()}.
+	 * @return string The embed HTML on success, otherwise the original URL.
+	 */
+	function parse_oembed( $id, $url, $attr, $rawattr ) {
+		$id = intval( $id );
+
+		if ( $id ) {
+			// Setup the cachekey
+			$cachekey = '_oembed_' . md5( $url . serialize( $attr ) );
+
+			// Let components / plugins grab their cache
+			$cache = '';
+			$cache = apply_filters( 'bp_embed_get_cache', $cache, $id, $cachekey, $url, $attr, $rawattr );
+
+			// Grab cache and return it if available
+			if ( !empty( $cache ) ) {
+				return apply_filters( 'bp_embed_oembed_html', $cache, $url, $attr, $rawattr );
+
+			// If no cache, ping the oEmbed provider and cache the result
+			} else {
+				$html = wp_oembed_get( $url, $attr );
+				$cache = ( $html ) ? $html : $url;
+
+				// Let components / plugins save their cache
+				do_action( 'bp_embed_update_cache', $cache, $cachekey, $id );
+
+				// If there was a result, return it
+				if ( $html )
+					return apply_filters( 'bp_embed_oembed_html', $html, $url, $attr, $rawattr );
+			}
+		}
+
+		// Still unknown
+		return $this->maybe_make_link( $url );
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-component.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-component.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-component.php	(revision 5057)
@@ -0,0 +1,345 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+if ( !class_exists( 'BP_Component' ) ) :
+/**
+ * BuddyPress Component Class
+ *
+ * The BuddyPress component class is responsible for simplifying the creation
+ * of components that share similar behaviors and routines. It is used
+ * internally by BuddyPress to create the bundled components, but can be
+ * extended to create other really neat things.
+ *
+ * @package BuddyPress
+ * @subpackage Component
+ *
+ * @since 1.5
+ */
+class BP_Component {
+
+	/**
+	 * @var string Unique name (for internal identification)
+	 * @internal
+	 */
+	var $name;
+
+	/**
+	 * @var Unique ID (normally for custom post type)
+	 */
+	var $id;
+
+	/**
+	 * @var string Unique slug (used in query string and permalinks)
+	 */
+	var $slug;
+
+	/**
+	 * @var bool Does this component need a top-level directory?
+	 */
+	var $has_directory;
+
+	/**
+	 * @var string The path to the plugins files
+	 */
+	var $path;
+
+	/**
+	 * @var WP_Query The loop for this component
+	 */
+	var $query;
+
+	/**
+	 * @var string The current ID of the queried object
+	 */
+	var $current_id;
+
+	/**
+	 * @var string Function to call for notifications
+	 */
+	var $notification_callback;
+
+	/**
+	 * @var array WordPress admin bar links
+	 */
+	var $admin_menu;
+
+	/**
+	 * Component loader
+	 *
+	 * @since 1.5
+	 *
+	 * @param mixed $args Required. Supports these args:
+	 *  - name: Unique name (for internal identification)
+	 *  - id: Unique ID (normally for custom post type)
+	 *  - slug: Unique slug (used in query string and permalinks)
+	 *  - query: The loop for this component (WP_Query)
+	 *  - current_id: The current ID of the queried object
+	 * @uses bp_Component::setup_globals() Setup the globals needed
+	 * @uses bp_Component::includes() Include the required files
+	 * @uses bp_Component::setup_actions() Setup the hooks and actions
+	 */
+	function start( $id, $name, $path ) {
+		// Internal identifier of component
+		$this->id   = $id;
+
+		// Internal component name
+		$this->name = $name;
+
+		// Path for includes
+		$this->path = $path;
+
+		// Move on to the next step
+		$this->setup_actions();
+	}
+
+	/**
+	 * Component global variables
+	 *
+	 * @since 1.5
+	 * @access private
+	 *
+	 * @uses apply_filters() Calls 'bp_{@link bp_Component::name}_id'
+	 * @uses apply_filters() Calls 'bp_{@link bp_Component::name}_slug'
+	 *
+	 * @param arr $args Used to
+	 */
+	function setup_globals( $args = '' ) {
+		global $bp;
+
+		/** Slugs *************************************************************/
+
+		$defaults = array(
+			'slug'                  => '',
+			'root_slug'             => '',
+			'has_directory'         => false,
+			'notification_callback' => '',
+			'search_string'         => '',
+			'global_tables'         => ''
+		);
+		$r = wp_parse_args( $args, $defaults );
+
+		// Slug used for permalink URI chunk after root
+		$this->slug          = apply_filters( 'bp_' . $this->id . '_slug',          $r['slug']          );
+
+		// Slug used for root directory
+		$this->root_slug     = apply_filters( 'bp_' . $this->id . '_root_slug',     $r['root_slug']     );
+
+		// Does this component have a top-level directory?
+		$this->has_directory = apply_filters( 'bp_' . $this->id . '_has_directory', $r['has_directory'] );
+
+		// Search string
+		$this->search_string = apply_filters( 'bp_' . $this->id . '_search_string', $r['search_string'] );
+
+		// Notifications callback
+		$this->notification_callback = apply_filters( 'bp_' . $this->id . '_notification_callback', $r['notification_callback'] );
+
+		// Setup global table names
+		if ( !empty( $r['global_tables'] ) )
+			foreach ( $r['global_tables'] as $global_name => $table_name )
+				$this->$global_name = $table_name;
+
+		/** BuddyPress ********************************************************/
+
+		// Register this component in the loaded components array
+		$bp->loaded_components[$this->slug] = $this->id;
+
+		// Call action
+		do_action( 'bp_' . $this->id . '_setup_globals' );
+	}
+
+	/**
+	 * Include required files
+	 *
+	 * @since 1.5
+	 * @access private
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}includes'
+	 */
+	function includes( $includes = '' ) {
+		if ( empty( $includes ) )
+			return;
+
+		// Loop through files to be included
+		foreach ( $includes as $file ) {
+
+			// Check path + file
+			if ( @is_file( $this->path . '/' . $file ) )
+				require( $this->path . '/' . $file );
+
+			// Check path + /bp-component/ + file
+			elseif ( @is_file( $this->path . '/bp-' . $this->id . '/' . $file ) )
+				require( $this->path . '/bp-' . $this->id . '/' . $file );
+
+			// Check buddypress/bp-component/bp-component-$file.php
+			elseif ( @is_file( $this->path . '/bp-' . $this->id . '/bp-' . $this->id . '-' . $file  . '.php' ) )
+				require( $this->path . '/bp-' . $this->id . '/bp-' . $this->id . '-' . $file . '.php' );
+
+		}
+
+		// Call action
+		do_action( 'bp_' . $this->id . '_includes' );
+	}
+
+	/**
+	 * Setup the actions
+	 *
+	 * @since 1.5
+	 * @access private
+	 *
+	 * @uses add_action() To add various actions
+	 * @uses do_action() Calls 'bp_{@link BP_Component::name}setup_actions'
+	 */
+	function setup_actions() {
+
+		// Register post types
+		add_action( 'bp_setup_globals',          array ( $this, 'setup_globals'          ), 10 );
+
+		// Include required files. Called early to ensure that BP core
+		// components are loaded before plugins that hook their loader functions
+		// to bp_include with the default priority of 10. This is for backwards
+		// compatibility; henceforth, plugins should register themselves by
+		// extending this base class.
+		add_action( 'bp_include',                array ( $this, 'includes'               ), 8 );
+
+		// Register post types
+		add_action( 'bp_setup_nav',              array ( $this, 'setup_nav'              ), 10 );
+
+		// Register post types
+		add_action( 'bp_setup_admin_bar',        array ( $this, 'setup_admin_bar'        ), 10 );
+
+		// Register post types
+		add_action( 'bp_setup_title',            array ( $this, 'setup_title'            ), 10 );
+
+		// Register post types
+		add_action( 'bp_register_post_types',    array ( $this, 'register_post_types'    ), 10 );
+
+		// Register taxonomies
+		add_action( 'bp_register_taxonomies',    array ( $this, 'register_taxonomies'    ), 10 );
+
+		// Add the rewrite tags
+		add_action( 'bp_add_rewrite_tags',       array ( $this, 'add_rewrite_tags'       ), 10 );
+
+		// Generate rewrite rules
+		add_action( 'bp_generate_rewrite_rules', array ( $this, 'generate_rewrite_rules' ), 10 );
+
+		// Additional actions can be attached here
+		do_action( 'bp_' . $this->id . '_setup_actions' );
+	}
+
+	/**
+	 * Setup the navigation
+	 *
+	 * @param arr $main_nav Optional
+	 * @param arr $sub_nav Optional
+	 */
+	function setup_nav( $main_nav = '', $sub_nav = '' ) {
+
+		// No sub nav items without a main nav item
+		if ( !empty( $main_nav ) ) {
+			bp_core_new_nav_item( $main_nav );
+
+			// Sub nav items are not required
+			if ( !empty( $sub_nav ) ) {
+				foreach( $sub_nav as $nav ) {
+					bp_core_new_subnav_item( $nav );
+				}
+			}
+		}
+
+		// Call action
+		do_action( 'bp_' . $this->id . '_setup_nav' );
+	}
+
+	/**
+	 * Setup the admin bar
+	 *
+	 * @global obj $wp_admin_bar
+	 * @param array $wp_admin_menus
+	 */
+	function setup_admin_bar( $wp_admin_nav = '' ) {
+
+		// Bail if this is an ajax request
+		if ( defined( 'DOING_AJAX' ) )
+			return;
+
+		// Do not proceed if BP_USE_WP_ADMIN_BAR constant is not set or is false
+		if ( !bp_use_wp_admin_bar() )
+			return;
+
+		// Do we have admin bar menus to add?
+		if ( !empty( $wp_admin_nav ) ) {
+
+			// Set this objects menus
+			$this->admin_menu = $wp_admin_nav;
+
+			// Define the WordPress global
+			global $wp_admin_bar;
+
+			// Add each admin menu
+			foreach( $this->admin_menu as $admin_menu )
+				$wp_admin_bar->add_menu( $admin_menu );
+		}
+
+		// Call action
+		do_action( 'bp_' . $this->id . '_setup_admin_bar' );
+	}
+
+	/**
+	 * Setup the component title
+	 *
+	 * @since 1.5
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}setup_title'
+	 */
+	function setup_title( ) {
+		do_action(  'bp_' . $this->id . '_setup_title' );
+	}
+
+	/**
+	 * Setup the component post types
+	 *
+	 * @since 1.5
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}_register_post_types'
+	 */
+	function register_post_types() {
+		do_action( 'bp_' . $this->id . '_register_post_types' );
+	}
+
+	/**
+	 * Register component specific taxonomies
+	 *
+	 * @since 1.5
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}_register_taxonomies'
+	 */
+	function register_taxonomies() {
+		do_action( 'bp_' . $this->id . '_register_taxonomies' );
+	}
+
+	/**
+	 * Add any additional rewrite tags
+	 *
+	 * @since 1.5
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}_add_rewrite_tags'
+	 */
+	function add_rewrite_tags() {
+		do_action( 'bp_' . $this->id . '_add_rewrite_tags' );
+	}
+
+	/**
+	 * Generate any additional rewrite rules
+	 *
+	 * @since 1.5
+	 *
+	 * @uses do_action() Calls 'bp_{@link bp_Component::name}_generate_rewrite_rules'
+	 */
+	function generate_rewrite_rules ( $wp_rewrite ) {
+		do_action( 'bp_' . $this->id . '_generate_rewrite_rules' );
+	}
+}
+endif; // BP_Component
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-cssjs.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-cssjs.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-cssjs.php	(revision 5057)
@@ -0,0 +1,176 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * bp_core_admin_menu_icon_css()
+ *
+ * Add a hover-able icon to the "BuddyPress" wp-admin area menu.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_admin_menu_icon_css() {
+	global $bp; ?>
+
+	<style type="text/css">
+		/* Wizard Icon */
+		ul#adminmenu li.toplevel_page_bp-wizard .wp-menu-image a img { display: none; }
+		ul#adminmenu li.toplevel_page_bp-wizard .wp-menu-image a { background-image: url( <?php echo plugins_url( 'buddypress/bp-core/images/admin_menu_icon.png' ) ?> ) !important; background-position: -1px -32px; }
+		ul#adminmenu li.toplevel_page_bp-wizard:hover .wp-menu-image a,
+		ul#adminmenu li.toplevel_page_bp-wizard.wp-has-current-submenu .wp-menu-image a {
+			background-position: -1px 0;
+		}
+
+		/* Settings Icon */
+		ul#adminmenu li.toplevel_page_bp-general-settings .wp-menu-image a img { display: none; }
+		ul#adminmenu li.toplevel_page_bp-general-settings .wp-menu-image a { background-image: url( <?php echo plugins_url( 'buddypress/bp-core/images/admin_menu_icon.png' ) ?> ) !important; background-position: -1px -32px; }
+		ul#adminmenu li.toplevel_page_bp-general-settings:hover .wp-menu-image a,
+		ul#adminmenu li.toplevel_page_bp-general-settings.wp-has-current-submenu .wp-menu-image a {
+			background-position: -1px 0;
+		}
+	</style>
+
+<?php
+}
+add_action( 'admin_head', 'bp_core_admin_menu_icon_css' );
+
+function bp_core_confirmation_js() {
+	global $wpdb;
+
+	if ( is_multisite() && $wpdb->blogid != bp_get_root_blog_id() )
+		return false;
+
+	if ( !wp_script_is( 'jquery' ) )
+		wp_enqueue_script( 'jquery' );
+
+	if ( !wp_script_is( 'jquery', 'done' ) )
+		wp_print_scripts( 'jquery' ); ?>
+
+	<script type="text/javascript"> jQuery(document).ready( function() { jQuery("a.confirm").click( function() { if ( confirm( '<?php _e( 'Are you sure?', 'buddypress' ) ?>' ) ) return true; else return false; }); });</script>
+
+<?php
+}
+add_action( 'wp_head', 'bp_core_confirmation_js', 100 );
+
+/**
+ * bp_core_add_jquery_cropper()
+ *
+ * Makes sure the jQuery jCrop library is loaded.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_add_jquery_cropper() {
+	wp_enqueue_script( 'jcrop', array( 'jquery' ) );
+	add_action( 'wp_head', 'bp_core_add_cropper_inline_js' );
+	add_action( 'wp_head', 'bp_core_add_cropper_inline_css' );
+}
+
+/**
+ * bp_core_add_cropper_inline_js()
+ *
+ * Adds the inline JS needed for the cropper to work on a per-page basis.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_add_cropper_inline_js() {
+	global $bp;
+
+	$image = apply_filters( 'bp_inline_cropper_image', getimagesize( bp_core_avatar_upload_path() . $bp->avatar_admin->image->dir ) );
+	$aspect_ratio = 1;
+
+	$full_height = bp_core_avatar_full_height();
+	$full_width  = bp_core_avatar_full_width();
+
+	// Calculate Aspect Ratio
+	if ( $full_height && ( $full_width != $full_height ) )
+		$aspect_ratio = $full_width / $full_height;
+
+	$width  = $image[0] / 2;
+	$height = $image[1] / 2;
+?>
+
+	<script type="text/javascript">
+		jQuery(window).load( function(){
+			jQuery('#avatar-to-crop').Jcrop({
+				onChange: showPreview,
+				onSelect: showPreview,
+				onSelect: updateCoords,
+				aspectRatio: <?php echo $aspect_ratio ?>,
+				setSelect: [ 50, 50, <?php echo $width ?>, <?php echo $height ?> ]
+			});
+			updateCoords({x: 50, y: 50, w: <?php echo $width ?>, h: <?php echo $height ?>});
+		});
+
+		function updateCoords(c) {
+			jQuery('#x').val(c.x);
+			jQuery('#y').val(c.y);
+			jQuery('#w').val(c.w);
+			jQuery('#h').val(c.h);
+		};
+
+		function showPreview(coords) {
+			if ( parseInt(coords.w) > 0 ) {
+				var rx = <?php echo $full_width; ?> / coords.w;
+				var ry = <?php echo $full_height; ?> / coords.h;
+
+				jQuery('#avatar-crop-preview').css({
+				<?php if ( $image ) : ?>
+					width: Math.round(rx * <?php echo $image[0]; ?>) + 'px',
+					height: Math.round(ry * <?php echo $image[1]; ?>) + 'px',
+				<?php endif; ?>
+					marginLeft: '-' + Math.round(rx * coords.x) + 'px',
+					marginTop: '-' + Math.round(ry * coords.y) + 'px'
+				});
+			}
+		}
+	</script>
+
+<?php
+}
+
+/**
+ * bp_core_add_cropper_inline_css()
+ *
+ * Adds the inline CSS needed for the cropper to work on a per-page basis.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_add_cropper_inline_css() {
+	global $bp;
+?>
+
+	<style type="text/css">
+		.jcrop-holder { float: left; margin: 0 20px 20px 0; text-align: left; }
+		.jcrop-vline, .jcrop-hline { font-size: 0; position: absolute; background: white top left repeat url( <?php echo BP_PLUGIN_URL ?>/bp-core/images/Jcrop.gif ); }
+		.jcrop-vline { height: 100%; width: 1px !important; }
+		.jcrop-hline { width: 100%; height: 1px !important; }
+		.jcrop-handle { font-size: 1px; width: 7px !important; height: 7px !important; border: 1px #eee solid; background-color: #333; *width: 9px; *height: 9px; }
+		.jcrop-tracker { width: 100%; height: 100%; }
+		.custom .jcrop-vline, .custom .jcrop-hline { background: yellow; }
+		.custom .jcrop-handle { border-color: black; background-color: #C7BB00; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
+		#avatar-crop-pane { width: <?php echo bp_core_avatar_full_width() ?>px; height: <?php echo bp_core_avatar_full_height() ?>px; overflow: hidden; }
+		#avatar-crop-submit { margin: 20px 0; }
+		#avatar-upload-form img, #create-group-form img, #group-settings-form img { border: none !important; }
+	</style>
+
+<?php
+}
+
+/**
+ * bp_core_add_ajax_url_js()
+ *
+ * Adds AJAX target URL so themes can access the WordPress AJAX functionality.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_add_ajax_url_js() {
+	global $bp;
+?>
+
+	<script type="text/javascript">var ajaxurl = "<?php echo site_url( 'wp-load.php' ); ?>";</script>
+
+<?php
+}
+add_action( 'wp_head', 'bp_core_add_ajax_url_js' );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-filters.php	(revision 5057)
@@ -0,0 +1,326 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * bp_core_exclude_pages()
+ *
+ * Excludes specific pages from showing on page listings, for example the "Activation" page.
+ *
+ * @package BuddyPress Core
+ * @uses bp_is_active() checks if a BuddyPress component is active.
+ * @return array The list of page ID's to exclude
+ */
+function bp_core_exclude_pages( $pages ) {
+	global $bp;
+
+	if ( !empty( $bp->pages->activate ) )
+		$pages[] = $bp->pages->activate->id;
+
+	if ( !empty( $bp->pages->register ) )
+		$pages[] = $bp->pages->register->id;
+
+	if ( !empty( $bp->pages->forums ) && ( !bp_is_active( 'forums' ) || ( bp_is_active( 'forums' ) && bp_forums_has_directory() && !bp_forums_is_installed_correctly() ) ) )
+		$pages[] = $bp->pages->forums->id;
+
+	return apply_filters( 'bp_core_exclude_pages', $pages );
+}
+add_filter( 'wp_list_pages_excludes', 'bp_core_exclude_pages' );
+
+/**
+ * bp_core_email_from_name_filter()
+ *
+ * Sets the "From" name in emails sent to the name of the site and not "WordPress"
+ *
+ * @package BuddyPress Core
+ * @uses get_blog_option() fetches the value for a meta_key in the wp_X_options table
+ * @return The blog name for the root blog
+ */
+function bp_core_email_from_name_filter() {
+ 	return apply_filters( 'bp_core_email_from_name_filter', wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES ) );
+}
+add_filter( 'wp_mail_from_name', 'bp_core_email_from_name_filter' );
+
+/**
+ * bp_core_email_from_name_filter()
+ *
+ * Sets the "From" address in emails sent
+ *
+ * @package BuddyPress Core
+ * @return noreply@sitedomain email address
+ */
+function bp_core_email_from_address_filter() {
+	$domain = (array) explode( '/', site_url() );
+
+	return apply_filters( 'bp_core_email_from_address_filter', __( 'noreply', 'buddypress' ) . '@' . $domain[2] );
+}
+add_filter( 'wp_mail_from', 'bp_core_email_from_address_filter' );
+
+/**
+ * bp_core_allow_default_theme()
+ *
+ * On multiblog installations you must first allow themes to be activated and show
+ * up on the theme selection screen. This function will let the BuddyPress bundled
+ * themes show up on the root blog selection screen and bypass this step. It also
+ * means that the themes won't show for selection on other blogs.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_allow_default_theme( $themes ) {
+	global $bp, $wpdb;
+
+	if ( !is_super_admin() )
+		return $themes;
+
+	if ( $wpdb->blogid == bp_get_root_blog_id() ) {
+		$themes['bp-default'] = 1;
+	}
+
+	return $themes;
+}
+add_filter( 'allowed_themes', 'bp_core_allow_default_theme' );
+
+/**
+ * bp_core_filter_comments()
+ *
+ * Filter the blog post comments array and insert BuddyPress URLs for users.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_filter_comments( $comments, $post_id ) {
+	global $wpdb;
+
+	foreach( (array)$comments as $comment ) {
+		if ( $comment->user_id )
+			$user_ids[] = $comment->user_id;
+	}
+
+	if ( empty( $user_ids ) )
+		return $comments;
+
+	$user_ids = implode( ',', $user_ids );
+
+	if ( !$userdata = $wpdb->get_results( $wpdb->prepare( "SELECT ID as user_id, user_login, user_nicename FROM {$wpdb->users} WHERE ID IN ({$user_ids})" ) ) )
+		return $comments;
+
+	foreach( (array)$userdata as $user )
+		$users[$user->user_id] = bp_core_get_user_domain( $user->user_id, $user->user_nicename, $user->user_login );
+
+	foreach( (array)$comments as $i => $comment ) {
+		if ( !empty( $comment->user_id ) ) {
+			if ( !empty( $users[$comment->user_id] ) )
+				$comments[$i]->comment_author_url = $users[$comment->user_id];
+		}
+	}
+
+	return $comments;
+}
+add_filter( 'comments_array', 'bp_core_filter_comments', 10, 2 );
+
+/**
+ * bp_core_login_redirect()
+ *
+ * When a user logs in, always redirect them back to the previous page. NOT the admin area.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_login_redirect( $redirect_to ) {
+	global $bp, $wpdb;
+
+	// Don't mess with the redirect if this is not the root blog
+	if ( is_multisite() && $wpdb->blogid != bp_get_root_blog_id() )
+		return $redirect_to;
+
+	// If the redirect doesn't contain 'wp-admin', it's OK
+	if ( !empty( $_REQUEST['redirect_to'] ) && false === strpos( $_REQUEST['redirect_to'], 'wp-admin' ) )
+		return $redirect_to;
+
+	if ( false === strpos( wp_get_referer(), 'wp-login.php' ) && false === strpos( wp_get_referer(), 'activate' ) && empty( $_REQUEST['nr'] ) )
+		return wp_get_referer();
+
+	return bp_get_root_domain();
+}
+add_filter( 'login_redirect', 'bp_core_login_redirect' );
+
+/***
+ * bp_core_filter_user_welcome_email()
+ *
+ * Replace the generated password in the welcome email.
+ * This will not filter when the site admin registers a user.
+ *
+ * @uses locate_template To see if custom registration files exist
+ * @param string $welcome_email Complete email passed through WordPress
+ * @return string Filtered $welcome_email with 'PASSWORD' replaced by [User Set]
+ */
+function bp_core_filter_user_welcome_email( $welcome_email ) {
+	/* Don't touch the email if we don't have a custom registration template */
+	if ( '' == locate_template( array( 'registration/register.php' ), false ) && '' == locate_template( array( 'register.php' ), false ) )
+		return $welcome_email;
+
+	// [User Set] Replaces 'PASSWORD' in welcome email; Represents value set by user
+	return str_replace( 'PASSWORD', __( '[User Set]', 'buddypress' ), $welcome_email );
+}
+if ( !is_admin() && empty( $_GET['e'] ) )
+	add_filter( 'update_welcome_user_email', 'bp_core_filter_user_welcome_email' );
+
+/***
+ * bp_core_filter_blog_welcome_email()
+ *
+ * Replace the generated password in the welcome email.
+ * This will not filter when the site admin registers a user.
+ *
+ * @uses locate_template To see if custom registration files exist
+ * @param string $welcome_email Complete email passed through WordPress
+ * @param integer $blog_id ID of the blog user is joining
+ * @param integer $user_id ID of the user joining
+ * @param string $password Password of user
+ * @return string Filtered $welcome_email with $password replaced by [User Set]
+ */
+function bp_core_filter_blog_welcome_email( $welcome_email, $blog_id, $user_id, $password ) {
+	/* Don't touch the email if we don't have a custom registration template */
+	if ( '' == locate_template( array( 'registration/register.php' ), false ) && '' == locate_template( array( 'register.php' ), false ) )
+		return $welcome_email;
+
+	// [User Set] Replaces $password in welcome email; Represents value set by user
+	return str_replace( $password, __( '[User Set]', 'buddypress' ), $welcome_email );
+}
+if ( !is_admin() && empty( $_GET['e'] ) )
+	add_filter( 'update_welcome_email', 'bp_core_filter_blog_welcome_email', 10, 4 );
+
+// Notify user of signup success.
+function bp_core_activation_signup_blog_notification( $domain, $path, $title, $user, $user_email, $key, $meta ) {
+
+	// Send email with activation link.
+	$activate_url = bp_get_activation_page() ."?key=$key";
+	$activate_url = esc_url( $activate_url );
+
+	$admin_email = get_site_option( 'admin_email' );
+
+	if ( empty( $admin_email ) )
+		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
+
+	$from_name       = ( '' == get_site_option( 'site_name' ) ) ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
+	$message_headers = "MIME-Version: 1.0\n" . "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option( 'blog_charset' ) . "\"\n";
+	$message         = sprintf( __( "Thanks for registering! To complete the activation of your account and blog, please click the following link:\n\n%1\$s\n\n\n\nAfter you activate, you can visit your blog here:\n\n%2\$s", 'buddypress' ), $activate_url, esc_url( "http://{$domain}{$path}" ) );
+	$subject         = '[' . $from_name . '] ' . sprintf(__( 'Activate %s', 'buddypress' ), esc_url( 'http://' . $domain . $path ) );
+
+	// Send the message
+	$to              = apply_filters( 'bp_core_activation_signup_blog_notification_to',   $user_email, $domain, $path, $title, $user, $user_email, $key, $meta );
+	$subject         = apply_filters( 'bp_core_activation_signup_blog_notification_subject', $subject, $domain, $path, $title, $user, $user_email, $key, $meta );
+	$message         = apply_filters( 'bp_core_activation_signup_blog_notification_message', $message, $domain, $path, $title, $user, $user_email, $key, $meta );
+
+	wp_mail( $to, $subject, $message, $message_headers );
+
+	do_action( 'bp_core_sent_blog_signup_email', $admin_email, $subject, $message, $domain, $path, $title, $user, $user_email, $key, $meta );
+
+	// Return false to stop the original WPMU function from continuing
+	return false;
+}
+if ( !is_admin() )
+	add_filter( 'wpmu_signup_blog_notification', 'bp_core_activation_signup_blog_notification', 1, 7 );
+
+function bp_core_activation_signup_user_notification( $user, $user_email, $key, $meta ) {
+
+	$activate_url = bp_get_activation_page() . "?key=$key";
+	$activate_url = esc_url($activate_url);
+	$admin_email  = get_site_option( 'admin_email' );
+
+	if ( empty( $admin_email ) )
+		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
+
+	// If this is an admin generated activation, add a param to email the
+	// user login details
+	$email = is_admin() ? '&e=1' : '';
+
+	$from_name       = ( '' == get_site_option( 'site_name' ) ) ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
+	$message_headers = "MIME-Version: 1.0\n" . "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option( 'blog_charset' ) . "\"\n";
+	$message         = sprintf( __( "Thanks for registering! To complete the activation of your account please click the following link:\n\n%s\n\n", 'buddypress' ), $activate_url . $email );
+	$subject         = '[' . $from_name . '] ' . __( 'Activate Your Account', 'buddypress' );
+
+	// Send the message
+	$to      = apply_filters( 'bp_core_activation_signup_user_notification_to',   $user_email, $user, $user_email, $key, $meta );
+	$subject = apply_filters( 'bp_core_activation_signup_user_notification_subject', $subject, $user, $user_email, $key, $meta );
+	$message = apply_filters( 'bp_core_activation_signup_user_notification_message', $message, $user, $user_email, $key, $meta );
+
+	wp_mail( $to, $subject, $message, $message_headers );
+
+	do_action( 'bp_core_sent_user_signup_email', $admin_email, $subject, $message, $user, $user_email, $key, $meta );
+
+	// Return false to stop the original WPMU function from continuing
+	return false;
+}
+if ( !is_admin() || ( is_admin() && empty( $_POST['noconfirmation'] ) ) )
+	add_filter( 'wpmu_signup_user_notification', 'bp_core_activation_signup_user_notification', 1, 4 );
+
+/**
+ * Filter the page title for BuddyPress pages
+ *
+ * @global object $bp BuddyPress global settings
+ * @global unknown $post
+ * @global WP_Query $wp_query WordPress query object
+ * @param string $title Original page title
+ * @param string $sep How to separate the various items within the page title.
+ * @param string $seplocation Direction to display title
+ * @return string new page title
+ * @see wp_title()
+ * @since 1.5
+ */
+function bp_modify_page_title( $title, $sep, $seplocation ) {
+	global $bp, $post, $wp_query;
+
+	if ( bp_is_blog_page() )
+		return $title;
+
+	$title = '';
+
+	// Displayed user
+	if ( !empty( $bp->displayed_user->fullname ) && !is_404() ) {
+		// translators: "displayed user's name | canonicalised component name"
+		$title = strip_tags( sprintf( __( '%1$s | %2$s', 'buddypress' ), bp_get_displayed_user_fullname(), ucwords( bp_current_component() ) ) );
+
+	// A single group
+	} elseif ( bp_is_active( 'groups' ) && !empty( $bp->groups->current_group ) && !empty( $bp->bp_options_nav[$bp->groups->current_group->slug] ) ) {
+		$subnav = isset( $bp->bp_options_nav[$bp->groups->current_group->slug][$bp->current_action]['name'] ) ? $bp->bp_options_nav[$bp->groups->current_group->slug][$bp->current_action]['name'] : '';
+		// translators: "group name | group nav section name"
+		$title = sprintf( __( '%1$s | %2$s', 'buddypress' ), $bp->bp_options_title, $subnav );
+
+	// A single item from a component other than groups
+	} elseif ( bp_is_single_item() ) {
+		// translators: "root component name | component item name | component nav section name"
+		$title = sprintf( __( '%1$s | %2$s | %3$s', 'buddypress' ), bp_get_name_from_root_slug(), $bp->bp_options_title, $bp->bp_options_nav[$bp->current_component][$bp->current_action]['name'] );
+
+	// An index or directory
+	} elseif ( bp_is_directory() ) {
+		if ( !bp_current_component() )
+			$title = sprintf( __( '%s Directory', 'buddypress' ), bp_get_name_from_root_slug( bp_members_slug() ) );
+		else
+			$title = sprintf( __( '%s Directory', 'buddypress' ), bp_get_name_from_root_slug() );
+
+	// Sign up page
+	} elseif ( bp_is_register_page() ) {
+		$title = __( 'Create an Account', 'buddypress' );
+
+	// Activation page
+	} elseif ( bp_is_activation_page() ) {
+		$title = __( 'Activate your Account', 'buddypress' );
+
+	// Group creation page
+	} elseif ( bp_is_group_create() ) {
+		$title = __( 'Create a Group', 'buddypress' );
+
+	// Blog creation page
+	} elseif ( bp_is_create_blog() ) {
+		$title = __( 'Create a Site', 'buddypress' );
+	}
+
+	// Some BP nav items contain item counts. Remove them
+	$title = preg_replace( '|<span>[0-9]+</span>|', '', $title );
+
+	return apply_filters( 'bp_modify_page_title', $title . " $sep ", $title, $sep, $seplocation );
+}
+add_filter( 'wp_title', 'bp_modify_page_title', 10, 3 );
+add_filter( 'bp_modify_page_title', 'wptexturize'     );
+add_filter( 'bp_modify_page_title', 'convert_chars'   );
+add_filter( 'bp_modify_page_title', 'esc_html'        );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-functions.php	(revision 5057)
@@ -0,0 +1,1454 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Retrieve an option
+ *
+ * This is a wrapper for get_blog_option(), which in turn stores settings data (such as bp-pages)
+ * on the appropriate blog, given your current setup.
+ *
+ * The 'bp_get_option' filter is primarily for backward-compatibility.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_root_blog_id()
+ * @param str $option_name The option to be retrieved
+ * @param str $default Optional. Default value to be returned if the option isn't set
+ * @return mixed The value for the option
+ */
+function bp_get_option( $option_name, $default = '' ) {
+	$value = get_blog_option( bp_get_root_blog_id(), $option_name, $default );
+
+	return apply_filters( 'bp_get_option', $value );
+}
+
+/**
+ * Save an option
+ *
+ * This is a wrapper for update_blog_option(), which in turn stores settings data (such as bp-pages)
+ * on the appropriate blog, given your current setup.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_root_blog_id()
+ * @param str $option_name The option key to be set
+ * @param str $value The value to be set
+ */
+function bp_update_option( $option_name, $value ) {
+	update_blog_option( bp_get_root_blog_id(), $option_name, $value );
+}
+
+/**
+ * Delete an option
+ *
+ * This is a wrapper for delete_blog_option(), which in turn deletes settings data (such as
+ * bp-pages) on the appropriate blog, given your current setup.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_root_blog_id()
+ * @param str $option_name The option key to be set
+ */
+function bp_delete_option( $option_name ) {
+	delete_blog_option( bp_get_root_blog_id(), $option_name );
+}
+
+/**
+ * Allow filtering of database prefix. Intended for use in multinetwork installations.
+ *
+ * @global object $wpdb WordPress database object
+ * @return string Filtered database prefix
+ */
+function bp_core_get_table_prefix() {
+	global $wpdb;
+
+	return apply_filters( 'bp_core_get_table_prefix', $wpdb->base_prefix );
+}
+
+/**
+ * Fetches BP pages from the meta table, depending on setup
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @todo Remove the "Upgrading from an earlier version of BP pre-1.5" block. Temporary measure for
+ *       people running trunk installations. Leave for a version or two, then remove.
+ */
+function bp_core_get_directory_page_ids() {
+	$page_ids = bp_get_option( 'bp-pages' );
+
+  	// Upgrading from an earlier version of BP pre-1.5
+	if ( !isset( $page_ids['members'] ) && $ms_page_ids = get_site_option( 'bp-pages' ) ) {
+		$page_blog_id = bp_is_multiblog_mode() ? get_current_blog_id() : bp_get_root_blog_id();
+
+		if ( isset( $ms_page_ids[$page_blog_id] ) ) {
+			$page_ids = $ms_page_ids[$page_blog_id];
+
+			bp_update_option( 'bp-pages', $page_ids );
+		}
+  	}
+
+	// Ensure that empty indexes are unset. Should only matter in edge cases
+	if ( $page_ids && is_array( $page_ids ) ) {
+		foreach( (array)$page_ids as $component_name => $page_id ) {
+			if ( empty( $component_name ) || empty( $page_id ) ) {
+				unset( $page_ids[$component_name] );
+			}
+		}
+	}
+
+	return apply_filters( 'bp_core_get_directory_page_ids', $page_ids );
+}
+
+/**
+ * Stores BP pages in the meta table, depending on setup
+ *
+ * bp-pages data is stored in site_options (falls back to options on non-MS), in an array keyed by
+ * blog_id. This allows you to change your bp_get_root_blog_id() and go through the setup process again.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @param array $blog_page_ids The IDs of the WP pages corresponding to BP component directories
+ */
+function bp_core_update_directory_page_ids( $blog_page_ids ) {
+	bp_update_option( 'bp-pages', $blog_page_ids );
+}
+
+/**
+ * Get bp-pages names and slugs
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @return obj $pages Page names, IDs, and slugs
+ */
+function bp_core_get_directory_pages() {
+	global $wpdb, $bp;
+
+	// Set pages as standard class
+	$pages = new stdClass;
+
+	// Get pages and IDs
+	if ( $page_ids = bp_core_get_directory_page_ids() ) {
+
+		$posts_table_name = bp_is_multiblog_mode() ? $wpdb->get_blog_prefix( bp_get_root_blog_id() ) . 'posts' : $wpdb->posts;
+		$page_ids_sql     = implode( ',', (array)$page_ids );
+		$page_names       = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent, post_title FROM {$posts_table_name} WHERE ID IN ({$page_ids_sql}) AND post_status = 'publish' " ) );
+
+		foreach ( (array)$page_ids as $component_id => $page_id ) {
+			foreach ( (array)$page_names as $page_name ) {
+				if ( $page_name->ID == $page_id ) {
+					$pages->{$component_id}->name  = $page_name->post_name;
+					$pages->{$component_id}->id    = $page_name->ID;
+					$pages->{$component_id}->title = $page_name->post_title;
+					$slug[]                        = $page_name->post_name;
+
+					// Get the slug
+					while ( $page_name->post_parent != 0 ) {
+						$parent                 = $wpdb->get_results( $wpdb->prepare( "SELECT post_name, post_parent FROM {$posts_table_name} WHERE ID = %d", $page_name->post_parent ) );
+						$slug[]                 = $parent[0]->post_name;
+						$page_name->post_parent = $parent[0]->post_parent;
+					}
+
+					$pages->{$component_id}->slug = implode( '/', array_reverse( (array)$slug ) );
+				}
+
+				unset( $slug );
+			}
+		}
+	}
+
+	return apply_filters( 'bp_core_get_directory_pages', $pages );
+}
+
+/**
+ * Creates a default component slug from a WP page root_slug
+ *
+ * Since 1.5, BP components get their root_slug (the slug used immediately
+ * following the root domain) from the slug of a corresponding WP page.
+ *
+ * E.g. if your BP installation at example.com has its members page at
+ * example.com/community/people, $bp->members->root_slug will be 'community/people'.
+ *
+ * By default, this function creates a shorter version of the root_slug for
+ * use elsewhere in the URL, by returning the content after the final '/'
+ * in the root_slug ('people' in the example above).
+ *
+ * Filter on 'bp_core_component_slug_from_root_slug' to override this method
+ * in general, or define a specific component slug constant (e.g. BP_MEMBERS_SLUG)
+ * to override specific component slugs.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @param str $root_slug The root slug, which comes from $bp->pages->[component]->slug
+ * @return str $slug The short slug for use in the middle of URLs
+ */
+function bp_core_component_slug_from_root_slug( $root_slug ) {
+	$slug_chunks = explode( '/', $root_slug );
+ 	$slug        = array_pop( $slug_chunks );
+
+ 	return apply_filters( 'bp_core_component_slug_from_root_slug', $slug, $root_slug );
+}
+
+function bp_core_do_network_admin() {
+	$do_network_admin = false;
+
+	if ( is_multisite() && !bp_is_multiblog_mode() )
+		$do_network_admin = true;
+
+	return apply_filters( 'bp_core_do_network_admin', $do_network_admin );
+}
+
+function bp_core_admin_hook() {
+	$hook = bp_core_do_network_admin() ? 'network_admin_menu' : 'admin_menu';
+
+	return apply_filters( 'bp_core_admin_hook', $hook );
+}
+
+/**
+ * Initializes the wp-admin area "BuddyPress" menus and sub menus.
+ *
+ * @package BuddyPress Core
+ * @uses is_super_admin() returns true if the current user is a site admin, false if not
+ */
+function bp_core_admin_menu_init() {
+	if ( !is_super_admin() )
+		return false;
+
+	add_action( bp_core_admin_hook(), 'bp_core_add_admin_menu', 9 );
+
+	require ( BP_PLUGIN_DIR . '/bp-core/admin/bp-core-admin.php' );
+}
+add_action( 'bp_init', 'bp_core_admin_menu_init' );
+
+/**
+ * Adds the "BuddyPress" admin submenu item to the Site Admin tab.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @uses is_super_admin() returns true if the current user is a site admin, false if not
+ * @uses add_submenu_page() WP function to add a submenu item
+ */
+function bp_core_add_admin_menu() {
+	if ( !is_super_admin() )
+		return false;
+
+	// Don't add this version of the admin menu if a BP upgrade is in progress
+ 	// See bp_core_update_add_admin_menu()
+	if ( defined( 'BP_IS_UPGRADE' ) && BP_IS_UPGRADE )
+ 		return false;
+
+	$hooks = array();
+
+	// Add the administration tab under the "Site Admin" tab for site administrators
+	$hooks[] = add_menu_page( __( 'BuddyPress', 'buddypress' ), __( 'BuddyPress', 'buddypress' ), 'manage_options', 'bp-general-settings', 'bp_core_admin_component_setup', '' );
+	$hooks[] = add_submenu_page( 'bp-general-settings', __( 'Components', 'buddypress' ), __( 'Components', 'buddypress' ), 'manage_options', 'bp-general-settings', 'bp_core_admin_component_setup'  );
+	$hooks[] = add_submenu_page( 'bp-general-settings', __( 'Pages',      'buddypress' ), __( 'Pages',      'buddypress' ), 'manage_options', 'bp-page-settings',    'bp_core_admin_page_setup'       );
+	$hooks[] = add_submenu_page( 'bp-general-settings', __( 'Settings',   'buddypress' ), __( 'Settings',   'buddypress' ), 'manage_options', 'bp-settings',         'bp_core_admin_settings'         );
+
+	// Add a hook for css/js
+	foreach( $hooks as $hook )
+		add_action( "admin_print_styles-$hook", 'bp_core_add_admin_menu_styles' );
+}
+
+/**
+ * Print admin messages to admin_notices or network_admin_notices
+ *
+ * BuddyPress combines all its messages into a single notice, to avoid a preponderance of yellow
+ * boxes.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @uses is_super_admin() to check current user permissions before showing the notices
+ * @uses bp_is_root_blog()
+ */
+function bp_core_print_admin_notices() {
+	global $bp;
+
+	// Only the super admin should see messages
+	if ( !is_super_admin() )
+		return;
+
+	// On multisite installs, don't show on the Site Admin of a non-root blog, unless
+	// do_network_admin is overridden
+	if ( is_multisite() && bp_core_do_network_admin() && !bp_is_root_blog() )
+		return;
+
+	// Show the messages
+	if ( !empty( $bp->admin->notices ) ) {
+	?>
+		<div id="message" class="updated fade">
+			<?php foreach( $bp->admin->notices as $notice ) : ?>
+				<p><?php echo $notice ?></p>
+			<?php endforeach ?>
+		</div>
+	<?php
+	}
+}
+add_action( 'admin_notices', 'bp_core_print_admin_notices' );
+add_action( 'network_admin_notices', 'bp_core_print_admin_notices' );
+
+/**
+ * Add an admin notice to the BP queue
+ *
+ * Messages added with this function are displayed in BuddyPress's general purpose admin notices
+ * box. It is recommended that you hook this function to admin_init, so that your messages are
+ * loaded in time.
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @param string $notice The notice you are adding to the queue
+ */
+function bp_core_add_admin_notice( $notice ) {
+	global $bp;
+
+	if ( empty( $bp->admin->notices ) ) {
+		$bp->admin->notices = array();
+	}
+
+	$bp->admin->notices[] = $notice;
+}
+
+/**
+ * Verify that some BP prerequisites are set up properly, and notify the admin if not
+ *
+ * On every Dashboard page, this function checks the following:
+ *   - that pretty permalinks are enabled
+ *   - that a BP-compatible theme is activated
+ *   - that every BP component that needs a WP page for a directory has one
+ *   - that no WP page has multiple BP components associated with it
+ * The administrator will be shown a notice for each check that fails.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_activation_notice() {
+	global $wp_rewrite, $wpdb, $bp;
+
+	// Only the super admin gets warnings
+	if ( !is_super_admin() )
+		return;
+
+	// On multisite installs, don't load on a non-root blog, unless do_network_admin is
+	// overridden
+	if ( is_multisite() && bp_core_do_network_admin() && !bp_is_root_blog() )
+		return;
+
+	// Don't show these messages during setup or upgrade
+	if ( isset( $bp->maintenance_mode ) )
+		return;
+
+	/**
+	 * Check to make sure that the blog setup routine has run. This can't happen during the
+	 * wizard because of the order which the components are loaded. We check for multisite here
+	 * on the off chance that someone has activated the blogs component and then disabled MS
+	 */
+	if ( bp_is_active( 'blogs' ) ) {
+		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$bp->blogs->table_name}" ) );
+
+		if ( !$count )
+			bp_blogs_record_existing_blogs();
+	}
+
+	/**
+	 * Are pretty permalinks enabled?
+	 */
+	if ( isset( $_POST['permalink_structure'] ) )
+		return false;
+
+	if ( empty( $wp_rewrite->permalink_structure ) ) {
+		bp_core_add_admin_notice( sprintf( __( '<strong>BuddyPress is almost ready</strong>. You must <a href="%s">update your permalink structure</a> to something other than the default for it to work.', 'buddypress' ), admin_url( 'options-permalink.php' ) ) );
+	}
+
+	/**
+	 * Are you using a BP-compatible theme?
+	 */
+
+	// Get current theme info
+	$ct = current_theme_info();
+
+	// The best way to remove this notice is to add a "buddypress" tag to
+	// your active theme's CSS header.
+	if ( !defined( 'BP_SILENCE_THEME_NOTICE' ) && !in_array( 'buddypress', (array)$ct->tags ) ) {
+		bp_core_add_admin_notice( sprintf( __( "You'll need to <a href='%s'>activate a <strong>BuddyPress-compatible theme</strong></a> to take advantage of all of BuddyPress's features. We've bundled a default theme, but you can always <a href='%s'>install some other compatible themes</a> or <a href='%s'>update your existing WordPress theme</a>.", 'buddypress' ), admin_url( 'themes.php' ), network_admin_url( 'theme-install.php?type=tag&s=buddypress&tab=search' ), network_admin_url( 'plugin-install.php?type=term&tab=search&s=%22bp-template-pack%22' ) ) );
+	}
+
+	/**
+	 * Check for orphaned BP components (BP component is enabled, no WP page exists)
+	 */
+
+	$orphaned_components = array();
+	$wp_page_components  = array();
+
+	// Only components with 'has_directory' require a WP page to function
+	foreach( $bp->loaded_components as $component_id => $is_active ) {
+		if ( !empty( $bp->{$component_id}->has_directory ) ) {
+			$wp_page_components[] = array(
+				'id'   => $component_id,
+				'name' => isset( $bp->{$component_id}->name ) ? $bp->{$component_id}->name : ucwords( $bp->{$component_id}->id )
+			);
+		}
+	}
+
+	// Activate and Register are special cases. They are not components but they need WP pages.
+	// If user registration is disabled, we can skip this step.
+	if ( isset( $bp->site_options['registration'] ) && ( 'user' == $bp->site_options['registration'] || ( 'all' == $bp->site_options['registration'] ) ) ) {
+		$wp_page_components[] = array(
+			'id'   => 'activate',
+			'name' => __( 'Activate', 'buddypress' )
+		);
+
+		$wp_page_components[] = array(
+			'id'   => 'register',
+			'name' => __( 'Register', 'buddypress' )
+		);
+	}
+
+	foreach( $wp_page_components as $component ) {
+		if ( !isset( $bp->pages->{$component['id']} ) ) {
+			$orphaned_components[] = $component['name'];
+		}
+	}
+
+	if ( !empty( $orphaned_components ) ) {
+		$admin_url = bp_get_admin_url( add_query_arg( array( 'page' => 'bp-page-settings' ), 'admin.php' ) );
+		$notice    = sprintf( __( 'The following active BuddyPress Components do not have associated WordPress Pages: %2$s. <a href="%1$s" class="button-secondary">Repair</a>', 'buddypress' ), $admin_url, '<strong>' . implode( '</strong>, <strong>', $orphaned_components ) . '</strong>' );
+
+		bp_core_add_admin_notice( $notice );
+	}
+
+	/**
+	 * BP components cannot share a single WP page. Check for duplicate assignments, and post
+	 * a message if found.
+	 */
+	$dupe_names = array();
+	$page_ids   = (array)bp_core_get_directory_page_ids();
+	$dupes      = array_diff_assoc( $page_ids, array_unique( $page_ids ) );
+
+	if ( !empty( $dupes ) ) {
+		foreach( $dupes as $dupe_component => $dupe_id ) {
+			$dupe_names[] = $bp->pages->{$dupe_component}->title;
+		}
+
+		// Make sure that there are no duplicate duplicates :)
+		$dupe_names = array_unique( $dupe_names );
+	}
+
+	// If there are duplicates, post a message about them
+	if ( !empty( $dupe_names ) ) {
+		$admin_url = bp_get_admin_url( add_query_arg( array( 'page' => 'bp-page-settings' ), 'admin.php' ) );
+		$notice    = sprintf( __( 'Each BuddyPress Component needs its own WordPress page. The following WordPress Pages have more than one component associated with them: %2$s. <a href="%1$s" class="button-secondary">Repair</a>', 'buddypress' ), $admin_url, '<strong>' . implode( '</strong>, <strong>', $dupe_names ) . '</strong>' );
+
+		bp_core_add_admin_notice( $notice );
+	}
+}
+add_action( 'admin_init', 'bp_core_activation_notice' );
+
+/**
+ * Returns the domain for the root blog.
+ * eg: http://domain.com/ OR https://domain.com
+ *
+ * @package BuddyPress Core
+ * @uses get_blog_option() WordPress function to fetch blog meta.
+ * @return $domain The domain URL for the blog.
+ */
+function bp_core_get_root_domain() {
+	global $wpdb;
+
+	$domain = get_home_url( bp_get_root_blog_id() );
+
+	return apply_filters( 'bp_core_get_root_domain', $domain );
+}
+
+/**
+ * Get the current GMT time to save into the DB
+ *
+ * @package BuddyPress Core
+ * @since 1.2.6
+ */
+function bp_core_current_time( $gmt = true ) {
+	// Get current time in MYSQL format
+	$current_time = current_time( 'mysql', $gmt );
+
+	return apply_filters( 'bp_core_current_time', $current_time );
+}
+
+/**
+ * Adds a feedback (error/success) message to the WP cookie so it can be
+ * displayed after the page reloads.
+ *
+ * @package BuddyPress Core
+ *
+ * @global obj $bp
+ * @param str $message Feedback to give to user
+ * @param str $type updated|success|error|warning
+ */
+function bp_core_add_message( $message, $type = '' ) {
+	global $bp;
+
+	// Success is the default
+	if ( empty( $type ) )
+		$type = 'success';
+
+	// Send the values to the cookie for page reload display
+	@setcookie( 'bp-message',      $message, time() + 60 * 60 * 24, COOKIEPATH );
+	@setcookie( 'bp-message-type', $type,    time() + 60 * 60 * 24, COOKIEPATH );
+
+	/***
+	 * Send the values to the $bp global so we can still output messages
+	 * without a page reload
+	 */
+	$bp->template_message      = $message;
+	$bp->template_message_type = $type;
+}
+
+/**
+ * Checks if there is a feedback message in the WP cookie, if so, adds a
+ * "template_notices" action so that the message can be parsed into the template
+ * and displayed to the user.
+ *
+ * After the message is displayed, it removes the message vars from the cookie
+ * so that the message is not shown to the user multiple times.
+ *
+ * @package BuddyPress Core
+ * @global $bp_message The message text
+ * @global $bp_message_type The type of message (error/success)
+ * @uses setcookie() Sets a cookie value for the user.
+ */
+function bp_core_setup_message() {
+	global $bp;
+
+	if ( empty( $bp->template_message ) && isset( $_COOKIE['bp-message'] ) )
+		$bp->template_message = $_COOKIE['bp-message'];
+
+	if ( empty( $bp->template_message_type ) && isset( $_COOKIE['bp-message-type'] ) )
+		$bp->template_message_type = $_COOKIE['bp-message-type'];
+
+	add_action( 'template_notices', 'bp_core_render_message' );
+
+	@setcookie( 'bp-message',      false, time() - 1000, COOKIEPATH );
+	@setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH );
+}
+add_action( 'bp_actions', 'bp_core_setup_message', 5 );
+
+/**
+ * Renders a feedback message (either error or success message) to the theme template.
+ * The hook action 'template_notices' is used to call this function, it is not called directly.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_render_message() {
+	global $bp;
+
+	if ( isset( $bp->template_message ) && $bp->template_message ) :
+		$type = ( 'success' == $bp->template_message_type ) ? 'updated' : 'error'; ?>
+
+		<div id="message" class="<?php echo $type; ?>">
+			<p><?php echo stripslashes( esc_attr( $bp->template_message ) ); ?></p>
+		</div>
+
+	<?php
+
+		do_action( 'bp_core_render_message' );
+
+	endif;
+}
+
+/**
+ * Format numbers the BuddyPress way
+ *
+ * @param str $number
+ * @param bool $decimals
+ * @return str
+ */
+function bp_core_number_format( $number, $decimals = false ) {
+	// Check we actually have a number first.
+	if ( empty( $number ) )
+		return $number;
+
+	return apply_filters( 'bp_core_number_format', number_format( $number, $decimals ), $number, $decimals );
+}
+
+/**
+ * Based on function created by Dunstan Orchard - http://1976design.com
+ *
+ * This function will return an English representation of the time elapsed
+ * since a given date.
+ * eg: 2 hours and 50 minutes
+ * eg: 4 days
+ * eg: 4 weeks and 6 days
+ *
+ * @package BuddyPress Core
+ * @param $older_date int Unix timestamp of date you want to calculate the time since for
+ * @param $newer_date int Unix timestamp of date to compare older date to. Default false (current time).
+ * @return str The time since.
+ */
+function bp_core_time_since( $older_date, $newer_date = false ) {
+
+	// Setup the strings
+	$unknown_text   = apply_filters( 'bp_core_time_since_unknown_text',   __( 'sometime',  'buddypress' ) );
+	$right_now_text = apply_filters( 'bp_core_time_since_right_now_text', __( 'right now', 'buddypress' ) );
+	$ago_text       = apply_filters( 'bp_core_time_since_ago_text',       __( '%s ago',    'buddypress' ) );
+
+	// array of time period chunks
+	$chunks = array(
+		array( 60 * 60 * 24 * 365 , __( 'year',   'buddypress' ), __( 'years',   'buddypress' ) ),
+		array( 60 * 60 * 24 * 30 ,  __( 'month',  'buddypress' ), __( 'months',  'buddypress' ) ),
+		array( 60 * 60 * 24 * 7,    __( 'week',   'buddypress' ), __( 'weeks',   'buddypress' ) ),
+		array( 60 * 60 * 24 ,       __( 'day',    'buddypress' ), __( 'days',    'buddypress' ) ),
+		array( 60 * 60 ,            __( 'hour',   'buddypress' ), __( 'hours',   'buddypress' ) ),
+		array( 60 ,                 __( 'minute', 'buddypress' ), __( 'minutes', 'buddypress' ) ),
+		array( 1,                   __( 'second', 'buddypress' ), __( 'seconds', 'buddypress' ) )
+	);
+
+	if ( !empty( $older_date ) && !is_numeric( $older_date ) ) {
+		$time_chunks = explode( ':', str_replace( ' ', ':', $older_date ) );
+		$date_chunks = explode( '-', str_replace( ' ', '-', $older_date ) );
+		$older_date  = gmmktime( (int)$time_chunks[1], (int)$time_chunks[2], (int)$time_chunks[3], (int)$date_chunks[1], (int)$date_chunks[2], (int)$date_chunks[0] );
+	}
+
+	/**
+	 * $newer_date will equal false if we want to know the time elapsed between
+	 * a date and the current time. $newer_date will have a value if we want to
+	 * work out time elapsed between two known dates.
+	 */
+	$newer_date = ( !$newer_date ) ? strtotime( bp_core_current_time() ) : $newer_date;
+
+	// Difference in seconds
+	$since = $newer_date - $older_date;
+
+	// Something went wrong with date calculation and we ended up with a negative date.
+	if ( 0 > $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)
+			if ( ( $count = floor($since / $seconds) ) != 0 ) {
+				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];
+
+				if ( ( $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ) ) != 0 ) {
+					// Add to output var
+					$output .= ( 1 == $count2 ) ? _x( ',', 'Separator in time since', 'buddypress' ) . ' 1 '. $chunks[$i + 1][1] : _x( ',', 'Separator in time since', 'buddypress' ) . ' ' . $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 $output;
+}
+
+/**
+ * Record user activity to the database. Many functions use a "last active" feature to
+ * show the length of time since the user was last active.
+ * This function will update that time as a usermeta setting for the user every 5 minutes.
+ *
+ * @package BuddyPress Core
+ * @global $userdata WordPress user data for the current logged in user.
+ * @uses bp_update_user_meta() BP function to update user metadata in the usermeta table.
+ */
+function bp_core_record_activity() {
+	global $bp;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+	$user_id = $bp->loggedin_user->id;
+
+	if ( bp_core_is_user_spammer( $user_id ) || bp_core_is_user_deleted( $user_id ) )
+		return false;
+
+	$activity = bp_get_user_meta( $user_id, 'last_activity', true );
+
+	if ( !is_numeric( $activity ) )
+		$activity = strtotime( $activity );
+
+	// Get current time
+	$current_time = bp_core_current_time();
+
+	if ( empty( $activity ) || strtotime( $current_time ) >= strtotime( '+5 minutes', $activity ) )
+		bp_update_user_meta( $user_id, 'last_activity', $current_time );
+}
+add_action( 'wp_head', 'bp_core_record_activity' );
+
+/**
+ * Formats last activity based on time since date given.
+ *
+ * @package BuddyPress Core
+ * @param last_activity_date The date of last activity.
+ * @param $before The text to prepend to the activity time since figure.
+ * @param $after The text to append to the activity time since figure.
+ * @uses bp_core_time_since() This function will return an English representation of the time elapsed.
+ */
+function bp_core_get_last_activity( $last_activity_date, $string ) {
+	if ( !$last_activity_date || empty( $last_activity_date ) )
+		$last_active = __( 'not recently active', 'buddypress' );
+	else
+		$last_active = sprintf( $string, bp_core_time_since( $last_activity_date ) );
+
+	return apply_filters( 'bp_core_get_last_activity', $last_active, $last_activity_date, $string );
+}
+
+/**
+ * Get the path of of the current site.
+ *
+ * @package BuddyPress Core
+ *
+ * @global $bp $bp
+ * @global object $current_site
+ * @return string
+ */
+function bp_core_get_site_path() {
+	global $bp, $current_site;
+
+	if ( is_multisite() )
+		$site_path = $current_site->path;
+	else {
+		$site_path = (array) explode( '/', home_url() );
+
+		if ( count( $site_path ) < 2 )
+			$site_path = '/';
+		else {
+			// Unset the first three segments (http(s)://domain.com part)
+			unset( $site_path[0] );
+			unset( $site_path[1] );
+			unset( $site_path[2] );
+
+			if ( !count( $site_path ) )
+				$site_path = '/';
+			else
+				$site_path = '/' . implode( '/', $site_path ) . '/';
+		}
+	}
+
+	return apply_filters( 'bp_core_get_site_path', $site_path );
+}
+
+/**
+ * Performs a status safe wp_redirect() that is compatible with bp_catch_uri()
+ *
+ * @package BuddyPress Core
+ * @global $bp_no_status_set Makes sure that there are no conflicts with status_header() called in bp_core_do_catch_uri()
+ * @uses get_themes()
+ * @return An array containing all of the themes.
+ */
+function bp_core_redirect( $location, $status = 302 ) {
+	global $bp_no_status_set;
+
+	// Make sure we don't call status_header() in bp_core_do_catch_uri()
+	// as this conflicts with wp_redirect()
+	$bp_no_status_set = true;
+
+	wp_redirect( $location, $status );
+	die;
+}
+
+/**
+ * Returns the referrer URL without the http(s)://
+ *
+ * @package BuddyPress Core
+ * @return The referrer URL
+ */
+function bp_core_referrer() {
+	$referer = explode( '/', wp_get_referer() );
+	unset( $referer[0], $referer[1], $referer[2] );
+	return implode( '/', $referer );
+}
+
+/**
+ * Adds illegal names to WP so that root components will not conflict with
+ * blog names on a subdirectory installation.
+ *
+ * For example, it would stop someone creating a blog with the slug "groups".
+ */
+function bp_core_add_illegal_names() {
+	update_site_option( 'illegal_names', get_site_option( 'illegal_names' ), array() );
+}
+
+/**
+ * A javascript free implementation of the search functions in BuddyPress
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @param string $slug The slug to redirect to for searching.
+ */
+function bp_core_action_search_site( $slug = '' ) {
+	global $bp;
+
+	if ( !bp_is_current_component( bp_get_search_slug() ) )
+		return;
+
+	if ( empty( $_POST['search-terms'] ) ) {
+		bp_core_redirect( bp_get_root_domain() );
+		return;
+	}
+
+	$search_terms = stripslashes( $_POST['search-terms'] );
+	$search_which = !empty( $_POST['search-which'] ) ? $_POST['search-which'] : '';
+	$query_string = '/?s=';
+
+	if ( empty( $slug ) ) {
+		switch ( $search_which ) {
+			case 'posts':
+				$slug = '';
+				$var  = '/?s=';
+				break;
+
+			case 'blogs':
+				$slug = bp_is_active( 'blogs' )  ? bp_get_blogs_root_slug()  : '';
+				break;
+
+			case 'forums':
+				$slug = bp_is_active( 'forums' ) ? bp_get_groups_root_slug() : '';
+				$query_string = '/?fs=';
+				break;
+
+			case 'groups':
+				$slug = bp_is_active( 'groups' ) ? bp_get_groups_root_slug() : '';
+				break;
+
+			case 'members':
+			default:
+				$slug = bp_get_members_root_slug();
+				break;
+		}
+
+		if ( empty( $slug ) && 'posts' != $search_which ) {
+			bp_core_redirect( bp_get_root_domain() );
+			return;
+		}
+	}
+
+	bp_core_redirect( apply_filters( 'bp_core_search_site', home_url( $slug . $query_string . urlencode( $search_terms ) ), $search_terms ) );
+}
+add_action( 'bp_init', 'bp_core_action_search_site', 7 );
+
+/**
+ * Prints the generation time in the footer of the site.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_print_generation_time() {
+?>
+
+<!-- Generated in <?php timer_stop(1); ?> seconds. (<?php echo get_num_queries(); ?> q) -->
+
+	<?php
+}
+add_action( 'wp_footer', 'bp_core_print_generation_time' );
+
+/**
+ * Load the buddypress translation file for current language
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_load_buddypress_textdomain() {
+	$locale        = apply_filters( 'buddypress_locale', get_locale() );
+	$mofile        = sprintf( 'buddypress-%s.mo', $locale );
+	$mofile_global = WP_LANG_DIR . '/' . $mofile;
+	$mofile_local  = BP_PLUGIN_DIR . '/bp-languages/' . $mofile;
+
+	if ( file_exists( $mofile_global ) )
+		return load_textdomain( 'buddypress', $mofile_global );
+	elseif ( file_exists( $mofile_local ) )
+		return load_textdomain( 'buddypress', $mofile_local );
+	else
+		return false;
+}
+add_action ( 'bp_init', 'bp_core_load_buddypress_textdomain', 2 );
+
+function bp_core_add_ajax_hook() {
+	// Theme only, we already have the wp_ajax_ hook firing in wp-admin
+	if ( !defined( 'WP_ADMIN' ) && isset( $_REQUEST['action'] ) )
+		do_action( 'wp_ajax_' . $_REQUEST['action'] );
+}
+add_action( 'bp_init', 'bp_core_add_ajax_hook' );
+
+/**
+ * Initializes {@link BP_Embed} after everything is loaded.
+ *
+ * @global object $bp BuddyPress global settings
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_embed_init() {
+	global $bp;
+
+	if ( empty( $bp->embed ) )
+		$bp->embed = new BP_Embed();
+}
+add_action( 'bp_init', 'bp_embed_init', 9 );
+
+/**
+ * When switching from single to multisite we need to copy blog options to
+ * site options.
+ *
+ * @package BuddyPress Core
+ * @todo Does this need to be here anymore after the introduction of bp_get_option etc?
+ */
+function bp_core_activate_site_options( $keys = array() ) {
+	global $bp;
+
+	if ( !empty( $keys ) && is_array( $keys ) ) {
+		$errors = false;
+
+		foreach ( $keys as $key => $default ) {
+			if ( empty( $bp->site_options[ $key ] ) ) {
+				$bp->site_options[ $key ] = bp_get_option( $key, $default );
+
+				if ( !bp_update_option( $key, $bp->site_options[ $key ] ) )
+					$errors = true;
+			}
+		}
+
+		if ( empty( $errors ) )
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * BuddyPress uses common options to store configuration settings. Many of these
+ * settings are needed at run time. Instead of fetching them all and adding many
+ * initial queries to each page load, let's fetch them all in one go.
+ *
+ * @package BuddyPress Core
+ * @todo Use settings API and audit these methods
+ */
+function bp_core_get_root_options() {
+	global $wpdb;
+
+	// These options come from the root blog options table
+	$root_blog_options = apply_filters( 'bp_core_site_options', array(
+
+		// BuddyPress core settings
+		'bp-deactivated-components'       => serialize( array( ) ),
+		'bp-blogs-first-install'          => '0',
+		'bp-disable-blog-forum-comments'  => '0',
+		'bp-xprofile-base-group-name'     => 'Base',
+		'bp-xprofile-fullname-field-name' => 'Name',
+		'bp-disable-profile-sync'         => '0',
+		'bp-disable-avatar-uploads'       => '0',
+		'bp-disable-account-deletion'     => '0',
+		'bp-disable-blogforum-comments'   => '0',
+		'bb-config-location'              => ABSPATH,
+		'hide-loggedout-adminbar'         => '0',
+
+		// Useful WordPress settings
+		'registration'                    => '0',
+		'avatar_default'                  => 'mysteryman'
+	) );
+
+	$root_blog_option_keys  = array_keys( $root_blog_options );
+	$blog_options_keys      = "'" . join( "', '", (array) $root_blog_option_keys ) . "'";
+	$blog_options_query     = $wpdb->prepare( "SELECT option_name AS name, option_value AS value FROM {$wpdb->options} WHERE option_name IN ( {$blog_options_keys} )" );
+	$root_blog_options_meta = $wpdb->get_results( $blog_options_query );
+
+	// On Multisite installations, some options must always be fetched from sitemeta
+	if ( is_multisite() ) {
+		$network_options = apply_filters( 'bp_core_network_options', array(
+			'tags_blog_id'    => '0',
+			'registration'    => '0',
+			'fileupload_maxk' => '1500'
+		) );
+
+		$current_site           = get_current_site();
+		$network_option_keys    = array_keys( $network_options );
+		$sitemeta_options_keys  = "'" . join( "', '", (array) $network_option_keys ) . "'";
+		$sitemeta_options_query = $wpdb->prepare( "SELECT meta_key AS name, meta_value AS value FROM {$wpdb->sitemeta} WHERE meta_key IN ( {$sitemeta_options_keys} ) AND site_id = %d", $current_site->id );
+		$network_options_meta   = $wpdb->get_results( $sitemeta_options_query );
+
+		// Sitemeta comes second in the merge, so that network 'registration' value wins
+		$root_blog_options_meta = array_merge( $root_blog_options_meta, $network_options_meta );
+	}
+
+	// Missing some options, so do some one-time fixing
+	if ( empty( $root_blog_options_meta ) || ( count( $root_blog_options_meta ) < count( $root_blog_option_keys ) ) ) {
+
+	// Unset the query - We'll be resetting it soon
+	unset( $root_blog_options_meta );
+
+	// Loop through options
+	foreach ( $root_blog_options as $old_meta_key => $old_meta_default ) {
+		// Clear out the value from the last time around
+		unset( $old_meta_value );
+
+		// Get old site option
+		if ( is_multisite() )
+			$old_meta_value = get_site_option( $old_meta_key );
+
+		// No site option so look in root blog
+		if ( empty( $old_meta_value ) )
+			$old_meta_value = bp_get_option( $old_meta_key, $old_meta_default );
+
+		// Update the root blog option
+		bp_update_option( $old_meta_key, $old_meta_value );
+
+		// Update the global array
+		$root_blog_options_meta[$old_meta_key] = $old_meta_value;
+	}
+
+	// We're all matched up
+	} else {
+		// Loop through our results and make them usable
+		foreach ( $root_blog_options_meta as $root_blog_option )
+			$root_blog_options[$root_blog_option->name] = $root_blog_option->value;
+
+		// Copy the options no the return val
+		$root_blog_options_meta = $root_blog_options;
+
+		// Clean up our temporary copy
+		unset( $root_blog_options );
+	}
+
+	return apply_filters( 'bp_core_get_root_options', $root_blog_options_meta );
+}
+
+/**
+ * This function originally let plugins add support for pages in the root of the install.
+ * These root level pages are now handled by actual WordPress pages and this function is now
+ * a convenience for compatibility with the new method.
+ *
+ * @global $bp BuddyPress global settings
+ * @param $slug str The slug of the component
+ */
+function bp_core_add_root_component( $slug ) {
+	global $bp;
+
+	if ( empty( $bp->pages ) )
+		$bp->pages = bp_core_get_directory_pages();
+
+	$match = false;
+
+	// Check if the slug is registered in the $bp->pages global
+	foreach ( (array)$bp->pages as $key => $page ) {
+		if ( $key == $slug || $page->slug == $slug )
+			$match = true;
+	}
+
+	// If there was no match, add a page for this root component
+	if ( empty( $match ) ) {
+		$bp->add_root[] = $slug;
+	}
+
+	// Make sure that this component is registered as requiring a top-level directory
+	if ( isset( $bp->{$slug} ) ) {
+		$bp->loaded_components[$bp->{$slug}->slug] = $bp->{$slug}->id;
+		$bp->{$slug}->has_directory = true;
+	}
+}
+
+function bp_core_create_root_component_page() {
+	global $bp;
+
+	$new_page_ids = array();
+
+	foreach ( (array)$bp->add_root as $slug )
+		$new_page_ids[$slug] = wp_insert_post( array( 'comment_status' => 'closed', 'ping_status' => 'closed', 'post_title' => ucwords( $slug ), 'post_status' => 'publish', 'post_type' => 'page' ) );
+
+	$page_ids = array_merge( (array) $new_page_ids, (array) bp_core_get_directory_page_ids() );
+	bp_core_update_directory_page_ids( $page_ids );
+}
+
+/**
+ * Is this the root blog ID?
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param int $blog_id Optional. Defaults to the current blog id.
+ * @return bool $is_root_blog Returns true if this is bp_get_root_blog_id().
+ */
+function bp_is_root_blog( $blog_id = 0 ) {
+	// Assume false
+	$is_root_blog = false;
+
+	// Use current blog if no ID is passed
+	if ( empty( $blog_id ) )
+		$blog_id = get_current_blog_id();
+
+	// Compare to root blog ID
+	if ( $blog_id == bp_get_root_blog_id() )
+		$is_root_blog = true;
+
+	return apply_filters( 'bp_is_root_blog', (bool) $is_root_blog );
+}
+
+/**
+ * Is this bp_get_root_blog_id()?
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param int $blog_id Optional. Defaults to the current blog id.
+ * @return bool $is_root_blog Returns true if this is bp_get_root_blog_id().
+ */
+function bp_get_root_blog_id( $blog_id = false ) {
+
+	// Define on which blog ID BuddyPress should run
+	if ( !defined( 'BP_ROOT_BLOG' ) ) {
+
+		// Root blog is the main site on this network
+		if ( is_multisite() && !bp_is_multiblog_mode() ) {
+			$current_site = get_current_site();
+			$root_blog_id = $current_site->blog_id;
+
+		// Root blog is whatever the current site is (could be any site on the network)
+		} elseif ( is_multisite() && bp_is_multiblog_mode() ) {
+			$root_blog_id = get_current_blog_id();
+
+		// Root blog is the only blog on this network
+		} elseif( !is_multisite() ) {
+			$root_blog_id = 1;
+		}
+
+		define( 'BP_ROOT_BLOG', $root_blog_id );
+
+	// Root blog is defined
+	} else {
+		$root_blog_id = BP_ROOT_BLOG;
+	}
+
+	return apply_filters( 'bp_get_root_blog_id', (int) $root_blog_id );
+}
+
+/**
+ * Get the meta_key for a given piece of user metadata
+ *
+ * BuddyPress stores a number of pieces of userdata in the WordPress central usermeta table. In
+ * order to allow plugins to enable multiple instances of BuddyPress on a single WP installation,
+ * BP's usermeta keys are filtered with this function, so that they can be altered on the fly.
+ *
+ * Plugin authors should use BP's _user_meta() functions, which bakes in bp_get_user_meta_key().
+ *    $last_active = bp_get_user_meta( $user_id, 'last_activity', true );
+ * If you have to use WP's _user_meta() functions for some reason, you should use this function, eg
+ *    $last_active = get_user_meta( $user_id, bp_get_user_meta_key( 'last_activity' ), true );
+ * If using the WP functions, do not not hardcode your meta keys.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses apply_filters() Filter bp_get_user_meta_key to modify keys individually
+ * @param str $key
+ * @return str $key
+ */
+function bp_get_user_meta_key( $key = false ) {
+	return apply_filters( 'bp_get_user_meta_key', $key );
+}
+
+/**
+ * Get a piece of usermeta
+ *
+ * This is a wrapper for get_user_meta() that allows for easy use of bp_get_user_meta_key(), thereby
+ * increasing compatibility with non-standard BP setups.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_user_meta_key() For a filterable version of the meta key
+ * @uses get_user_meta() See get_user_meta() docs for more details on parameters
+ * @param int $user_id The id of the user whose meta you're fetching
+ * @param string $key The meta key to retrieve.
+ * @param bool $single Whether to return a single value.
+ * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
+ *  is true.
+ */
+function bp_get_user_meta( $user_id, $key, $single = false ) {
+	return get_user_meta( $user_id, bp_get_user_meta_key( $key ), $single );
+}
+
+/**
+ * Update a piece of usermeta
+ *
+ * This is a wrapper for update_user_meta() that allows for easy use of bp_get_user_meta_key(),
+ * thereby increasing compatibility with non-standard BP setups.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_user_meta_key() For a filterable version of the meta key
+ * @uses update_user_meta() See update_user_meta() docs for more details on parameters
+ * @param int $user_id The id of the user whose meta you're setting
+ * @param string $key The meta key to set.
+ * @param mixed $value Metadata value.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * @return bool False on failure, true if success.
+ */
+function bp_update_user_meta( $user_id, $key, $value, $prev_value = '' ) {
+	return update_user_meta( $user_id, bp_get_user_meta_key( $key ), $value, $prev_value );
+}
+
+/**
+ * Delete a piece of usermeta
+ *
+ * This is a wrapper for delete_user_meta() that allows for easy use of bp_get_user_meta_key(),
+ * thereby increasing compatibility with non-standard BP setups.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_user_meta_key() For a filterable version of the meta key
+ * @uses delete_user_meta() See delete_user_meta() docs for more details on parameters
+ * @param int $user_id The id of the user whose meta you're deleting
+ * @param string $key The meta key to delete.
+ * @param mixed $value Optional. Metadata value.
+ * @return bool False for failure. True for success.
+ */
+function bp_delete_user_meta( $user_id, $key, $value = '' ) {
+	return delete_user_meta( $user_id, bp_get_user_meta_key( $key ), $value );
+}
+
+/**
+ * Are we running username compatibility mode?
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses apply_filters() Filter 'bp_is_username_compatibility_mode' to alter
+ * @return bool False when compatibility mode is disabled (default); true when enabled
+ */
+function bp_is_username_compatibility_mode() {
+	return apply_filters( 'bp_is_username_compatibility_mode', defined( 'BP_ENABLE_USERNAME_COMPATIBILITY_MODE' ) && BP_ENABLE_USERNAME_COMPATIBILITY_MODE );
+}
+
+/**
+ * Are we running multiblog mode?
+ *
+ * Note that BP_ENABLE_MULTIBLOG is different from (but dependent on) WP Multisite. "Multiblog" is
+ * a BP setup that allows BP content to be viewed in the theme, and with the URL, of every blog
+ * on the network. Thus, instead of having all 'boonebgorges' links go to
+ *   http://example.com/members/boonebgorges
+ * on the root blog, each blog will have its own version of the same profile content, eg
+ *   http://site2.example.com/members/boonebgorges (for subdomains)
+ *   http://example.com/site2/members/boonebgorges (for subdirectories)
+ *
+ * Multiblog mode is disabled by default, meaning that all BP content must be viewed on the root
+ * blog.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses apply_filters() Filter 'bp_is_multiblog_mode' to alter
+ * @return bool False when multiblog mode is disabled (default); true when enabled
+ */
+function bp_is_multiblog_mode() {
+	return apply_filters( 'bp_is_multiblog_mode', is_multisite() && defined( 'BP_ENABLE_MULTIBLOG' ) && BP_ENABLE_MULTIBLOG );
+}
+
+/**
+ * Should we use the WP admin bar?
+ *
+ * The WP Admin Bar, introduced in WP 3.1, is fully supported in BuddyPress as of BP 1.5.
+ *
+ * For the BP 1.5 development cycle, the BuddyBar will remain the default navigation for BP
+ * installations. In the future, this behavior will be changed, so that the WP Admin Bar is the
+ * default.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses apply_filters() Filter 'bp_use_wp_admin_bar' to alter
+ * @return bool False when WP Admin Bar support is disabled (default); true when enabled
+ */
+function bp_use_wp_admin_bar() {
+	return apply_filters( 'bp_use_wp_admin_bar', defined( 'BP_USE_WP_ADMIN_BAR' ) && BP_USE_WP_ADMIN_BAR );
+}
+
+/**
+ * Are oembeds allowed in activity items?
+ *
+ * @return bool False when activity embed support is disabled; true when enabled (default)
+ * @since 1.5
+ */
+function bp_use_embed_in_activity() {
+	return apply_filters( 'bp_use_oembed_in_activity', !defined( 'BP_EMBED_DISABLE_ACTIVITY' ) || !BP_EMBED_DISABLE_ACTIVITY );
+}
+
+/**
+ * Are oembeds allwoed in activity replies?
+ *
+ * @return bool False when activity replies embed support is disabled; true when enabled (default)
+ * @since 1.5
+ */
+function bp_use_embed_in_activity_replies() {
+	return apply_filters( 'bp_use_embed_in_activity_replies', !defined( 'BP_EMBED_DISABLE_ACTIVITY_REPLIES' ) || !BP_EMBED_DISABLE_ACTIVITY_REPLIES );
+}
+
+/**
+ * Are oembeds allowed in forum posts?
+ *
+ * @return bool False when form post embed support is disabled; true when enabled (default)
+ * @since 1.5
+ */
+function bp_use_embed_in_forum_posts() {
+	return apply_filters( 'bp_use_embed_in_forum_posts', !defined( 'BP_EMBED_DISABLE_FORUM_POSTS' ) || !BP_EMBED_DISABLE_FORUM_POSTS );
+}
+
+/**
+ * Are oembeds allowed in private messages?
+ *
+ * @return bool False when form post embed support is disabled; true when enabled (default)
+ * @since 1.5
+ */
+function bp_use_embed_in_private_messages() {
+	return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES );
+}
+
+/**
+ * Output the correct URL based on BuddyPress and WordPress configuration
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param string $path
+ * @param string $scheme
+ *
+ * @uses bp_get_admin_url()
+ */
+function bp_admin_url( $path = '', $scheme = 'admin' ) {
+	echo bp_get_admin_url( $path, $scheme );
+}
+	/**
+	 * Return the correct URL based on BuddyPress and WordPress configuration
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 *
+	 * @param string $path
+	 * @param string $scheme
+	 *
+	 * @uses bp_core_do_network_admin()
+	 * @uses network_admin_url()
+	 * @uses admin_url()
+	 */
+	function bp_get_admin_url( $path = '', $scheme = 'admin' ) {
+
+		// Links belong in network admin
+		if ( bp_core_do_network_admin() )
+			$url = network_admin_url( $path, $scheme );
+
+		// Links belong in site admin
+		else
+			$url = admin_url( $path, $scheme );
+
+		return $url;
+	}
+
+/** Global Manipulators *******************************************************/
+
+/**
+ * Set the $bp->is_directory global
+ *
+ * @global obj $bp
+ * @param bool $is_directory
+ * @param str $component
+ */
+function bp_update_is_directory( $is_directory = false, $component = '' ) {
+	global $bp;
+
+	if ( empty( $component ) )
+		$component = bp_current_component();
+
+	$bp->is_directory = apply_filters( 'bp_update_is_directory', $is_directory, $component );
+}
+
+/**
+ * Set the $bp->is_item_admin global
+ *
+ * @global obj $bp
+ * @param bool $is_item_admin
+ * @param str $component
+ */
+function bp_update_is_item_admin( $is_item_admin = false, $component = '' ) {
+	global $bp;
+
+	if ( empty( $component ) )
+		$component = bp_current_component();
+
+	$bp->is_item_admin = apply_filters( 'bp_update_is_item_admin', $is_item_admin, $component );
+}
+
+/**
+ * Set the $bp->is_item_mod global
+ *
+ * @global obj $bp
+ * @param bool $is_item_mod
+ * @param str $component
+ */
+function bp_update_is_item_mod( $is_item_mod = false, $component = '' ) {
+	global $bp;
+
+	if ( empty( $component ) )
+		$component = bp_current_component();
+
+	$bp->is_item_mod = apply_filters( 'bp_update_is_item_mod', $is_item_mod, $component );
+}
+
+/**
+ * Trigger a 404
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @global WP_Query $wp_query WordPress query object
+ * @param string $redirect If 'remove_canonical_direct', remove WordPress' "helpful" redirect_canonical action.
+ * @since 1.5
+ */
+function bp_do_404( $redirect = 'remove_canonical_direct' ) {
+	global $bp, $wp_query;
+
+	do_action( 'bp_do_404', $redirect );
+
+	$wp_query->set_404();
+	status_header( 404 );
+	nocache_headers();
+
+	if ( 'remove_canonical_direct' == $redirect )
+		remove_action( 'template_redirect', 'redirect_canonical' );
+}
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-hooks.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-hooks.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-hooks.php	(revision 5057)
@@ -0,0 +1,114 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/** Loaded ********************************************************************/
+
+add_action( 'plugins_loaded', 'bp_loaded',  10 );
+
+add_action( 'bp_loaded',      'bp_include', 2  );
+
+add_action( 'wp',             'bp_actions', 3  );
+
+add_action( 'wp',             'bp_screens', 4  );
+
+/** Init **********************************************************************/
+
+// Attach bp_init to WordPress init
+add_action( 'init',       'bp_init'                    );
+
+// Parse the URI and set globals
+add_action( 'bp_init',    'bp_core_set_uri_globals', 2 );
+
+// Setup component globals
+add_action( 'bp_init',    'bp_setup_globals',        4 );
+
+// Setup the navigation menu
+add_action( 'bp_init',    'bp_setup_nav',            7 );
+
+// Setup the navigation menu
+add_action( 'admin_bar_menu',    'bp_setup_admin_bar'  );
+
+// Setup the title
+add_action( 'bp_init',    'bp_setup_title',          9 );
+
+// Setup widgets
+add_action( 'bp_loaded',  'bp_setup_widgets'           );
+
+// Setup admin bar
+add_action( 'bp_loaded',  'bp_core_load_admin_bar'     );
+
+/** The hooks *****************************************************************/
+
+/**
+ * Include files on this action
+ */
+function bp_include() {
+	do_action( 'bp_include' );
+}
+
+/**
+ * Setup global variables and objects
+ */
+function bp_setup_globals() {
+	do_action( 'bp_setup_globals' );
+}
+
+/**
+ * Set navigation elements
+ */
+function bp_setup_nav() {
+	do_action( 'bp_setup_nav' );
+}
+
+/**
+ * Set up BuddyPress implementation of the WP admin bar
+ */
+function bp_setup_admin_bar() {
+	if ( bp_use_wp_admin_bar() )
+		do_action( 'bp_setup_admin_bar' );
+}
+
+/**
+ * Set the page title
+ */
+function bp_setup_title() {
+	do_action( 'bp_setup_title' );
+}
+
+/**
+ * Register widgets
+ */
+function bp_setup_widgets() {
+	do_action( 'bp_register_widgets' );
+}
+
+/**
+ * Initlialize code
+ */
+function bp_init() {
+	do_action( 'bp_init' );
+}
+
+/**
+ * Attached to plugins_loaded
+ */
+function bp_loaded() {
+	do_action( 'bp_loaded' );
+}
+
+/**
+ * Attach potential template actions
+ */
+function bp_actions() {
+	do_action( 'bp_actions' );
+}
+
+/**
+ * Attach potential template screens
+ */
+function bp_screens() {
+	do_action( 'bp_screens' );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-loader.php	(revision 5057)
@@ -0,0 +1,234 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// Require all of the BuddyPress core libraries
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-cache.php'     );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-hooks.php'     );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-cssjs.php'     );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-classes.php'   );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-filters.php'   );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-avatars.php'   );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-widgets.php'   );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-template.php'  );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-buddybar.php'  );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-catchuri.php'  );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-component.php' );
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-functions.php' );
+
+// Load deprecated functions
+require( BP_PLUGIN_DIR . '/bp-core/deprecated/1.5.php'    );
+
+// Load the WP admin bar.
+if ( !defined( 'BP_DISABLE_ADMIN_BAR' ) )
+	require( BP_PLUGIN_DIR . '/bp-core/bp-core-adminbar.php'  );
+
+// Move active components from sitemeta, if necessary
+// Provides backpat with earlier versions of BP
+if ( is_multisite() && $active_components = get_site_option( 'bp-active-components' ) )
+	bp_update_option( 'bp-active-components', $active_components );
+
+/** "And now for something completely different" ******************************/
+
+class BP_Core extends BP_Component {
+
+	function __construct() {
+		parent::start(
+			'_core',
+			__( 'BuddyPress Core', 'buddypress' )
+			, BP_PLUGIN_DIR
+		);
+
+		$this->bootstrap();
+	}
+
+	private function bootstrap() {
+		global $bp;
+
+		/**
+		 * At this point in the stack, BuddyPress core has been loaded but
+		 * individual components (friends/activity/groups/etc...) have not.
+		 *
+		 * The 'bp_core_loaded' action lets you execute code ahead of the
+		 * other components.
+		 */
+		do_action( 'bp_core_loaded' );
+
+		/** Components ********************************************************/
+
+		// Set the included and optional components.
+		$bp->optional_components = apply_filters( 'bp_optional_components', array( 'activity', 'blogs', 'forums', 'friends', 'groups', 'messages', 'settings', 'xprofile' ) );
+
+		// Set the required components
+		$bp->required_components = apply_filters( 'bp_required_components', array( 'members' ) );
+
+		// Get a list of activated components
+		if ( $active_components = bp_get_option( 'bp-active-components' ) ) {
+			$bp->active_components      = apply_filters( 'bp_active_components', $active_components );
+			$bp->deactivated_components = apply_filters( 'bp_deactivated_components', array_values( array_diff( array_values( array_merge( $bp->optional_components, $bp->required_components ) ), array_keys( $bp->active_components ) ) ) );
+
+		// Pre 1.5 Backwards compatibility
+		} elseif ( $deactivated_components = bp_get_option( 'bp-deactivated-components' ) ) {
+			// Trim off namespace and filename
+			foreach ( (array) $deactivated_components as $component => $value )
+				$trimmed[] = str_replace( '.php', '', str_replace( 'bp-', '', $component ) );
+
+			// Set globals
+			$bp->deactivated_components = apply_filters( 'bp_deactivated_components', $trimmed );
+
+			// Setup the active components
+			$active_components     = array_flip( array_diff( array_values( array_merge( $bp->optional_components, $bp->required_components ) ), array_values( $bp->deactivated_components ) ) );
+
+			// Loop through active components and set the values
+			$bp->active_components = array_map( '__return_true', $active_components );
+
+			// Set the active component global
+			$bp->active_components = apply_filters( 'bp_active_components', $bp->active_components );
+
+		// Default to all components active
+		} else {
+			// Set globals
+			$bp->deactivated_components = array();
+
+			// Setup the active components
+			$active_components     = array_flip( array_values( array_merge( $bp->optional_components, $bp->required_components ) ) );
+
+			// Loop through active components and set the values
+			$bp->active_components = array_map( '__return_true', $active_components );
+
+			// Set the active component global
+			$bp->active_components = apply_filters( 'bp_active_components', $bp->active_components );
+		}
+
+		// Loop through optional components
+		foreach( $bp->optional_components as $component )
+			if ( bp_is_active( $component ) && file_exists( BP_PLUGIN_DIR . '/bp-' . $component . '/bp-' . $component . '-loader.php' ) )
+				include( BP_PLUGIN_DIR . '/bp-' . $component . '/bp-' . $component . '-loader.php' );
+
+		// Loop through required components
+		foreach( $bp->required_components as $component )
+			if ( file_exists( BP_PLUGIN_DIR . '/bp-' . $component . '/bp-' . $component . '-loader.php' ) )
+				include( BP_PLUGIN_DIR . '/bp-' . $component . '/bp-' . $component . '-loader.php' );
+
+		// Add Core to required components
+		$bp->required_components[] = 'core';
+	}
+
+	function setup_globals() {
+		global $bp;
+
+		/** Database **********************************************************/
+
+		// Get the base database prefix
+		if ( empty( $bp->table_prefix ) )
+			$bp->table_prefix = bp_core_get_table_prefix();
+
+		// The domain for the root of the site where the main blog resides
+		if ( empty( $bp->root_domain ) )
+			$bp->root_domain = bp_core_get_root_domain();
+
+		// Fetches all of the core BuddyPress settings in one fell swoop
+		if ( empty( $bp->site_options ) )
+			$bp->site_options = bp_core_get_root_options();
+
+		// The names of the core WordPress pages used to display BuddyPress content
+		if ( empty( $bp->pages ) )
+			$bp->pages = bp_core_get_directory_pages();
+
+		/** Admin Bar *********************************************************/
+
+		// Set the 'My Account' global to prevent debug notices
+		$bp->my_account_menu_id = false;
+
+		/** Component and Action **********************************************/
+
+		// Used for overriding the 2nd level navigation menu so it can be used to
+		// display custom navigation for an item (for example a group)
+		$bp->is_single_item = false;
+
+		// Sets up the array container for the component navigation rendered
+		// by bp_get_nav()
+		$bp->bp_nav            = array();
+
+		// Sets up the array container for the component options navigation
+		// rendered by bp_get_options_nav()
+		$bp->bp_options_nav    = array();
+
+		// Contains an array of all the active components. The key is the slug,
+		// value the internal ID of the component.
+		//$bp->active_components = array();
+
+		/** Basic current user data *******************************************/
+
+		// Logged in user is the 'current_user'
+		$current_user            = wp_get_current_user();
+
+		// The user ID of the user who is currently logged in.
+		$bp->loggedin_user->id   = $current_user->ID;
+
+		/** Avatars ***********************************************************/
+
+		// Fetches the default Gravatar image to use if the user/group/blog has no avatar or gravatar
+		$bp->grav_default->user  = apply_filters( 'bp_user_gravatar_default',  $bp->site_options['avatar_default'] );
+		$bp->grav_default->group = apply_filters( 'bp_group_gravatar_default', $bp->grav_default->user );
+		$bp->grav_default->blog  = apply_filters( 'bp_blog_gravatar_default',  $bp->grav_default->user );
+
+		// Notifications Table
+		$bp->core->table_name_notifications = $bp->table_prefix . 'bp_notifications';
+
+		/**
+		 * Used to determine if user has admin rights on current content. If the
+		 * logged in user is viewing their own profile and wants to delete
+		 * something, is_item_admin is used. This is a generic variable so it
+		 * can be used by other components. It can also be modified, so when
+		 * viewing a group 'is_item_admin' would be 'true' if they are a group
+		 * admin, and 'false' if they are not.
+		 */
+		bp_update_is_item_admin( bp_user_has_access(), 'core' );
+
+		// Is the logged in user is a mod for the current item?
+		bp_update_is_item_mod( false,                  'core' );
+
+		do_action( 'bp_core_setup_globals' );
+	}
+
+	function setup_nav() {
+		global $bp;
+
+		/***
+		 * If the extended profiles component is disabled, we need to revert to using the
+		 * built in WordPress profile information
+		 */
+		if ( !bp_is_active( 'xprofile' ) ) {
+
+			// Fallback values if xprofile is disabled
+			$bp->core->profile->slug = 'profile';
+			$bp->active_components[$bp->core->profile->slug] = $bp->core->profile->slug;
+
+			// Add 'Profile' to the main navigation
+			$main_nav = array(
+				'name'                => __( 'Profile', 'buddypress' ),
+				'slug'                => $bp->core->profile->slug,
+				'position'            => 20,
+				'screen_function'     => 'bp_core_catch_profile_uri',
+				'default_subnav_slug' => 'public'
+			);
+
+			$profile_link = trailingslashit( $bp->loggedin_user->domain . '/' . $bp->core->profile->slug );
+
+			// Add the subnav items to the profile
+			$sub_nav[] = array(
+				'name'            => __( 'Public', 'buddypress' ),
+				'slug'            => 'public',
+				'parent_url'      => $profile_link,
+				'parent_slug'     => $bp->core->profile->slug,
+				'screen_function' => 'bp_core_catch_profile_uri'
+			);
+		}
+	}
+}
+
+// Initialize the BuddyPress Core
+$bp->core = new BP_Core();
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-template.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-template.php	(revision 5057)
@@ -0,0 +1,1616 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Uses the $bp->bp_options_nav global to render out the sub navigation for the current component.
+ * Each component adds to its sub navigation array within its own setup_nav() function.
+ *
+ * This sub navigation array is the secondary level navigation, so for profile it contains:
+ *      [Public, Edit Profile, Change Avatar]
+ *
+ * The function will also analyze the current action for the current component to determine whether
+ * or not to highlight a particular sub nav item.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @uses bp_get_user_nav() Renders the navigation for a profile of a currently viewed user.
+ */
+function bp_get_options_nav() {
+	global $bp;
+
+	// If we are looking at a member profile, then the we can use the current component as an
+	// index. Otherwise we need to use the component's root_slug
+	$component_index = !empty( $bp->displayed_user ) ? $bp->current_component : bp_get_root_slug( $bp->current_component );
+
+	if ( !bp_is_single_item() ) {
+		if ( !isset( $bp->bp_options_nav[$component_index] ) || count( $bp->bp_options_nav[$component_index] ) < 1 ) {
+			return false;
+		} else {
+			$the_index = $component_index;
+		}
+	} else {
+		if ( !isset( $bp->bp_options_nav[$bp->current_item] ) || count( $bp->bp_options_nav[$bp->current_item] ) < 1 ) {
+			return false;
+		} else {
+			$the_index = $bp->current_item;
+		}
+	}
+
+	// Loop through each navigation item
+	foreach ( (array)$bp->bp_options_nav[$the_index] as $subnav_item ) {
+		if ( !$subnav_item['user_has_access'] )
+			continue;
+
+		// If the current action or an action variable matches the nav item id, then add a highlight CSS class.
+		if ( $subnav_item['slug'] == $bp->current_action ) {
+			$selected = ' class="current selected"';
+		} else {
+			$selected = '';
+		}
+
+		// echo out the final list item
+		echo apply_filters( 'bp_get_options_nav_' . $subnav_item['css_id'], '<li id="' . $subnav_item['css_id'] . '-personal-li" ' . $selected . '><a id="' . $subnav_item['css_id'] . '" href="' . $subnav_item['link'] . '">' . $subnav_item['name'] . '</a></li>', $subnav_item );
+	}
+}
+
+function bp_get_options_title() {
+	global $bp;
+
+	if ( empty( $bp->bp_options_title ) )
+		$bp->bp_options_title = __( 'Options', 'buddypress' );
+
+	echo apply_filters( 'bp_get_options_title', esc_attr( $bp->bp_options_title ) );
+}
+
+/** Avatars *******************************************************************/
+
+/**
+ * Check to see if there is an options avatar. An options avatar is an avatar for something
+ * like a group, or a friend. Basically an avatar that appears in the sub nav options bar.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_has_options_avatar() {
+	global $bp;
+
+	if ( empty( $bp->bp_options_avatar ) )
+		return false;
+
+	return true;
+}
+
+function bp_get_options_avatar() {
+	global $bp;
+
+	echo apply_filters( 'bp_get_options_avatar', $bp->bp_options_avatar );
+}
+
+function bp_comment_author_avatar() {
+	global $comment;
+
+	if ( function_exists( 'bp_core_fetch_avatar' ) )
+		echo apply_filters( 'bp_comment_author_avatar', bp_core_fetch_avatar( array( 'item_id' => $comment->user_id, 'type' => 'thumb' ) ) );
+	else if ( function_exists('get_avatar') )
+		get_avatar();
+}
+
+function bp_post_author_avatar() {
+	global $post;
+
+	if ( function_exists( 'bp_core_fetch_avatar' ) )
+		echo apply_filters( 'bp_post_author_avatar', bp_core_fetch_avatar( array( 'item_id' => $post->post_author, 'type' => 'thumb' ) ) );
+	else if ( function_exists('get_avatar') )
+		get_avatar();
+}
+
+function bp_avatar_admin_step() {
+	echo bp_get_avatar_admin_step();
+}
+	function bp_get_avatar_admin_step() {
+		global $bp;
+
+		if ( isset( $bp->avatar_admin->step ) )
+			$step = $bp->avatar_admin->step;
+		else
+			$step = 'upload-image';
+
+		return apply_filters( 'bp_get_avatar_admin_step', $step );
+	}
+
+function bp_avatar_to_crop() {
+	echo bp_get_avatar_to_crop();
+}
+	function bp_get_avatar_to_crop() {
+		global $bp;
+
+		if ( isset( $bp->avatar_admin->image->url ) )
+			$url = $bp->avatar_admin->image->url;
+		else
+			$url = '';
+
+		return apply_filters( 'bp_get_avatar_to_crop', $url );
+	}
+
+function bp_avatar_to_crop_src() {
+	echo bp_get_avatar_to_crop_src();
+}
+	function bp_get_avatar_to_crop_src() {
+		global $bp;
+
+		return apply_filters( 'bp_get_avatar_to_crop_src', str_replace( WP_CONTENT_DIR, '', $bp->avatar_admin->image->dir ) );
+	}
+
+function bp_avatar_cropper() {
+	global $bp;
+
+	echo '<img id="avatar-to-crop" class="avatar" src="' . $bp->avatar_admin->image . '" />';
+}
+
+function bp_site_name() {
+	echo apply_filters( 'bp_site_name', get_bloginfo( 'name', 'display' ) );
+}
+
+function bp_get_profile_header() {
+	locate_template( array( '/profile/profile-header.php' ), true );
+}
+
+function bp_exists( $component_name ) {
+	if ( function_exists( $component_name . '_install' ) )
+		return true;
+
+	return false;
+}
+
+function bp_format_time( $time, $just_date = false, $localize_time = true ) {
+	if ( !isset( $time ) || !is_numeric( $time ) )
+		return false;
+
+	// Get GMT offset from root blog
+	$root_blog_offset = false;
+	if ( $localize_time )
+		$root_blog_offset = get_blog_option( bp_get_root_blog_id(), 'gmt_offset' );
+
+	// Calculate offset time
+	$time_offset = $time + ( $root_blog_offset * 3600 );
+
+	// Current date (January 1, 2010)
+	$date = date_i18n( get_option( 'date_format' ), $time_offset );
+
+	// Should we show the time also?
+	if ( !$just_date ) {
+		// Current time (9:50pm)
+		$time = date_i18n( get_option( 'time_format' ), $time_offset );
+
+		// Return string formatted with date and time
+		$date = sprintf( __( '%1$s at %2$s', 'buddypress' ), $date, $time );
+	}
+
+	return apply_filters( 'bp_format_time', $date );
+}
+
+function bp_word_or_name( $youtext, $nametext, $capitalize = true, $echo = true ) {
+	global $bp;
+
+	if ( $capitalize )
+		$youtext = bp_core_ucfirst($youtext);
+
+	if ( $bp->displayed_user->id == $bp->loggedin_user->id ) {
+		if ( $echo )
+			echo apply_filters( 'bp_word_or_name', $youtext );
+		else
+			return apply_filters( 'bp_word_or_name', $youtext );
+	} else {
+		$fullname = (array)explode( ' ', $bp->displayed_user->fullname );
+		$nametext = sprintf( $nametext, $fullname[0] );
+		if ( $echo )
+			echo apply_filters( 'bp_word_or_name', $nametext );
+		else
+			return apply_filters( 'bp_word_or_name', $nametext );
+	}
+}
+
+function bp_get_plugin_sidebar() {
+	locate_template( array( 'plugin-sidebar.php' ), true );
+}
+
+function bp_styles() {
+	do_action( 'bp_styles' );
+	wp_print_styles();
+}
+
+/** Search Form ***************************************************************/
+
+function bp_search_form_action() {
+	return apply_filters( 'bp_search_form_action', bp_get_root_domain() . '/' . bp_get_search_slug() );
+}
+
+/**
+ * Generates the basic search form as used in BP-Default's header.
+ *
+ * @global object $bp BuddyPress global settings
+ * @return string HTML <select> element
+ * @since 1.0
+ */
+function bp_search_form_type_select() {
+	global $bp;
+
+	$options = array();
+
+	if ( bp_is_active( 'xprofile' ) )
+		$options['members'] = __( 'Members', 'buddypress' );
+
+	if ( bp_is_active( 'groups' ) )
+		$options['groups']  = __( 'Groups',  'buddypress' );
+
+	if ( bp_is_active( 'blogs' ) && is_multisite() )
+		$options['blogs']   = __( 'Blogs',   'buddypress' );
+
+	if ( bp_is_active( 'forums' ) && bp_forums_is_installed_correctly() && bp_forums_has_directory() )
+		$options['forums']  = __( 'Forums',  'buddypress' );
+
+	$options['posts'] = __( 'Posts', 'buddypress' );
+
+	// Eventually this won't be needed and a page will be built to integrate all search results.
+	$selection_box  = '<label for="search-which" class="accessibly-hidden">' . __( 'Search these:', 'buddypress' ) . '</label>';
+	$selection_box .= '<select name="search-which" id="search-which" style="width: auto">';
+
+	$options = apply_filters( 'bp_search_form_type_select_options', $options );
+	foreach( (array)$options as $option_value => $option_title )
+		$selection_box .= sprintf( '<option value="%s">%s</option>', $option_value, $option_title );
+
+	$selection_box .= '</select>';
+
+	return apply_filters( 'bp_search_form_type_select', $selection_box );
+}
+
+/**
+ * Get the default text for the search box for a given component.
+ *
+ * @global object $bp BuddyPress global settings
+ * @return string
+ * @since 1.5
+ */
+function bp_search_default_text( $component = '' ) {
+	echo bp_get_search_default_text( $component );
+}
+	function bp_get_search_default_text( $component = '' ) {
+		global $bp;
+
+		if ( empty( $component ) )
+			$component = bp_current_component();
+
+		$default_text = __( 'Search anything...', 'buddypress' );
+
+		// Most of the time, $component will be the actual component ID
+		if ( !empty( $component ) ) {
+			if ( !empty( $bp->{$component}->search_string ) ) {
+				$default_text = $bp->{$component}->search_string;
+			} else {
+				// When the request comes through AJAX, we need to get the component
+				// name out of $bp->pages
+				if ( !empty( $bp->pages->{$component}->slug ) ) {
+					$key = $bp->pages->{$component}->slug;
+					if ( !empty( $bp->{$key}->search_string ) )
+						$default_text = $bp->{$key}->search_string;
+				}
+			}
+		}
+
+		return apply_filters( 'bp_get_search_default_text', $default_text, $component );
+	}
+
+function bp_custom_profile_boxes() {
+	do_action( 'bp_custom_profile_boxes' );
+}
+
+function bp_custom_profile_sidebar_boxes() {
+	do_action( 'bp_custom_profile_sidebar_boxes' );
+}
+
+/**
+ * Creates and outputs a button.
+ *
+ * @param array $args See bp_get_button() for the list of arguments.
+ * @see bp_get_button()
+ */
+function bp_button( $args = '' ) {
+	echo bp_get_button( $args );
+}
+	/**
+	 * Creates and returns a button.
+	 *
+	 * Args:
+	 * component: Which component this button is for
+	 * must_be_logged_in: Button only appears for logged in users
+	 * block_self: Button will not appear when viewing your own profile.
+	 * wrapper: div|span|p|li|
+	 * wrapper_id: The DOM ID of the button wrapper
+	 * wrapper_class: The DOM class of the button wrapper
+	 * link_href: The destination link of the button
+	 * link_title: Title of the button
+	 * link_id: The DOM ID of the button
+	 * link_class: The DOM class of the button
+	 * link_rel: The DOM rel of the button
+	 * link_text: The contents of the button
+	 *
+	 * @param array $button
+	 * @return string
+	 * @see bp_add_friend_button()
+	 * @see bp_send_public_message_button()
+	 * @see bp_send_private_message_button()
+	 */
+	function bp_get_button( $args = '' ) {
+		$button = new BP_Button( $args );
+		return apply_filters( 'bp_get_button', $button->contents, $args, $button );
+	}
+
+
+/**
+ * Truncates text.
+ *
+ * Cuts a string to the length of $length and replaces the last characters
+ * with the ending if the text is longer than length.
+ *
+ * This function is borrowed from CakePHP v2.0, under the MIT license. See
+ * http://book.cakephp.org/view/1469/Text#truncate-1625
+ *
+ * ### Options:
+ *
+ * - `ending` Will be used as Ending and appended to the trimmed string
+ * - `exact` If false, $text will not be cut mid-word
+ * - `html` If true, HTML tags would be handled correctly
+ * - `filter_shortcodes` If true, shortcodes will be stripped before truncating
+ *
+ * @package BuddyPress
+ *
+ * @param string  $text String to truncate.
+ * @param integer $length Length of returned string, including ellipsis.
+ * @param array $options An array of html attributes and options.
+ * @return string Trimmed string.
+ */
+function bp_create_excerpt( $text, $length = 225, $options = array() ) {
+	// Backward compatibility. The third argument used to be a boolean $filter_shortcodes
+	$filter_shortcodes_default = is_bool( $options ) ? $options : true;
+
+	$defaults = array(
+		'ending'            => __( ' [&hellip;]', 'buddypress' ),
+		'exact'             => false,
+		'html'              => true,
+		'filter_shortcodes' => $filter_shortcodes_default
+	);
+	$r = wp_parse_args( $options, $defaults );
+	extract( $r );
+
+	// Save the original text, to be passed along to the filter
+	$original_text = $text;
+
+	// Allow plugins to modify these values globally
+	$length = apply_filters( 'bp_excerpt_length', $length );
+	$ending = apply_filters( 'bp_excerpt_append_text', $ending );
+
+	// Remove shortcodes if necessary
+	if ( $filter_shortcodes )
+		$text = strip_shortcodes( $text );
+
+	// When $html is true, the excerpt should be created without including HTML tags in the
+	// excerpt length
+	if ( $html ) {
+		// The text is short enough. No need to truncate
+		if ( mb_strlen( preg_replace( '/<.*?>/', '', $text ) ) <= $length ) {
+			return $text;
+		}
+
+		$totalLength = mb_strlen( strip_tags( $ending ) );
+		$openTags    = array();
+		$truncate    = '';
+
+		// Find all the tags and put them in a stack for later use
+		preg_match_all( '/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER );
+		foreach ( $tags as $tag ) {
+			// Process tags that need to be closed
+			if ( !preg_match( '/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s',  $tag[2] ) ) {
+				if ( preg_match( '/<[\w]+[^>]*>/s', $tag[0] ) ) {
+					array_unshift( $openTags, $tag[2] );
+				} else if ( preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag ) ) {
+					$pos = array_search( $closeTag[1], $openTags );
+					if ( $pos !== false ) {
+						array_splice( $openTags, $pos, 1 );
+					}
+				}
+			}
+			$truncate .= $tag[1];
+
+			$contentLength = mb_strlen( preg_replace( '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3] ) );
+			if ( $contentLength + $totalLength > $length ) {
+				$left = $length - $totalLength;
+				$entitiesLength = 0;
+				if ( preg_match_all( '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE ) ) {
+					foreach ( $entities[0] as $entity ) {
+						if ( $entity[1] + 1 - $entitiesLength <= $left ) {
+							$left--;
+							$entitiesLength += mb_strlen( $entity[0] );
+						} else {
+							break;
+						}
+					}
+				}
+
+				$truncate .= mb_substr( $tag[3], 0 , $left + $entitiesLength );
+				break;
+			} else {
+				$truncate .= $tag[3];
+				$totalLength += $contentLength;
+			}
+			if ( $totalLength >= $length ) {
+				break;
+			}
+		}
+	} else {
+		if ( mb_strlen( $text ) <= $length ) {
+			return $text;
+		} else {
+			$truncate = mb_substr( $text, 0, $length - mb_strlen( $ending ) );
+		}
+	}
+
+	// If $exact is false, we can't break on words
+	if ( !$exact ) {
+		$spacepos = mb_strrpos( $truncate, ' ' );
+		if ( isset( $spacepos ) ) {
+			if ( $html ) {
+				$bits = mb_substr( $truncate, $spacepos );
+				preg_match_all( '/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER );
+				if ( !empty( $droppedTags ) ) {
+					foreach ( $droppedTags as $closingTag ) {
+						if ( !in_array( $closingTag[1], $openTags ) ) {
+							array_unshift( $openTags, $closingTag[1] );
+						}
+					}
+				}
+			}
+			$truncate = mb_substr( $truncate, 0, $spacepos );
+		}
+	}
+	$truncate .= $ending;
+
+	if ( $html ) {
+		foreach ( $openTags as $tag ) {
+			$truncate .= '</' . $tag . '>';
+		}
+	}
+
+	return apply_filters( 'bp_create_excerpt', $truncate, $original_text, $length, $options );
+
+}
+add_filter( 'bp_create_excerpt', 'stripslashes_deep' );
+add_filter( 'bp_create_excerpt', 'force_balance_tags' );
+
+function bp_total_member_count() {
+	echo bp_get_total_member_count();
+}
+	function bp_get_total_member_count() {
+		return apply_filters( 'bp_get_total_member_count', bp_core_get_total_member_count() );
+	}
+	add_filter( 'bp_get_total_member_count', 'bp_core_number_format' );
+
+function bp_blog_signup_allowed() {
+	echo bp_get_blog_signup_allowed();
+}
+	function bp_get_blog_signup_allowed() {
+		global $bp;
+
+		if ( !is_multisite() )
+			return false;
+
+		$status = $bp->site_options['registration'];
+		if ( 'none' != $status && 'user' != $status )
+			return true;
+
+		return false;
+	}
+
+function bp_account_was_activated() {
+	global $bp;
+
+	$activation_complete = !empty( $bp->activation_complete ) ? $bp->activation_complete : false;
+
+	return $activation_complete;
+}
+
+function bp_registration_needs_activation() {
+	return apply_filters( 'bp_registration_needs_activation', true );
+}
+
+/**
+ * Allow templates to pass parameters directly into the template loops via AJAX
+ *
+ * For the most part this will be filtered in a theme's functions.php for example
+ * in the default theme it is filtered via bp_dtheme_ajax_querystring()
+ *
+ * By using this template tag in the templates it will stop them from showing errors
+ * if someone copies the templates from the default theme into another WordPress theme
+ * without coping the functions from functions.php.
+ */
+function bp_ajax_querystring( $object = false ) {
+	global $bp;
+
+	if ( !isset( $bp->ajax_querystring ) )
+		$bp->ajax_querystring = '';
+
+	return apply_filters( 'bp_ajax_querystring', $bp->ajax_querystring, $object );
+}
+
+/** Template Classes and _is functions ****************************************/
+
+function bp_current_component() {
+	global $bp;
+	$current_component = !empty( $bp->current_component ) ? $bp->current_component : false;
+	return apply_filters( 'bp_current_component', $current_component );
+}
+
+function bp_current_action() {
+	global $bp;
+	$current_action = !empty( $bp->current_action ) ? $bp->current_action : false;
+	return apply_filters( 'bp_current_action', $current_action );
+}
+
+function bp_current_item() {
+	global $bp;
+	$current_item = !empty( $bp->current_item ) ? $bp->current_item : false;
+	return apply_filters( 'bp_current_item', $current_item );
+}
+
+/**
+ * Return the value of $bp->action_variables
+ *
+ * @package BuddyPress
+ *
+ * @param mixed $action_variables The action variables array, or false if the array is empty
+ */
+function bp_action_variables() {
+	global $bp;
+	$action_variables = !empty( $bp->action_variables ) ? $bp->action_variables : false;
+	return apply_filters( 'bp_action_variables', $action_variables );
+}
+
+/**
+ * Return the value of a given action variable
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param int $position The key of the action_variables array that you want
+ * @return str $action_variable The value of that position in the array
+ */
+function bp_action_variable( $position = 0 ) {
+	$action_variables = bp_action_variables();
+
+	$action_variable = isset( $action_variables[$position] ) ? $action_variables[$position] : false;
+
+	return apply_filters( 'bp_action_variable', $action_variable, $position );
+}
+
+function bp_root_domain() {
+	echo bp_get_root_domain();
+}
+	function bp_get_root_domain() {
+		global $bp;
+
+		if ( isset( $bp->root_domain ) && !empty( $bp->root_domain ) ) {
+			$domain = $bp->root_domain;
+		} else {
+			$domain          = bp_core_get_root_domain();
+			$bp->root_domain = $domain;
+		}
+
+		return apply_filters( 'bp_get_root_domain', $domain );
+	}
+
+/**
+ * Echoes the output of bp_get_root_slug()
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ */
+function bp_root_slug( $component = '' ) {
+	echo bp_get_root_slug( $component );
+}
+	/**
+	 * Gets the root slug for a component slug
+	 *
+	 * In order to maintain backward compatibility, the following procedure is used:
+	 * 1) Use the short slug to get the canonical component name from the
+	 *    active component array
+	 * 2) Use the component name to get the root slug out of the appropriate part of the $bp
+	 *    global
+	 * 3) If nothing turns up, it probably means that $component is itself a root slug
+	 *
+	 * Example: If your groups directory is at /community/companies, this function first uses
+	 * the short slug 'companies' (ie the current component) to look up the canonical name
+	 * 'groups' in $bp->active_components. Then it uses 'groups' to get the root slug, from
+	 * $bp->groups->root_slug.
+	 *
+	 * @package BuddyPress Core
+	 * @since 1.5
+	 *
+	 * @global object $bp Global BuddyPress settings object
+	 * @param string $component Optional. Defaults to the current component
+	 * @return string $root_slug The root slug
+	 */
+	function bp_get_root_slug( $component = '' ) {
+		global $bp;
+
+		$root_slug = '';
+
+		// Use current global component if none passed
+		if ( empty( $component ) )
+			$component = $bp->current_component;
+
+		// Component is active
+		if ( !empty( $bp->active_components[$component] ) ) {
+			$component_name = $bp->active_components[$component];
+
+			// Component has specific root slug
+			if ( !empty( $bp->{$component_name}->root_slug ) )
+				$root_slug = $bp->{$component_name}->root_slug;
+		}
+
+		// No specific root slug, so fall back to component slug
+		if ( empty( $root_slug ) )
+			$root_slug = $component;
+
+		return apply_filters( 'bp_get_root_slug', $root_slug, $component );
+	}
+
+/**
+ * Return the component name based on the current root slug
+ *
+ * @since BuddyPress {r3923}
+ * @global object $bp Global BuddyPress settings object
+ * @param str $root_slug Needle to our active component haystack
+ * @return mixed False if none found, component name if found
+ */
+function bp_get_name_from_root_slug( $root_slug = '' ) {
+	global $bp;
+
+	// If no slug is passed, look at current_component
+	if ( empty( $root_slug ) )
+		$root_slug = $bp->current_component;
+
+	// No current component or root slug, so flee
+	if ( empty( $root_slug ) )
+		return false;
+
+	// Loop through active components and look for a match
+	foreach ( $bp->active_components as $component => $id )
+		if (	isset( $bp->{$component}->root_slug ) &&
+				!empty( $bp->{$component}->root_slug ) &&
+				$bp->{$component}->root_slug == $root_slug )
+			return $bp->{$component}->name;
+
+	return false;
+}
+
+function bp_user_has_access() {
+	$has_access = ( is_super_admin() || bp_is_my_profile() ) ? true : false;
+
+	return apply_filters( 'bp_user_has_access', $has_access );
+}
+
+/**
+ * Output the search slug
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @uses bp_get_search_slug()
+ */
+function bp_search_slug() {
+	echo bp_get_search_slug();
+}
+	/**
+	 * Return the search slug
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 */
+	function bp_get_search_slug() {
+		global $bp;
+
+		return apply_filters( 'bp_get_search_slug', BP_SEARCH_SLUG );
+	}
+
+/** is_() functions to determine the current page *****************************/
+
+/**
+ * Checks to see whether the current page belongs to the specified component
+ *
+ * This function is designed to be generous, accepting several different kinds
+ * of value for the $component parameter. It checks $component_name against:
+ * - the component's root_slug, which matches the page slug in $bp->pages
+ * - the component's regular slug
+ * - the component's id, or 'canonical' name
+ *
+ * @package BuddyPress Core
+ * @since 1.5
+ * @return bool Returns true if the component matches, or else false.
+ */
+function bp_is_current_component( $component ) {
+	global $bp;
+
+	$is_current_component = false;
+
+	// Backward compatibility: 'xprofile' should be read as 'profile'
+	if ( 'xprofile' == $component )
+		$component = 'profile';
+
+	if ( !empty( $bp->current_component ) ) {
+		// First, check to see whether $component_name and the current
+		// component are a simple match
+		if ( $bp->current_component == $component ) {
+			$is_current_component = true;
+
+		// Since the current component is based on the visible URL slug let's
+		// check the component being passed and see if its root_slug matches
+		} elseif ( isset( $bp->{$component}->root_slug ) && $bp->{$component}->root_slug == $bp->current_component ) {
+			$is_current_component = true;
+
+		// Because slugs can differ from root_slugs, we should check them too
+		} elseif ( isset( $bp->{$component}->slug ) && $bp->{$component}->slug == $bp->current_component ) {
+			$is_current_component = true;
+
+		// Next, check to see whether $component is a canonical,
+		// non-translatable component name. If so, we can return its
+		// corresponding slug from $bp->active_components.
+		} else if ( $key = array_search( $component, $bp->active_components ) ) {
+			if ( strstr( $bp->current_component, $key ) )
+				$is_current_component = true;
+
+		// If we haven't found a match yet, check against the root_slugs
+		// created by $bp->pages, as well as the regular slugs
+		} else {
+			foreach ( $bp->active_components as $id ) {
+				// If the $component parameter does not match the current_component,
+				// then move along, these are not the droids you are looking for
+				if ( empty( $bp->{$id}->root_slug ) || $bp->{$id}->root_slug != $bp->current_component )
+					continue;
+
+				if ( $id == $component ) {
+					$is_current_component = true;
+					break;
+				}
+			}
+		}
+
+	// Page template fallback check if $bp->current_component is empty
+	} elseif ( !is_admin() && is_page() ) {
+		global $wp_query;
+		$page          = $wp_query->get_queried_object();
+		$custom_fields = get_post_custom_values( '_wp_page_template', $page->ID );
+		$page_template = $custom_fields[0];
+
+		// Component name is in the page template name
+		if ( !empty( $page_template ) && strstr( strtolower( $page_template ), strtolower( $component ) ) )
+			$is_current_component = true;
+	}
+
+ 	return apply_filters( 'bp_is_current_component', $is_current_component, $component );
+}
+
+/**
+ * Check to see whether the current page matches a given action.
+ *
+ * Along with bp_is_current_component() and bp_is_action_variable(), this function is mostly used
+ * to help determine when to use a given screen function.
+ *
+ * In BP parlance, the current_action is the URL chunk that comes directly after the
+ * current item slug. E.g., in
+ *   http://example.com/groups/my-group/members
+ * the current_action is 'members'.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param str $action The action being tested against
+ * @return bool True if the current action matches $action
+ */
+function bp_is_current_action( $action = '' ) {
+	global $bp;
+
+	if ( $action == $bp->current_action )
+		return true;
+
+	return false;
+}
+
+/**
+ * Check to see whether the current page matches a given action_variable.
+ *
+ * Along with bp_is_current_component() and bp_is_current_action(), this function is mostly used
+ * to help determine when to use a given screen function.
+ *
+ * In BP parlance, action_variables are an array made up of the URL chunks appearing after the
+ * current_action in a URL. For example,
+ *   http://example.com/groups/my-group/admin/group-settings
+ * $action_variables[0] is 'group-settings'.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param str $action_variable The action_variable being tested against
+ * @param int $position The array key you're testing against. If you don't provide a $position,
+ *   the function will return true if the $action_variable is found *anywhere* in the action
+ *   variables array.
+ * @return bool
+ */
+function bp_is_action_variable( $action_variable = '', $position = false ) {
+	$is_action_variable = false;
+
+	if ( false !== $position ) {
+		// When a $position is specified, check that slot in the action_variables array
+		if ( $action_variable ) {
+			$is_action_variable = $action_variable == bp_action_variable( $position );
+		} else {
+			// If no $action_variable is provided, we are essentially checking to see
+			// whether the slot is empty
+			$is_action_variable = !bp_action_variable( $position );
+		}
+	} else {
+		// When no $position is specified, check the entire array
+		$is_action_variable = in_array( $action_variable, (array)bp_action_variables() );
+	}
+
+	return apply_filters( 'bp_is_action_variable', $is_action_variable, $action_variable, $position );
+}
+
+function bp_is_current_item( $item = '' ) {
+	if ( !empty( $item ) && $item == bp_current_item() )
+		return true;
+
+	return false;
+}
+
+function bp_is_single_item() {
+	global $bp;
+
+	if ( !empty( $bp->is_single_item ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_item_admin() {
+	global $bp;
+
+	if ( !empty( $bp->is_item_admin ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_item_mod() {
+	global $bp;
+
+	if ( !empty( $bp->is_item_mod ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_directory() {
+	global $bp;
+
+	if ( !empty( $bp->is_directory ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Checks to see if a component's URL should be in the root, not under a
+ * member page:
+ *
+ *   Yes: http://domain.com/groups/the-group
+ *   No:  http://domain.com/members/andy/groups/the-group
+ *
+ * @package BuddyPress Core
+ * @return true if root component, else false.
+ */
+function bp_is_root_component( $component_name ) {
+	global $bp;
+
+	if ( !isset( $bp->active_components ) )
+		return false;
+
+	foreach ( (array) $bp->active_components as $key => $slug ) {
+		if ( $key == $component_name || $slug == $component_name )
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * Checks if the site's front page is set to the specified BuddyPress component
+ * page in wp-admin's Settings > Reading screen.
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @global $current_blog WordPress global for the current blog
+ * @param string $component Optional; Name of the component to check for.
+ * @return bool True If the specified component is set to be the site's front page.
+ * @since 1.5
+ */
+function bp_is_component_front_page( $component = '' ) {
+	global $bp, $current_blog;
+
+	if ( !$component && !empty( $bp->current_component ) )
+		$component = $bp->current_component;
+
+	$path = is_main_site() ? bp_core_get_site_path() : $current_blog->path;
+
+	if ( 'page' != get_option( 'show_on_front' ) || !$component || empty( $bp->pages->{$component} ) || $_SERVER['REQUEST_URI'] != $path )
+		return false;
+
+	return apply_filters( 'bp_is_component_front_page', ( $bp->pages->{$component}->id == get_option( 'page_on_front' ) ), $component );
+}
+
+/**
+ * Is this a blog page, ie a non-BP page?
+ *
+ * You can tell if a page is displaying BP content by whether the current_component has been defined
+ *
+ * @package BuddyPress
+ *
+ * @return bool True if it's a non-BP page, false otherwise
+ */
+function bp_is_blog_page() {
+	global $bp, $wp_query;
+
+	$is_blog_page = false;
+
+	if ( !bp_current_component() )
+		$is_blog_page = true;
+
+	return apply_filters( 'bp_is_blog_page', $is_blog_page );
+}
+
+function bp_is_page( $page ) {
+	global $bp;
+
+	if ( !bp_is_user() && bp_is_current_component( $page )  )
+		return true;
+
+	if ( 'home' == $page )
+		return is_front_page();
+
+	return false;
+}
+
+/** Components ****************************************************************/
+
+function bp_is_active( $component ) {
+	global $bp;
+
+	if ( isset( $bp->active_components[$component] ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_profile_component() {
+	if ( bp_is_current_component( 'xprofile' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_activity_component() {
+	if ( bp_is_current_component( 'activity' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_blogs_component() {
+	if ( is_multisite() && bp_is_current_component( 'blogs' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_messages_component() {
+	if ( bp_is_current_component( 'messages' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_friends_component() {
+
+	if ( bp_is_current_component( 'friends' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_groups_component() {
+	if ( bp_is_current_component( 'groups' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_forums_component() {
+	if ( bp_is_current_component( 'forums' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_settings_component() {
+	if ( bp_is_current_component( 'settings' ) )
+		return true;
+
+	return false;
+}
+
+/** Activity ******************************************************************/
+
+function bp_is_single_activity() {
+	global $bp;
+
+	if ( bp_is_current_component( 'activity' ) && is_numeric( $bp->current_action ) )
+		return true;
+
+	return false;
+}
+
+/** User **********************************************************************/
+
+function bp_is_my_profile() {
+	global $bp;
+
+	if ( is_user_logged_in() && $bp->loggedin_user->id == $bp->displayed_user->id )
+		$my_profile = true;
+	else
+		$my_profile = false;
+
+	return apply_filters( 'bp_is_my_profile', $my_profile );
+}
+
+function bp_is_user() {
+	global $bp;
+
+	if ( !empty( $bp->displayed_user->id ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_activity() {
+	global $bp;
+
+	if ( bp_is_current_component( 'activity' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_friends_activity() {
+	global $bp;
+
+	if ( bp_is_current_component( 'activity' ) && bp_is_current_action( 'my-friends' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_profile() {
+	global $bp;
+
+	if ( bp_is_current_component( 'xprofile' ) || bp_is_current_component( 'profile' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_profile_edit() {
+	global $bp;
+
+	if ( bp_is_current_component( 'xprofile' ) && bp_is_current_action( 'edit' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_change_avatar() {
+	global $bp;
+
+	if ( bp_is_current_component( 'xprofile' ) && bp_is_current_action( 'change-avatar' ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Is this a user's forums page?
+ *
+ * @package BuddyPress
+ *
+ * @return bool
+ */
+function bp_is_user_forums() {
+	if ( bp_is_current_component( 'forums' ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Is this a user's "Topics Started" page?
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return bool
+ */
+function bp_is_user_forums_started() {
+	if ( bp_is_current_component( 'forums' ) && bp_is_current_action( 'topics' ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Is this a user's "Replied To" page?
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return bool
+ */
+function bp_is_user_forums_replied_to() {
+	if ( bp_is_current_component( 'forums' ) && bp_is_current_action( 'replies' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_groups() {
+	global $bp;
+
+	if ( bp_is_current_component( 'groups' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_blogs() {
+	global $bp;
+
+	if ( is_multisite() && bp_is_current_component( 'blogs' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_recent_posts() {
+	global $bp;
+
+	if ( is_multisite() && bp_is_current_component( 'blogs' ) && bp_is_current_action( 'recent-posts' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_recent_commments() {
+	global $bp;
+
+	if ( is_multisite() && bp_is_current_component( 'blogs' ) && bp_is_current_action( 'recent-comments' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_friends() {
+
+	if ( bp_is_current_component( 'friends' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_user_friend_requests() {
+	global $bp;
+
+	if ( bp_is_current_component( 'friends' ) && bp_is_current_action( 'requests' ) )
+		return true;
+
+	return false;
+}
+
+/** Groups ******************************************************************/
+
+function bp_is_group() {
+	global $bp;
+
+	if ( bp_is_groups_component() && isset( $bp->groups->current_group ) && $bp->groups->current_group )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_home() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && ( !bp_current_action() || bp_is_current_action( 'home' ) ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_create() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_current_action( 'create' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_admin_page() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'admin' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_forum() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'forum' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_activity() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'activity' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_forum_topic() {
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'forum' ) && bp_is_action_variable( 'topic', 0 ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_forum_topic_edit() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'forum' ) && bp_is_action_variable( 'topic', 0 ) && bp_is_action_variable( 'edit', 2 ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_members() {
+	global $bp;
+
+	if ( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( 'members' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_invites() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_current_action( 'send-invites' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_membership_request() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_current_action( 'request-membership' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_leave() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_single_item() && bp_is_current_action( 'leave-group' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_single() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_single_item() )
+		return true;
+
+	return false;
+}
+
+function bp_is_create_blog() {
+	global $bp;
+
+	if ( is_multisite() && bp_is_current_component( 'blogs' ) && bp_is_current_action( 'create' ) )
+		return true;
+
+	return false;
+}
+
+/** Messages ******************************************************************/
+
+function bp_is_user_messages() {
+
+	if ( bp_is_current_component( 'messages' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_messages_inbox() {
+	global $bp;
+
+	if ( bp_is_current_component( 'messages' ) && ( !bp_current_action() || bp_is_current_action( 'inbox' ) ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_messages_sentbox() {
+	global $bp;
+
+	if ( bp_is_current_component( 'messages' ) && bp_is_current_action( 'sentbox' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_messages_compose_screen() {
+	global $bp;
+
+	if ( bp_is_current_component( 'messages' ) && bp_is_current_action( 'compose' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_notices() {
+	global $bp;
+
+	if ( bp_is_current_component( 'messages' ) && bp_is_current_action( 'notices' ) )
+		return true;
+
+	return false;
+}
+
+
+function bp_is_single( $component, $callback ) {
+	global $bp;
+
+	if ( bp_is_current_component( $component ) && ( true === call_user_func( $callback ) ) )
+		return true;
+
+	return false;
+}
+
+/** Registration **************************************************************/
+
+function bp_is_activation_page() {
+	if ( bp_is_current_component( 'activate' ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_register_page() {
+	if ( bp_is_current_component( 'register' ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Use the above is_() functions to output a body class for each scenario
+ *
+ * @package BuddyPress
+ * @subpackage Core Template
+ *
+ * @param array $wp_classes The body classes coming from WP
+ * @param array $custom_classes Classes that were passed to get_body_class()
+ * @return array $classes The BP-adjusted body classes
+ */
+function bp_the_body_class() {
+	echo bp_get_the_body_class();
+}
+	function bp_get_the_body_class( $wp_classes, $custom_classes = false ) {
+		global $bp;
+
+		$bp_classes = array();
+
+		/** Pages *************************************************************/
+
+		if ( is_front_page() )
+			$bp_classes[] = 'home-page';
+
+		if ( bp_is_directory() )
+			$bp_classes[] = 'directory';
+
+		if ( bp_is_single_item() )
+			$bp_classes[] = 'single-item';
+
+		/** Components ********************************************************/
+
+		if ( !bp_is_blog_page() ) :
+			if ( bp_is_user_profile() )
+				$bp_classes[] = 'xprofile';
+
+			if ( bp_is_activity_component() )
+				$bp_classes[] = 'activity';
+
+			if ( bp_is_blogs_component() )
+				$bp_classes[] = 'blogs';
+
+			if ( bp_is_messages_component() )
+				$bp_classes[] = 'messages';
+
+			if ( bp_is_friends_component() )
+				$bp_classes[] = 'friends';
+
+			if ( bp_is_groups_component() )
+				$bp_classes[] = 'groups';
+
+			if ( bp_is_settings_component()  )
+				$bp_classes[] = 'settings';
+		endif;
+
+		/** User **************************************************************/
+
+		if ( !bp_is_directory() ) :
+			if ( bp_is_user_blogs() )
+				$bp_classes[] = 'my-blogs';
+
+			if ( bp_is_user_groups() )
+				$bp_classes[] = 'my-groups';
+
+			if ( bp_is_user_activity() )
+				$bp_classes[] = 'my-activity';
+		endif;
+
+		if ( bp_is_my_profile() )
+			$bp_classes[] = 'my-account';
+
+		if ( bp_is_user_profile() )
+			$bp_classes[] = 'my-profile';
+
+		if ( bp_is_user_friends() )
+			$bp_classes[] = 'my-friends';
+
+		if ( bp_is_user_messages() )
+			$bp_classes[] = 'my-messages';
+
+		if ( bp_is_user_recent_commments() )
+			$bp_classes[] = 'recent-comments';
+
+		if ( bp_is_user_recent_posts() )
+			$bp_classes[] = 'recent-posts';
+
+		if ( bp_is_user_change_avatar() )
+			$bp_classes[] = 'change-avatar';
+
+		if ( bp_is_user_profile_edit() )
+			$bp_classes[] = 'profile-edit';
+
+		if ( bp_is_user_friends_activity() )
+			$bp_classes[] = 'friends-activity';
+
+		if ( is_user_logged_in() )
+			$bp_classes[] = 'logged-in';
+
+		/** Messages **********************************************************/
+
+		if ( bp_is_messages_inbox() )
+			$bp_classes[] = 'inbox';
+
+		if ( bp_is_messages_sentbox() )
+			$bp_classes[] = 'sentbox';
+
+		if ( bp_is_messages_compose_screen() )
+			$bp_classes[] = 'compose';
+
+		if ( bp_is_notices() )
+			$bp_classes[] = 'notices';
+
+		if ( bp_is_user_friend_requests() )
+			$bp_classes[] = 'friend-requests';
+
+		if ( bp_is_create_blog() )
+			$bp_classes[] = 'create-blog';
+
+		/** Groups ************************************************************/
+
+		if ( bp_is_group_leave() )
+			$bp_classes[] = 'leave-group';
+
+		if ( bp_is_group_invites() )
+			$bp_classes[] = 'group-invites';
+
+		if ( bp_is_group_members() )
+			$bp_classes[] = 'group-members';
+
+		if ( bp_is_group_forum_topic() )
+			$bp_classes[] = 'group-forum-topic';
+
+		if ( bp_is_group_forum_topic_edit() )
+			$bp_classes[] = 'group-forum-topic-edit';
+
+		if ( bp_is_group_forum() )
+			$bp_classes[] = 'group-forum';
+
+		if ( bp_is_group_admin_page() )
+			$bp_classes[] = 'group-admin';
+
+		if ( bp_is_group_create() )
+			$bp_classes[] = 'group-create';
+
+		if ( bp_is_group_home() )
+			$bp_classes[] = 'group-home';
+
+		if ( bp_is_single_activity() )
+			$bp_classes[] = 'activity-permalink';
+
+		/** Registration ******************************************************/
+
+		if ( bp_is_register_page() )
+			$bp_classes[] = 'registration';
+
+		if ( bp_is_activation_page() )
+			$bp_classes[] = 'activation';
+
+		/** Current Component & Action ****************************************/
+
+		if ( !bp_is_blog_page() ) {
+			$bp_classes[] = bp_current_component();
+			$bp_classes[] = bp_current_action();
+		}
+
+		/** Clean up***********************************************************/
+
+		// We don't want WordPress blog classes to appear on non-blog pages.
+		if ( !bp_is_blog_page() ) {
+			// Preserve any custom classes already set
+			if ( !empty( $custom_classes ) )
+				$wp_classes = (array) $custom_classes;
+			else
+				$wp_classes = array();
+		}
+
+		// Merge WP classes with BP classes
+		$classes = array_merge( (array) $bp_classes, (array) $wp_classes );
+
+		// Remove any duplicates
+		$classes = array_unique( $classes );
+
+		return apply_filters( 'bp_get_the_body_class', $classes, $bp_classes, $wp_classes, $custom_classes );
+	}
+add_filter( 'body_class', 'bp_get_the_body_class', 10, 2 )
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-widgets.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-widgets.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-widgets.php	(revision 5057)
@@ -0,0 +1,334 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Register widgets for the core component */
+function bp_core_register_widgets() {
+	add_action('widgets_init', create_function('', 'return register_widget("BP_Core_Members_Widget");') );
+	add_action('widgets_init', create_function('', 'return register_widget("BP_Core_Whos_Online_Widget");') );
+	add_action('widgets_init', create_function('', 'return register_widget("BP_Core_Recently_Active_Widget");') );
+}
+add_action( 'bp_register_widgets', 'bp_core_register_widgets' );
+
+/*** MEMBERS WIDGET *****************/
+
+class BP_Core_Members_Widget extends WP_Widget {
+
+	function bp_core_members_widget() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		$widget_ops = array( 'description' => __( 'A dynamic list of recently active, popular, and newest members', 'buddypress' ) );
+		parent::__construct( false, $name = __( 'Members', 'buddypress' ), $widget_ops );
+
+		if ( is_active_widget( false, false, $this->id_base ) ) {
+			if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+				wp_enqueue_script( 'bp_core_widget_members-js', BP_PLUGIN_URL . '/bp-core/js/widget-members.dev.js', array( 'jquery' ), '20110723' );
+			else
+				wp_enqueue_script( 'bp_core_widget_members-js', BP_PLUGIN_URL . '/bp-core/js/widget-members.js', array( 'jquery' ), '20110723' );
+		}
+	}
+
+	function widget( $args, $instance ) {
+		global $bp;
+
+		extract( $args );
+
+		if ( !$instance['member_default'] )
+			$instance['member_default'] = 'active';
+
+		echo $before_widget;
+		echo $before_title
+		   . $instance['title']
+		   . $after_title; ?>
+
+		<?php if ( bp_has_members( 'user_id=0&type=' . $instance['member_default'] . '&max=' . $instance['max_members'] . '&populate_extras=0' ) ) : ?>
+			<div class="item-options" id="members-list-options">
+				<a href="<?php echo site_url( bp_get_members_root_slug() ); ?>" id="newest-members" <?php if ( $instance['member_default'] == 'newest' ) : ?>class="selected"<?php endif; ?>><?php _e( 'Newest', 'buddypress' ) ?></a>
+				|  <a href="<?php echo site_url( bp_get_members_root_slug() ); ?>" id="recently-active-members" <?php if ( $instance['member_default'] == 'active' ) : ?>class="selected"<?php endif; ?>><?php _e( 'Active', 'buddypress' ) ?></a>
+
+				<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+					| <a href="<?php echo site_url( bp_get_members_root_slug() ); ?>" id="popular-members" <?php if ( $instance['member_default'] == 'popular' ) : ?>class="selected"<?php endif; ?>><?php _e( 'Popular', 'buddypress' ) ?></a>
+
+				<?php endif; ?>
+			</div>
+
+			<ul id="members-list" class="item-list">
+				<?php while ( bp_members() ) : bp_the_member(); ?>
+					<li class="vcard">
+						<div class="item-avatar">
+							<a href="<?php bp_member_permalink() ?>" title="<?php bp_member_name() ?>"><?php bp_member_avatar() ?></a>
+						</div>
+
+						<div class="item">
+							<div class="item-title fn"><a href="<?php bp_member_permalink() ?>" title="<?php bp_member_name() ?>"><?php bp_member_name() ?></a></div>
+							<div class="item-meta">
+								<span class="activity">
+								<?php
+									if ( 'newest' == $instance['member_default'] )
+										bp_member_registered();
+									if ( 'active' == $instance['member_default'] )
+										bp_member_last_active();
+									if ( 'popular' == $instance['member_default'] )
+										bp_member_total_friend_count();
+								?>
+								</span>
+							</div>
+						</div>
+					</li>
+
+				<?php endwhile; ?>
+			</ul>
+			<?php wp_nonce_field( 'bp_core_widget_members', '_wpnonce-members' ); ?>
+			<input type="hidden" name="members_widget_max" id="members_widget_max" value="<?php echo esc_attr( $instance['max_members'] ); ?>" />
+
+		<?php else: ?>
+
+			<div class="widget-error">
+				<?php _e('No one has signed up yet!', 'buddypress') ?>
+			</div>
+
+		<?php endif; ?>
+
+		<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['title'] = strip_tags( $new_instance['title'] );
+		$instance['max_members'] = strip_tags( $new_instance['max_members'] );
+		$instance['member_default'] = strip_tags( $new_instance['member_default'] );
+
+		return $instance;
+	}
+
+	function form( $instance ) {
+		$defaults = array(
+			'title' => __( 'Members', 'buddypress' ),
+			'max_members' => 5,
+			'member_default' => 'active'
+		);
+		$instance = wp_parse_args( (array) $instance, $defaults );
+
+		$title = strip_tags( $instance['title'] );
+		$max_members = strip_tags( $instance['max_members'] );
+		$member_default = strip_tags( $instance['member_default'] );
+		?>
+
+		<p><label for="bp-core-widget-title"><?php _e('Title:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" style="width: 100%" /></label></p>
+
+		<p><label for="bp-core-widget-members-max"><?php _e('Max members to show:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'max_members' ); ?>" name="<?php echo $this->get_field_name( 'max_members' ); ?>" type="text" value="<?php echo esc_attr( $max_members ); ?>" style="width: 30%" /></label></p>
+
+		<p>
+			<label for="bp-core-widget-groups-default"><?php _e('Default members to show:', 'buddypress'); ?>
+			<select name="<?php echo $this->get_field_name( 'member_default' ) ?>">
+				<option value="newest" <?php if ( $member_default == 'newest' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Newest', 'buddypress' ) ?></option>
+				<option value="active" <?php if ( $member_default == 'active' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Active', 'buddypress' ) ?></option>
+				<option value="popular"  <?php if ( $member_default == 'popular' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Popular', 'buddypress' ) ?></option>
+			</select>
+			</label>
+		</p>
+
+	<?php
+	}
+}
+
+/*** WHO'S ONLINE WIDGET *****************/
+
+class BP_Core_Whos_Online_Widget extends WP_Widget {
+
+	function bp_core_whos_online_widget() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		$widget_ops = array( 'description' => __( 'Avatars of users who are currently online', 'buddypress' ) );
+		parent::__construct( false, $name = __( "Who's Online Avatars", 'buddypress' ), $widget_ops );
+	}
+
+	function widget($args, $instance) {
+		global $bp;
+
+	    extract( $args );
+
+		echo $before_widget;
+		echo $before_title
+		   . $instance['title']
+		   . $after_title; ?>
+
+		<?php if ( bp_has_members( 'user_id=0&type=online&per_page=' . $instance['max_members'] . '&max=' . $instance['max_members'] . '&populate_extras=0' ) ) : ?>
+			<div class="avatar-block">
+				<?php while ( bp_members() ) : bp_the_member(); ?>
+					<div class="item-avatar">
+						<a href="<?php bp_member_permalink() ?>" title="<?php bp_member_name() ?>"><?php bp_member_avatar() ?></a>
+					</div>
+				<?php endwhile; ?>
+			</div>
+		<?php else: ?>
+
+			<div class="widget-error">
+				<?php _e( 'There are no users currently online', 'buddypress' ) ?>
+			</div>
+
+		<?php endif; ?>
+
+		<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['title'] = strip_tags( $new_instance['title'] );
+		$instance['max_members'] = strip_tags( $new_instance['max_members'] );
+
+		return $instance;
+	}
+
+	function form( $instance ) {
+		$defaults = array(
+			'title' => __( "Who's Online", 'buddypress' ),
+			'max_members' => 15
+		);
+		$instance = wp_parse_args( (array) $instance, $defaults );
+
+		$title = strip_tags( $instance['title'] );
+		$max_members = strip_tags( $instance['max_members'] );
+		?>
+
+		<p><label for="bp-core-widget-title"><?php _e('Title:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" style="width: 100%" /></label></p>
+
+		<p><label for="bp-core-widget-members-max"><?php _e('Max Members to show:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'max_members' ); ?>" name="<?php echo $this->get_field_name( 'max_members' ); ?>" type="text" value="<?php echo esc_attr( $max_members ); ?>" style="width: 30%" /></label></p>
+	<?php
+	}
+}
+
+/*** RECENTLY ACTIVE WIDGET *****************/
+
+class BP_Core_Recently_Active_Widget extends WP_Widget {
+
+	function bp_core_recently_active_widget() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		$widget_ops = array( 'description' => __( 'Avatars of recently active members', 'buddypress' ) );
+		parent::__construct( false, $name = __( 'Recently Active Member Avatars', 'buddypress' ), $widget_ops );
+	}
+
+	function widget($args, $instance) {
+		global $bp;
+
+		extract( $args );
+
+		echo $before_widget;
+		echo $before_title
+		   . $instance['title']
+		   . $after_title; ?>
+
+		<?php if ( bp_has_members( 'user_id=0&type=active&per_page=' . $instance['max_members'] . '&max=' . $instance['max_members'] . '&populate_extras=0' ) ) : ?>
+			<div class="avatar-block">
+				<?php while ( bp_members() ) : bp_the_member(); ?>
+					<div class="item-avatar">
+						<a href="<?php bp_member_permalink() ?>" title="<?php bp_member_name() ?>"><?php bp_member_avatar() ?></a>
+					</div>
+				<?php endwhile; ?>
+			</div>
+		<?php else: ?>
+
+			<div class="widget-error">
+				<?php _e( 'There are no recently active members', 'buddypress' ) ?>
+			</div>
+
+		<?php endif; ?>
+
+		<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['title'] = strip_tags( $new_instance['title'] );
+		$instance['max_members'] = strip_tags( $new_instance['max_members'] );
+
+		return $instance;
+	}
+
+	function form( $instance ) {
+		$defaults = array(
+			'title' => 'Recently Active Members',
+			'max_members' => 15
+		);
+		$instance = wp_parse_args( (array) $instance, $defaults );
+
+		$title = strip_tags( $instance['title'] );
+		$max_members = strip_tags( $instance['max_members'] );
+		?>
+
+		<p><label for="bp-core-widget-members-title"><?php _e('Title:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" style="width: 100%" /></label></p>
+
+		<p><label for="bp-core-widget-members-max"><?php _e('Max Members to show:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'max_members' ); ?>" name="<?php echo $this->get_field_name( 'max_members' ); ?>" type="text" value="<?php echo esc_attr( $max_members ); ?>" style="width: 30%" /></label></p>
+	<?php
+	}
+}
+
+/** Widget AJAX ******************/
+
+function bp_core_ajax_widget_members() {
+	global $bp;
+
+	check_ajax_referer( 'bp_core_widget_members' );
+
+	switch ( $_POST['filter'] ) {
+		case 'newest-members':
+			$type = 'newest';
+			break;
+
+		case 'recently-active-members':
+			$type = 'active';
+			break;
+
+		case 'popular-members':
+			if ( bp_is_active( 'friends' ) )
+				$type = 'popular';
+			else
+				$type = 'active';
+
+			break;
+	}
+
+	if ( bp_has_members( 'user_id=0&type=' . $type . '&per_page=' . $_POST['max-members'] . '&max=' . $_POST['max-members'] . '&populate_extras=0' ) ) : ?>
+		<?php echo '0[[SPLIT]]'; // return valid result. TODO: remove this. ?>
+		<div class="avatar-block">
+			<?php while ( bp_members() ) : bp_the_member(); ?>
+				<li class="vcard">
+					<div class="item-avatar">
+						<a href="<?php bp_member_permalink() ?>"><?php bp_member_avatar() ?></a>
+					</div>
+
+					<div class="item">
+						<div class="item-title fn"><a href="<?php bp_member_permalink() ?>" title="<?php bp_member_name() ?>"><?php bp_member_name() ?></a></div>
+						<?php if ( 'active' == $type ) : ?>
+							<div class="item-meta"><span class="activity"><?php bp_member_last_active() ?></span></div>
+						<?php elseif ( 'newest' == $type ) : ?>
+							<div class="item-meta"><span class="activity"><?php bp_member_registered() ?></span></div>
+						<?php elseif ( bp_is_active( 'friends' ) ) : ?>
+							<div class="item-meta"><span class="activity"><?php bp_member_total_friend_count() ?></span></div>
+						<?php endif; ?>
+					</div>
+				</li>
+
+			<?php endwhile; ?>
+		</div>
+
+	<?php else: ?>
+		<?php echo "-1[[SPLIT]]<li>"; ?>
+		<?php _e( 'There were no members found, please try another filter.', 'buddypress' ) ?>
+		<?php echo "</li>"; ?>
+	<?php endif;
+}
+add_action( 'wp_ajax_widget_members', 'bp_core_ajax_widget_members' );
+
+?>
Index: /tags/1.5-beta-3/bp-core/bp-core-wpabstraction.php
===================================================================
--- /tags/1.5-beta-3/bp-core/bp-core-wpabstraction.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/bp-core-wpabstraction.php	(revision 5057)
@@ -0,0 +1,156 @@
+<?php
+/*****
+ * WordPress Abstraction
+ *
+ * The functions within this file will detect the version of WordPress you are running
+ * and will alter the environment so BuddyPress can run regardless.
+ *
+ * The code below mostly contains function mappings. This code is subject to change once
+ * the 3.0 WordPress version merge takes place.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Only add abstraction functions if WordPress is not in multisite mode
+ */
+if ( !is_multisite() ) {
+	global $wpdb;
+
+	$wpdb->base_prefix = $wpdb->prefix;
+	$wpdb->blogid      = BP_ROOT_BLOG;
+
+	if ( !function_exists( 'get_blog_option' ) ) {
+		function get_blog_option( $blog_id, $option_name, $default = false ) {
+			return get_option( $option_name, $default );
+		}
+	}
+
+	if ( !function_exists( 'update_blog_option' ) ) {
+		function update_blog_option( $blog_id, $option_name, $value ) {
+			return update_option( $option_name, $value );
+		}
+	}
+
+	if ( !function_exists( 'switch_to_blog' ) ) {
+		function switch_to_blog() {
+			return bp_get_root_blog_id();
+		}
+	}
+
+	if ( !function_exists( 'restore_current_blog' ) ) {
+		function restore_current_blog() {
+			return bp_get_root_blog_id();
+		}
+	}
+
+	if ( !function_exists( 'get_blogs_of_user' ) ) {
+		function get_blogs_of_user() {
+			return false;
+		}
+	}
+
+	if ( !function_exists( 'update_blog_status' ) ) {
+		function update_blog_status() {
+			return true;
+		}
+	}
+
+	if ( !function_exists( 'is_subdomain_install' ) ) {
+		function is_subdomain_install() {
+			if ( ( defined( 'VHOST' ) && 'yes' == VHOST ) || ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) )
+				return true;
+
+			return false;
+		}
+	}
+}
+
+function bp_core_get_status_sql( $prefix = false ) {
+	if ( !is_multisite() )
+		return "{$prefix}user_status = 0";
+	else
+		return "{$prefix}spam = 0 AND {$prefix}deleted = 0 AND {$prefix}user_status = 0";
+}
+
+/**
+ * Multibyte encoding fallback functions
+ *
+ * The PHP multibyte encoding extension is not enabled by default. In cases where it is not enabled,
+ * these functions provide a fallback.
+ *
+ * Borrowed from MediaWiki, under the GPLv2. Thanks!
+ */
+if ( !function_exists( 'mb_strlen' ) ) {
+	/**
+	 * Fallback implementation of mb_strlen, hardcoded to UTF-8.
+	 * @param string $str
+	 * @param string $enc optional encoding; ignored
+	 * @return int
+	 */
+	function mb_strlen( $str, $enc = '' ) {
+		$counts = count_chars( $str );
+		$total = 0;
+
+		// Count ASCII bytes
+		for( $i = 0; $i < 0x80; $i++ ) {
+			$total += $counts[$i];
+		}
+
+		// Count multibyte sequence heads
+		for( $i = 0xc0; $i < 0xff; $i++ ) {
+			$total += $counts[$i];
+		}
+		return $total;
+	}
+}
+
+if ( !function_exists( 'mb_strpos' ) ) {
+	/**
+	 * Fallback implementation of mb_strpos, hardcoded to UTF-8.
+	 * @param $haystack String
+	 * @param $needle String
+	 * @param $offset String: optional start position
+	 * @param $encoding String: optional encoding; ignored
+	 * @return int
+	 */
+	function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
+		$needle = preg_quote( $needle, '/' );
+
+		$ar = array();
+		preg_match( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
+
+		if( isset( $ar[0][1] ) ) {
+			return $ar[0][1];
+		} else {
+			return false;
+		}
+	}
+}
+
+if ( !function_exists( 'mb_strrpos' ) ) {
+	/**
+	 * Fallback implementation of mb_strrpos, hardcoded to UTF-8.
+	 * @param $haystack String
+	 * @param $needle String
+	 * @param $offset String: optional start position
+	 * @param $encoding String: optional encoding; ignored
+	 * @return int
+	 */
+	function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
+		$needle = preg_quote( $needle, '/' );
+
+		$ar = array();
+		preg_match_all( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
+
+		if( isset( $ar[0] ) && count( $ar[0] ) > 0 &&
+			isset( $ar[0][count( $ar[0] ) - 1][1] ) ) {
+			return $ar[0][count( $ar[0] ) - 1][1];
+		} else {
+			return false;
+		}
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.css	(revision 5057)
@@ -0,0 +1,1 @@
+#wpadminbar .quicklinks li#wp-admin-bar-my-account ul,#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar ul{left:auto;right:0;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar>a img,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar>a img{margin:-2px -5px 0 23px;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul{left:auto;right:30px;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul ul,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul{left:auto;right:0;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul{left:auto;right:0;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul{left:auto;right:0;}
Index: /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin-bar-rtl.dev.css	(revision 5057)
@@ -0,0 +1,27 @@
+#wpadminbar .quicklinks li#wp-admin-bar-my-account ul,
+#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar ul {
+	left: auto;
+	right: 0;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar > a img,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar > a img {
+	margin: -2px -5px 0 23px;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul {
+	left: auto;
+	right: 30px;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul ul,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul {
+	left: auto;
+ 	right: 0;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul {
+	left: auto;
+ 	right: 0;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul {
+	left: auto;
+ 	right: 0;
+}
Index: /tags/1.5-beta-3/bp-core/css/admin-bar.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin-bar.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin-bar.css	(revision 5057)
@@ -0,0 +1,1 @@
+#wpadminbar .quicklinks li#wp-admin-bar-my-account ul,#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar ul{left:0;right:auto;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar>a{border-left:none;background:url(../images/admin-bar-sprite.png?d=11122010) top left no-repeat;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar>a img,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar>a img{width:16px;height:16px;display:inline;border:1px solid #999;vertical-align:middle;margin:-2px 23px 0 -5px;padding:0;background:#eee;float:none;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul{left:30px;}#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul ul,#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul{left:0;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar>a{border-left:none;background:url(../images/admin-bar-sprite.png?d=11122010) top left no-repeat;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar>a img{width:16px;height:16px;display:inline;border:1px solid #999;vertical-align:middle;margin:-2px 23px 0 -5px;padding:0;background:#eee;float:none;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul{left:0;}#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul{left:0;}#wpadminbar .quicklinks li#wp-admin-bar-my-account a span.count,#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar a span.count,#wpadminbar .quicklinks li#wp-admin-bar-bp-notifications #ab-pending-notifications{background:#eee;color:#333;text-shadow:none;display:inline;padding:2px 5px;font-size:10px;font-weight:bold;-moz-border-radius:10px;-khtml-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;}
Index: /tags/1.5-beta-3/bp-core/css/admin-bar.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin-bar.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin-bar.dev.css	(revision 5057)
@@ -0,0 +1,58 @@
+#wpadminbar .quicklinks li#wp-admin-bar-my-account ul,
+#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar ul {
+	left: 0;
+	right: auto;
+}
+
+/* Displayed User */
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar > a {
+	border-left: none;
+	background: url(../images/admin-bar-sprite.png?d=11122010) top left no-repeat;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar > a img,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar > a img {
+	width: 16px;
+	height: 16px;
+	display: inline;
+	border: 1px solid #999;
+	vertical-align: middle;
+	margin: -2px 23px 0 -5px;
+	padding: 0;
+	background: #eee;
+	float: none;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul {
+	left: 30px;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-user-admin-with-avatar ul ul,
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul {
+ 	left: 0;
+}
+
+/* Displayed Group */
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar > a {
+	border-left: none;
+	background: url(../images/admin-bar-sprite.png?d=11122010) top left no-repeat;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul {
+	left: 0;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-group-admin-with-avatar ul ul {
+ 	left: 0;
+}
+#wpadminbar .quicklinks li#wp-admin-bar-my-account a span.count,
+#wpadminbar .quicklinks li#wp-admin-bar-my-account-with-avatar a span.count,
+#wpadminbar .quicklinks li#wp-admin-bar-bp-notifications #ab-pending-notifications {
+	background: #eee;
+	color: #333;
+	text-shadow: none;
+	display: inline;
+	padding: 2px 5px;
+	font-size: 10px;
+	font-weight: bold;
+	-moz-border-radius: 10px;
+	-khtml-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	border-radius: 10px;
+}
Index: /tags/1.5-beta-3/bp-core/css/admin.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin.css	(revision 5057)
@@ -0,0 +1,1 @@
+div#icon-buddypress{background:url(../images/icons32.png) no-repeat -4px 0;}div#bp-admin{color:#555;min-width:680px;}div#bp-admin p{line-height:170%;}form#bp-admin-form{margin:0 10px;}div#bp-admin-nav{background:#e0e0e0;margin:15px -10px 10px;padding:10px 10px 0 10px;height:30px;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;}div#bp-admin-nav div.submit{position:relative;float:right;margin-top:-36px!important;}div#bp-admin-nav div.submit input{padding:6px 10px;}div#bp-admin-nav ol{list-style-type:decimal;margin:0;padding:0;}div#bp-admin-nav ol li{float:left;margin:0 10px 0 0;color:#666;}div#bp-admin-nav ol li{padding:8px 12px;display:block;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none;}div#bp-admin-nav ol li.current{background:#f9f9f9;color:#555;}div#bp-admin-nav ol li span.complete{background:url(../images/completed.gif) center left no-repeat;padding:6px;}div.component{width:323px;float:left;margin:15px 30px 0 0;}div.component div.radio{position:relative;background:#e0e0e0;padding:5px 10px;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;top:-6px;}div.component img{margin-top:10px;}div.component p{margin-left:4px;}div.component h5{font-size:18px;float:left;margin:0 0 0 4px;width:100px;}div.component div.radio{font-size:11px;float:right;top:5px;}div#bp-admin div.prev-next,div#bp-admin div.prev-next p,div#bp-admin div.submit,div#bp-admin div.submit p{margin:0;padding:0;}div#bp-admin div.submit{margin-top:30px;}div#bp-admin div.prev-next{text-align:right;margin-top:0;}div#bp-admin div.prev-next{margin:0 -20px;padding:0 20px 10px;}div#bp-admin table td{font-family:helvetica,arial,tahoma,sans-serif;vertical-align:top;}div#bp-admin table.form-table th{border-right:1px solid #eee;padding:15px 25px 15px 0;color:#555;vertical-align:top;}div#bp-admin table.form-table tr{border-bottom:1px dotted #f0f0f0;}div#bp-admin table.form-table td{padding-left:25px;font-size:12px;}div#bp-admin table.form-table th h5{font-size:14px;margin:0;line-height:150%;}div#bp-admin table.form-table th img{margin-top:5px;}div#bp-admin table.form-table th p{margin:3px 0 0 0;}div#bp-admin code,div#bp-admin kbd{font-size:12px;background:#f0f0f0;}div#bp-admin div#message code,div#bp-admin div#message kbd{background:#ffe293;}a.button{padding-top:4px;}a.installed,a.installed:hover,a.installed:active{padding-left:15px;color:#888;border-color:#ccc;cursor:default;}a.installed span{background:url(../images/installed.gif) left center no-repeat;padding:6px 10px 7px 6px;margin-left:-5px;}
Index: /tags/1.5-beta-3/bp-core/css/admin.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/admin.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/admin.dev.css	(revision 5057)
@@ -0,0 +1,160 @@
+div#icon-buddypress {
+	background: url( ../images/icons32.png ) no-repeat -4px 0px;
+}
+
+div#bp-admin {
+	color: #555;
+	min-width: 680px;
+	}
+	div#bp-admin p { line-height: 170%; }
+
+form#bp-admin-form {
+	margin: 0 10px;
+}
+
+div#bp-admin-nav {
+	background: #e0e0e0;
+	margin: 15px -10px 10px;
+	padding: 10px 10px 0 10px;
+	height: 30px;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+}
+div#bp-admin-nav div.submit {
+	position: relative;
+	float: right;
+	margin-top: -36px !important;
+}
+div#bp-admin-nav div.submit input {
+	padding: 6px 10px;
+}
+	div#bp-admin-nav ol {
+		list-style-type: decimal;
+		margin: 0;
+		padding: 0;
+	}
+		div#bp-admin-nav ol li {
+			float: left;
+			margin: 0 10px 0 0;
+			color: #666;
+		}
+			div#bp-admin-nav ol li {
+				padding: 8px 12px;
+				display: block;
+				-moz-border-radius: 3px;
+				-webkit-border-radius: 3px;
+				border-radius: 3px;
+				text-decoration: none;
+			}
+				div#bp-admin-nav ol li.current {
+					background: #f9f9f9;
+					color: #555;
+				}
+	div#bp-admin-nav ol li span.complete {
+		background: url( ../images/completed.gif ) center left no-repeat;
+		padding: 6px;
+	}
+
+div.component {
+	width: 323px;
+	float: left;
+	margin: 15px 30px 0 0;
+}
+	div.component div.radio {
+		position: relative;
+		background: #e0e0e0;
+		padding: 5px 10px;
+		-moz-border-radius: 6px;
+		-webkit-border-radius: 6px;
+		border-radius: 6px;
+		top: -6px;
+	}
+
+	div.component img {
+		margin-top: 10px;
+	}
+
+	div.component p {
+		margin-left: 4px;
+	}
+
+	div.component h5 {
+		font-size: 18px;
+		float: left;
+		margin: 0 0 0 4px;
+		width: 100px;
+	}
+
+	div.component div.radio {
+		font-size: 11px;
+		float: right;
+		top: 5px;
+	}
+
+div#bp-admin div.prev-next, div#bp-admin div.prev-next p,
+div#bp-admin div.submit, div#bp-admin div.submit p {
+	margin: 0;
+	padding: 0;
+}
+div#bp-admin div.submit { margin-top: 30px; }
+div#bp-admin div.prev-next { text-align: right; margin-top: 0; }
+
+div#bp-admin div.prev-next {
+	margin: 0 -20px;
+	padding: 0 20px 10px;
+}
+
+div#bp-admin table td {
+	font-family: helvetica, arial, tahoma, sans-serif;
+	vertical-align: top;
+}
+
+div#bp-admin table.form-table th {
+	border-right: 1px solid #eee;
+	padding: 15px 25px 15px 0;
+	color: #555;
+	vertical-align: top;
+}
+	div#bp-admin table.form-table tr {
+		border-bottom: 1px dotted #f0f0f0;
+	}
+	div#bp-admin table.form-table td { padding-left: 25px; font-size: 12px; }
+
+	div#bp-admin table.form-table th h5 {
+		font-size: 14px;
+		margin: 0;
+		line-height: 150%;
+	}
+
+	div#bp-admin table.form-table th img {
+		margin-top: 5px;
+	}
+
+	div#bp-admin table.form-table th p { margin: 3px 0 0 0; }
+
+div#bp-admin code, div#bp-admin kbd {
+	font-size: 12px;
+	background: #f0f0f0;
+}
+
+div#bp-admin div#message code, div#bp-admin div#message kbd {
+	background: #ffe293;
+}
+
+a.button {
+	padding-top: 4px;
+}
+
+a.installed, a.installed:hover, a.installed:active {
+	padding-left: 15px;
+	color: #888;
+	border-color: #ccc;
+	cursor: default;
+}
+
+a.installed span {
+	background: url( ../images/installed.gif) left center no-repeat;
+	padding: 6px 10px 7px 6px;
+	margin-left: -5px;
+}
Index: /tags/1.5-beta-3/bp-core/css/buddybar-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/buddybar-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/buddybar-rtl.css	(revision 5057)
@@ -0,0 +1,1 @@
+#wp-admin-bar{right:0;left:auto;}#wp-admin-bar div#admin-bar-logo{right:10px;left:auto;}#wp-admin-bar li{text-align:right;}#wp-admin-bar li.no-arrow a{padding-left:15px;padding-right:0;}#admin-bar-logo{float:right;}#wp-admin-bar ul li{float:right;background:url(_inc/images/admin-menu-arrow.gif) 12% 53% no-repeat;padding-left:11px;padding-right:0;}#wp-admin-bar ul li.no-arrow{padding-left:0;}#wp-admin-bar ul li.align-right{left:0;right:auto;}#wp-admin-bar ul li ul{right:-999em;left:auto;margin-right:0;}#wp-admin-bar ul li ul li{float:right;}#wp-admin-bar ul li ul ul{margin:-25px 184px 0 0;}#wp-admin-bar ul li:hover ul,#wp-admin-bar ul li li:hover ul,#wp-admin-bar ul li.sfhover ul,#wp-admin-bar ul li ul li.sfhover ul{right:auto;}#wp-admin-bar ul li.align-right:hover ul{left:0;right:auto;}#wp-admin-bar ul li:hover ul ul,#wp-admin-bar li.sfhover ul li ul{right:-999em;left:auto;}#wp-admin-bar img.avatar{float:right;margin-left:8px;margin-right:0;}#wp-admin-bar span.activity{margin-right:34px;margin-left:0;}#wp-admin-bar ul li#bp-adminbar-notifications-menu a span{margin-right:2px;margin-left:0;}
Index: /tags/1.5-beta-3/bp-core/css/buddybar-rtl.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/buddybar-rtl.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/buddybar-rtl.dev.css	(revision 5057)
@@ -0,0 +1,77 @@
+#wp-admin-bar {
+	right: 0;
+	left: auto;
+}
+#wp-admin-bar div#admin-bar-logo {
+	right: 10px;
+	left: auto;
+}
+#wp-admin-bar li {
+	text-align: right;
+}
+#wp-admin-bar li.no-arrow a {
+	padding-left: 15px;
+	padding-right: 0;
+}
+#admin-bar-logo {
+	float: right;
+}
+
+#wp-admin-bar ul li {/* all list items */
+	float: right;
+	background: url( _inc/images/admin-menu-arrow.gif ) 12% 53% no-repeat;
+	padding-left: 11px;
+	padding-right: 0;
+}
+#wp-admin-bar ul li.no-arrow {
+	padding-left: 0;
+}
+#wp-admin-bar ul li.align-right {
+	left: 0;
+	right: auto;
+}
+
+/* second-level lists */
+#wp-admin-bar ul li ul {
+	right: -999em;
+	left: auto;
+	margin-right: 0;
+}
+#wp-admin-bar ul li ul li {
+	float: right;
+}
+
+/* third-and-above-level lists */
+#wp-admin-bar ul li ul ul {
+	margin: -25px 184px 0 0;
+}
+#wp-admin-bar ul li:hover ul,
+#wp-admin-bar ul li li:hover ul,
+#wp-admin-bar ul li.sfhover ul,
+#wp-admin-bar ul li ul li.sfhover ul {/* lists nested under hovered list items */
+	right: auto;
+}
+#wp-admin-bar ul li.align-right:hover ul {
+	left: 0;
+	right: auto;
+}
+#wp-admin-bar ul li:hover ul ul,
+#wp-admin-bar li.sfhover ul li ul {
+	right: -999em;
+	left: auto;
+}
+
+/* Menu item css */
+#wp-admin-bar img.avatar {
+	float: right;
+	margin-left: 8px;
+	margin-right: 0;
+}
+#wp-admin-bar span.activity {
+	margin-right: 34px;
+	margin-left: 0;
+}
+#wp-admin-bar ul li#bp-adminbar-notifications-menu a span {
+	margin-right: 2px;
+	margin-left: 0;
+}
Index: /tags/1.5-beta-3/bp-core/css/buddybar.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/buddybar.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/buddybar.css	(revision 5057)
@@ -0,0 +1,1 @@
+body,body.wp-admin{padding-top:25px!important;}#wp-admin-bar{position:fixed;top:0;left:0;z-index:99;height:25px;font-size:11px;width:100%;z-index:1000;}#wp-admin-bar .padder{position:relative;padding:0;width:100%;margin:0 auto;background:url(../images/60pc_black.png);height:25px;}body#bp-default #wp-admin-bar .padder{min-width:960px;max-width:1250px;}#wp-admin-bar *{z-index:999;}#wp-admin-bar div#admin-bar-logo{position:absolute;top:5px;left:10px;}#wp-admin-bar a img{border:none;}#wp-admin-bar li{list-style:none;margin:0;padding:0;line-height:100%;text-align:left;}#wp-admin-bar li a{padding:7px 15px 7px 15px;color:#eee;text-decoration:none;font-size:11px;}#wp-admin-bar li.alt{border:none;}#wp-admin-bar li.no-arrow a{padding-right:15px;}#wp-admin-bar ul li ul li a span{display:none;}#wp-admin-bar li:hover,#wp-admin-bar li.hover{position:static;}#admin-bar-logo{float:left;font-weight:bold;font-size:11px;padding:5px 8px;margin:0;text-decoration:none;color:#fff;}body#bp-default #admin-bar-logo{padding:2px 8px;}#wp-admin-bar ul{margin:0;list-style:none;line-height:1;cursor:pointer;height:auto;padding:0;}#wp-admin-bar ul li{padding:0;float:left;position:relative;background:url(../images/admin-menu-arrow.gif) 88% 53% no-repeat;padding-right:11px;}#wp-admin-bar ul li.no-arrow{background:none;padding-right:0;}#wp-admin-bar ul li ul li{background-image:none;}#wp-admin-bar ul li.align-right{position:absolute;right:0;}#wp-admin-bar ul li a{display:block;}#wp-admin-bar ul.main-nav li:hover,#wp-admin-bar ul.main-nav li.sfhover,#wp-admin-bar ul.main-nav li ul li.sfhover{background-color:#333;}#wp-admin-bar ul li ul{position:absolute;width:185px;left:-999em;margin-left:0;background:#333;border:1px solid #222;-moz-box-shadow:0 4px 8px rgba(0,0,0,0.1);-webkit-box-shadow:0 4px 8px rgba(0,0,0,0.1);-moz-border-radius:3px;-webkit-border-radius:3px;-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;}#wp-admin-bar ul li>ul{border-top:none;}#wp-admin-bar ul li ul a{color:#eee;}#wp-admin-bar ul li ul li{float:left;width:174px;margin:0;}#wp-admin-bar ul li ul li:hover a{color:#fff;}#wp-admin-bar ul li div.admin-bar-clear{clear:both;}#wp-admin-bar ul.main-nav li ul li:hover,#wp-admin-bar ul.main-nav li ul li.sfhover,#wp-admin-bar ul.main-nav li ul li.sfhover{background-color:#222;}#wp-admin-bar ul li ul ul{margin:-25px 0 0 184px;-moz-border-radius:3px;-webkit-border-radius:3px;}#wp-admin-bar ul li ul li:hover ul li a{color:#eee;}#wp-admin-bar ul li ul li ul li:hover a{color:#fff;}#wp-admin-bar ul li:hover ul,#wp-admin-bar ul li ul li:hover ul,#wp-admin-bar ul li.sfhover ul,#wp-admin-bar ul li ul li.sfhover ul{left:auto;}#wp-admin-bar ul li.align-right:hover ul{right:0;}#wp-admin-bar ul li:hover ul ul,#wp-admin-bar li.sfhover ul li ul{left:-999em;}#wp-admin-bar img.avatar{float:left;margin-right:8px;}#wp-admin-bar span.activity{display:block;margin-left:34px;padding:0;}#wp-admin-bar ul.author-list li a{height:17px;}#wp-admin-bar ul li#bp-adminbar-notifications-menu a span{padding:0 6px;margin-left:2px;background:#fff;color:#000;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;}
Index: /tags/1.5-beta-3/bp-core/css/buddybar.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-core/css/buddybar.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/css/buddybar.dev.css	(revision 5057)
@@ -0,0 +1,201 @@
+body,
+body.wp-admin {
+	padding-top: 25px !important;
+}
+#wp-admin-bar {
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 99;
+	height: 25px;
+	font-size: 11px;
+	width: 100%;
+	z-index: 1000;
+}
+#wp-admin-bar .padder {
+	position: relative;
+	padding: 0;
+	width: 100%;
+	margin: 0 auto;
+	background: url( ../images/60pc_black.png );
+	height: 25px;
+}
+body#bp-default #wp-admin-bar .padder {
+	max-width: 1250px;
+}
+#wp-admin-bar * {
+	z-index: 999;
+}
+#wp-admin-bar div#admin-bar-logo {
+	position: absolute;
+	top: 5px;
+	left: 10px;
+}
+#wp-admin-bar a img {
+	border: none;
+}
+#wp-admin-bar li {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	line-height: 100%;
+	text-align: left;
+}
+#wp-admin-bar li a {
+	padding: 7px 15px 7px 15px;
+	color: #eee;
+	text-decoration: none;
+	font-size: 11px;
+}
+#wp-admin-bar li.alt {
+	border: none;
+}
+#wp-admin-bar li.no-arrow a {
+	padding-right: 15px;
+}
+#wp-admin-bar ul li ul li a span {
+	display: none;
+}
+#wp-admin-bar li:hover,
+#wp-admin-bar li.hover {
+	position: static;
+}
+#admin-bar-logo {
+	float: left;
+	font-weight: bold;
+	font-size: 11px;
+	padding: 5px 8px;
+	margin: 0;
+	text-decoration: none;
+	color: #fff;
+}
+body#bp-default #admin-bar-logo {
+	padding: 2px 8px;
+}
+
+/* all lists */
+#wp-admin-bar ul {
+	margin: 0;
+	list-style: none;
+	line-height: 1;
+	cursor: pointer;
+	height: auto;
+	padding: 0;
+}
+
+/* all list items */
+#wp-admin-bar ul li {
+	padding: 0;
+	float: left;
+	position: relative;
+	background: url( ../images/admin-menu-arrow.gif ) 88% 53% no-repeat;
+	padding-right: 11px;
+}
+#wp-admin-bar ul li.no-arrow {
+	background: none;
+	padding-right: 0;
+}
+#wp-admin-bar ul li ul li {
+	background-image: none;
+}
+#wp-admin-bar ul li.align-right {
+	position: absolute;
+	right: 0;
+}
+#wp-admin-bar ul li a {
+	display: block;
+}
+#wp-admin-bar ul.main-nav li:hover,
+#wp-admin-bar ul.main-nav li.sfhover,
+#wp-admin-bar ul.main-nav li ul li.sfhover {
+	background-color: #333;
+}
+
+/* second-level lists */
+#wp-admin-bar ul li ul {
+	position: absolute;
+	width: 185px;
+	left: -999em;
+	margin-left: 0;
+	background: #333;
+	border: 1px solid #222;
+	-moz-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+	-webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	-moz-border-radius-topleft: 0;
+	-webkit-border-top-left-radius: 0;
+	-moz-border-radius-topright: 0;
+	-webkit-border-top-right-radius: 0;
+}
+#wp-admin-bar ul li > ul {
+	border-top: none;
+}
+#wp-admin-bar ul li ul a {
+	color: #eee;
+}
+#wp-admin-bar ul li ul li {
+	float: left;
+	width: 174px;
+	margin: 0;
+}
+#wp-admin-bar ul li ul li:hover a {
+	color: #fff;
+}
+#wp-admin-bar ul li div.admin-bar-clear {
+	clear: both;
+}
+#wp-admin-bar ul.main-nav li ul li:hover,
+#wp-admin-bar ul.main-nav li ul li.sfhover,
+#wp-admin-bar ul.main-nav li ul li.sfhover {
+	background-color: #222;
+}
+
+/* third-and-above-level lists */
+#wp-admin-bar ul li ul ul {
+	margin: -25px 0 0 184px;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+}
+#wp-admin-bar ul li ul li:hover ul li a {
+	color: #eee;
+}
+#wp-admin-bar ul li ul li ul li:hover a {
+	color: #fff;
+}
+#wp-admin-bar ul li:hover ul,
+#wp-admin-bar ul li ul li:hover ul,
+#wp-admin-bar ul li.sfhover ul,
+#wp-admin-bar ul li ul li.sfhover ul {
+	left: auto;
+}
+#wp-admin-bar ul li.align-right:hover ul {
+	right: 0;
+}
+#wp-admin-bar ul li:hover ul ul,
+#wp-admin-bar li.sfhover ul li ul {
+	left: -999em;
+}
+
+/* Menu item css */
+#wp-admin-bar img.avatar {
+	float: left;
+	margin-right: 8px;
+}
+#wp-admin-bar span.activity {
+	display: block;
+	margin-left: 34px;
+	padding: 0;
+}
+#wp-admin-bar ul.author-list li a {
+	height: 17px;
+}
+#wp-admin-bar ul li#bp-adminbar-notifications-menu a span {
+	padding: 0 6px;
+	margin-left: 2px;
+	background: #fff;
+	color: #000;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
Index: /tags/1.5-beta-3/bp-core/deprecated/1.5.php
===================================================================
--- /tags/1.5-beta-3/bp-core/deprecated/1.5.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/deprecated/1.5.php	(revision 5057)
@@ -0,0 +1,516 @@
+<?php
+/**
+ * Deprecated Functions
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ * @deprecated Since 1.5
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/** Loader ********************************************************************/
+
+function bp_setup_root_components() {
+	do_action( 'bp_setup_root_components' );
+}
+add_action( 'bp_init', 'bp_setup_root_components', 6 );
+
+/** WP Abstraction ************************************************************/
+
+/**
+ * bp_core_is_multisite()
+ *
+ * This function originally served as a wrapper when WordPress and WordPress MU were separate entities.
+ * Use is_multisite() instead.
+ *
+ * @deprecated 1.5
+ * @deprecated Use is_multisite()
+ */
+function bp_core_is_multisite() {
+	_deprecated_function( __FUNCTION__, '1.5', 'is_multisite()' );
+	return is_multisite();
+}
+
+/**
+ * bp_core_is_main_site
+ *
+ * Checks if current blog is root blog of site. Deprecated in 1.5.
+ *
+ * @deprecated 1.5
+ * @deprecated Use is_main_site()
+ * @package BuddyPress
+ * @param int $blog_id optional blog id to test (default current blog)
+ * @return bool True if not multisite or $blog_id is main site
+ * @since 1.2.6
+ */
+function bp_core_is_main_site( $blog_id = '' ) {
+	_deprecated_function( __FUNCTION__, '1.5', 'is_main_site()' );
+	return is_main_site( $blog_id );
+}
+
+/** Admin ******************************************************************/
+
+/**
+ * In BuddyPress 1.1 - 1.2.x, this function provided a better version of add_menu_page()
+ * that allowed positioning of menus. Deprecated in 1.5 in favour of a WP core function.
+ *
+ * @deprecated 1.5
+ * @deprecated Use add_menu_page().
+ * @since 1.1
+ */
+function bp_core_add_admin_menu_page( $args = '' ) {
+	global $_registered_pages, $admin_page_hooks, $menu;
+
+	_deprecated_function( __FUNCTION__, '1.5', 'Use add_menu_page()' );
+
+	$defaults = array(
+		'access_level' => 2,
+		'file'         => false,
+		'function'     => false,
+		'icon_url'     => false,
+		'menu_title'   => '',
+		'page_title'   => '',
+		'position'     => 100
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	$file     = plugin_basename( $file );
+	$hookname = get_plugin_page_hookname( $file, '' );
+
+	$admin_page_hooks[$file] = sanitize_title( $menu_title );
+
+	if ( !empty( $function ) && !empty ( $hookname ) )
+		add_action( $hookname, $function );
+
+	if ( empty( $icon_url ) )
+		$icon_url = 'images/generic.png';
+	elseif ( is_ssl() && 0 === strpos( $icon_url, 'http://' ) )
+		$icon_url = 'https://' . substr( $icon_url, 7 );
+
+	do {
+		$position++;
+	} while ( !empty( $menu[$position] ) );
+
+	$menu[$position] = array ( $menu_title, $access_level, $file, $page_title, 'menu-top ' . $hookname, $hookname, $icon_url );
+	$_registered_pages[$hookname] = true;
+
+	return $hookname;
+}
+/** Activity ******************************************************************/
+
+function bp_is_activity_permalink() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_is_single_activity' );
+	bp_is_single_activity();
+}
+
+/** Core **********************************************************************/
+
+function bp_core_get_wp_profile() {
+	_deprecated_function( __FUNCTION__, '1.5' );
+
+	global $bp;
+
+	$ud = get_userdata( $bp->displayed_user->id ); ?>
+
+<div class="bp-widget wp-profile">
+	<h4><?php _e( 'My Profile' ) ?></h4>
+
+	<table class="wp-profile-fields">
+
+		<?php if ( $ud->display_name ) : ?>
+
+			<tr id="wp_displayname">
+				<td class="label"><?php _e( 'Name', 'buddypress' ); ?></td>
+				<td class="data"><?php echo $ud->display_name; ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+		<?php if ( $ud->user_description ) : ?>
+
+			<tr id="wp_desc">
+				<td class="label"><?php _e( 'About Me', 'buddypress' ); ?></td>
+				<td class="data"><?php echo $ud->user_description; ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+		<?php if ( $ud->user_url ) : ?>
+
+			<tr id="wp_website">
+				<td class="label"><?php _e( 'Website', 'buddypress' ); ?></td>
+				<td class="data"><?php echo make_clickable( $ud->user_url ); ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+		<?php if ( $ud->jabber ) : ?>
+
+			<tr id="wp_jabber">
+				<td class="label"><?php _e( 'Jabber', 'buddypress' ); ?></td>
+				<td class="data"><?php echo $ud->jabber; ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+		<?php if ( $ud->aim ) : ?>
+
+			<tr id="wp_aim">
+				<td class="label"><?php _e( 'AOL Messenger', 'buddypress' ); ?></td>
+				<td class="data"><?php echo $ud->aim; ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+		<?php if ( $ud->yim ) : ?>
+
+			<tr id="wp_yim">
+				<td class="label"><?php _e( 'Yahoo Messenger', 'buddypress' ); ?></td>
+				<td class="data"><?php echo $ud->yim; ?></td>
+			</tr>
+
+		<?php endif; ?>
+
+	</table>
+</div>
+
+<?php
+}
+
+function bp_is_home() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_is_my_profile' );
+	return bp_is_my_profile();
+}
+
+/**
+ * Is the user on the front page of the site?
+ *
+ * @deprecated 1.5
+ * @deprecated Use is_front_page()
+ * @return bool
+ */
+function bp_is_front_page() {
+	_deprecated_function( __FUNCTION__, '1.5', "is_front_page()" );
+	return is_front_page();
+}
+
+/**
+ * Is the front page of the site set to the Activity component?
+ *
+ * @deprecated 1.5
+ * @deprecated Use bp_is_component_front_page( 'activity' )
+ * @return bool
+ */
+function bp_is_activity_front_page() {
+	_deprecated_function( __FUNCTION__, '1.5', "bp_is_component_front_page( 'activity' )" );
+	return bp_is_component_front_page( 'activity' );
+}
+
+function bp_is_member() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_is_user' );
+	bp_is_user();
+}
+
+function bp_loggedinuser_link() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_logged_in_user_link' );
+	bp_loggedin_user_link();
+}
+
+/**
+ * Only show the search form if there are available objects to search for.
+ * Deprecated in 1.5; not used anymore.
+ *
+ * @return bool
+ */
+function bp_search_form_enabled() {
+	_deprecated_function( __FUNCTION__, '1.5', 'No longer required.' );
+	return apply_filters( 'bp_search_form_enabled', true );
+}
+
+/**
+ * Template tag version of bp_get_page_title()
+ *
+ * @deprecated 1.5
+ * @deprecated Use wp_title()
+ * @since 1.0
+ */
+function bp_page_title() {
+	echo bp_get_page_title();
+}
+	/**
+	 * Prior to BuddyPress 1.5, this was used to generate the page's <title> text.
+	 * Now, just simply use wp_title().
+	 *
+	 * @deprecated 1.5
+	 * @deprecated Use wp_title()
+	 * @since 1.0
+	 */
+	function bp_get_page_title() {
+		_deprecated_function( __FUNCTION__, '1.5', 'wp_title()' );
+		$title = wp_title( '|', false, 'right' ) . get_bloginfo( 'name', 'display' );
+
+		// Backpat for BP 1.2 filter
+		$title = apply_filters( 'bp_page_title', esc_attr( $title ), esc_attr( $title ) );
+
+		return apply_filters( 'bp_get_page_title', $title );
+	}
+
+/**
+ * Generate a link to log out. Last used in BP 1.2-beta. You should be using wp_logout_url().
+ *
+ * @deprecated 1.5
+ * @deprecated Use wp_logout_url()
+ * @since 1.0
+ */
+function bp_log_out_link() {
+	_deprecated_function( __FUNCTION__, '1.5', 'wp_logout_url()' );
+
+	$logout_link = '<a href="' . wp_logout_url( bp_get_root_domain() ) . '">' . __( 'Log Out', 'buddypress' ) . '</a>';
+	echo apply_filters( 'bp_logout_link', $logout_link );
+}
+
+/**
+ * Send an email and a BP notification on receipt of an @-mention in a group
+ *
+ * @deprecated 1.5
+ * @deprecated Deprecated in favor of the more general bp_activity_at_message_notification()
+ */
+function groups_at_message_notification( $content, $poster_user_id, $group_id, $activity_id ) {
+	global $bp;
+
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_activity_at_message_notification()' );
+
+	/* Scan for @username strings in an activity update. Notify each user. */
+	$pattern = '/[@]+([A-Za-z0-9-_\.@]+)/';
+	preg_match_all( $pattern, $content, $usernames );
+
+	/* Make sure there's only one instance of each username */
+	if ( !$usernames = array_unique( $usernames[1] ) )
+		return false;
+
+	$group = new BP_Groups_Group( $group_id );
+
+	foreach( (array)$usernames as $username ) {
+		if ( !$receiver_user_id = bp_core_get_userid( $username ) )
+			continue;
+
+		/* Check the user is a member of the group before sending the update. */
+		if ( !groups_is_user_member( $receiver_user_id, $group_id ) )
+			continue;
+
+		// Now email the user with the contents of the message (if they have enabled email notifications)
+		if ( 'no' != bp_get_user_meta( $receiver_user_id, 'notification_activity_new_mention', true ) ) {
+			$poster_name = bp_core_get_user_displayname( $poster_user_id );
+
+			$message_link = bp_activity_get_permalink( $activity_id );
+			$settings_link = bp_core_get_user_domain( $receiver_user_id ) . bp_get_settings_slug() . '/notifications/';
+
+			$poster_name = stripslashes( $poster_name );
+			$content = bp_groups_filter_kses( stripslashes( $content ) );
+
+			// Set up and send the message
+			$ud = bp_core_get_core_userdata( $receiver_user_id );
+			$to = $ud->user_email;
+			$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+			$subject  = '[' . $sitename . '] ' . sprintf( __( '%1$s mentioned you in the group "%2$s"', 'buddypress' ), $poster_name, $group->name );
+
+$message = sprintf( __(
+'%1$s mentioned you in the group "%2$s":
+
+"%3$s"
+
+To view and respond to the message, log in and visit: %4$s
+
+---------------------
+', 'buddypress' ), $poster_name, $group->name, $content, $message_link );
+
+			$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+			/* Send the message */
+			$to = apply_filters( 'groups_at_message_notification_to', $to );
+			$subject = apply_filters( 'groups_at_message_notification_subject', $subject, $group, $poster_name );
+			$message = apply_filters( 'groups_at_message_notification_message', $message, $group, $poster_name, $content, $message_link, $settings_link );
+
+			wp_mail( $to, $subject, $message );
+		}
+	}
+
+	do_action( 'bp_groups_sent_mention_email', $usernames, $subject, $message, $content, $poster_user_id, $group_id, $activity_id );
+}
+
+/**
+ * BP 1.5 simplified notification functions a bit
+ */
+function bp_core_delete_notifications_for_user_by_type( $user_id, $component_name, $component_action ) {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_core_delete_notifications_by_type()' );
+	return BP_Core_Notification::delete_for_user_by_type( $user_id, $component_name, $component_action );
+}
+
+function bp_core_delete_notifications_for_user_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id = false ) {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_core_delete_notifications_by_item_id()' );
+	return BP_Core_Notification::delete_for_user_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id );
+}
+
+/**
+ * In BP 1.5, these functions were renamed for greater consistency
+ */
+function bp_forum_directory_permalink() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_forums_directory_permalink()' );
+	bp_forums_directory_permalink();
+}
+	function bp_get_forum_directory_permalink() {
+		_deprecated_function( __FUNCTION__, '1.5', 'bp_get_forums_directory_permalink()' );
+		return bp_get_forums_directory_permalink();
+	}
+
+/**
+ * Last used by core in BP 1.1. The markup was merged into DTheme's header.php template.
+ */
+function bp_search_form() {
+	_deprecated_function( __FUNCTION__, '1.1', 'No longer required.' );
+
+	$form = '
+		<form action="' . bp_search_form_action() . '" method="post" id="search-form">
+			<input type="text" id="search-terms" name="search-terms" value="" />
+			' . bp_search_form_type_select() . '
+
+			<input type="submit" name="search-submit" id="search-submit" value="' . __( 'Search', 'buddypress' ) . '" />
+			' . wp_nonce_field( 'bp_search_form' ) . '
+		</form>
+	';
+
+	echo apply_filters( 'bp_search_form', $form );
+}
+
+/**
+ * Some _is_ function had their names normalized
+ */
+function bp_is_profile_edit() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_is_user_profile_edit()' );
+	return bp_is_user_profile_edit();
+}
+
+function bp_is_change_avatar() {
+	_deprecated_function( __FUNCTION__, '1.5', 'bp_is_user_change_avatar()' );
+	return bp_is_user_change_avatar();
+}
+
+/** Theme *********************************************************************/
+
+/**
+ * Contains functions which were moved out of BP-Default's functions.php
+ * in BuddyPress 1.5.
+ *
+ * @since 1.5
+ */
+function bp_dtheme_deprecated() {
+	if ( !function_exists( 'bp_dtheme_wp_pages_filter' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this function filtered the dropdown on the
+	 * Settings > Reading screen for selecting the page to show on front to
+	 * include "Activity Stream." As of 1.5.x, it is no longer required.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated No longer required.
+	 * @param string $page_html A list of pages as a dropdown (select list)
+	 * @return string
+	 * @see wp_dropdown_pages()
+	 * @since 1.2
+	 */
+	function bp_dtheme_wp_pages_filter( $page_html ) {
+		_deprecated_function( __FUNCTION__, '1.5', "No longer required." );
+		return $page_html;
+	}
+	endif;
+
+	if ( !function_exists( 'bp_dtheme_page_on_front_update' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this function hijacked the saving of page on front setting to save the activity stream setting.
+	 * As of 1.5.x, it is no longer required.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated No longer required.
+	 * @param $string $oldvalue Previous value of get_option( 'page_on_front' )
+	 * @param $string $oldvalue New value of get_option( 'page_on_front' )
+	 * @return string
+	 * @since 1.2
+	 */
+	function bp_dtheme_page_on_front_update( $oldvalue, $newvalue ) {
+		_deprecated_function( __FUNCTION__, '1.5', "No longer required." );
+		if ( !is_admin() || !is_super_admin() )
+			return false;
+
+		return $oldvalue;
+	}
+	endif;
+
+	if ( !function_exists( 'bp_dtheme_page_on_front_template' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this function loaded the activity stream template if the front page display settings allow.
+	 * As of 1.5.x, it is no longer required.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated No longer required.
+	 * @param string $template Absolute path to the page template
+	 * @return string
+	 * @since 1.2
+	 */
+	function bp_dtheme_page_on_front_template( $template ) {
+		_deprecated_function( __FUNCTION__, '1.5', "No longer required." );
+		return $template;
+	}
+	endif;
+
+	if ( !function_exists( 'bp_dtheme_fix_get_posts_on_activity_front' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this forced the page ID as a string to stop the get_posts query from kicking up a fuss.
+	 * As of 1.5.x, it is no longer required.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated No longer required.
+	 * @since 1.2
+	 */
+	function bp_dtheme_fix_get_posts_on_activity_front() {
+		_deprecated_function( __FUNCTION__, '1.5', "No longer required." );
+	}
+	endif;
+
+	if ( !function_exists( 'bp_dtheme_fix_the_posts_on_activity_front' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this was used as part of the code that set the activity stream to be on the front page.
+	 * As of 1.5.x, it is no longer required.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated No longer required.
+	 * @param array $posts Posts as retrieved by WP_Query
+	 * @return array
+	 * @since 1.2.5
+	 */
+	function bp_dtheme_fix_the_posts_on_activity_front( $posts ) {
+		_deprecated_function( __FUNCTION__, '1.5', "No longer required." );
+		return $posts;
+	}
+	endif;
+
+	if ( !function_exists( 'bp_dtheme_add_blog_comments_js' ) ) :
+	/**
+	 * In BuddyPress 1.2.x, this added the javascript needed for blog comment replies.
+	 * As of 1.5.x, we recommend that you enqueue the comment-reply javascript in your theme's header.php.
+	 *
+	 * @deprecated 1.5
+	 * @deprecated Enqueue the comment-reply script in your theme's header.php.
+	 * @since 1.2
+	 */
+	function bp_dtheme_add_blog_comments_js() {
+		_deprecated_function( __FUNCTION__, '1.5', "Enqueue the comment-reply script in your theme's header.php." );
+		if ( is_singular() && bp_is_blog_page() && get_option( 'thread_comments' ) )
+			wp_enqueue_script( 'comment-reply' );
+	}
+	endif;
+}
+add_action( 'after_setup_theme', 'bp_dtheme_deprecated', 15 );
+?>
Index: /tags/1.5-beta-3/bp-core/js/update.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-core/js/update.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/js/update.dev.js	(revision 5057)
@@ -0,0 +1,11 @@
+jQuery(document).ready( function($) {
+	var toggle = $('#site-tracking-enabled');
+
+	if ( !$(toggle).is(':checked') ) {
+		$('#site-tracking-page-selector').hide();
+	}
+
+	$(toggle).click(function(){
+		$('#site-tracking-page-selector').toggle('fast');
+	});
+},jQuery );
Index: /tags/1.5-beta-3/bp-core/js/update.js
===================================================================
--- /tags/1.5-beta-3/bp-core/js/update.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/js/update.js	(revision 5057)
@@ -0,0 +1,1 @@
+jQuery(document).ready(function(b){var a=b("#site-tracking-enabled");if(!b(a).is(":checked")){b("#site-tracking-page-selector").hide()}b(a).click(function(){b("#site-tracking-page-selector").toggle("fast")})},jQuery);
Index: /tags/1.5-beta-3/bp-core/js/widget-members.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-core/js/widget-members.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/js/widget-members.dev.js	(revision 5057)
@@ -0,0 +1,49 @@
+jQuery(document).ready( function() {
+	jQuery(".widget div#members-list-options a").live('click',
+		function() {
+			var link = this;
+			jQuery(link).addClass('loading');
+
+			jQuery(".widget div#members-list-options a").removeClass("selected");
+			jQuery(this).addClass('selected');
+
+			jQuery.post( ajaxurl, {
+				action: 'widget_members',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce': jQuery("input#_wpnonce-members").val(),
+				'max-members': jQuery("input#members_widget_max").val(),
+				'filter': jQuery(this).attr('id')
+			},
+			function(response)
+			{
+				jQuery(link).removeClass('loading');
+				member_wiget_response(response);
+			});
+
+			return false;
+		}
+	);
+});
+
+function member_wiget_response(response) {
+	response = response.substr(0, response.length-1);
+	response = response.split('[[SPLIT]]');
+
+	if ( response[0] != "-1" ) {
+		jQuery(".widget ul#members-list").fadeOut(200,
+			function() {
+				jQuery(".widget ul#members-list").html(response[1]);
+				jQuery(".widget ul#members-list").fadeIn(200);
+			}
+		);
+
+	} else {
+		jQuery(".widget ul#members-list").fadeOut(200,
+			function() {
+				var message = '<p>' + response[1] + '</p>';
+				jQuery(".widget ul#members-list").html(message);
+				jQuery(".widget ul#members-list").fadeIn(200);
+			}
+		);
+	}
+}
Index: /tags/1.5-beta-3/bp-core/js/widget-members.js
===================================================================
--- /tags/1.5-beta-3/bp-core/js/widget-members.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-core/js/widget-members.js	(revision 5057)
@@ -0,0 +1,1 @@
+jQuery(document).ready(function(){jQuery(".widget div#members-list-options a").live("click",function(){var a=this;jQuery(a).addClass("loading");jQuery(".widget div#members-list-options a").removeClass("selected");jQuery(this).addClass("selected");jQuery.post(ajaxurl,{action:"widget_members",cookie:encodeURIComponent(document.cookie),_wpnonce:jQuery("input#_wpnonce-members").val(),"max-members":jQuery("input#members_widget_max").val(),filter:jQuery(this).attr("id")},function(b){jQuery(a).removeClass("loading");member_wiget_response(b)});return false})});function member_wiget_response(a){a=a.substr(0,a.length-1);a=a.split("[[SPLIT]]");if(a[0]!="-1"){jQuery(".widget ul#members-list").fadeOut(200,function(){jQuery(".widget ul#members-list").html(a[1]);jQuery(".widget ul#members-list").fadeIn(200)})}else{jQuery(".widget ul#members-list").fadeOut(200,function(){var b="<p>"+a[1]+"</p>";jQuery(".widget ul#members-list").html(b);jQuery(".widget ul#members-list").fadeIn(200)})}};
Index: /tags/1.5-beta-3/bp-forums/bb-config.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bb-config.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bb-config.php	(revision 5057)
@@ -0,0 +1,11 @@
+<?php
+/***
+ * *** IMPORTANT ****
+ * This file will stop people from accessing your bbPress installation directly.
+ * It is very important from a security standpoint that this file is not moved.
+ * Your actual bb-config.php file will be installed in the root of your WordPress
+ * installation once you have set up the forums component in BuddyPress.
+ */
+
+header("HTTP/1.0 403 Forbidden"); die;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-action.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-action.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-action.php	(revision 5057)
@@ -0,0 +1,5 @@
+<?php
+require_once('../bb-load.php');
+
+bb_auth();
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-ajax.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-ajax.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-ajax.php	(revision 5057)
@@ -0,0 +1,264 @@
+<?php
+
+define( 'BB_IS_ADMIN', true );
+define( 'DOING_AJAX', true );
+
+require_once('../bb-load.php');
+
+if ( !class_exists( 'WP_Ajax_Response' ) )
+	require_once( BACKPRESS_PATH . 'class.wp-ajax-response.php' );
+
+require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+if ( !$bb_current_id = bb_get_current_user_info( 'id' ) )
+	die('-1');
+
+function bb_grab_results() {
+	global $ajax_results;
+	$ajax_results = @ unserialize(func_get_arg(0));
+	if ( false === $ajax_results )
+		$ajax_results = func_get_args();
+	return;
+}
+
+$id = (int) @$_POST['id'];
+
+switch ( $action = $_POST['action'] ) :
+case 'add-tag' : // $id is topic_id
+	if ( !bb_current_user_can('edit_tag_by_on', $bb_current_id, $id) )
+		die('-1');
+
+	bb_check_ajax_referer( "add-tag_$id" );
+
+	global $tag, $topic;
+	add_action('bb_tag_added', 'bb_grab_results', 10, 3);
+	add_action('bb_already_tagged', 'bb_grab_results', 10, 3);
+	$tag_name = @$_POST['tag'];
+	$tag_name = stripslashes( $tag_name );
+
+	$topic = get_topic( $id );
+	if ( !$topic )
+		die('0');
+
+	$tag_name = rawurldecode($tag_name);
+	$x = new WP_Ajax_Response();
+	foreach ( bb_add_topic_tags( $id, $tag_name ) as $tag_id ) {
+		if ( !is_numeric($tag_id) || !$tag = bb_get_tag( (int) $tag_id, bb_get_current_user_info( 'id' ), $topic->topic_id ) ) {
+			if ( !$tag = bb_get_tag( $tag_id ) ) {
+				continue;
+			}
+		}
+		$tag->tag_id  = $tag_id;
+		$tag->user_id = bb_get_current_user_info( 'id' );
+		$tag_id_val   = $tag->tag_id . '_' . $tag->user_id;
+		$tag->raw_tag = esc_attr( $tag_name );
+		$x->add( array(
+			'what' => 'tag',
+			'id'   => $tag_id_val,
+			'data' => _bb_list_tag_item( $tag, array( 'list_id' => 'tags-list', 'format' => 'list' ) )
+		) );
+	}
+	$x->send();
+	break;
+
+case 'delete-tag' :
+	list($tag_id, $user_id) = explode('_', $_POST['id']);
+	$tag_id   = (int) $tag_id;
+	$user_id  = (int) $user_id;
+	$topic_id = (int) $_POST['topic_id'];
+
+	if ( !bb_current_user_can('edit_tag_by_on', $user_id, $topic_id) )
+		die('-1');
+
+	bb_check_ajax_referer( "remove-tag_$tag_id|$topic_id" );
+
+	add_action('bb_rpe_tag_removed', 'bb_grab_results', 10, 3);
+
+	$tag   = bb_get_tag( $tag_id );
+	$user  = bb_get_user( $user_id );
+	$topic = get_topic ( $topic_id );
+	if ( !$tag || !$topic )
+		die('0');
+	if ( false !== bb_remove_topic_tag( $tag_id, $user_id, $topic_id ) )
+		die('1');
+	break;
+
+case 'dim-favorite' :
+	$user_id  = bb_get_current_user_info( 'id' );
+
+	if ( !$topic = get_topic( $id ) )
+		die('0');
+
+	if ( !bb_current_user_can( 'edit_favorites_of', $user_id ) )
+		die('-1');
+
+	bb_check_ajax_referer( "toggle-favorite_$topic->topic_id" );
+
+	$is_fav = is_user_favorite( $user_id, $topic->topic_id );
+
+	if ( 1 == $is_fav ) {
+		if ( bb_remove_user_favorite( $user_id, $topic->topic_id ) )
+			die('1');
+	} elseif ( false === $is_fav ) {
+		if ( bb_add_user_favorite( $user_id, $topic->topic_id ) )
+			die('1');
+	}
+	break;
+
+case 'delete-post' : // $id is post_id
+	if ( !bb_current_user_can( 'delete_post', $id ) )
+		die('-1');
+
+	bb_check_ajax_referer( "delete-post_$id" );
+
+	$status = (int) $_POST['status'];
+
+	if ( !$bb_post = bb_get_post( $id ) )
+		die('0');
+
+	if ( $status == $bb_post->post_status )
+		die('1'); // We're already there
+
+	if ( bb_delete_post( $id, $status ) ) {
+		$topic = get_topic( $bb_post->topic_id );
+		if ( 0 == $topic->topic_posts ) {
+			// If we deleted the only post, send back a WP_Ajax_Response object with a URL to redirect to
+			if ( $ref = wp_get_referer() ) {
+				$ref_topic = bb_get_topic_from_uri( $ref );
+				if ( $ref_topic && $ref_topic->topic_id == $topic->topic_id )
+					$ref = add_query_arg( 'view', 'all', $ref );
+				if ( false === strpos( $ref, '#' ) )
+					$ref .= "#post-{$bb_post->post_id}";
+			} else {
+				$ref = add_query_arg( 'view', 'all', get_post_link( $topic->topic_id ) );
+			}
+			$x = new WP_Ajax_Response( array(
+				'what' => 'post',
+				'id' => $bb_post->post_id,
+				'data' => $ref,
+			) );
+			$x->send();
+		}
+		die('1');
+	}
+	break;
+/*
+case 'add-post' : // Can put last_modified stuff back in later
+	bb_check_ajax_referer( $action );
+	$error = false;
+	$post_id = 0;
+	$topic_id = (int) $_POST['topic_id'];
+	$last_mod = (int) $_POST['last_mod'];
+	if ( !$post_content = trim($_POST['post_content']) )
+		$error = new WP_Error( 'no-content', __('You need to actually submit some content!') );
+	if ( !bb_current_user_can( 'write_post', $topic_id ) )
+		die('-1');
+	if ( !$topic = get_topic( $topic_id ) )
+		die('0');
+	if ( !topic_is_open( $topic_id ) )
+		$error = new WP_Error( 'topic-closed', __('This topic is closed.') );
+	if ( $throttle_time = bb_get_option( 'throttle_time' ) )
+		if ( isset($bb_current_user->data->last_posted) && time() < $bb_current_user->data->last_posted + $throttle_time && !bb_current_user_can('throttle') )
+			$error = new WP_Error( 'throttle-limit', sprintf( __('Slow down!  You can only post every %d seconds.'), $throttle_time );
+
+	if ( !$error ) :
+		if ( !$post_id = bb_new_post( $topic_id, rawurldecode($_POST['post_content']) ) )
+			die('0');
+
+		$bb_post = bb_get_post( $post_id );
+
+		$new_page = bb_get_page_number( $bb_post->post_position );
+
+		ob_start();
+			echo "<li id='post-$post_id'>";
+			bb_post_template();
+			echo '</li>';
+		$data = ob_get_contents();
+		ob_end_clean();
+	endif;
+	$x = new WP_Ajax_Response( array(
+		'what' => 'post',
+		'id' => $post_id,
+		'data' => is_wp_error($error) ? $error : $data
+	) );
+	$x->send();
+	break;
+*/
+case 'add-forum' :
+	if ( !bb_current_user_can( 'manage_forums' ) )
+		die('-1');
+
+	bb_check_ajax_referer( $action );
+
+	if ( !$forum_id = bb_new_forum( $_POST ) )
+		die('0');
+
+	global $forums_count;
+	$forums_count = 2; // Hack
+
+	$data = bb_forum_row( $forum_id, false, true );
+
+	$forum = bb_get_forum( $forum_id );
+	if ( $forum->forum_parent ) {
+		$siblings = bb_get_forums( $forum->forum_parent );
+		$last_sibling = array_pop( $siblings );
+		if ( $last_sibling->forum_id == $forum_id )
+			$last_sibling = array_pop( $siblings );
+		if ( $last_sibling ) {
+			$position = "forum-$last_sibling->forum_id";
+		} else {
+			$position = "+forum-$forum->forum_parent";
+			$data = "<ul id='forum-root-$forum->forum_parent' class='list-block holder'>$data</ul>";
+		}
+	} else {
+		$position = 1;
+	}
+
+	$x = new WP_Ajax_Response( array(
+		'what' => 'forum',
+		'id' => $forum_id,
+		'data' => $data,
+		'position' => $position,
+		'supplemental' => array( 'name' => $forum->forum_name )
+	) );
+	$x->send();
+	break;
+
+case 'order-forums' :
+	if ( !bb_current_user_can( 'manage_forums' ) )
+		die('-1');
+
+	bb_check_ajax_referer( $action );
+
+	if ( !is_array($_POST['order']) )
+		die('0');
+
+	global $bbdb;
+
+	$forums = array();
+
+	bb_get_forums(); // cache
+
+	foreach ( $_POST['order'] as $pos => $forum_id ) :
+		$forum = $bbdb->escape_deep( get_object_vars( bb_get_forum( $forum_id ) ) );
+		$forum['forum_order'] = $pos;
+		$forums[(int) $forum_id] = $forum;
+	endforeach;
+
+	foreach ( $_POST['root'] as $root => $ids )
+		foreach ( $ids as $forum_id )
+			$forums[(int) $forum_id]['forum_parent'] = (int) $root;
+
+	foreach ( $forums as $forum )
+		bb_update_forum( $forum );
+
+	die('1');
+	break;
+
+default :
+	do_action( 'bb_ajax_' . $_POST['action'] );
+	break;
+endswitch;
+
+die('0');
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-base.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-base.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-base.php	(revision 5057)
@@ -0,0 +1,23 @@
+<?php
+require_once('admin.php');
+
+do_action( $bb_admin_page . '_pre_head' );
+
+bb_get_admin_header(); 
+?>
+
+<div class="wrap">
+
+<?php if ( is_callable($bb_admin_page) ) : call_user_func( $bb_admin_page ); else : ?>
+
+<h2><?php _e( 'Page not found' ); ?></h2>
+<?php
+bb_admin_notice( __( 'There is no administration page at the requested address. Please check the address you entered and try again.' ), 'error' );
+do_action( 'bb_admin_notices' );
+?>
+
+<?php endif; ?>
+
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-footer.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-footer.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-footer.php	(revision 5057)
@@ -0,0 +1,33 @@
+			</div>
+			<div class="clear"></div>
+			<!-- If you like showing off the fact that your server rocks -->
+			<!-- <p id="bbShowOff">
+<?php
+global $bbdb;
+printf(
+__( 'This page generated in %s seconds, using %d queries.' ),
+bb_number_format_i18n( bb_timer_stop(), 2 ),
+bb_number_format_i18n( $bbdb->num_queries )
+);
+?>
+			</p> -->
+			<div class="clear"></div>
+		</div>
+	</div>
+	<div id="bbFoot">
+		<p id="bbThanks">
+<?php
+printf(
+	__( 'Thank you for using <a href="%s">bbPress</a>. | <a href="%s">Documentation</a> | <a href="%s">Feedback</a>' ),
+	'http://bbpress.org/',
+	'http://bbpress.org/documentation/',
+	'http://bbpress.org/forums/forum/requests-and-feedback'
+);
+?>
+		</p>
+		<p id="bbVersion"><?php printf( __( 'Version %s' ), bb_get_option( 'version' ) ); ?></p>
+	</div>
+
+	<?php do_action( 'bb_admin_footer' ); ?>
+</body>
+</html>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-header.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin-header.php	(revision 5057)
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"<?php bb_language_attributes( '1.1' ); ?>>
+<head>
+	<meta http-equiv="X-UA-Compatible" content="IE=8" />
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title><?php bb_admin_title() ?></title>
+	<link rel="stylesheet" href="<?php bb_uri('bb-admin/style.css', null, BB_URI_CONTEXT_LINK_STYLESHEET_HREF + BB_URI_CONTEXT_BB_ADMIN); ?>" type="text/css" />
+<?php if ( 'rtl' == bb_get_option( 'text_direction' ) ) : ?>
+	<link rel="stylesheet" href="<?php bb_uri('bb-admin/style-rtl.css', null, BB_URI_CONTEXT_LINK_STYLESHEET_HREF + BB_URI_CONTEXT_BB_ADMIN); ?>" type="text/css" />
+<?php endif; do_action('bb_admin_print_scripts'); ?>
+	<!--[if IE 6]>
+	<style type="text/css">
+	ul#bbAdminMenu{ margin: 15px 5px 15px -85px; } body.bb-menu-folded div#bbBody{ margin-left: 110px; }
+	</style>
+	<![endif]-->
+	<link rel="shortcut icon" type="image/ico" href="<?php bb_uri('bb-admin/images/favicon.ico', null, BB_URI_CONTEXT_BB_ADMIN); ?>" />
+	<script type="text/javascript">
+		//<![CDATA[
+		addLoadEvent = function(func){if(typeof jQuery!="undefined")jQuery(document).ready(func);else if(typeof wpOnload!='function'){wpOnload=func;}else{var oldonload=wpOnload;wpOnload=function(){oldonload();func();}}};
+		var userSettings = {'url':'<?php echo $bb->cookie_path; ?>','uid':'<?php if ( ! isset($bb_current_user) ) $bb_current_user = bb_get_current_user(); echo $bb_current_user->ID; ?>','time':'<?php echo time(); ?>'};
+		//]]>
+	</script>
+<?php do_action( 'bb_admin_head' ); ?>
+</head>
+
+<?php
+global $bb_admin_body_class;
+if ( 'f' == bb_get_user_setting( 'fm' ) ) {
+	$bb_admin_body_class .= ' bb-menu-folded';
+}
+?>
+
+<body class="bb-admin no-js <?php echo trim( $bb_admin_body_class ); ?>">
+	<script type="text/javascript">
+		//<![CDATA[
+		(function(){
+			var c = document.body.className;
+			c = c.replace(/no-js/, 'js');
+			document.body.className = c;
+		})();
+		//]]>
+	</script>
+	<div id="bbWrap">
+		<div id="bbContent">
+			<div id="bbHead">
+				<h1><a href="<?php bb_uri(); ?>"><span><?php bb_option('name'); ?></span> <em><?php _e('Visit Site'); ?></em></a></h1>
+				<div id="bbUserInfo">
+					<p>
+						<?php printf( __('Howdy, %1$s'), bb_get_profile_link( array( 'text' => bb_get_current_user_info( 'name' ) ) ) );?>
+						| <?php bb_logout_link( array( 'redirect' => bb_get_uri( null, null, BB_URI_CONTEXT_HEADER ) ) ); ?>
+					</p>
+				</div>
+			</div>
+
+			<div id="bbBody">
+
+<?php bb_admin_menu(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/admin.php	(revision 5057)
@@ -0,0 +1,45 @@
+<?php
+define( 'BB_IS_ADMIN', true );
+
+require_once('../bb-load.php');
+
+bb_ssl_redirect();
+
+bb_auth();
+
+if ( bb_get_option( 'bb_db_version' ) > bb_get_option_from_db( 'bb_db_version' ) ) {
+	bb_safe_redirect( 'upgrade.php' );
+	die();
+}
+
+require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+$bb_admin_page = bb_find_filename( $_SERVER['PHP_SELF'] );
+
+$_check_callback = false;
+if ( $bb_admin_page == 'admin-base.php' ) {
+	$bb_admin_page = (string) @$_GET['plugin'];
+	$_check_callback = true;
+}
+
+wp_enqueue_script( 'common' );
+
+bb_user_settings();
+if ( isset( $_GET['foldmenu'] ) ) {
+	if ( $_GET['foldmenu'] ) {
+		bb_update_user_setting( 'fm', 'f' );
+	} else {
+		bb_delete_user_setting( 'fm' );
+	}
+	bb_safe_redirect( remove_query_arg( 'foldmenu', stripslashes( $_SERVER['REQUEST_URI'] ) ) );
+	die;
+}
+bb_admin_menu_generator();
+bb_get_current_admin_menu();
+
+if ( $_check_callback ) {
+	if ( empty( $bb_registered_plugin_callbacks ) || empty( $bb_admin_page ) || !in_array( $bb_admin_page, $bb_registered_plugin_callbacks ) ) {
+		unset( $bb_admin_page );
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/bb-forum.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/bb-forum.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/bb-forum.php	(revision 5057)
@@ -0,0 +1,61 @@
+<?php
+require_once('admin.php');
+
+if ( !bb_current_user_can('manage_forums') )
+	bb_die(__("You don't have the authority to mess with the forums."));
+
+if ( !isset($_POST['action']) ) {
+	wp_redirect( bb_get_uri('bb-admin/forums.php', null, BB_URI_CONTEXT_HEADER + BB_URI_CONTEXT_BB_ADMIN) );
+	exit;
+}
+
+$sent_from = wp_get_referer();
+
+switch ( $_POST['action'] ) :
+case 'add' :
+	if ( !isset($_POST['forum_name']) || '' === $_POST['forum_name'] )
+		bb_die(__('Bad forum name.  Go back and try again.'));
+
+	bb_check_admin_referer( 'add-forum' );
+
+	if ( false !== bb_new_forum( $_POST ) ) :
+		bb_safe_redirect( $sent_from );
+		exit;
+	else :
+		bb_die(__('The forum was not added'));
+	endif;
+	break;
+case 'update' :
+	bb_check_admin_referer( 'update-forum' );
+
+	if ( !$forums = bb_get_forums() )
+		bb_die(__('No forums to update!'));
+	if ( (int) $_POST['forum_id'] && isset($_POST['forum_name']) && '' !== $_POST['forum_name'] )
+		bb_update_forum( $_POST );
+	foreach ( array('action', 'id') as $arg )
+		$sent_from = remove_query_arg( $arg, $sent_from );
+	bb_safe_redirect( add_query_arg( 'message', 'updated', $sent_from ) );
+	exit;
+	break;
+case 'delete' :
+	bb_check_admin_referer( 'delete-forums' );
+
+	$forum_id = (int) $_POST['forum_id'];
+	$move_topics_forum = (int) $_POST['move_topics_forum'];
+
+	if ( !bb_current_user_can( 'delete_forum', $forum_id ) )
+		bb_die(__("You don't have the authority to kill off the forums."));
+
+	if ( isset($_POST['move_topics']) && $_POST['move_topics'] != 'delete' )
+		bb_move_forum_topics( $forum_id, $move_topics_forum );
+
+	if ( !bb_delete_forum( $forum_id ) )
+		bb_die( __('Error occured while trying to delete forum') );
+
+	foreach ( array('action', 'id') as $arg )
+		$sent_from = remove_query_arg( $arg, $sent_from );
+	bb_safe_redirect( add_query_arg( 'message', 'deleted', $sent_from ) );
+	exit;
+	break;
+endswitch;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-post.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-post.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-post.php	(revision 5057)
@@ -0,0 +1,81 @@
+<?php
+require('admin-action.php');
+
+$post_id = (int) $_GET['id'];
+
+if ( !bb_current_user_can( 'delete_post', $post_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	exit;
+}
+
+bb_check_admin_referer( 'delete-post_' . $post_id );
+
+$status  = (int) $_GET['status'];
+$bb_post = bb_get_post ( $post_id );
+$old_status = (int) $bb_post->post_status;
+
+if ( !$bb_post )
+	bb_die(__('There is a problem with that post, pardner.'));
+
+if ( 0 == $status && 0 != $bb_post->post_status ) // We're undeleting
+	add_filter('bb_delete_post', 'bb_topics_replied_on_undelete_post');
+
+bb_delete_post( $post_id, $status );
+
+$message = '';
+switch ( $old_status ) {
+	case 0:
+		switch ( $status ) {
+			case 0:
+				break;
+			case 1:
+				$message = 'deleted';
+				break;
+			default:
+				$message = 'spammed';
+				break;
+		}
+		break;
+	case 1:
+		switch ( $status ) {
+			case 0:
+				$message = 'undeleted';
+				break;
+			case 1:
+				break;
+			default:
+				$message = 'spammed';
+				break;
+		}
+		break;
+	default:
+		switch ( $status ) {
+			case 0:
+				$message = 'unspammed-normal';
+				break;
+			case 1:
+				$message = 'unspammed-deleted';
+				break;
+			default:
+				break;
+		}
+		break;
+}
+
+$topic = get_topic( $bb_post->topic_id );
+
+if ( $sendto = wp_get_referer() ) {
+	$sendto = remove_query_arg( 'message', $sendto );
+	$sendto = add_query_arg( 'message', $message, $sendto );
+	$send_to_topic = bb_get_topic_from_uri( $sendto );
+	if ( $send_to_topic && $topic->topic_id == $send_to_topic->topic_id )
+		$sendto = add_query_arg( 'view', 'all', $sendto );
+} else if ( $topic->topic_posts == 0 ) {
+	$sendto = get_forum_link( $topic->forum_id );
+} else {
+	$the_page = bb_get_page_number( $bb_post->post_position );
+	$sendto = get_topic_link( $bb_post->topic_id, $the_page );
+}
+
+bb_safe_redirect( $sendto );
+exit;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-topic.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-topic.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/delete-topic.php	(revision 5057)
@@ -0,0 +1,53 @@
+<?php
+require('admin-action.php');
+
+$topic_id = (int) $_GET['id'];
+
+if ( !bb_current_user_can( 'delete_topic', $topic_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	exit;
+}
+
+bb_check_admin_referer( 'delete-topic_' . $topic_id );
+
+$topic = get_topic( $topic_id );
+$old_status = (int) $topic->topic_status;
+
+if ( !$topic )
+	bb_die(__('There is a problem with that topic, pardner.'));
+
+$status = $topic->topic_status ? 0 : 1;
+bb_delete_topic( $topic->topic_id, $status );
+
+$message = '';
+switch ( $old_status ) {
+	case 0:
+		switch ( $status ) {
+			case 0:
+				break;
+			case 1:
+				$message = 'deleted';
+				break;
+		}
+		break;
+	case 1:
+		switch ( $status ) {
+			case 0:
+				$message = 'undeleted';
+				break;
+			case 1:
+				break;
+		}
+		break;
+}
+
+if ( $sendto = wp_get_referer() ) {
+	$sendto = remove_query_arg( 'message', $sendto );
+	$sendto = add_query_arg( 'message', $message, $sendto );
+} elseif ( 0 == $topic->topic_status )
+	$sendto = get_forum_link( $topic->forum_id );
+else
+	$sendto = get_topic_link( $topic_id );
+	
+wp_redirect( $sendto );
+exit;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/export.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/export.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/export.php	(revision 5057)
@@ -0,0 +1,305 @@
+<?php
+
+require_once( '../bb-load.php' );
+require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+define('BB_EXPORT_USERS', 1);
+define('BB_EXPORT_FORUMS', 2);
+define('BB_EXPORT_TOPICS', 4);
+
+// Some example usage of the bitwise export levels (can be defined in bb-config.php)
+//define('BB_EXPORT_LEVEL', BB_EXPORT_USERS);
+//define('BB_EXPORT_LEVEL', BB_EXPORT_USERS + BB_EXPORT_FORUMS);
+//define('BB_EXPORT_LEVEL', BB_EXPORT_USERS + BB_EXPORT_FORUMS + BB_EXPORT_TOPICS);
+
+if ( !defined('BB_EXPORT_LEVEL') )
+	define('BB_EXPORT_LEVEL', 0);
+
+if ( !BB_EXPORT_LEVEL || !bb_current_user_can( 'import_export' ) )
+	bb_die( __('Either export is disabled or you are not allowed to export.') );
+
+// See bb_export_user for syntax
+function _bb_export_object( $object, $properties = null, $tabs = 1 ) {
+	$r = '';
+
+	if ( !$type = $object['type'] )
+		return;
+	unset($object['type']);
+
+	$atts = '';
+	$id = 0;
+	foreach ( $object as $att => $v ) {
+		if ( 'id' == $att ) {
+			$id = $v;
+			$v = $type . '_' . $v;
+		}
+		$atts .= " $att='$v'";
+	}
+	unset($att, $v);
+
+	$r .= str_repeat("\t", $tabs) . "<$type{$atts}>\n";
+
+	foreach ( (array) $properties as $k => $v ) {
+		if ( 'meta' == $k ) {
+			$data = '';
+			foreach ( $v as $mk => $mv )
+				$data .= str_repeat("\t", $tabs + 1) . "<meta key='$mk'><![CDATA[$mv]]></meta>\n";
+		} else {
+			if ( '!' == $k{0} ) {
+				$k = substr($k, 1);
+				$v = "<![CDATA[$v]]>";
+			}
+			$data = str_repeat("\t", $tabs + 1) . "<$k>$v</$k>\n";
+		}
+		$r .= $data;
+	}
+
+	$r .= apply_filters( 'in_bb_export_object_' . $type, '', $id );
+
+	$r .= str_repeat("\t", $tabs) . "</$type>\n\n";
+
+	return $r;
+}
+
+// See bb_export_user for syntax
+function _bb_translate_for_export( $translate, &$data ) {
+	$r = array();
+	foreach ( $translate as $prop => $export ) {
+		if ( '?' == $export{0} ) {
+			$export = substr($export, 1);
+			if ( !$data[$prop] ) {
+				unset($data[$prop]);
+				continue;
+			}
+		}
+		if ( false === $export ) {
+			unset($data[$prop]);
+			continue;
+		}
+		$r[$export] = $data[$prop];
+		unset($data[$prop]);
+	}
+	unset($export, $prop);
+	return $r;
+}
+
+function bb_export_user( $user_id ) {
+	global $bbdb;
+	if ( !$_user = bb_get_user( $user_id ) )
+		return;
+
+	$_user = get_object_vars($_user);
+
+	$atts = array(
+		'type' => 'user',
+		'id' => $_user['ID']
+	);
+
+	// ?url means url is optional.  Only include it in the export if it exists
+	// !title means the title should be wrapped in CDATA
+	// ?! is the correct order, not !?
+	$translate = array(
+		'user_login' => 'login',
+		'user_pass' => 'pass',
+		'user_email' => 'email',
+		'user_url' => '?url',
+		'user_registered' => 'incept',
+		'display_name' => '?!title',
+		'user_nicename' => '?nicename',
+		'user_status' => '?status',
+		'ID' => false
+	);
+
+	$user = _bb_translate_for_export( $translate, $_user );
+
+	$meta = array();
+	foreach ( $_user as $k => $v ) {
+		if ( 0 !== strpos($k, $bbdb->prefix) && isset($_user[$bbdb->prefix . $k]) )
+			continue;
+		$meta[$k] = maybe_serialize($v);
+	}
+	unset($_user, $k, $v);
+
+	$user['meta'] = $meta;
+
+	return _bb_export_object( $atts, $user );
+}
+
+function bb_export_forum( $forum_id ) {
+	if ( !$_forum = bb_get_forum( $forum_id ) )
+		return;
+
+	$_forum = get_object_vars( $_forum );
+
+	$translate = array(
+		'forum_name'   => '!title',
+		'forum_desc'   => '?!content',
+		'forum_parent' => '?parent'
+	);
+
+	$forum = _bb_translate_for_export( $translate, $_forum );
+
+	return _bb_export_object( array('type' => 'forum', 'id' => $_forum['forum_id']), $forum );
+}
+
+function bb_export_topic( $topic_id ) {
+	if ( !$_topic = get_topic( $topic_id ) )
+		return;
+
+	$_topic = get_object_vars( $_topic );
+
+	$atts = array(
+		'type' => 'topic',
+		'id' => $_topic['topic_id'],
+		'author' => 'user_' . $_topic['topic_poster'],
+		'in' => 'forum_' . $_topic['forum_id']
+	);
+
+	$translate = array(
+		'topic_title' => '!title',
+		'topic_start_time' => 'incept',
+		'topic_status' => '?status',
+		'topic_id' => false,
+		'topic_poster' => false,
+		'topic_poster_name' => false,
+		'topic_last_poster' => false,
+		'topic_last_poster_name' => false,
+		'topic_time' => false,
+		'forum_id' => false,
+		'topic_last_post_id' => false,
+		'topic_posts' => false,
+		'tag_count' => false
+	);
+
+	$topic = _bb_translate_for_export( $translate, $_topic );
+
+	$meta = array();
+	foreach ( $_topic as $k => $v )
+		$meta[$k] = maybe_serialize($v);
+	unset($_topic, $k, $v);
+
+	$topic['meta'] = $meta;
+
+	return _bb_export_object( $atts, $topic );
+}
+
+function bb_export_post( $post_id ) {
+	if ( !$_post = bb_get_post( $post_id ) )
+		return;
+
+	$_post = get_object_vars($_post);
+
+	$atts = array(
+		'type' => 'post',
+		'id' => $_post['post_id'],
+		'author' => 'user_' . $_post['poster_id']
+	);
+
+	$translate = array(
+		'post_time' => 'incept',
+		'post_text' => '!content',
+		'post_status' => '?status',
+		'post_id' => false,
+		'poster_id' => false,
+		'forum_id' => false,
+		'topic_id' => false,
+		'post_position' => false
+	);
+
+	$post = _bb_translate_for_export( $translate, $_post );
+
+	$post['meta'] = $_post;
+
+	return _bb_export_object( $atts, $post, 2 );
+}
+
+// One of these things is not like the others...
+function bb_export_tag( $tag ) {
+	// id here is not numeric.  does not currently preserve tagged_on
+	return "\t\t<tag author='user_$tag->user_id' id='tag_$tag->tag'><![CDATA[$tag->raw_tag]]></tag>\n";
+}
+
+function bb_export_topic_tags( $r, $topic_id ) {
+	global $topic_tag_cache;
+	if ( !get_topic( $topic_id ) )
+		return;
+
+	if ( !$tags = bb_get_topic_tags( $topic_id ) )
+		return $r;
+
+	$r .= "\n";
+
+	foreach ( (array) $tags as $tag )
+		$r .= bb_export_tag( $tag );
+	$topic_tag_cache = array();
+
+	return $r;
+}
+
+function bb_export_topic_posts( $r, $topic_id ) {
+	if ( !get_topic( $topic_id ) )
+		return;
+
+	$r .= "\n";
+
+	$page = 1;
+	while ( $posts = get_thread( $topic_id, array( 'post_status' => 'all', 'page' => $page++ ) ) ) {
+		foreach ( $posts as $post )
+			$r .= bb_export_post( $post->post_id );
+	}
+
+	return $r;
+}
+
+function bb_export() {
+	global $bb;
+
+	define( 'BB_EXPORTING', true );
+	do_action( 'bb_pre_export' );
+
+	$bb->use_cache = false; // Turn off hard cache
+	$bb->page_topics = 100;
+
+	echo "<forums-data version='0.75'>\n";
+
+	if (BB_EXPORT_LEVEL & BB_EXPORT_USERS) {
+		$page = 1;
+		while ( ( $users = bb_user_search( array('page' => $page++) ) ) && !is_wp_error( $users ) ) {
+			foreach ( $users as $user )
+				echo bb_export_user( $user->ID );
+		}
+		unset($users, $user, $page);
+	}
+
+	if (BB_EXPORT_LEVEL & BB_EXPORT_FORUMS) {
+		$forums = bb_get_forums();
+		foreach ( $forums as $forum )
+			echo bb_export_forum( $forum->forum_id );
+		unset($forums, $forum);
+	}
+
+	if (BB_EXPORT_LEVEL & BB_EXPORT_TOPICS) {
+		$page = 1;
+		while ( $topics = get_latest_topics( 0, $page++ ) ) {
+			foreach ( $topics as $topic )
+				echo bb_export_topic( $topic->topic_id );
+		}
+		unset($topics, $topic, $page);
+	}
+
+	do_action( 'bb_export' );
+
+	echo '</forums-data>';
+}
+
+add_filter( 'in_bb_export_object_topic', 'bb_export_topic_tags', 10, 2 );
+add_filter( 'in_bb_export_object_topic', 'bb_export_topic_posts', 10, 2 );
+add_filter( 'get_forum_where', 'bb_no_where', 9999 );
+add_filter( 'get_forums_where', 'bb_no_where', 9999 );
+add_filter( 'get_latest_topics_where', 'bb_no_where', 9999 );
+add_filter( 'get_user_where', 'bb_no_where', 9999 );
+add_filter( 'cache_users_where', 'bb_no_where', 9999 );
+
+bb_export();
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/forums.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/forums.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/forums.php	(revision 5057)
@@ -0,0 +1,106 @@
+<?php
+require_once('admin.php');
+
+$forums = bb_get_forums();
+$forums_count = $forums ? count($forums) : 0;
+
+if ( isset($_GET['action']) && 'delete' == $_GET['action'] ) {
+	$forum_to_delete = (int) $_GET['id'];
+	$deleted_forum = bb_get_forum( $forum_to_delete );
+	if ( !$deleted_forum || $forums_count < 2 || !bb_current_user_can( 'delete_forum', $forum_to_delete ) ) {
+		bb_safe_redirect( add_query_arg( array('action' => false, 'id' => false) ) );
+		exit;
+	}
+}
+
+if ( isset($_GET['message']) ) {
+	switch ( $_GET['message'] ) :
+	case 'updated' :
+		bb_admin_notice( __( '<strong>Forum Updated.</strong>' ) );
+		break;
+	case 'deleted' :
+		bb_admin_notice( sprintf(
+			__( '<strong>Forum deleted.</strong>  You should <a href="%s">recount your site information</a>.' ),
+			bb_get_uri('bb-admin/tools-recount.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN)
+		) );
+		break;
+	endswitch;
+}
+
+if ( !isset($_GET['action']) )
+	wp_enqueue_script( 'admin-forums' );
+elseif ( 'delete' == @$_GET['action'] )
+	bb_admin_notice( sprintf( __( 'Are you sure you want to delete the "<strong>%s</strong>" forum?' ), $deleted_forum->forum_name ) );
+
+$bb_admin_body_class = ' bb-admin-forums';
+
+bb_get_admin_header();
+?>
+
+<div class="wrap">
+
+<h2><?php _e('Forums'); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+<?php switch ( @$_GET['action'] ) : ?>
+<?php case 'edit' : ?>
+<?php bb_forum_form( (int) $_GET['id'] ); ?>
+<?php break; case 'delete' : ?>
+
+<form class="settings" method="post" id="delete-forums" action="<?php bb_uri('bb-admin/bb-forum.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>">
+	<fieldset>
+		<legend><?php _e('Delete Forum'); ?></legend>
+		<p><?php _e('This forum contains:'); ?></p>
+		<ul>
+			<li><?php printf(__ngettext('%d topic', '%d topics', $deleted_forum->topics), $deleted_forum->topics); ?></li>
+			<li><?php printf(__ngettext('%d post', '%d posts', $deleted_forum->posts), $deleted_forum->posts); ?></li>
+		</ul>
+		<div id="option-forum-delete-contents">
+			<div class="label"><?php _e( 'Action' ); ?></div>
+			<div class="inputs">
+				<label class="radios">
+					<input type="radio" name="move_topics" id="move-topics-delete" value="delete" /> <?php _e('Delete all topics and posts in this forum. <em>This can never be undone.</em>'); ?>
+				</label>
+				<label class="radios">
+					<input type="radio" name="move_topics" id="move-topics-move" value="move" checked="checked" /> <?php _e('Move topics from this forum into the replacement forum below.'); ?>
+				</label>
+			</div>
+		</div>
+		<div id="option-forum-delete-contents">
+			<label for="move-topics-forum"><?php _e( 'Replacement forum' ); ?></label>
+			<div class="inputs">
+				<?php bb_forum_dropdown( array('id' => 'move_topics_forum', 'callback' => 'strcmp', 'callback_args' => array($deleted_forum->forum_id), 'selected' => $deleted_forum->forum_parent) ); ?>
+			</div>
+		</div>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'delete-forums' ); ?>
+		<input type="hidden" name="action" value="delete" />
+		<input type="hidden" name="forum_id" value="<?php echo $deleted_forum->forum_id; ?>" />
+		<a href="<?php bb_uri('bb-admin/forums.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN); ?>" class="cancel"><?php _e('Cancel') ?></a>
+		<input class="submit delete" type="submit" name="submit" value="<?php _e('Delete Forum') ?>" />
+	</fieldset>
+</form>
+
+<?php break; default : ?>
+
+
+<?php if ( bb_forums( 'type=list&walker=BB_Walker_ForumAdminlistitems' ) ) : ?>
+<ul id="forum-list" class="list:forum list-block holder">
+	<li class="thead list-block"><?php _e('Forum'); ?></li>
+<?php while ( bb_forum() ) : ?>
+<?php bb_forum_row(); ?>
+<?php endwhile; ?>
+</ul>
+<?php endif; // bb_forums() ?>
+
+<hr class="settings" />
+
+<?php bb_forum_form(); ?>
+
+<?php break; endswitch; // action ?>
+
+<div id="ajax-response"></div>
+
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/class.bb-install.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/class.bb-install.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/class.bb-install.php	(revision 5057)
@@ -0,0 +1,2766 @@
+<?php
+/**
+ * bbPress Installation class
+ *
+ * @since 0.9
+ */
+class BB_Install
+{
+	/**
+	 * The file where the class was instantiated
+	 *
+	 * @var string
+	 */
+	var $caller;
+
+	/**
+	 * Whether or not we need to load some of the includes normally loaded by bb-settings.php
+	 *
+	 * @var boolean
+	 */
+	var $load_includes = false;
+
+	/**
+	 * An array of available languages to use in the installer
+	 *
+	 * @var array
+	 */
+	var $languages = array( 'en_US' => 'en_US' );
+
+	/**
+	 * The currently selected language for the installer
+	 *
+	 * @var string
+	 */
+	var $language = 'en_US';
+
+	/**
+	 * The current step in the install process
+	 *
+	 * @var integer
+	 */
+	var $step;
+
+	/**
+	 * Info about config files and their locations
+	 *
+	 * @var array
+	 */
+	var $configs = array(
+		'writable' => false,
+		'bb-config.php' => false,
+		'config.php' => false
+	);
+
+	/**
+	 * An array of the current status of each step
+	 *
+	 * @var array
+	 */
+	var $step_status = array(
+		0 => 'incomplete',
+		1 => 'incomplete',
+		2 => 'incomplete',
+		3 => 'incomplete',
+		4 => 'incomplete'
+	);
+
+	/**
+	 * An array of most strings in use, including errors
+	 *
+	 * @var array
+	 */
+	var $strings = array();
+
+	/**
+	 * The data being manipulated as we go through the forms
+	 *
+	 * @var array
+	 */
+	var $data = array();
+
+	/**
+	 * A boolean that can get flagged to stop posting of a form getting processed
+	 *
+	 * @var boolean
+	 */
+	var $stop_process = false;
+
+	/**
+	 * Keeps track of where the tabindex is up to
+	 *
+	 * @var integer
+	 */
+	var $tabindex = 0;
+
+	/**
+	 * Constructor
+	 *
+	 * Loads everything we might need to do the business
+	 *
+	 * @param string $caller The full path of the file that instantiated the class
+	 * @return boolean Always returns true
+	 */
+	function BB_Install( $caller )
+	{
+		$this->caller = $caller;
+
+		$this->set_initial_step();
+		$this->define_paths();
+
+		// We need to load these when bb-settings.php isn't loaded
+		if ( $this->load_includes ) {
+			require_once( BACKPRESS_PATH . 'functions.core.php' );
+			require_once( BACKPRESS_PATH . 'functions.compat.php' );
+			require_once( BACKPRESS_PATH . 'functions.formatting.php' );
+			require_once( BACKPRESS_PATH . 'functions.plugin-api.php' );
+			require_once( BACKPRESS_PATH . 'class.wp-error.php' );
+			require_once( BB_PATH . BB_INC . 'functions.bb-core.php' );
+			require_once( BB_PATH . BB_INC . 'functions.bb-meta.php' );
+			require_once( BB_PATH . BB_INC . 'class.bp-options.php' );
+			require_once( BACKPRESS_PATH . 'functions.bp-options.php' );
+			require_once( BACKPRESS_PATH . 'functions.kses.php' );
+			require_once( BB_PATH . BB_INC . 'functions.bb-l10n.php' );
+			require_once( BB_PATH . BB_INC . 'functions.bb-template.php' );
+		}
+
+		$this->get_languages();
+		$this->set_language();
+
+		if ( $this->language ) {
+			global $locale;
+			global $l10n;
+			$locale = $this->language;
+			unset( $l10n['default'] );
+
+			if ( !class_exists( 'MO' ) ) {
+				require_once( BACKPRESS_PATH . 'pomo/mo.php' );
+			}
+		}
+
+		// Load the default text localization domain. Doing this twice doesn't hurt too much.
+		bb_load_default_textdomain();
+
+		// Pull in locale data after loading text domain.
+		if ( $this->load_includes ) {
+			require_once( BB_PATH . BB_INC . 'class.bb-locale.php' );
+		}
+		global $bb_locale;
+		$bb_locale = new BB_Locale();
+
+		$this->prepare_strings();
+		$this->check_prerequisites();
+		$this->check_configs();
+
+		if ( $this->step > -1 ) {
+			$this->set_step();
+			$this->prepare_data();
+			$this->process_form();
+		}
+
+		return true;
+	}
+
+	/**
+	 * Set initial step
+	 *
+	 * Sets the step from the post data and keeps it within range
+	 *
+	 * @return integer The calculated step
+	 */
+	function set_initial_step()
+	{
+		// Set the step based on the $_POST value or 0
+		$this->step = $_POST['step'] ? (integer) $_POST['step'] : 0;
+
+		// Make sure the requested step is from 0-4
+		if ( 0 > $this->step || 4 < $this->step ) {
+			$this->step = 0;
+		}
+		return $this->step;
+	}
+
+	/**
+	 * Prepare text strings
+	 *
+	 * Sets up many of the strings to be used by the class that may
+	 * be later subject to change due to processing of the forms
+	 */
+	function prepare_strings()
+	{
+		$this->strings = array(
+			-1 => array(
+				'title'       => __( 'bbPress &rsaquo; Error' ),
+				'h1'          => __( 'Oh dear!' ),
+				'messages'    => array()
+			),
+			0 => array(
+				'title'       => sprintf( __( '%1$s &rsaquo; %2$s' ), __( 'bbPress installer' ), __( 'Welcome' ) ),
+				'h2'          => __( 'Welcome to the bbPress installer' ),
+				'status'      => '',
+				'messages'    => array(),
+				'intro'       => array(
+					__( 'We\'re now going to go through a few steps to get you up and running.' ),
+					__( 'Ready? Then let\'s get started!' )
+				)
+			),
+			1 => array(
+				'title'       => sprintf( __( '%1$s &rsaquo; %2$s' ), __( 'bbPress installer' ), __( 'Step 1' ) ),
+				'h2'          => sprintf( __( '%1$s - %2$s' ), __( 'Step 1' ), __( 'Database configuration' ) ),
+				'status'      => '',
+				'intro'       => array(
+					__( 'In this step you need to enter your database connection details. The installer will attempt to create a file called <code>bb-config.php</code> in the root directory of your bbPress installation.' ),
+					__( 'If you\'re not sure what to put here, contact your web hosting provider.' )
+				),
+				'messages'    => array()
+			),
+			2 => array(
+				'title'       => sprintf( __( '%1$s &rsaquo; %2$s' ), __( 'bbPress installer' ), __( 'Step 2' ) ),
+				'h2'          => sprintf( __( '%1$s - %2$s' ), __( 'Step 2' ), __( 'WordPress integration (optional)' ) ),
+				'status'      => __( '&laquo; skipped' ),
+				'intro'       => array(
+					__( 'bbPress can integrate login and user data seamlessly with WordPress. You can safely skip this step if you do not wish to integrate with an existing WordPress install.' )
+				),
+				'messages'    => array(),
+				'form_errors' => array()
+			),
+			3 => array(
+				'title'       => sprintf( __( '%1$s &rsaquo; %2$s' ), __( 'bbPress installer' ), __( 'Step 3' ) ),
+				'h2'          => sprintf( __( '%1$s - %2$s' ), __( 'Step 3' ), __( 'Site settings' ) ),
+				'status'      => '',
+				'intro'       => array(
+					__( 'Finalize your installation by adding a name, your first user and your first forum.' )
+				),
+				'messages'    => array(),
+				'form_errors' => array(),
+				'scripts'     => array()
+			),
+			4 => array(
+				'title'       => sprintf( __( '%1$s &rsaquo; %2$s' ), __( 'bbPress installer' ), __( 'Finished' ) ),
+				'h2'          => __( 'Installation complete!' ),
+				'messages'    => array()
+			)
+		);
+	}
+
+	/**
+	 * Check installation pre-requisites
+	 *
+	 * Checks for appropriate PHP extensions.
+	 *
+	 * @return boolean False if any pre-requisites are not met, otherwise true
+	 */
+	function check_prerequisites()
+	{
+		// BPDB wants the MySQL extension - this is also checked when BPDB is initialised so may be a bit redundant here
+		if ( !extension_loaded( 'mysql' ) ) {
+			$this->strings[-1]['messages']['error'][] = __( 'Your PHP installation appears to be missing the MySQL extension which is required for bbPress' );
+			$this->step = -1;
+		}
+
+		if ( defined( 'BB_IS_WP_LOADED' ) && BB_IS_WP_LOADED ) {
+			$this->strings[-1]['messages']['error'][] = __( 'Please complete your installation before attempting to include WordPress within bbPress' );
+			$this->step = -1;
+		}
+
+		if ( $this->step === -1 ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Define path constants
+	 *
+	 * Sets some bbPress constants if they are not otherwise available based
+	 * on the classes initiating file path.
+	 *
+	 * @return boolean False if no path was supplied, otherwise always true
+	 */
+	function define_paths()
+	{
+		if ( !$this->caller ) {
+			return false;
+		}
+
+		if ( !defined( 'BACKPRESS_PATH' ) ) {
+			// Define BACKPRESS_PATH
+			// Tell us to load includes because bb-settings.php was not loaded
+			// bb-settings.php is generally not loaded on steps -1, 0 and 1 but
+			// there are exceptions, so this is safer than just reading the step
+			$this->load_includes = true;
+			define( 'BACKPRESS_PATH', BB_PATH . BB_INC . 'backpress/' );
+		}
+
+		// Define the language file directory
+		if ( !defined( 'BB_LANG_DIR' ) ) {
+			define( 'BB_LANG_DIR', BB_PATH . 'my-languages/' );
+		}
+
+		return true;
+	}
+
+	/**
+	 * Gets an array of available languages form the language directory
+	 *
+	 * @return array
+	 */
+	function get_languages()
+	{
+		foreach ( bb_glob( BB_LANG_DIR . '*.mo' ) as $language ) {
+			if ( substr( $language, 0, 18 ) === 'continents-cities-' ) {
+				continue;
+			}
+			$language = str_replace( '.mo', '', basename( $language ) );
+			$this->languages[$language] = $language;
+		}
+		
+		return $this->languages;
+	}
+
+	/**
+	 * Returns a language selector for switching installation languages
+	 *
+	 * @return string|false Either the html for the selector or false if there are no languages
+	 */
+	function get_language_selector()
+	{
+		// Don't provide a selection if there is only english
+		if ( 2 > count( $this->languages ) ) {
+			return false;
+		}
+
+		$r = '<script type="text/javascript" charset="utf-8">' . "\n";
+		$r .= "\t" . 'function changeLanguage(selectObj) {' . "\n";
+		$r .= "\t\t" . 'var selectedLanguage = selectObj.options[selectObj.selectedIndex].value;' . "\n";
+		$r .= "\t\t" . 'location.href = "install.php?language=" + selectedLanguage;' . "\n";
+		$r .= "\t" . '}' . "\n";
+		$r .= '</script>' . "\n";
+		//$r .= '<form id="lang" action="install.php">' . "\n";
+		$r .= "\t" . '<fieldset>' . "\n";
+		$r .= "\t\t" . '<label class="has-note has-label for-select">' . "\n";
+		$r .= "\t\t\t" . '<span>' . __( 'Installation language' ) . '</span>' . "\n";
+		$this->tabindex++;
+		$r .= "\t\t\t" . '<select class="has-note" onchange="changeLanguage(this);" name="language" tabindex="' . $this->tabindex . '">' . "\n";
+		foreach ( $this->languages as $language ) {
+			$selected = '';
+			if ( $language == $this->language ) {
+				$selected = ' selected="selected"';
+			}
+			$r .= "\t\t\t\t" . '<option value="' . $language . '"' . $selected . '>' . $language . '</option>' . "\n";
+		}
+		$r .= "\t\t\t" . '</select>' . "\n";
+		$r .= "\t\t\t" . '<a class="note-toggle" href="javascript:void(0);" onclick="toggleNote(\'note-language\');">?</a>' . "\n";
+		$r .= "\t\t\t" . '<p id="note-language" class="note" style="display:none">' . __( 'Sets the language to be used during the installation process only.' ) . '</p>' . "\n";
+		$r .= "\t\t\t" . '<div class="clear"></div>' . "\n";
+		$r .= "\t\t" . '</label>' . "\n";
+		$r .= "\t" . '</fieldset>' . "\n";
+		//$r .= '</form>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Sets the current installation language
+	 *
+	 * @return string The currently set language
+	 */
+	function set_language()
+	{
+		if ( isset( $_COOKIE['bb_install_language'] ) && 1 < count( $this->languages ) ) {
+			if ( in_array( $_COOKIE['bb_install_language'], $this->languages ) ) {
+				$this->language = $_COOKIE['bb_install_language'];
+			}
+		}
+
+		$language_requested = false;
+		if ( isset( $_POST['language'] ) && $_POST['language'] ) {
+			$language_requested = (string) $_POST['language'];
+		} elseif ( isset( $_GET['language'] ) && $_GET['language'] ) {
+			$language_requested = (string) $_GET['language'];
+		}
+
+		if ( $language_requested && 1 < count( $this->languages ) ) {
+			if ( in_array( $language_requested, $this->languages ) ) {
+				$this->language = $language_requested;
+				setcookie( 'bb_install_language', $this->language );
+			}
+		}
+
+		if ( !$this->language || 'en_US' === $this->language ) {
+			$this->language = 'en_US';
+			setcookie( 'bb_install_language', ' ', time() - 31536000 );
+		}
+
+		return $this->language;
+	}
+
+	/**
+	 * Tests whether database tables exist
+	 *
+	 * Checks for the existence of the forum table in the database that is
+	 * currently configured.
+	 *
+	 * @return boolean False if the table isn't found, otherwise true
+	 */
+	function database_tables_are_installed()
+	{
+		global $bbdb;
+		$bbdb->suppress_errors();
+		$installed = (boolean) $bbdb->get_results( 'DESCRIBE `' . $bbdb->forums . '`;', ARRAY_A );
+		$bbdb->suppress_errors( false );
+		return $installed;
+	}
+
+	/**
+	 * Tests whether an option is set in the database
+	 *
+	 * @return boolean False if the option isn't set, otherwise true
+	 */
+	function bb_options_are_set()
+	{
+		if ( $this->load_includes ) {
+			return false;
+		}
+		if ( !bb_get_option( 'uri' ) ) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Tests whether bbPress is installed
+	 *
+	 * @return boolean False if bbPress isn't installed, otherwise true
+	 */
+	function is_installed()
+	{
+		if ( !$this->database_tables_are_installed() ) {
+			return false;
+		}
+		if ( !$this->bb_options_are_set() ) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Checks for configs and sets variables describing current install state
+	 *
+	 * @return integer The current step we should be on based on the existence of the config file
+	 */
+	function check_configs()
+	{
+		// Check for a config file
+		if ( file_exists( BB_PATH . 'bb-config.php' ) ) {
+			$this->configs['bb-config.php'] = BB_PATH . 'bb-config.php';
+		} elseif ( file_exists( dirname( BB_PATH ) . '/bb-config.php' ) ) {
+			$this->configs['bb-config.php'] = dirname( BB_PATH ) . '/bb-config.php';
+		}
+
+		// Check for an old config file
+		if ( file_exists( BB_PATH . 'config.php' ) ) {
+			$this->configs['config.php'] = BB_PATH . 'config.php';
+		} elseif ( file_exists( dirname( BB_PATH ) . '/config.php' ) ) {
+			$this->configs['config.php'] = dirname( BB_PATH ) . '/config.php';
+		}
+
+		if ( $this->configs['config.php'] && !$this->configs['bb-config.php'] ) {
+			// There is an old school config file
+			// Step -1 is where we send fatal errors from any screen
+			$this->strings[-1]['messages']['error'][] = __( 'An old <code>config.php</code> file has been detected in your installation. You should remove it and run the <a href="install.php">installer</a> again. You can use the same database connection details if you do.' );
+			$this->step = -1;
+			return $this->step;
+		}
+
+		// Check if bbPress is already installed
+		// Is there a current config file
+		if ( $this->configs['bb-config.php'] ) {
+
+			// Is it valid
+			if ( $this->validate_current_config() ) {
+				// Step 1 is complete
+				$this->step_status[1] = 'complete';
+				$this->strings[1]['status'] = __( '&laquo; completed' );
+
+				// On step 1 we want to report that the file is good and allow the user to continue
+				if ( $this->step === 1 ) {
+					// Stop form processing if it is posted
+					$this->stop_process = true;
+
+					// Display a nice message saying the config file exists
+					$this->strings[1]['messages']['message'][] = __( 'A valid configuration file was found at <code>bb-config.php</code><br />You may continue to the next step.' );
+					return $this->step;
+				}
+			} else {
+				// Invalid config files on any step cause us to exit to step 0
+				$this->strings[-1]['messages']['error'][] = __( 'An invalid configuration file was found at <code>bb-config.php</code><br />The installation cannot continue.' );
+				$this->strings[-1]['messages'][0][] = __( 'Usually this is caused by one of the database connection settings being incorrect. Make sure that the specified user has appropriate permission to access the database.' );
+				$this->step = -1;
+			}
+
+			// If we have made it this far, then we can check if the database tables exist and have content
+			if ( $this->is_installed() ) {
+				// The database is installed
+				// The following standard functions should be available
+				if ( bb_get_option( 'bb_db_version' ) > bb_get_option_from_db( 'bb_db_version' ) ) {
+					// The database needs upgrading
+					$this->strings[-1]['messages'][0][] = __( 'bbPress is already installed, but appears to require an upgrade.' );
+				} else {
+					$this->strings[-1]['messages'][0][] = __( 'bbPress is already installed.' );
+				}
+				$this->strings[-1]['messages'][0][] = sprintf(
+					__( 'Perhaps you meant to run the <a href="%s">upgrade script</a> instead?' ),
+					bb_get_uri( 'bb-admin/upgrade.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN )
+				);
+				$this->step = -1;
+			}
+
+		} else {
+
+			if ( 2 > $this->step && !file_exists( BB_PATH . 'bb-config-sample.php' ) ) {
+				// There is no sample config file
+				$this->strings[0]['messages']['error'][] = __( 'I could not find the file <code>bb-config-sample.php</code><br />Please upload it to the root directory of your bbPress installation.' );
+				$this->step = 0;
+			}
+
+			if ( 1 !== $this->step ) {
+				// There is no config file, go back to the beginning
+				$this->strings[0]['messages']['message'][] = __( 'There doesn\'t seem to be a <code>bb-config.php</code> file. This usually means that you want to install bbPress.' );
+				$this->step = 0;
+			}
+
+		}
+
+		// Check if the config file path is writable
+		if ( file_exists( $this->configs['bb-config.php'] ) ) {
+			if ( is_writable( $this->configs['bb-config.php'] ) ) {
+				$this->configs['writable'] = true;
+			}
+		} elseif ( is_writable( BB_PATH ) ) {
+			$this->configs['writable'] = true;
+		}
+
+		return $this->step;
+	}
+
+	/**
+	 * Determines if the current config is valid
+	 *
+	 * @return boolean False if the config is bad, otherwise true
+	 */
+	function validate_current_config()
+	{
+		// If we are validating then the config file has already been included
+		// So we can just check for valid constants
+
+		// The required constants for a valid config file
+		$required_constants = array(
+			'BBDB_NAME',
+			'BBDB_USER',
+			'BBDB_PASSWORD',
+			'BBDB_HOST'
+		);
+
+		// Check the required constants are defined
+		foreach ( $required_constants as $required_constant ) {
+			if ( !defined( $required_constant ) ) {
+				return false;
+			}
+		}
+
+		global $bb_table_prefix;
+
+		if ( !isset( $bb_table_prefix ) ) {
+			return false;
+		}
+
+		// Everthing is OK so far, validate the connection as well
+		return $this->validate_current_database();
+	}
+
+	/**
+	 * Validates the current database settings
+	 *
+	 * @return boolean False if the current database isn't valid, otherwise true
+	 */
+	function validate_current_database()
+	{
+		global $bbdb;
+		$db = $bbdb->db_connect( "SELECT * FROM $bbdb->forums LIMIT 1" );
+
+		if ( !is_resource( $db ) ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Sets up default values for input data as well as labels and notes
+	 *
+	 * @return void
+	 */
+	function prepare_data()
+	{
+		/**
+		 * Should be exactly the same as the default value of the KEYS in bb-config-sample.php
+		 * @since 1.0
+		 */
+		$_bb_default_secret_key = 'put your unique phrase here';
+
+		$this->data = array(
+			0 => array(
+				'form' => array(
+					'forward_0_0' => array(
+						'value' => __( 'Go to step 1' )
+					)
+				)
+			),
+			1 => array(
+				'form' => array(
+					'bbdb_name' => array(
+						'value' => '',
+						'label' => __( 'Database name' ),
+						'note'  => __( 'The name of the database in which you want to run bbPress.' )
+					),
+					'bbdb_user' => array(
+						'value' => '',
+						'label' => __( 'Database user' ),
+						'note'  => __( 'The database user that has access to that database.' ),
+						'autocomplete' => 'off'
+					),
+					'bbdb_password' => array(
+						'type'  => 'password',
+						'value' => '',
+						'label' => __( 'Database password' ),
+						'note'  => __( 'That database user\'s password.' ),
+						'autocomplete' => 'off'
+					),
+					'bb_lang' => array(
+						'value' => '',
+						'label' => __( 'Language' ),
+						'note' => sprintf( __( 'The language which bbPress will be presented in once installed. Your current installer language choice (%s) will be the same for the rest of the install process.' ), $this->language )
+					),
+					'toggle_1' => array(
+						'value'   => 0,
+						'label'   => __( 'Show advanced settings' ),
+						'note'    => __( 'These settings usually do not have to be changed.' ),
+						'checked' => '',
+						'display' => 'none'
+					),
+					'bbdb_host'        => array(
+						'value'        => 'localhost',
+						'label'        => __( 'Database host' ),
+						'note'         => __( 'The domain name or IP address of the server where the database is located. If the database is on the same server as the web site, then this probably should remain <strong>localhost</strong>.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'bbdb_charset' => array(
+						'value'        => 'utf8',
+						'label'        => __( 'Database character set' ),
+						'note'         => __( 'The best choice is <strong>utf8</strong>, but you will need to match the character set which you created the database with.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'bbdb_collate' => array(
+						'value'        => '',
+						'label'        => __( 'Database character collation' ),
+						'note'         => __( 'The character collation value set when the database was created.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					/*
+					'bb_auth_key' => array(
+						'value'        => $_bb_default_secret_key,
+						'label'        => __( 'bbPress "auth" cookie key' ),
+						'note'         => __( 'This should be a unique and secret phrase, it will be used to make your bbPress "auth" cookie unique and harder for an attacker to decipher.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'bb_secure_auth_key' => array(
+						'value'        => $_bb_default_secret_key,
+						'label'        => __( 'bbPress "secure auth" cookie key' ),
+						'note'         => __( 'This should be a unique and secret phrase, it will be used to make your bbPress "secure auth" cookie unique and harder for an attacker to decipher.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'bb_logged_in_key' => array(
+						'value'        => $_bb_default_secret_key,
+						'label'        => __( 'bbPress "logged in" cookie key' ),
+						'note'         => __( 'This should be a unique and secret phrase, it will be used to make your bbPress "logged in" cookie unique and harder for an attacker to decipher.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'bb_nonce_key' => array(
+						'value'        => $_bb_default_secret_key,
+						'label'        => __( 'bbPress "nonce" key' ),
+						'note'         => __( 'This should be a unique and secret phrase, it will be used to make form submission harder for an attacker to spoof.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					*/
+					'bb_table_prefix' => array(
+						'value'        => 'bb_',
+						'label'        => __( 'Table name prefix' ),
+						'note'         => __( 'If you are running multiple bbPress sites in a single database, you will probably want to change this.' ),
+						'prerequisite' => 'toggle_1'
+					),
+					'config' => array(
+						'value' => '',
+						'label' => __( 'Contents for <code>bb-config.php</code>' ),
+						'note'  => __( 'Once you have created the configuration file, you can check for it below.' )
+					),
+					'forward_1_0' => array(
+						'value' => __( 'Save database configuration file' )
+					),
+					'back_1_1' => array(
+						'value' => __( '&laquo; Go back' )
+					),
+					'forward_1_1' => array(
+						'value' => __( 'Check for configuration file' )
+					),
+					'forward_1_2' => array(
+						'value' => __( 'Go to step 2' )
+					)
+				)
+			),
+
+			2 => array(
+				'form' => array(
+					'toggle_2_0' => array(
+						'value'        => 0,
+						'label'        => __( 'Add integration settings' ),
+						'note'         => __( 'If you want to integrate bbPress with an existing WordPress site.' ),
+						'checked'      => '',
+						'display'      => 'none',
+						'toggle_value' => array(
+							'target'    => 'forward_2_0',
+							'off_value' => __( 'Skip WordPress integration' ),
+							'on_value'  => __( 'Save WordPress integration settings' )
+						)
+					),
+					'toggle_2_1' => array(
+						'value'   => 0,
+						'label'   => __( 'Add cookie integration settings' ),
+						'note'    => __( 'If you want to allow shared logins with an existing WordPress site.' ),
+						'checked' => '',
+						'display' => 'none',
+						'prerequisite' => 'toggle_2_0'
+					),
+					'wp_siteurl' => array(
+						'value' => '',
+						'label' => __( 'WordPress address (URL)' ),
+						'note'  => __( 'This value should exactly match the <strong>WordPress address (URL)</strong> setting in your WordPress general settings.' ),
+						'prerequisite' => 'toggle_2_1'
+					),
+					'wp_home' => array(
+						'value' => '',
+						'label' => __( 'Blog address (URL)' ),
+						'note'  => __( 'This value should exactly match the <strong>Blog address (URL)</strong> setting in your WordPress general settings.' ),
+						'prerequisite' => 'toggle_2_1'
+					),
+					'wp_auth_key' => array(
+						'value' => '',
+						'label' => __( 'WordPress "auth" cookie key' ),
+						'note'  => __( 'This value must match the value of the constant named "AUTH_KEY" in your WordPress <code>wp-config.php</code> file. This will replace the bbPress "auth" cookie key set in the first step.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'wp_auth_salt' => array(
+						'value' => '',
+						'label' => __( 'WordPress "auth" cookie salt' ),
+						'note'  => __( 'This must match the value of the WordPress setting named "auth_salt" in your WordPress site. Look for the option labeled "auth_salt" in <a href="#" id="getAuthSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>. If you leave this blank the installer will try to fetch the value based on your WordPress database integration settings.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'wp_secure_auth_key' => array(
+						'value' => '',
+						'label' => __( 'WordPress "secure auth" cookie key' ),
+						'note'  => __( 'This value must match the value of the constant named "SECURE_AUTH_KEY" in your WordPress <code>wp-config.php</code> file. This will replace the bbPress "secure auth" cookie key set in the first step.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'wp_secure_auth_salt' => array(
+						'value' => '',
+						'label' => __( 'WordPress "secure auth" cookie salt' ),
+						'note'  => __( 'This must match the value of the WordPress setting named "secure_auth_salt" in your WordPress site. Look for the option labeled "secure_auth_salt" in <a href="#" id="getSecureAuthSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>. If you leave this blank the installer will try to fetch the value based on your WordPress database integration settings. Sometimes this value is not set in WordPress, in that case you can leave this setting blank as well.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'wp_logged_in_key' => array(
+						'value' => '',
+						'label' => __( 'WordPress "logged in" cookie key' ),
+						'note'  => __( 'This value must match the value of the constant named "LOGGED_IN_KEY" in your WordPress <code>wp-config.php</code> file. This will replace the bbPress "logged in" cookie key set in the first step.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'wp_logged_in_salt' => array(
+						'value' => '',
+						'label' => __( 'WordPress "logged in" cookie salt' ),
+						'note'  => __( 'This must match the value of the WordPress setting named "logged_in_salt" in your WordPress site. Look for the option labeled "logged_in_salt" in <a href="#" id="getLoggedInSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>. If you leave this blank the installer will try to fetch the value based on your WordPress database integration settings.' ),
+						'prerequisite' => 'toggle_2_1',
+						'autocomplete' => 'off'
+					),
+					'toggle_2_2' => array(
+						'value'   => 0,
+						'label'   => __( 'Add user database integration settings' ),
+						'note'    => __( 'If you want to share user data with an existing WordPress site.' ),
+						'checked' => '',
+						'display' => 'none',
+						'prerequisite' => 'toggle_2_0'
+					),
+					'wp_table_prefix' => array(
+						'value' => 'wp_',
+						'default_value' => '', // Used when setting is ignored
+						'label' => __( 'User database table prefix' ),
+						'note'  => __( 'If your bbPress and WordPress sites share the same database, then this is the same value as <code>$table_prefix</code> in your WordPress <code>wp-config.php</code> file. It is usually <strong>wp_</strong>.' ),
+						'prerequisite' => 'toggle_2_2'
+					),
+					'wordpress_mu_primary_blog_id' => array(
+						'value' => '',
+						'default_value' => '',
+						'label' => __( 'WordPress MU primary blog ID' ),
+						'note'  => __( 'If you are integrating with a WordPress MU site you need to specify the primary blog ID for that site. It is usually <strong>1</strong>. You should probably leave this blank if you are integrating with a standard WordPress site' ),
+						'prerequisite' => 'toggle_2_2'
+					),
+					'toggle_2_3' => array(
+						'value'   => 0,
+						'label'   => __( 'Show advanced database settings' ),
+						'note'    => __( 'If your bbPress and WordPress site do not share the same database, then you will need to add advanced settings.' ),
+						'checked' => '',
+						'display' => 'none',
+						'prerequisite' => 'toggle_2_2'
+					),
+					'user_bbdb_name' => array(
+						'value' => '',
+						'label' => __( 'User database name' ),
+						'note'  => __( 'The name of the database in which your user tables reside.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'user_bbdb_user' => array(
+						'value' => '',
+						'label' => __( 'User database user' ),
+						'note'  => __( 'The database user that has access to that database.' ),
+						'prerequisite' => 'toggle_2_3',
+						'autocomplete' => 'off'
+					),
+					'user_bbdb_password' => array(
+						'type'  => 'password',
+						'value' => '',
+						'label' => __( 'User database password' ),
+						'note'  => __( 'That database user\'s password.' ),
+						'prerequisite' => 'toggle_2_3',
+						'autocomplete' => 'off'
+					),
+					'user_bbdb_host' => array(
+						'value' => '',
+						'label' => __( 'User database host' ),
+						'note'  => __( 'The domain name or IP address of the server where the database is located. If the database is on the same server as the web site, then this probably should be <strong>localhost</strong>.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'user_bbdb_charset' => array(
+						'value' => '',
+						'label' => __( 'User database character set' ),
+						'note'  => __( 'The best choice is <strong>utf8</strong>, but you will need to match the character set which you created the database with.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'user_bbdb_collate' => array(
+						'value' => '',
+						'label' => __( 'User database character collation' ),
+						'note'  => __( 'The character collation value set when the user database was created.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'custom_user_table' => array(
+						'value' => '',
+						'label' => __( 'User database "user" table' ),
+						'note'  => __( 'The complete table name, including any prefix.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'custom_user_meta_table' => array(
+						'value' => '',
+						'label' => __( 'User database "user meta" table' ),
+						'note'  => __( 'The complete table name, including any prefix.' ),
+						'prerequisite' => 'toggle_2_3'
+					),
+					'forward_2_0' => array(
+						'value' => __( 'Skip WordPress integration' )
+					),
+					'back_2_1' => array(
+						'value' => __( '&laquo; Go back' )
+					),
+					'forward_2_1' => array(
+						'value' => __( 'Go to step 3' )
+					)
+				)
+			),
+
+			3 => array(
+				'form' => array(
+					'name' => array(
+						'value' => '',
+						'label' => __( 'Site name' ),
+						'note'  => __( 'This is what you are going to call your bbPress site.' )
+					),
+					'uri' => array(
+						'value' => $this->guess_uri(),
+						'label' => __( 'Site address (URL)' ),
+						'note'  => __( 'We have attempted to guess this; it\'s usually correct, but change it here if you wish.' )
+					),
+					'keymaster_user_login' => array(
+						'value'     => '',
+						'maxlength' => 60,
+						'label'     => __( '"Key Master" Username' ),
+						'note'      => __( 'This is the user login for the initial bbPress administrator (known as a "Key Master").' ),
+						'autocomplete' => 'off'
+					),
+					'keymaster_user_email' => array(
+						'value'     => '',
+						'maxlength' => 100,
+						'label'     => __( '"Key Master" Email address' ),
+						'note'      => __( 'The login details will be emailed to this address.' ),
+						'autocomplete' => 'off'
+					),
+					'keymaster_user_type' => array(
+						'value' => 'new'
+					),
+					'forum_name' => array(
+						'value'     => '',
+						'maxlength' => 150,
+						'label'     => __( 'First forum name' ),
+						'note'      => __( 'This can be changed after installation, so don\'t worry about it too much.' )
+					),
+					'forward_3_0' => array(
+						'value' => __( 'Save site settings' )
+					),
+					'back_3_1' => array(
+						'value' => __( '&laquo; Go back' )
+					),
+					'forward_3_1' => array(
+						'value' => __( 'Complete the installation' )
+					)
+				)
+			),
+
+			4 => array(
+				'form' => array(
+					'toggle_4' => array(
+						'value' => 0,
+						'label' => __( 'Show installation messages' )
+					),
+					'error_log' => array(
+						'value' => '',
+						'label' => __( 'Installation errors' )
+					),
+					'installation_log' => array(
+						'value' => '',
+						'label' => __( 'Installation log' )
+					)
+				)
+			)
+		);
+	}
+
+	/**
+	 * Guesses the final installed URI based on the location of the install script
+	 *
+	 * @return string The guessed URI
+	 */
+	function guess_uri()
+	{
+		global $bb;
+
+		if ( $bb->uri ) {
+			$uri = $bb->uri;
+		} else {
+			$schema = 'http://';
+			if ( isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on' ) {
+				$schema = 'https://';
+			}
+			$uri = preg_replace( '|/bb-admin/.*|i', '/', $schema . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
+		}
+
+		return rtrim( $uri, " \t\n\r\0\x0B/" ) . '/';
+	}
+
+	/**
+	 * Writes the given alterations to file
+	 *
+	 * @param $file_source string The full path to the file to be read from
+	 * @param $file_target string The full path to the file to be written to
+	 * @param $alterations array An array of arrays containing alterations to be made
+	 * @return void
+	 */
+	function write_lines_to_file( $file_source, $file_target, $alterations )
+	{
+		if ( !$file_source || !file_exists( $file_source ) || !is_file( $file_source ) ) {
+			return -1;
+		}
+
+		if ( !$file_target ) {
+			$file_target = $file_source;
+		}
+
+		if ( !$alterations || !is_array( $alterations ) ) {
+			return -2;
+		}
+
+		/*
+		Alterations array takes the form
+		array(
+			'1st 20 chars of line' => array( 'Search string', 'Replacement string' ),
+			'1st 20 chars of line' => array( 'Search string', 'Replacement string' )
+		);
+		*/
+
+		// Get the existing lines in the file
+		$lines = file( $file_source );
+
+		// Initialise an array to store the modified lines
+		$modified_lines = array();
+
+		// Loop through the lines and modify them
+		foreach ( $lines as $line ) {
+			if ( isset( $alterations[substr( $line, 0, 20 )] ) ) {
+				$alteration = $alterations[substr( $line, 0, 20 )];
+				$modified_lines[] = str_replace( $alteration[0], $alteration[1], $line );
+			} else {
+				$modified_lines[] = $line;
+			}
+		}
+
+		$writable = true;
+		if ( file_exists( $file_target ) ) {
+			if ( !is_writable( $file_target ) ) {
+				$writable = false;
+			}
+		} else {
+			$dir_target = dirname( $file_target );
+			if ( file_exists( $dir_target ) ) {
+				if ( !is_writable( $dir_target ) || !is_dir( $dir_target ) ) {
+					$writable = false;
+				}
+			} else {
+				$writable = false;
+			}
+		}
+
+		if ( !$writable ) {
+			return trim( join( null, $modified_lines ) );
+		}
+
+		// Open the file for writing - rewrites the whole file
+		$file_handle = fopen( $file_target, 'w' );
+
+		// Write lines one by one to avoid OS specific newline hassles
+		foreach ( $modified_lines as $modified_line ) {
+			if ( false !== strpos( $modified_line, '?>' ) ) {
+				$modified_line = '?>';
+			}
+			fwrite( $file_handle, $modified_line );
+			if ( $modified_line == '?>' ) {
+				break;
+			}
+		}
+
+		// Close the config file
+		fclose( $file_handle );
+
+		@chmod( $file_target, 0666 );
+
+		return 1;
+	}
+
+	/**
+	 * Reports whether the request method is post or not
+	 *
+	 * @return boolean True if the page was posted, otherwise false
+	 */
+	function is_posted()
+	{
+		if ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Determines which step the installer is on based on user input
+	 *
+	 * @return boolean Always returns true
+	 **/
+	function set_step()
+	{
+		if ( $this->is_posted() ) {
+			switch ( $this->step ) {
+				case 1:
+					$this->set_language();
+					if ( $_POST['forward_0_0'] ) {
+						$this->stop_process = 1;
+					}
+					break;
+
+				case 2:
+					if ( $_POST['forward_1_2'] ) {
+						$this->stop_process = 1;
+					}
+					break;
+
+				case 3:
+					// If this is actually a request to go back to step 2
+					if ( $_POST['back_2_1'] ) {
+						$this->step = 2;
+						break;
+					}
+
+					// If we have come forward from step 2 then don't process form 3
+					if ( $_POST['forward_2_1'] ) {
+						$this->stop_process = true;
+					}
+
+					// Determine what the status of the previous step was based on input
+					if ( $_POST['toggle_2_0'] ) {
+						$this->strings[2]['status'] = __( '&laquo; completed' );
+						$this->step_status[2] = 'complete';
+					}
+					break;
+
+				case 4:
+					// Determine what the status of the previous step was based on input
+					if ( $_POST['toggle_2_0'] ) {
+						$this->strings[2]['status'] = __( '&laquo; completed' );
+						$this->step_status[2] = 'complete';
+					}
+
+					// If this is actually a request to go back to step 3
+					if ( $_POST['back_3_1'] ) {
+						$this->step = 3;
+						break;
+					}
+
+					// We have to have come forward from step 3
+					if ( $_POST['forward_3_1'] ) {
+						$this->strings[3]['status'] = __( '&laquo; completed' );
+						$this->step_status[3] = 'complete';
+					} else {
+						$this->step = 2;
+					}
+					break;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Sanitizes all data stored in the data array
+	 *
+	 * @return boolean Always returns true
+	 **/
+	function sanitize_form_data()
+	{
+		foreach ( $this->data as $step => $data ) {
+			if ( isset( $data['form'] ) && is_array( $data['form'] ) ) {
+				foreach ( $data['form'] as $key => $value ) {
+					$this->data[$step]['form'][$key]['value'] = esc_attr( $value['value'] );
+				}
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Directs processing of the form data based on the current step
+	 *
+	 * @return boolean Always returns true
+	 **/
+	function process_form()
+	{
+		if ( $this->is_posted() && !$this->stop_process ) {
+			switch ( $this->step ) {
+				case 1:
+					$this->process_form_config_file();
+					break;
+
+				case 2:
+					$this->process_form_wordpress_integration();
+					break;
+
+				case 3:
+					$this->process_form_site_options();
+					break;
+
+				case 4:
+					$this->process_form_finalise_installation();
+					break;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Takes inputted form data and injects it into the data array
+	 *
+	 * @param integer $step Which steps data to process
+	 * @return boolean Always returns true
+	 **/
+	function inject_form_values_into_data( $step )
+	{
+		$data =& $this->data[$step]['form'];
+
+		foreach ( $data as $key => $value ) {
+			if ( 'forward_' !== substr( $key, 0, 8 ) && 'back_' !== substr( $key, 0, 5 ) ) {
+				if ( isset( $data[$key]['prerequisite'] ) && !$_POST[$data[$key]['prerequisite']] ) {
+					if ( isset( $data[$key]['default_value'] ) ) {
+						$data[$key]['value'] = $data[$key]['default_value'];
+					}
+					// do nothing - keep the default value
+				} else {
+					$data[$key]['value'] = stripslashes_deep( trim( $_POST[$key] ) );
+				}
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Validates the supplied config file data and writes it to the config file.
+	 *
+	 * @return void
+	 **/
+	function process_form_config_file()
+	{
+		$this->inject_form_values_into_data( 1 );
+
+		$data =& $this->data[1]['form'];
+		
+		if ( 'en_US' == $data['bb_lang']['value'] ) {
+			$data['bb_lang']['value'] = '';
+		}
+
+		if ( $data['toggle_1']['value'] ) {
+			$data['toggle_1']['checked'] = 'checked="checked"';
+			$data['toggle_1']['display'] = 'block';
+
+			// Deal with slashes in the keys
+			//$data['bb_auth_key']['value']        = addslashes( stripslashes( $data['bb_auth_key']['value'] ) );
+			//$data['bb_secure_auth_key']['value'] = addslashes( stripslashes( $data['bb_secure_auth_key']['value'] ) );
+			//$data['bb_logged_in_key']['value']   = addslashes( stripslashes( $data['bb_logged_in_key']['value'] ) );
+			//$data['bb_nonce_key']['value']       = addslashes( stripslashes( $data['bb_nonce_key']['value'] ) );
+		}
+
+		$requested_prefix = $data['bb_table_prefix']['value'];
+		$data['bb_table_prefix']['value'] = preg_replace( '/[^0-9a-zA-Z_]/', '', $data['bb_table_prefix']['value'] );
+		if ( $requested_prefix !== $data['bb_table_prefix']['value'] ) {
+			$data['toggle_1']['checked'] = 'checked="checked"';
+			$data['toggle_1']['display'] = 'block';
+			$this->step_status[1] = 'incomplete';
+			$this->strings[1]['messages']['error'][] = __( 'The table prefix can only contain letters, numbers and underscores.<br />Please review the suggestion below.' );
+			$this->strings[1]['form_errors']['bb_table_prefix'][] = __( '&bull; Based on your input the following prefix is suggested.' );
+			return 'incomplete';
+		}
+		if ( empty( $data['bb_table_prefix']['value'] ) ) {
+			$data['bb_table_prefix']['value'] = 'bb_';
+			$data['toggle_1']['checked'] = 'checked="checked"';
+			$data['toggle_1']['display'] = 'block';
+			$this->step_status[1] = 'incomplete';
+			$this->strings[1]['messages']['error'][] = __( 'The table prefix can not be blank.<br />Please review the suggestion below.' );
+			$this->strings[1]['form_errors']['bb_table_prefix'][] = __( '&bull; The default prefix has been inserted.' );
+			return 'incomplete';
+		}
+
+		// Stop here if we are going backwards
+		if ( $_POST['back_1_1'] ) {
+			$this->step_status[1] = 'incomplete';
+			return 'incomplete';
+		}
+
+		// Test the db connection.
+		define( 'BBDB_NAME',     $data['bbdb_name']['value'] );
+		define( 'BBDB_USER',     $data['bbdb_user']['value'] );
+		define( 'BBDB_PASSWORD', $data['bbdb_password']['value'] );
+		define( 'BBDB_HOST',     $data['bbdb_host']['value'] );
+		define( 'BBDB_CHARSET',  $data['bbdb_charset']['value'] );
+		define( 'BBDB_COLLATE',  $data['bbdb_collate']['value'] );
+
+		// We'll fail here if the values are no good.
+		require_once( BACKPRESS_PATH . 'class.bpdb-multi.php' );
+
+		$bbdb =& new BPDB_Multi( array(
+			'name'     => BBDB_NAME,
+			'user'     => BBDB_USER,
+			'password' => BBDB_PASSWORD,
+			'host'     => BBDB_HOST,
+			'charset'  => defined( 'BBDB_CHARSET' ) ? BBDB_CHARSET : false,
+			'collate'  => defined( 'BBDB_COLLATE' ) ? BBDB_COLLATE : false,
+			'errors'   => 'suppress'
+		) );
+
+		if ( !$bbdb->db_connect( 'SHOW TABLES;' ) ) {
+			$bbdb->suppress_errors( false );
+			$this->step_status[1] = 'incomplete';
+			$this->strings[1]['messages']['error'][] = __( 'There was a problem connecting to the database you specified.<br />Please check the settings, then try again.' );
+			return 'error';
+		}
+		$bbdb->suppress_errors( false );
+
+		$config_result = $this->write_lines_to_file(
+			BB_PATH . 'bb-config-sample.php',
+			BB_PATH . 'bb-config.php',
+			array(
+				"define( 'BBDB_NAME',"  => array( "'bbpress'",                     "'" . $data['bbdb_name']['value'] . "'" ),
+				"define( 'BBDB_USER',"  => array( "'username'",                    "'" . $data['bbdb_user']['value'] . "'" ),
+				"define( 'BBDB_PASSWO"  => array( "'password'",                    "'" . $data['bbdb_password']['value'] . "'" ),
+				"define( 'BBDB_HOST',"  => array( "'localhost'",                   "'" . $data['bbdb_host']['value'] . "'" ),
+				"define( 'BBDB_CHARSE"  => array( "'utf8'",                        "'" . $data['bbdb_charset']['value'] . "'" ),
+				"define( 'BBDB_COLLAT"  => array( "''",                            "'" . $data['bbdb_collate']['value'] . "'" ),
+				//"define( 'BB_AUTH_KEY"  => array( "'put your unique phrase here'", "'" . $data['bb_auth_key']['value'] . "'" ),
+				//"define( 'BB_SECURE_A"  => array( "'put your unique phrase here'", "'" . $data['bb_secure_auth_key']['value'] . "'" ),
+				//"define( 'BB_LOGGED_I"  => array( "'put your unique phrase here'", "'" . $data['bb_logged_in_key']['value'] . "'" ),
+				//"define( 'BB_NONCE_KE"  => array( "'put your unique phrase here'", "'" . $data['bb_nonce_key']['value'] . "'" ),
+				"\$bb_table_prefix = '" => array( "'bb_'",                         "'" . $data['bb_table_prefix']['value'] . "'" ),
+				"define( 'BB_LANG', '"  => array( "''",                            "'" . $data['bb_lang']['value'] . "'" )
+			)
+		);
+
+		switch ( $config_result ) {
+			case -1:
+				$this->step_status[1] = 'error';
+				$this->strings[1]['messages']['error'][] = __( 'I could not find the file <code>bb-config-sample.php</code><br />Please upload it to the root directory of your bbPress installation.' );
+				return 'error';
+				break;
+			case 1:
+				$this->configs['bb-config.php'] = BB_PATH . 'bb-config.php';
+				$this->step_status[1] = 'complete';
+				$this->strings[1]['messages']['message'][] = __( 'Your settings have been saved to the file <code>bb-config.php</code><br />You can now continue to the next step.' );
+				break;
+			default:
+				// Just write the contents to screen
+				$this->data[1]['form']['config']['value'] = $config_result;
+
+				$this->step_status[1] = 'manual';
+				$this->strings[1]['messages']['error'][] = __( 'Your settings could not be saved to a configuration file. You will need to save the text shown below into a file named <code>bb-config.php</code> in the root directory of your bbPress installation before you can continue.' );
+				break;
+		}
+	}
+
+	/**
+	 * Validates the WordPress integration settings
+	 *
+	 * @return void
+	 **/
+	function process_form_wordpress_integration()
+	{
+		// Check the referer
+		bb_check_admin_referer( 'bbpress-installer' );
+
+		$this->inject_form_values_into_data( 2 );
+
+		$data =& $this->data[2]['form'];
+
+		// If there are no settings then goto step 3
+		if ( !$data['toggle_2_0']['value'] && !$_POST['back_2_1'] ) {
+			$this->step_status[2] = 'complete';
+			$this->strings[2]['messages']['message'][] = __( 'You have chosen to skip the WordPress integration step. You can always integrate WordPress later from within the admin area of bbPress.' );
+			return 'complete';
+		}
+
+		// If integration is selected
+		if ( $data['toggle_2_0']['value'] ) {
+			$data['toggle_2_0']['checked'] = 'checked="checked"';
+			$data['toggle_2_0']['display'] = 'block';
+			$data['forward_2_0']['value'] = $data['toggle_2_0']['toggle_value']['on_value'];
+
+			if ( $data['toggle_2_1']['value'] ) {
+				$data['toggle_2_1']['checked'] = 'checked="checked"';
+				$data['toggle_2_1']['display'] = 'block';
+
+				// Check the wp_siteurl URL for errors
+				$data['wp_siteurl']['value'] = $data['wp_siteurl']['value'] ? rtrim( $data['wp_siteurl']['value'], " \t\n\r\0\x0B/" ) . '/' : '';
+				$this->strings[2]['form_errors']['wp_siteurl'][] = empty( $data['wp_siteurl']['value'] ) ? 'empty' : false;
+				if ( $parsed = parse_url( $data['wp_siteurl']['value'] ) ) {
+					$this->strings[2]['form_errors']['wp_siteurl'][] = preg_match( '/https?/i', $parsed['scheme'] ) ? false : 'urlscheme';
+					$this->strings[2]['form_errors']['wp_siteurl'][] = empty( $parsed['host'] ) ? 'urlhost' : false;
+				} else {
+					$this->strings[2]['form_errors']['wp_siteurl'][] = 'urlparse';
+				}
+
+				// Check the wp_home URL for errors
+				$data['wp_home']['value'] = $data['wp_home']['value'] ? rtrim( $data['wp_home']['value'], " \t\n\r\0\x0B/" ) . '/' : '';
+				$this->strings[2]['form_errors']['wp_home'][] = empty( $data['wp_home']['value'] ) ? 'empty' : false;
+				if ( $parsed = parse_url( $data['wp_home']['value'] ) ) {
+					$this->strings[2]['form_errors']['wp_home'][] = preg_match( '/https?/i', $parsed['scheme'] ) ? false : 'urlscheme';
+					$this->strings[2]['form_errors']['wp_home'][] = empty( $parsed['host'] ) ? 'urlhost' : false;
+				} else {
+					$this->strings[2]['form_errors']['wp_home'][] = 'urlparse';
+				}
+
+				// Deal with slashes in the keys
+				$data['wp_auth_key']['value']         = addslashes( stripslashes( $data['wp_auth_key']['value'] ) );
+				$data['wp_auth_salt']['value']        = addslashes( stripslashes( $data['wp_auth_salt']['value'] ) );
+				$data['wp_secure_auth_key']['value']  = addslashes( stripslashes( $data['wp_secure_auth_key']['value'] ) );
+				$data['wp_secure_auth_salt']['value'] = addslashes( stripslashes( $data['wp_secure_auth_salt']['value'] ) );
+				$data['wp_logged_in_key']['value']    = addslashes( stripslashes( $data['wp_logged_in_key']['value'] ) );
+				$data['wp_logged_in_salt']['value']   = addslashes( stripslashes( $data['wp_logged_in_salt']['value'] ) );
+
+				// Check the keys for errors
+				$this->strings[2]['form_errors']['wp_auth_key'][]         = empty( $data['wp_auth_key']['value'] ) ? 'empty' : false;
+				$this->strings[2]['form_errors']['wp_secure_auth_key'][]  = empty( $data['wp_secure_auth_key']['value'] ) ? 'empty' : false;
+				$this->strings[2]['form_errors']['wp_logged_in_key'][]    = empty( $data['wp_logged_in_key']['value'] ) ? 'empty' : false;
+
+				// Salts can be taken from the database if specified
+				if ( !$data['toggle_2_2']['value'] ) {
+					$this->strings[2]['form_errors']['wp_auth_salt'][]        = empty( $data['wp_auth_salt']['value'] ) ? 'empty' : false;
+					// NB; secure_auth_salt is not always set in WordPress
+					$this->strings[2]['form_errors']['wp_logged_in_salt'][]   = empty( $data['wp_logged_in_salt']['value'] ) ? 'empty' : false;
+				}
+			}
+
+			// If database integration is selected
+			if ( $data['toggle_2_2']['value'] ) {
+				$data['toggle_2_2']['checked'] = 'checked="checked"';
+				$data['toggle_2_2']['display'] = 'block';
+
+				// Make the wp_table_prefix valid
+				$data['wp_table_prefix']['value'] = preg_replace( '/[^0-9a-zA-Z_]/', '', $data['wp_table_prefix']['value'] );
+				$data['wp_table_prefix']['value'] = empty( $data['wp_table_prefix']['value'] ) ? 'wp_' : $data['wp_table_prefix']['value'];
+
+				// Make the wordpress_mu_primary_blog_id valid
+				$data['wordpress_mu_primary_blog_id']['value'] = preg_replace( '/[^0-9]/', '', $data['wordpress_mu_primary_blog_id']['value'] );
+
+				// If advanced database integration is selected
+				if ( $data['toggle_2_3']['value'] ) {
+					$data['toggle_2_3']['checked'] = 'checked="checked"';
+					$data['toggle_2_3']['display'] = 'block';
+				}
+			}
+
+			if ( !$data['toggle_2_1']['value'] && !$data['toggle_2_2']['value'] ) {
+				$this->step_status[2] = 'incomplete';
+				$this->strings[2]['messages']['error'][] = __( 'You must enter your settings for integration setup to complete. Choose which integration settings you wish to enter from the options below.' );
+				$this->strings[2]['form_errors']['toggle_2_1'][] = true;
+				$this->strings[2]['form_errors']['toggle_2_2'][] = true;
+				return 'incomplete';
+			}
+
+			// Remove empty values from the error array
+			foreach ( $this->strings[2]['form_errors'] as $input => $types) {
+				$types = array_filter( $types);
+				if ( !count( $types ) ) {
+					unset( $this->strings[2]['form_errors'][$input] );
+				}
+			}
+
+			// Check for errors and build error messages
+			if ( count( $this->strings[2]['form_errors'] ) ) {
+
+				$this->step_status[2] = 'incomplete';
+				$this->strings[2]['messages']['error'][] = __( 'Your integration settings have not been processed due to errors with the items marked below.' );
+
+				foreach ( $this->strings[2]['form_errors'] as $input => $types ) {
+					$errors = array();
+
+					foreach ( $types as $type ) {
+						switch ( $type ) {
+							case 'empty':
+								// Only return this error when empty
+								$errors = array( __( '&bull; This value is required to continue.' ) );
+								break(2);
+							case 'urlparse':
+								$errors[] = __( '&bull; This does not appear to be a valid URL.' );
+								break;
+							case 'urlscheme':
+								$errors[] = __( '&bull; The URL must begin with "http" or "https".' );
+								break;
+							case 'urlhost':
+								$errors[] = __( '&bull; The URL does not contain a host name.' );
+								break;
+						}
+					}
+
+					$this->strings[2]['form_errors'][$input] = $errors;
+				}
+
+				return 'incomplete';
+			}
+
+			// If database integration is selected
+			if ( $data['toggle_2_2']['value'] ) {
+
+				// Test the db connection.
+
+				// Setup variables and constants if available
+				global $bb;
+				$bb->wp_table_prefix = $data['wp_table_prefix']['value'];
+				if ( $data['toggle_2_3']['value'] ) {
+					// These may be empty at this particular stage
+					if ( !empty( $data['user_bbdb_name']['value'] ) ) {
+						$bb->user_bbdb_name = $data['user_bbdb_name']['value'];
+					}
+					if ( !empty( $data['user_bbdb_user']['value'] ) ) {
+						$bb->user_bbdb_user = $data['user_bbdb_user']['value'];
+					}
+					if ( !empty( $data['user_bbdb_password']['value'] ) ) {
+						$bb->user_bbdb_password = $data['user_bbdb_password']['value'];
+					}
+					if ( !empty( $data['user_bbdb_host']['value'] ) ) {
+						$bb->user_bbdb_host = $data['user_bbdb_host']['value'];
+					}
+					if ( !empty( $data['user_bbdb_charset']['value'] ) ) {
+						$bb->user_bbdb_charset = preg_replace( '/[^a-z0-9_-]/i', '', $data['user_bbdb_charset']['value'] );
+					}
+					if ( !empty( $data['user_bbdb_collate']['value'] ) ) {
+						$bb->user_bbdb_collate = preg_replace( '/[^a-z0-9_-]/i', '', $data['user_bbdb_collate']['value'] );
+					}
+					if ( !empty( $data['custom_user_table']['value'] ) ) {
+						$bb->custom_user_table = preg_replace( '/[^a-z0-9_-]/i', '', $data['custom_user_table']['value'] );
+					}
+					if ( !empty( $data['custom_user_meta_table']['value'] ) ) {
+						$bb->custom_user_meta_table = preg_replace( '/[^a-z0-9_-]/i', '', $data['custom_user_meta_table']['value'] );
+					}
+				}
+
+				// Bring in the database object
+				global $bbdb;
+				global $bb_table_prefix;
+
+				// Resolve the custom user tables for bpdb
+				bb_set_custom_user_tables();
+
+				if ( isset( $bb->custom_databases) && isset( $bb->custom_databases['user'] ) ) {
+					$bbdb->add_db_server( 'user', $bb->custom_databases['user'] );
+				}
+
+				// Add custom tables if required
+				if ( isset( $bb->custom_tables['users'] ) || isset( $bb->custom_tables['usermeta'] ) ) {
+					$bbdb->tables = array_merge( $bbdb->tables, $bb->custom_tables );
+					if ( is_wp_error( $bbdb->set_prefix( $bb_table_prefix ) ) ) {
+						die( __( 'Your user table prefix may only contain letters, numbers and underscores.' ) );
+					}
+				}
+
+				// Hide errors for the test
+				$bbdb->hide_errors();
+
+				$result = $bbdb->query( 'DESCRIBE ' . $bbdb->users . ';' );
+				$result_error = $bbdb->get_error();
+
+				// Select from the user table (may fail if there are no records in the table)
+				if ( !$result && $result_error ) {
+					// We couldn't connect to the database at all
+
+					// Turn errors back on
+					$bbdb->show_errors();
+
+					// Set the status
+					$this->step_status[2] = 'incomplete';
+					if ( !empty( $data['user_bbdb_name']['value'] ) ) {
+						$this->strings[2]['form_errors']['user_bbdb_name'][] = true;
+					}
+					if ( !empty( $data['user_bbdb_user']['value'] ) ) {
+						$this->strings[2]['form_errors']['user_bbdb_user'][] = true;
+					}
+					if ( !empty( $data['user_bbdb_password']['value'] ) ) {
+						$this->strings[2]['form_errors']['user_bbdb_password'][] = true;
+					}
+					if ( !empty( $data['user_bbdb_host']['value'] ) ) {
+						$this->strings[2]['form_errors']['user_bbdb_host'][] = true;
+					}
+					if ( !empty( $data['custom_user_table']['value'] ) ) {
+						$this->strings[2]['form_errors']['custom_user_table'][] = true;
+					}
+					if ( !empty( $data['custom_user_meta_table']['value'] ) ) {
+						$this->strings[2]['form_errors']['custom_user_meta_table'][] = true;
+					}
+					$this->strings[2]['messages']['error'][] = __( 'There was a problem connecting to the WordPress user database you specified. Please check the settings, then try again.' );
+					return 'incomplete';
+				}
+
+				if ( $result_error ) {
+					// The result is an error, presumably telling us the table doesn't exist
+
+					// Turn errors back on
+					$bbdb->show_errors();
+
+					// Set the status
+					$this->step_status[2] = 'incomplete';
+
+					if ( $data['toggle_2_3']['value'] ) {
+						$this->strings[2]['messages']['error'][] = __( 'Existing WordPress user tables could not be found in the WordPress database you specified.' );
+					} else {
+						$this->strings[2]['messages']['error'][] = __( 'Existing WordPress user tables could not be found in the bbPress database you specified in step 1.<br /><br />This is probably because the database does not already contain working WordPress tables. You may need to specify advanced database settings or leave integration until after installation.' );
+					}
+					$this->strings[2]['form_errors']['wp_table_prefix'][] = __( '&bull; This may not be a valid user table prefix.' );
+					return 'incomplete';
+				}
+
+				// Turn errors back on
+				$bbdb->show_errors();
+			}
+		}
+
+		// Stop here if we are going backwards
+		if ( $_POST['back_2_1'] ) {
+			$this->step_status[2] = 'incomplete';
+			return 'incomplete';
+		}
+
+		// If we make it this may we are complete, so set the status to complete
+		$this->step_status[2] = 'complete';
+		$this->strings[2]['messages']['message'][] = __( 'Your WordPress integration cookie and database settings have been successfully validated. They will be saved after the next step.<br /><br />Once you have finished installing, you should visit the WordPress integration section of the bbPress admin area for further options and integration instructions, including user mapping and the correct cookie settings to add to your WordPress configuration file.' );
+		return 'complete';
+	}
+
+	/**
+	 * Validates the site options.
+	 *
+	 * @return void
+	 **/
+	function process_form_site_options()
+	{
+		// Check the referer
+		bb_check_admin_referer( 'bbpress-installer' );
+
+		$this->inject_form_values_into_data( 2 );
+		$this->inject_form_values_into_data( 3 );
+
+		$data =& $this->data[3]['form'];
+
+		$this->strings[3]['form_errors']['name'][] = empty( $data['name']['value'] ) ? 'empty' : false;
+
+		$data['uri']['value'] = $data['uri']['value'] ? rtrim( $data['uri']['value'], " \t\n\r\0\x0B/" ) . '/' : '';
+		$this->strings[3]['form_errors']['uri'][] = empty( $data['uri']['value'] ) ? 'empty' : false;
+		if ( $parsed = parse_url( $data['uri']['value'] ) ) {
+			$this->strings[3]['form_errors']['uri'][] = preg_match( '/https?/i', $parsed['scheme'] ) ? false : 'urlscheme';
+			$this->strings[3]['form_errors']['uri'][] = empty( $parsed['host'] ) ? 'urlhost' : false;
+		} else {
+			$this->strings[3]['form_errors']['uri'][] = 'urlparse';
+		}
+
+		$this->strings[3]['form_errors']['keymaster_user_login'][] = empty( $data['keymaster_user_login']['value'] ) ? 'empty' : false;
+		if ( $data['keymaster_user_login']['value'] != sanitize_user( $data['keymaster_user_login']['value'], true ) ) {
+			$this->strings[3]['form_errors']['keymaster_user_login'][] = 'userlogin';
+		}
+		$data['keymaster_user_login']['value'] = sanitize_user( $data['keymaster_user_login']['value'], true );
+
+		// Check for a valid email
+		$this->strings[3]['form_errors']['keymaster_user_email'][] = empty( $data['keymaster_user_email']['value'] ) ? 'empty' : false;
+		$this->strings[3]['form_errors']['keymaster_user_email'][] = !is_email( $data['keymaster_user_email']['value'] ) ? 'email' : false;
+
+		// Check for a forum name
+		if ( !$this->database_tables_are_installed() ) {
+			$this->strings[3]['form_errors']['forum_name'][] = empty( $data['forum_name']['value'] ) ? 'empty' : false;
+		}
+
+		// Remove empty values from the error array
+		foreach ( $this->strings[3]['form_errors'] as $input => $types ) {
+			$types = array_filter( $types );
+			if ( !count( $types ) ) {
+				unset( $this->strings[3]['form_errors'][$input] );
+			}
+		}
+
+		// Check for errors and build error messages
+		if ( count( $this->strings[3]['form_errors'] ) ) {
+
+			$this->step_status[3] = 'incomplete';
+			$this->strings[3]['messages']['error'][] = __( 'Your site settings have not been processed due to errors with the items marked below.' );
+
+			foreach ( $this->strings[3]['form_errors'] as $input => $types ) {
+				$errors = array();
+
+				foreach ( $types as $type ) {
+					switch ( $type ) {
+						case 'empty':
+							// Only return this error when empty
+							$errors = array( __( '&bull; This value is required to continue.' ) );
+							break(2);
+						case 'urlparse':
+							$errors[] = __( '&bull; This does not appear to be a valid URL.' );
+							break;
+						case 'urlscheme':
+							$errors[] = __( '&bull; The URL must begin with "http" or "https".' );
+							break;
+						case 'urlhost':
+							$errors[] = __( '&bull; The URL does not contain a host name.' );
+							break;
+						case 'userlogin':
+							$errors[] = __( '&bull; Contains disallowed characters which have been removed.' );
+							break;
+						case 'email':
+							$errors[] = __( '&bull; The user email address appears to be invalid.' );
+							break;
+					}
+				}
+
+				$this->strings[3]['form_errors'][$input] = $errors;
+			}
+
+			return 'incomplete';
+		}
+
+		// Stop here if we are going backwards
+		if ( $_POST['back_3_1'] ) {
+			$this->step_status[3] = 'incomplete';
+			return 'incomplete';
+		}
+
+		// If we make it this far we are good to go
+		$this->step_status[3] = 'complete';
+		$this->strings[3]['messages']['message'][] = __( 'Your site settings have been saved and we are now ready to complete the installation. So what are you waiting for?' );
+		return 'complete';
+	}
+
+	/**
+	 * Finalises the installation by creating the database and writing all the supplied data to the database.
+	 *
+	 * @return void
+	 **/
+	function process_form_finalise_installation()
+	{
+		require_once( BB_PATH . 'bb-admin/includes/functions.bb-upgrade.php' );
+		require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+		$this->inject_form_values_into_data( 2 );
+		$this->inject_form_values_into_data( 3 );
+
+		$data2 =& $this->data[2]['form'];
+		$data3 =& $this->data[3]['form'];
+		$data4 =& $this->data[4]['form'];
+
+		$error_log = array();
+		$installation_log = array();
+
+		// Check the referer
+		bb_check_admin_referer( 'bbpress-installer' );
+		$installation_log[] = __( 'Referrer is OK, beginning installation&hellip;' );
+
+		global $bbdb;
+
+		// Setup user table variables and constants if available
+		if ( $data2['toggle_2_2']['value'] ) {
+
+			$installation_log[] = '>>> ' . __( 'Setting up custom user table constants' );
+
+			global $bb;
+			global $bb_table_prefix;
+
+			if ( !empty( $data2['wp_table_prefix']['value'] ) ) {
+				$bb->wp_table_prefix = $data2['wp_table_prefix']['value'];
+			}
+			if ( !empty( $data2['user_bbdb_name']['value'] ) ) {
+				$bb->user_bbdb_name = $data2['user_bbdb_name']['value'];
+			}
+			if ( !empty( $data2['user_bbdb_user']['value'] ) ) {
+				$bb->user_bbdb_user = $data2['user_bbdb_user']['value'];
+			}
+			if ( !empty( $data2['user_bbdb_password']['value'] ) ) {
+				$bb->user_bbdb_password = $data2['user_bbdb_password']['value'];
+			}
+			if ( !empty( $data2['user_bbdb_host']['value'] ) ) {
+				$bb->user_bbdb_host = $data2['user_bbdb_host']['value'];
+			}
+			if ( !empty( $data2['user_bbdb_charset']['value'] ) ) {
+				$bb->user_bbdb_charset = preg_replace( '/[^a-z0-9_-]/i', '', $data2['user_bbdb_charset']['value'] );
+			}
+			if ( !empty( $data2['user_bbdb_collate']['value'] ) ) {
+				$bb->user_bbdb_collate = preg_replace( '/[^a-z0-9_-]/i', '', $data2['user_bbdb_collate']['value'] );
+			}
+
+			bb_set_custom_user_tables();
+
+			// Add custom user database if required
+			if ( isset( $bb->custom_databases['user'] ) ) {
+				$bbdb->add_db_server( 'user', $bb->custom_databases['user'] );
+			}
+
+			// Add custom tables if required
+			if ( isset( $bb->custom_tables ) ) {
+				$bbdb->tables = array_merge( $bbdb->tables, $bb->custom_tables );
+				if ( is_wp_error( $bbdb->set_prefix( $bb_table_prefix ) ) )
+					die( __( 'Your user table prefix may only contain letters, numbers and underscores.' ) );
+			}
+		}
+
+		// Create the database
+		$installation_log[] = "\n" . __( 'Step 1 - Creating database tables' );
+
+		if ( !$this->database_tables_are_installed() ) {
+			// Hide db errors
+			$bbdb->hide_errors();
+			// Install the database
+			$alterations = bb_install();
+			// Show db errors
+			$bbdb->show_errors();
+
+			if ( isset( $alterations['errors'] ) && is_array( $alterations['errors'] ) ) {
+				$error_log = array_merge( $error_log, $alterations['errors'] );
+			}
+			if ( isset( $alterations['messages'] ) && is_array( $alterations['messages'] ) ) {
+				$installation_log = array_merge( $installation_log, $alterations['messages'] );
+			}
+
+			if ( !$this->database_tables_are_installed() ) {
+				$installation_log[] = '>>> ' . __( 'Database installation failed!!!' );
+				$installation_log[] = '>>>>>> ' . __( 'Halting installation!' );
+				$error_log[] = __( 'Database installation failed!!!' );
+
+				$this->step_status[4] = 'incomplete';
+				$this->strings[4]['h2'] = __( 'Installation failed!' );
+				$this->strings[4]['messages']['error'][] = __( 'The database failed to install. You may need to replace bbPress with a fresh copy and start again.' );
+
+				$data4['installation_log']['value'] = join( "\n", $installation_log );
+				$data4['error_log']['value'] = join( "\n", $error_log );
+
+				return 'incomplete';
+			}
+		} else {
+			$installation_log[] = '>>> ' . __( 'Database is already installed!!!' );
+		}
+
+		// Integration settings passed from step 2
+		// These are already validated provided that the referer checks out
+		$installation_log[] = "\n" . __( 'Step 2 - WordPress integration (optional)' );
+		if ( $data2['toggle_2_0']['value'] ) {
+			if ( $data2['toggle_2_1']['value'] ) {
+				bb_update_option( 'wp_siteurl', $data2['wp_siteurl']['value'] );
+				$installation_log[] = '>>> ' . __( 'WordPress address (URL):' ) . ' ' . $data2['wp_siteurl']['value'];
+
+				bb_update_option( 'wp_home', $data2['wp_home']['value'] );
+				$installation_log[] = '>>> ' . __( 'Blog address (URL):' ) . ' ' . $data2['wp_home']['value'];
+
+				$config_result = $this->write_lines_to_file(
+					BB_PATH . 'bb-config.php',
+					false,
+					array(
+						"define( 'BB_AUTH_KEY"  => array( "'" . BB_AUTH_KEY . "'",        "'" . $data2['wp_auth_key']['value'] . "'" ),
+						"define( 'BB_SECURE_A"  => array( "'" . BB_SECURE_AUTH_KEY . "'", "'" . $data2['wp_secure_auth_key']['value'] . "'" ),
+						"define( 'BB_LOGGED_I"  => array( "'" . BB_LOGGED_IN_KEY . "'",   "'" . $data2['wp_logged_in_key']['value'] . "'" ),
+					)
+				);
+
+				switch ( $config_result ) {
+					case 1:
+						$installation_log[] = '>>> ' . __( 'WordPress cookie keys set.' );
+						break;
+					default:
+						$error_log[] = '>>> ' . __( 'WordPress cookie keys not set.' );
+						$error_log[] = '>>>>>> ' . __( 'Your "bb-config.php" file was not writable.' );
+						$error_log[] = '>>>>>> ' . __( 'You will need to manually re-define "BB_AUTH_KEY", "BB_SECURE_AUTH_KEY" and "BB_LOGGED_IN_KEY" in your "bb-config.php" file.' );
+						$installation_log[] = '>>> ' . __( 'WordPress cookie keys not set.' );
+						break;
+				}
+
+				if ( !empty( $data2['wp_auth_salt']['value'] ) ) {
+					bb_update_option( 'bb_auth_salt', $data2['wp_auth_salt']['value'] );
+					$installation_log[] = '>>> ' . __( 'WordPress "auth" cookie salt set from input.' );
+				}
+
+				if ( !empty( $data2['wp_secure_auth_salt']['value'] ) ) {
+					bb_update_option( 'bb_secure_auth_salt', $data2['wp_secure_auth_salt']['value'] );
+					$installation_log[] = '>>> ' . __( 'WordPress "secure auth" cookie salt set from input.' );
+				}
+
+				if ( !empty( $data2['wp_logged_in_salt']['value'] ) ) {
+					bb_update_option( 'bb_logged_in_salt', $data2['wp_logged_in_salt']['value'] );
+					$installation_log[] = '>>> ' . __( 'WordPress "logged in" cookie salt set from input.' );
+				}
+			}
+
+			if ( $data2['toggle_2_2']['value'] ) {
+				if (
+					!bb_get_option( 'bb_auth_salt' ) ||
+					!bb_get_option( 'bb_secure_auth_salt' ) ||
+					!bb_get_option( 'bb_logged_in_salt' )
+				) {
+					$installation_log[] = '>>> ' . __( 'Fetching missing WordPress cookie salts.' );
+
+					$_prefix = $bb->wp_table_prefix;
+					if ( !empty( $data2['wordpress_mu_primary_blog_id']['value'] ) ) {
+						$_prefix .= $data2['wordpress_mu_primary_blog_id']['value'] . '_';
+					}
+
+					if ( isset( $bb->custom_databases['user'] ) ) {
+						$bbdb->tables['options'] = array( 'user', $_prefix . 'options' );
+					} else {
+						$bbdb->tables['options'] = $_prefix . 'options';
+					}
+
+					unset( $_prefix );
+
+					$bbdb->set_prefix( $bb_table_prefix );
+
+					if ( !bb_get_option( 'bb_auth_salt' ) ) {
+						$wp_auth_salt = $bbdb->get_var( "SELECT `option_value` FROM $bbdb->options WHERE `option_name` = 'auth_salt' LIMIT 1" );
+						if ( $wp_auth_salt ) {
+							bb_update_option( 'bb_auth_salt', $wp_auth_salt );
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "auth" cookie salt set.' );
+						} else {
+							$error_log[] = '>>> ' . __( 'WordPress "auth" cookie salt not set.' );
+							$error_log[] = '>>>>>> ' . __( 'Could not fetch "auth" cookie salt from the WordPress options table.' );
+							$error_log[] = '>>>>>> ' . __( 'You will need to manually define the "auth" cookie salt in your database.' );
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "auth" cookie salt not set.' );
+						}
+					}
+
+					if ( !bb_get_option( 'bb_secure_auth_salt' ) ) {
+						$wp_secure_auth_salt = $bbdb->get_var( "SELECT `option_value` FROM $bbdb->options WHERE `option_name` = 'secure_auth_salt' LIMIT 1" );
+						if ( $wp_secure_auth_salt ) {
+							bb_update_option( 'bb_secure_auth_salt', $wp_secure_auth_salt );
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "secure auth" cookie salt set.' );
+						} else {
+							// This cookie salt is sometimes empty so don't error
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "secure auth" cookie salt not set.' );
+						}
+					}
+
+					if ( !bb_get_option( 'bb_logged_in_salt' ) ) {
+						$wp_logged_in_salt = $bbdb->get_var( "SELECT `option_value` FROM $bbdb->options WHERE `option_name` = 'logged_in_salt' LIMIT 1" );
+						if ( $wp_logged_in_salt ) {
+							bb_update_option( 'bb_logged_in_salt', $wp_logged_in_salt );
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "logged in" cookie salt set.' );
+						} else {
+							$error_log[] = '>>> ' . __( 'WordPress "logged in" cookie salt not set.' );
+							$error_log[] = '>>>>>> ' . __( 'Could not fetch "logged in" cookie salt from the WordPress options table.' );
+							$error_log[] = '>>>>>> ' . __( 'You will need to manually define the "logged in" cookie salt in your database.' );
+							$installation_log[] = '>>>>>> ' . __( 'WordPress "logged in" cookie salt not set.' );
+						}
+					}
+				}
+
+				if ( !empty( $data2['wp_table_prefix']['value'] ) ) {
+					bb_update_option( 'wp_table_prefix', $data2['wp_table_prefix']['value'] );
+					$installation_log[] = '>>> ' . __( 'User database table prefix:' ) . ' ' . $data2['wp_table_prefix']['value'];
+				}
+
+				if ( !empty( $data2['wordpress_mu_primary_blog_id']['value'] ) ) {
+					bb_update_option( 'wordpress_mu_primary_blog_id', $data2['wordpress_mu_primary_blog_id']['value'] );
+					$installation_log[] = '>>> ' . __( 'WordPress MU primary blog ID:' ) . ' ' . $data2['wordpress_mu_primary_blog_id']['value'];
+				}
+
+				if ( $data2['toggle_2_3']['value'] ) {
+					if ( !empty( $data2['user_bbdb_name']['value'] ) ) {
+						bb_update_option( 'user_bbdb_name', $data2['user_bbdb_name']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database name:' ) . ' ' . $data2['user_bbdb_name']['value'];
+					}
+					if ( !empty( $data2['user_bbdb_user']['value'] ) ) {
+						bb_update_option( 'user_bbdb_user', $data2['user_bbdb_user']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database user:' ) . ' ' . $data2['user_bbdb_user']['value'];
+					}
+					if ( !empty( $data2['user_bbdb_password']['value'] ) ) {
+						bb_update_option( 'user_bbdb_password', $data2['user_bbdb_password']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database password:' ) . ' ' . $data2['user_bbdb_password']['value'];
+					}
+					if ( !empty( $data2['user_bbdb_host']['value'] ) ) {
+						bb_update_option( 'user_bbdb_host', $data2['user_bbdb_host']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database host:' ) . ' ' . $data2['user_bbdb_host']['value'];
+					}
+					if ( !empty( $data2['user_bbdb_charset']['value'] ) ) {
+						bb_update_option( 'user_bbdb_charset', $data2['user_bbdb_charset']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database character set:' ) . ' ' . $data2['user_bbdb_charset']['value'];
+					}
+					if ( !empty( $data2['user_bbdb_collate']['value'] ) ) {
+						bb_update_option( 'user_bbdb_collate', $data2['user_bbdb_collate']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database collation:' ) . ' ' . $data2['user_bbdb_collate']['value'];
+					}
+					if ( !empty( $data2['custom_user_table']['value'] ) ) {
+						bb_update_option( 'custom_user_table', $data2['custom_user_table']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database "user" table:' ) . ' ' . $data2['custom_user_table']['value'];
+					}
+					if ( !empty( $data2['custom_user_meta_table']['value'] ) ) {
+						bb_update_option( 'custom_user_meta_table', $data2['custom_user_meta_table']['value'] );
+						$installation_log[] = '>>> ' . __( 'User database "user meta" table:' ) . ' ' . $data2['custom_user_meta_table']['value'];
+					}
+				}
+			}
+		} else {
+			$installation_log[] = '>>> ' . __( 'Integration not enabled' );
+		}
+
+		// Site settings passed from step 3
+		// These are already validated provided that the referer checks out
+		$installation_log[] = "\n" . __( 'Step 3 - Site settings' );
+		bb_update_option( 'name', $data3['name']['value'] );
+		$installation_log[] = '>>> ' . __( 'Site name:' ) . ' ' . $data3['name']['value'];
+		bb_update_option( 'uri', $data3['uri']['value'] );
+		$installation_log[] = '>>> ' . __( 'Site address (URL):' ) . ' ' . $data3['uri']['value'];
+		bb_update_option( 'from_email', $data3['keymaster_user_email']['value'] );
+		$installation_log[] = '>>> ' . __( 'From email address:' ) . ' ' . $data3['keymaster_user_email']['value'];
+
+		// Create the key master
+		$keymaster_created = false;
+
+		switch ( $data3['keymaster_user_type']['value'] ) {
+			case 'new':
+
+				// Check to see if the user login already exists
+				if ( $keymaster_user = bb_get_user( $data3['keymaster_user_login']['value'], array( 'by' => 'login' ) ) ) {
+					// The keymaster is an existing bbPress user
+					$installation_log[] = '>>> ' . __( 'Key master could not be created!' );
+					$installation_log[] = '>>>>>> ' . __( 'That login is already taken!' );
+					$error_log[] = __( 'Key master could not be created!' );
+
+					if ( $keymaster_user->bb_capabilities['keymaster'] ) {
+						// The existing user is a key master - continue
+						$bb_current_user = bb_set_current_user( $keymaster_user->ID );
+						$installation_log[] = '>>>>>> ' . __( 'Existing key master entered!' );
+						$data4['keymaster_user_password']['value'] = __( 'Your bbPress password' );
+						$data3['keymaster_user_email']['value'] = $keymaster_user->user_email;
+						bb_update_option( 'from_email', $keymaster_user->user_email);
+						$installation_log[] = '>>>>>> ' . __( 'Re-setting admin email address.' );
+						$keymaster_created = true;
+					} else {
+						// The existing user is a non-key master user - halt installation
+						$installation_log[] = '>>>>>> ' . __( 'Existing user without key master role entered!' );
+						$installation_log[] = '>>>>>>>>> ' . __( 'Halting installation!' );
+						$this->step_status[4] = 'incomplete';
+						$this->strings[4]['h2'] = __( 'Installation failed!' );
+						$this->strings[4]['messages']['error'][] = __( 'The key master could not be created. An existing user was found with that user login.' );
+
+						$data4['installation_log']['value'] = join( "\n", $installation_log );
+						$data4['error_log']['value'] = join( "\n", $error_log );
+
+						return 'incomplete';
+					}
+
+					break;
+				}
+
+				// Helper function to let us know the password that was created
+				global $keymaster_password;
+				function bb_get_keymaster_password( $user_id, $pass ) {
+					global $keymaster_password;
+					$keymaster_password = $pass;
+				}
+				add_action( 'bb_new_user', 'bb_get_keymaster_password', 10, 2 );
+
+				// Create the new user (automattically given key master role when BB_INSTALLING is true)
+				if ( $keymaster_user_id = bb_new_user( $data3['keymaster_user_login']['value'], $data3['keymaster_user_email']['value'], '' ) ) {
+					$bb_current_user = bb_set_current_user( $keymaster_user_id );
+					$data4['keymaster_user_password']['value'] = $keymaster_password;
+					$installation_log[] = '>>> ' . __( 'Key master created' );
+					$installation_log[] = '>>>>>> ' . __( 'Username:' ) . ' ' . $data3['keymaster_user_login']['value'];
+					$installation_log[] = '>>>>>> ' . __( 'Email address:' ) . ' ' . $data3['keymaster_user_email']['value'];
+					$installation_log[] = '>>>>>> ' . __( 'Password:' ) . ' ' . $data4['keymaster_user_password']['value'];
+					$keymaster_created = true;
+				} else {
+					$installation_log[] = '>>> ' . __( 'Key master could not be created!' );
+					$installation_log[] = '>>>>>> ' . __( 'Halting installation!' );
+					$error_log[] = __( 'Key master could not be created!' );
+					$this->step_status[4] = 'incomplete';
+					$this->strings[4]['h2'] = __( 'Installation failed!' );
+					$this->strings[4]['messages']['error'][] = __( 'The key master could not be created. You may need to replace bbPress with a fresh copy and start again.' );
+
+					$data4['installation_log']['value'] = join( "\n", $installation_log );
+					$data4['error_log']['value'] = join( "\n", $error_log );
+
+					return 'incomplete';
+				}
+				break;
+
+			case 'old':
+				if ( $keymaster_user = bb_get_user( $data3['keymaster_user_login']['value'], array( 'by' => 'login' ) ) ) {
+					// The keymaster is an existing bbPress or WordPress user
+					$bb_current_user = bb_set_current_user( $keymaster_user->ID );
+					$bb_current_user->set_role( 'keymaster' );
+					$data4['keymaster_user_password']['value'] = __( 'Your existing password' );
+					$installation_log[] = '>>> ' . __( 'Key master role assigned to existing user' );
+					$installation_log[] = '>>>>>> ' . __( 'Username:' ) . ' ' . $data3['keymaster_user_login']['value'];
+					$installation_log[] = '>>>>>> ' . __( 'Email address:' ) . ' ' . $data3['keymaster_user_email']['value'];
+					$installation_log[] = '>>>>>> ' . __( 'Password:' ) . ' ' . $data4['keymaster_user_password']['value'];
+					$keymaster_created = true;
+				} else {
+					$installation_log[] = '>>> ' . __( 'Key master role could not be assigned to existing user!' );
+					$installation_log[] = '>>>>>> ' . __( 'Halting installation!' );
+					$error_log[] = __( 'Key master could not be created!' );
+					$this->step_status[4] = 'incomplete';
+					$this->strings[4]['h2'] = __( 'Installation failed!' );
+					$this->strings[4]['messages']['error'][] = __( 'The key master could not be assigned. You may need to replace bbPress with a fresh copy and start again.' );
+
+					$data4['installation_log']['value'] = join( "\n", $installation_log );
+					$data4['error_log']['value'] = join( "\n", $error_log );
+
+					return 'incomplete';
+				}
+				break;
+		}
+
+		// Don't create an initial forum if any forums already exist
+		if (!$bbdb->get_results( 'SELECT `forum_id` FROM `' . $bbdb->forums . '` LIMIT 1;' ) ) {
+			if ( $this->language != BB_LANG) {
+				global $locale, $l10n;
+				$locale = BB_LANG;
+				unset( $l10n['default'] );
+				bb_load_default_textdomain();
+			}
+
+			$description = __( 'Just another bbPress community' );
+			bb_update_option( 'description', $description);
+
+			if ( $this->language != BB_LANG) {
+				$locale = $this->language;
+				unset( $l10n['default'] );
+				bb_load_default_textdomain();
+			}
+
+			$installation_log[] = '>>> ' . __( 'Description:' ) . ' ' . $description;
+
+			if ( $forum_id = bb_new_forum( array( 'forum_name' => $data3['forum_name']['value'] ) ) ) {
+				$installation_log[] = '>>> ' . __( 'Forum name:' ) . ' ' . $data3['forum_name']['value'];
+
+				if ( $this->language != BB_LANG) {
+					$locale = BB_LANG;
+					unset( $l10n['default'] );
+					bb_load_default_textdomain();
+				}
+
+				$topic_title = __( 'Your first topic' );
+				$topic_id = bb_insert_topic(
+					array(
+						'topic_title' => $topic_title,
+						'forum_id' => $forum_id,
+						'tags' => 'bbPress'
+					)
+				);
+				$post_text = __( 'First Post!  w00t.' );
+				bb_insert_post(
+					array(
+						'topic_id' => $topic_id,
+						'post_text' => $post_text
+					)
+				);
+
+				if ( $this->language != BB_LANG ) {
+					$locale = $this->language;
+					unset( $l10n['default'] );
+					bb_load_default_textdomain();
+				}
+
+				$installation_log[] = '>>>>>> ' . __( 'Topic:' ) . ' ' . $topic_title;
+				$installation_log[] = '>>>>>>>>> ' . __( 'Post:' ) . ' ' . $post_text;
+			} else {
+				$installation_log[] = '>>> ' . __( 'Forum could not be created!' );
+				$error_log[] = __( 'Forum could not be created!' );
+			}
+		} else {
+			$installation_log[] = '>>> ' . __( 'There are existing forums in this database.' );
+			$installation_log[] = '>>>>>> ' . __( 'No new forum created.' );
+			$error_log[] = __( 'Forums already exist!' );
+		}
+
+		if ( defined( 'BB_PLUGIN_DIR' ) && BB_PLUGIN_DIR && !file_exists( BB_PLUGIN_DIR ) ) {
+			// Just suppress errors as this is not critical
+			if ( @mkdir( BB_PLUGIN_DIR, 0755 ) ) {
+				$installation_log[] = '>>> ' . sprintf( __( 'Making plugin directory at %s.' ),  BB_PLUGIN_DIR );
+			}
+		}
+
+		if ( defined( 'BB_THEME_DIR' ) && BB_THEME_DIR && !file_exists( BB_THEME_DIR ) ) {
+			// Just suppress errors as this is not critical
+			if ( @mkdir( BB_THEME_DIR, 0755 ) ) {
+				$installation_log[] = '>>> ' . sprintf( __( 'Making theme directory at %s.' ),  BB_THEME_DIR );
+			}
+		}
+
+		if ( $keymaster_created ) {
+			$keymaster_email_message = sprintf(
+				__( "Your new bbPress site has been successfully set up at:\n\n%1\$s\n\nYou can log in to the key master account with the following information:\n\nUsername: %2\$s\nPassword: %3\$s\n\nWe hope you enjoy your new forums. Thanks!\n\n--The bbPress Team\nhttp://bbpress.org/" ),
+				bb_get_uri( null, null, BB_URI_CONTEXT_TEXT ),
+				$data3['keymaster_user_login']['value'],
+				$data4['keymaster_user_password']['value']
+			);
+
+			if ( bb_mail( $data3['keymaster_user_email']['value'], __( 'New bbPress installation' ), $keymaster_email_message ) ) {
+				$installation_log[] = '>>> ' . __( 'Key master email sent' );
+			} else {
+				$installation_log[] = '>>> ' . __( 'Key master email not sent!' );
+				$error_log[] = __( 'Key master email not sent!' );
+			}
+		}
+
+		if ( count( $error_log ) ) {
+			$this->strings[4]['h2'] = __( 'Installation completed with some errors!' );
+			$this->strings[4]['messages']['error'][] = __( 'Your installation completed with some minor errors. See the error log below for more specific information.' );
+			$installation_log[] = "\n" . __( 'There were some errors encountered during installation!' );
+		} else {
+			$this->strings[4]['messages']['message'][] = __( 'Your installation completed successfully.' );
+			$installation_log[] = "\n" . __( 'Installation complete!' );
+		}
+
+		$this->step_status[4] = 'complete';
+
+		$data4['installation_log']['value'] = join( "\n", $installation_log );
+		$data4['error_log']['value'] = join( "\n", $error_log );
+
+		return 'complete';
+	}
+
+	/**
+	 * Prints a text input form element.
+	 *
+	 * @param $key string The key of the data to populate the element with.
+	 * @param $direction string Optional. The text direction, only 'ltr' or 'rtl' are acceptable.
+	 * @return void
+	 **/
+	function input_text( $key, $direction = false )
+	{
+		$data = $this->data[$this->step]['form'][$key];
+
+		$class = '';
+		$classes = array();
+		if ( isset( $data['note'] ) ) {
+			$classes[] = 'has-note';
+		}
+		if ( isset( $data['label'] ) ) {
+			$classes[] = 'has-label';
+		}
+
+		if ( isset( $this->data[$this->step]['form'][$key]['type'] ) ) {
+			$type = $this->data[$this->step]['form'][$key]['type'];
+		} else {
+			$type = 'text';
+		}
+		$classes[] = 'for-input-' . $type;
+
+		if ( isset( $this->strings[$this->step]['form_errors'][$key] ) ) {
+			$classes[] = 'error';
+		}
+		if ( count( $classes ) ) {
+			$class = ' class="' . join( ' ', $classes ) . '"';
+		}
+
+		$r = "\t" . '<label id="label-' . esc_attr( $key ) . '" for="' . esc_attr( $key ) . '"' . $class . '>' . "\n";
+
+		if ( isset( $data['label'] ) ) {
+			$r .= "\t\t" . '<span>' . $data['label'] . '</span>' . "\n";
+		}
+
+		if ( isset( $this->strings[$this->step]['form_errors'][$key] ) ) {
+			foreach ( $this->strings[$this->step]['form_errors'][$key] as $error ) {
+				if ( !is_bool( $error ) ) {
+					$r .= "\t\t" . '<span class="error">' . $error . '</span>' . "\n";
+				}
+			}
+		}
+
+		if ( isset( $data['maxlength'] ) && is_integer( $data['maxlength'] ) ) {
+			$maxlength = ' maxlength="' . esc_attr( $data['maxlength'] ) . '"';
+		}
+
+		if ( $direction && in_array( strtolower( $direction ), array( 'ltr', 'rtl' ) ) ) {
+			$direction = ' dir="' . esc_attr( strtolower( $direction ) ) . '"';
+		}
+
+		if ( isset( $data['autocomplete'] ) ) {
+			$autocomplete = ' autocomplete="' . esc_attr( $data['autocomplete'] ) . '"';
+		} else {
+			$autocomplete = '';
+		}
+
+		$this->tabindex++;
+		$r .= "\t\t" . '<input' . $direction . ' type="' . esc_attr( $type ) . '" id="' . esc_attr( $key ) . '" name="' . esc_attr( $key ) . '" class="text' . $has_note_class . '" value="' . esc_attr( $data['value'] ) . '"' . $maxlength . $autocomplete . ' tabindex="' . $this->tabindex . '" />' . "\n";
+
+		if ( isset( $data['note'] ) ) {
+			$r .= "\t\t" . '<a class="note-toggle" href="javascript:void(0);" onclick="toggleNote(\'note-' . esc_attr( $key ) . '\');">?</a>' . "\n";
+			$r .= "\t\t" . '<p id="note-' . esc_attr( $key ) . '" class="note" style="display:none">' . $data['note'] . '</p>' . "\n";
+		}
+
+		$r .= "\t\t" . '<div class="clear"></div>' . "\n";
+		$r .= "\t" . '</label>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints a hidden input form element.
+	 *
+	 * @param $key string The key of the data to populate the element with.
+	 * @return void
+	 **/
+	function input_hidden( $key )
+	{
+		$r = "\t" . '<input type="hidden" id="' . esc_attr( $key ) . '" name="' . esc_attr( $key ) . '" value="' . esc_attr( $this->data[$this->step]['form'][$key]['value'] ) . '" />' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints a textarea form element.
+	 *
+	 * @param $key string The key of the data to populate the element with.
+	 * @param $direction string Optional. The text direction, only 'ltr' or 'rtl' are acceptable.
+	 * @return void
+	 **/
+	function textarea( $key, $direction = false)
+	{
+		$data = $this->data[$this->step]['form'][$key];
+
+		$class = '';
+		$classes = array( 'for-textarea' );
+		if ( isset( $data['note'] ) ) {
+			$classes[] = 'has-note';
+		}
+		if ( isset( $data['label'] ) ) {
+			$classes[] = 'has-label';
+		}
+		if ( count( $classes ) ) {
+			$class = ' class="' . join( ' ', $classes ) . '"';
+		}
+
+		$r = "\t" . '<label id="label-' . esc_attr( $key ) . '"' . $class . ' for="' . esc_attr( $key ) . '">' . "\n";
+
+		if ( isset( $data['label'] ) ) {
+			$r .= "\t\t" . '<span>' . $data['label'] . '</span>' . "\n";
+		}
+
+		if ( isset( $data['note'] ) ) {
+			$r .= "\t\t" . '<a class="note-toggle" href="javascript:void(0);" onclick="toggleNote(\'note-' . esc_attr( $key ) . '\');">?</a>' . "\n";
+			$r .= "\t\t" . '<p id="note-' . esc_attr( $key ) . '" class="note" style="display:none">' . $data['note'] . '</p>' . "\n";
+		}
+
+		if ( $direction && in_array( strtolower( $direction ), array( 'ltr', 'rtl' ) ) ) {
+			$direction = ' dir="' . esc_attr( strtolower( $direction ) ) . '"';
+		}
+
+		$this->tabindex++;
+		$r .= "\t\t" . '<textarea id="' . esc_attr( $key ) . '" rows="5" cols="30"' . $direction . ' tabindex="' . $this->tabindex . '">' . esc_html( $data['value'] ) . '</textarea>' . "\n";
+
+		$r .= "\t" . '</label>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints a select form element populated with options.
+	 *
+	 * @param $key string The key of the data to populate the element with.
+	 * @return void
+	 **/
+	function select( $key )
+	{
+		$data = $this->data[$this->step]['form'][$key];
+
+		$class = '';
+		$classes = array( 'for-select' );
+		if ( isset( $data['note'] ) ) {
+			$classes[] = 'has-note';
+		}
+		if ( isset( $data['label'] ) ) {
+			$classes[] = 'has-label';
+		}
+		if ( count( $classes ) ) {
+			$class = ' class="' . join( ' ', $classes ) . '"';
+		}
+
+		$r = "\t" . '<label id="label-' . esc_attr( $key ) . '"' . $class . ' for="' . esc_attr( $key ) . '">' . "\n";
+
+		if ( isset( $data['label'] ) ) {
+			$r .= "\t\t" . '<span>' . $data['label'] . '</span>' . "\n";
+		}
+
+		if ( isset( $data['options'] ) ) {
+			$r .= "\t\t" . '<select id="' . esc_attr( $key ) . '" name="' . esc_attr( $key ) . '"';
+			if ( isset( $data['onchange'] ) ) {
+				$r .= ' onchange="' . esc_attr( $data['onchange'] ) . '"';
+			}
+			$this->tabindex++;
+			$r .= ' tabindex="' . $this->tabindex . '">' . "\n";
+
+			foreach ( $data['options'] as $value => $display ) {
+				if ( $data['value'] == $value ) {
+					$selected = ' selected="selected"';
+				} else {
+					$selected = '';
+				}
+
+				$r .= "\t\t\t" . '<option value="' . esc_attr( $value ) . '"' . $selected . '>' . esc_html( $display ) . '</option>' . "\n";
+			}
+
+			$r .= "\t\t" . '</select>';
+		}
+
+		if ( isset( $data['note'] ) ) {
+			$r .= "\t\t" . '<a class="note-toggle" href="javascript:void(0);" onclick="toggleNote(\'note-' . esc_attr( $key ) . '\');">?</a>' . "\n";
+			$r .= "\t\t" . '<p id="note-' . esc_attr( $key ) . '" class="note" style="display:none">' . $data['note'] . '</p>' . "\n";
+		}
+
+		$r .= "\t\t" . '<div class="clear"></div>' . "\n";
+		$r .= "\t" . '</label>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints an appropriate language selection form element if there are any available.
+	 *
+	 * @return void
+	 **/
+	function select_language()
+	{
+		if ( count( $this->languages ) > 1 ) {
+			$this->data[1]['form']['bb_lang']['value'] = $this->language;
+			$this->data[1]['form']['bb_lang']['options'] = $this->languages;
+			$this->select( 'bb_lang' );
+		} else {
+			$this->data[1]['form']['bb_lang']['value'] = 'en_US';
+			$this->input_hidden( 'bb_lang' );
+		}
+	}
+
+	/**
+	 * Prints an input checkbox which controls display of an optional section of settings.
+	 *
+	 * @param string $key The identifier of the area to be toggled.
+	 * @return void
+	 **/
+	function input_toggle( $key )
+	{
+		$data = $this->data[$this->step]['form'][$key];
+
+		$class = '';
+		$classes = array( 'for-toggle' );
+		if ( isset( $data['note'] ) ) {
+			$classes[] = 'has-note';
+		}
+		if ( isset( $data['label'] ) ) {
+			$classes[] = 'has-label';
+		}
+
+		$onclick = 'toggleBlock(this, \'' . esc_js( $key . '_target' ) . '\' );';
+		if ( isset( $data['toggle_value'] ) ) {
+			$onclick .= ' toggleValue(this, \'' . esc_js( $data['toggle_value']['target'] ) . '\', \'' . esc_js( $data['toggle_value']['off_value'] ) . '\', \'' . esc_js( $data['toggle_value']['on_value'] ) . '\' );';
+		}
+
+		$checked = $data['checked'] ? ' ' . trim( $data['checked'] ) : '';
+
+		if ( isset( $this->strings[$this->step]['form_errors'][$key] ) ) {
+			$classes[] = 'error';
+		}
+		if ( count( $classes ) ) {
+			$class = ' class="' . join( ' ', $classes ) . '"';
+		}
+
+		$r = "\t" . '<label id="label-' . esc_attr( $key ) . '"' . $class . ' for="' . esc_attr( $key ) . '">' . "\n";
+
+		$r .= "\t\t" . '<span>' . "\n";
+		$this->tabindex++;
+		$r .= "\t\t\t" . '<input type="checkbox" id="' . esc_attr( $key ) . '" name="' . esc_attr( $key ) . '" class="checkbox" onclick="' . esc_attr( $onclick ) . '"' . $checked . ' value="1" tabindex="' . $this->tabindex . '" />' . "\n";
+		if ( isset( $data['label'] ) ) {
+			$r .= "\t\t\t" . $data['label'] . "\n";
+		}
+		$r .= "\t\t" . '</span>' . "\n";
+
+		if ( isset( $data['note'] ) ) {
+			$r .= "\t\t" . '<a class="note-toggle" href="javascript:void(0);" onclick="toggleNote(\'note-' . esc_attr( $key ) . '\');">?</a>' . "\n";
+			$r .= "\t\t" . '<p id="note-' . esc_attr( $key ) . '" class="note" style="display:none">' . $data['note'] . '</p>' . "\n";
+		}
+
+		$r .= "\t\t" . '<div class="clear"></div>' . "\n";
+		$r .= "\t" . '</label>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints the input buttons which post each step and optionally go back a step.
+	 *
+	 * @param string $forward The HTML element ID of the forward button.
+	 * @param string $back Optional. The HTML element ID of the back button.
+	 * @return void
+	 **/
+	function input_buttons( $forward, $back = false, $step = false )
+	{
+		$data_back = $back ? $this->data[$this->step]['form'][$back] : false;
+		$data_forward = $this->data[$this->step]['form'][$forward];
+
+		$r = '<fieldset class="buttons">' . "\n";
+
+		if ( !$step ) {
+			$step = $this->step;
+		}
+		$r .= "\t" . '<input type="hidden" id="step" name="step" value="' . (int) $step . '" />' . "\n";
+
+		if ( $back) {
+			$r .= "\t" . '<label id="label-' . esc_attr( $back ) . '" for="' . esc_attr( $back ) . '" class="back">' . "\n";
+			$this->tabindex++;
+			$r .= "\t\t" . '<input type="submit" id="' . esc_attr( $back ) . '" name="' . esc_attr( $back ) . '" class="button" value="' . esc_attr( $data_back['value'] ) . '" tabindex="' . $this->tabindex . '" />' . "\n";
+			$r .= "\t" . '</label>' . "\n";
+		}
+
+		$r .= "\t" . '<label id="label-' . esc_attr( $forward ) . '" for="' . esc_attr( $forward ) . '" class="forward">' . "\n";
+		$this->tabindex++;
+		$r .= "\t\t" . '<input type="submit" id="' . esc_attr( $forward ) . '" name="' . esc_attr( $forward ) . '" class="button" value="' . esc_attr( $data_forward['value'] ) . '" tabindex="' . $this->tabindex . '" />' . "\n";
+		$r .= "\t" . '</label>' . "\n";
+
+		$r .= '</fieldset>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Prints hidden input elements containing the data inputted in a given step.
+	 *
+	 * @param integer $step Optional. The number of the step whose hidden inputs should be printed.
+	 * @return void
+	 **/
+	function hidden_step_inputs( $step = false )
+	{
+		if ( !$step ) {
+			$step = $this->step;
+		} elseif ( $step !== $this->step ) {
+			$this->inject_form_values_into_data( $step );
+		}
+
+		$data = $this->data[$step]['form'];
+
+		$r = '<fieldset>' . "\n";
+
+		foreach ( $data as $key => $value ) {
+			if ( 'forward_' !== substr( $key, 0, 8 ) && 'back_' !== substr( $key, 0, 5 ) ) {
+				$r .= "\t" . '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value['value'] ) . '" />' . "\n";
+			}
+		}
+
+		$r .= '</fieldset>' . "\n";
+
+		echo $r;
+	}
+
+	/**
+	 * Rewrites the admin user input into a select element containing existing WordPress administrators.
+	 *
+	 * @return boolean True if the select element was created, otherwise false.
+	 **/
+	function populate_keymaster_user_login_from_user_tables()
+	{
+		$data =& $this->data[3]['form']['keymaster_user_login'];
+
+		// Get the existing WordPress admin users
+
+		// Setup variables and constants if available
+		global $bb;
+		if ( !empty( $this->data[2]['form']['wp_table_prefix']['value'] ) ) {
+			$bb->wp_table_prefix = $this->data[2]['form']['wp_table_prefix']['value'];
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_name']['value'] ) ) {
+			$bb->user_bbdb_name = $this->data[2]['form']['user_bbdb_name']['value'];
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_user']['value'] ) ) {
+			$bb->user_bbdb_user = $this->data[2]['form']['user_bbdb_user']['value'];
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_password']['value'] ) ) {
+			$bb->user_bbdb_password = $this->data[2]['form']['user_bbdb_password']['value'];
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_host']['value'] ) ) {
+			$bb->user_bbdb_host = $this->data[2]['form']['user_bbdb_host']['value'];
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_charset']['value'] ) ) {
+			$bb->user_bbdb_charset = preg_replace( '/[^a-z0-9_-]/i', '', $this->data[2]['form']['user_bbdb_charset']['value'] );
+		}
+		if ( !empty( $this->data[2]['form']['user_bbdb_collate']['value'] ) ) {
+			$bb->user_bbdb_charset = preg_replace( '/[^a-z0-9_-]/i', '', $this->data[2]['form']['user_bbdb_collate']['value'] );
+		}
+		if ( !empty( $this->data[2]['form']['custom_user_table']['value'] ) ) {
+			$bb->custom_user_table = preg_replace( '/[^a-z0-9_-]/i', '', $this->data[2]['form']['custom_user_table']['value'] );
+		}
+		if ( !empty( $this->data[2]['form']['custom_user_meta_table']['value'] ) ) {
+			$bb->custom_user_meta_table = preg_replace( '/[^a-z0-9_-]/i', '', $this->data[2]['form']['custom_user_meta_table']['value'] );
+		}
+
+		global $bbdb;
+		global $bb_table_prefix;
+
+		// Resolve the custom user tables for bpdb
+		bb_set_custom_user_tables();
+
+		if ( isset( $bb->custom_databases ) && isset( $bb->custom_databases['user'] ) ) {
+			$bbdb->add_db_server( 'user', $bb->custom_databases['user'] );
+		}
+
+		// Add custom tables if required
+		if ( isset( $bb->custom_tables['users'] ) || isset( $bb->custom_tables['usermeta'] ) ) {
+			$bbdb->tables = array_merge( $bbdb->tables, $bb->custom_tables );
+			if ( is_wp_error( $bbdb->set_prefix( $bb_table_prefix ) ) ) {
+				die( __( 'Your user table prefix may only contain letters, numbers and underscores.' ) );
+			}
+		}
+
+		$bb_keymaster_meta_key       = $bbdb->escape( $bb_table_prefix . 'capabilities' );
+		$wp_administrator_meta_key   = $bbdb->escape( $bb->wp_table_prefix . 'capabilities' );
+		if ( !empty( $this->data[2]['form']['wordpress_mu_primary_blog_id']['value'] ) ) {
+			$wp_administrator_meta_key = $bb->wp_table_prefix . $this->data[2]['form']['wordpress_mu_primary_blog_id']['value'] . '_capabilities';
+		}
+
+		$keymaster_query = <<<EOQ
+			SELECT
+				user_login, user_email, display_name
+			FROM
+				$bbdb->users
+			LEFT JOIN
+				$bbdb->usermeta ON
+				$bbdb->users.ID = $bbdb->usermeta.user_id
+			WHERE
+				(
+					(
+						meta_key = '$wp_administrator_meta_key' AND
+						meta_value LIKE '%administrator%'
+					) OR
+					(
+						meta_key = '$bb_keymaster_meta_key' AND
+						meta_value LIKE '%keymaster%'
+					)
+				) AND
+				user_email IS NOT NULL AND
+				user_email != ''
+			ORDER BY
+				user_login;
+EOQ;
+		$bbdb->suppress_errors();
+
+		if ( $keymasters = $bbdb->get_results( $keymaster_query, ARRAY_A ) ) {
+
+			$bbdb->suppress_errors( false );
+
+			if ( count( $keymasters ) ) {
+				$email_maps = '';
+				$data['options']  = array();
+				$data['onchange'] = 'changeKeymasterEmail( this, \'keymaster_user_email\' );';
+				$data['note']     = __( 'Please select an existing bbPress Keymaster or WordPress administrator.' );
+
+				$data['options'][''] = '';
+				foreach ( $keymasters as $keymaster ) {
+					$email_maps .= 'emailMap[\'' . $keymaster['user_login'] . '\'] = \'' . $keymaster['user_email'] . '\';' . "\n\t\t\t\t\t\t\t\t";
+					if ( $keymaster['display_name'] ) {
+						$data['options'][$keymaster['user_login']] = $keymaster['user_login'] . ' (' . $keymaster['display_name'] . ')';
+					} else {
+						$data['options'][$keymaster['user_login']] = $keymaster['user_login'];
+					}
+				}
+
+				$this->strings[3]['scripts']['changeKeymasterEmail'] = <<<EOS
+						<script type="text/javascript" charset="utf-8">
+							function changeKeymasterEmail( selectObj, target ) {
+								var emailMap = new Array;
+								emailMap[''] = '';
+								$email_maps
+								var targetObj = document.getElementById( target );
+								var selectedAdmin = selectObj.options[selectObj.selectedIndex].value;
+								targetObj.value = emailMap[selectedAdmin];
+							}
+						</script>
+EOS;
+
+				$this->data[3]['form']['keymaster_user_type']['value'] = 'old';
+
+				return true;
+			}
+		}
+
+		$bbdb->suppress_errors( false );
+
+		return false;
+	}
+
+	/**
+	 * Sends HTTP headers and prints the page header.
+	 *
+	 * @return void
+	 **/
+	function header()
+	{
+		nocache_headers();
+
+		bb_install_header( $this->strings[$this->step]['title'], $this->strings[$this->step]['h1'], true );
+	}
+
+	/**
+	 * Prints the page footer.
+	 *
+	 * @return void
+	 **/
+	function footer()
+	{
+		bb_install_footer();
+	}
+
+	/**
+	 * Prints the returned messages for the current step.
+	 *
+	 * @return void
+	 **/
+	function messages()
+	{
+		if ( isset( $this->strings[$this->step]['messages'] ) ) {
+			$messages = $this->strings[$this->step]['messages'];
+
+			// This count works as long as $messages is only two-dimensional
+			$count = ( count( $messages, COUNT_RECURSIVE ) - count( $messages ) );
+			$i = 0;
+			$r = '';
+			foreach ( $messages as $type => $paragraphs ) {
+				$class = $type ? $type : '';
+
+				foreach ( $paragraphs as $paragraph ) {
+					$i++;
+					$class = ( $i === $count ) ? ( $class . ' last' ) : $class;
+					$r .= '<p class="' . esc_attr( $class ) . '">' . $paragraph . '</p>' . "\n";
+				}
+			}
+			echo $r;
+		}
+	}
+
+	/**
+	 * Prints the introduction paragraphs for the current step.
+	 *
+	 * @return void
+	 **/
+	function intro()
+	{
+		if ( 'incomplete' == $this->step_status[$this->step] && isset( $this->strings[$this->step]['intro'] ) ) {
+			$messages = $this->strings[$this->step]['intro'];
+			$count = count( $messages );
+			$i = 0;
+			$r = '';
+			foreach ( $messages as $paragraph ) {
+				$i++;
+				$class = ( $i === $count ) ? 'intro last' : 'intro';
+				$r .= '<p class="' . $class . '">' . $paragraph . '</p>' . "\n";
+			}
+			echo $r;
+		}
+	}
+
+	/**
+	 * Prints the standard header for each step.
+	 *
+	 * @param integer $step The number of the step whose header should be printed.
+	 * @return void
+	 **/
+	function step_header( $step )
+	{
+		$class = ( $step == $this->step ) ? 'open' : 'closed';
+
+		$r = '<div id="' . esc_attr( 'step' . $step ) . '" class="' . $class . '">' . "\n";
+		$r .= '<h2 class="' . $class . '">' . $this->strings[$step]['h2'] . '</h2>' . "\n";
+		$r .= '<div>' . "\n";
+
+		if ( $step < $this->step && $this->strings[$step]['status'] ) {
+			$r .= '<p class="status">' . $this->strings[$step]['status'] . '</p>' . "\n";
+		}
+
+		echo $r;
+
+		if ( $step == $this->step ) {
+			$this->intro();
+		}
+
+		$this->tabindex = 0;
+	}
+
+	/**
+	 * Prints the standard step footer.
+	 *
+	 * @return void
+	 **/
+	function step_footer()
+	{
+		$r = '</div></div>' . "\n";
+
+		echo $r;
+	}
+} // END class BB_Install
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-htaccess.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-htaccess.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-htaccess.php	(revision 5057)
@@ -0,0 +1,46 @@
+<?php
+
+$_rules = <<<EOF
+# BEGIN bbPress
+
+Options -MultiViews
+
+<IfModule mod_rewrite.c>
+RewriteEngine On
+RewriteBase %PATH%
+
+RewriteRule ^page/([0-9]+)/?$ %PATH%index.php?page=$1 [L,QSA]
+RewriteRule ^forum/([^/]+)/page/([0-9]+)/?$ %PATH%forum.php?id=$1&page=$2 [L,QSA]
+RewriteRule ^forum/([^/]+)/?$ %PATH%forum.php?id=$1 [L,QSA]
+RewriteRule ^forum/?$ %PATH% [R=302,L,QSA]
+RewriteRule ^topic/([^/]+)/page/([0-9]+)/?$ %PATH%topic.php?id=$1&page=$2 [L,QSA]
+RewriteRule ^topic/([^/]+)/?$ %PATH%topic.php?id=$1 [L,QSA]
+RewriteRule ^topic/?$ %PATH% [R=302,L,QSA]
+RewriteRule ^tags/([^/]+)/page/([0-9]+)/?$ %PATH%tags.php?tag=$1&page=$2 [L,QSA]
+RewriteRule ^tags/([^/]+)/?$ %PATH%tags.php?tag=$1 [L,QSA]
+RewriteRule ^tags/?$ %PATH%tags.php [L,QSA]
+RewriteRule ^profile/([^/]+)/page/([0-9]+)/?$ %PATH%profile.php?id=$1&page=$2 [L,QSA]
+RewriteRule ^profile/([^/]+)/([^/]+)/?$ %PATH%profile.php?id=$1&tab=$2 [L,QSA]
+RewriteRule ^profile/([^/]+)/([^/]+)/page/([0-9]+)/?$ %PATH%profile.php?id=$1&tab=$2&page=$3 [L,QSA]
+RewriteRule ^profile/([^/]+)/?$ %PATH%profile.php?id=$1 [L,QSA]
+RewriteRule ^profile/?$ %PATH%profile.php [L,QSA]
+RewriteRule ^view/([^/]+)/page/([0-9]+)/?$ %PATH%view.php?view=$1&page=$2 [L,QSA]
+RewriteRule ^view/([^/]+)/?$ %PATH%view.php?view=$1 [L,QSA]
+RewriteRule ^rss/?$ %PATH%rss.php [L,QSA]
+RewriteRule ^rss/topics/?$ %PATH%rss.php?topics=1 [L,QSA]
+RewriteRule ^rss/forum/([^/]+)/?$ %PATH%rss.php?forum=$1 [L,QSA]
+RewriteRule ^rss/forum/([^/]+)/topics/?$ %PATH%rss.php?forum=$1&topics=1 [L,QSA]
+RewriteRule ^rss/topic/([^/]+)/?$ %PATH%rss.php?topic=$1 [L,QSA]
+RewriteRule ^rss/tags/([^/]+)/?$ %PATH%rss.php?tag=$1 [L,QSA]
+RewriteRule ^rss/tags/([^/]+)/topics/?$ %PATH%rss.php?tag=$1&topics=1 [L,QSA]
+RewriteRule ^rss/profile/([^/]+)/?$ %PATH%rss.php?profile=$1 [L,QSA]
+RewriteRule ^rss/view/([^/]+)/?$ %PATH%rss.php?view=$1 [L,QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule ^.*$ %PATH%index.php [L]
+</IfModule>
+
+# END bbPress
+EOF;
+
+$_rules = str_replace( '%PATH%', bb_get_option( 'path' ), $_rules );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-schema.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-schema.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/defaults.bb-schema.php	(revision 5057)
@@ -0,0 +1,235 @@
+<?php
+
+// Globalise as this file is included within the functions bb_install() and bb_upgrade_all()
+global $bb_queries, $bbdb, $bb_schema_ignore;
+
+// Die if no database class is loaded
+if ( !isset($bbdb) || ( !is_a( $bbdb, 'BPDB' ) && !is_a( $bbdb, 'db' ) ) )
+	die( __('Database class not loaded.') );
+
+// Initialise the query array
+$bb_queries = array();
+
+// forums
+$bb_queries['forums'] = "CREATE TABLE IF NOT EXISTS `$bbdb->forums` (
+	`forum_id` int(10) NOT NULL auto_increment,
+	`forum_name` varchar(150) NOT NULL default '',
+	`forum_slug` varchar(255) NOT NULL default '',
+	`forum_desc` text NOT NULL,
+	`forum_parent` int(10) NOT NULL default 0,
+	`forum_order` int(10) NOT NULL default 0,
+	`topics` bigint(20) NOT NULL default 0,
+	`posts` bigint(20) NOT NULL default 0,
+	PRIMARY KEY (`forum_id`),
+	KEY `forum_slug` (`forum_slug`)
+);";
+
+// meta
+$bb_queries['meta'] = "CREATE TABLE IF NOT EXISTS `$bbdb->meta` (
+	`meta_id` bigint(20) NOT NULL auto_increment,
+	`object_type` varchar(16) NOT NULL default 'bb_option',
+	`object_id` bigint(20) NOT NULL default 0,
+	`meta_key` varchar(255) default NULL,
+	`meta_value` longtext default NULL,
+	PRIMARY KEY (`meta_id`),
+	KEY `object_type__meta_key` (`object_type`, `meta_key`),
+	KEY `object_type__object_id__meta_key` (`object_type`, `object_id`, `meta_key`)
+);";
+
+// posts
+$bb_queries['posts'] = "CREATE TABLE IF NOT EXISTS `$bbdb->posts` (
+	`post_id` bigint(20) NOT NULL auto_increment,
+	`forum_id` int(10) NOT NULL default 1,
+	`topic_id` bigint(20) NOT NULL default 1,
+	`poster_id` int(10) NOT NULL default 0,
+	`post_text` text NOT NULL,
+	`post_time` datetime NOT NULL default '0000-00-00 00:00:00',
+	`poster_ip` varchar(15) NOT NULL default '',
+	`post_status` tinyint(1) NOT NULL default 0,
+	`post_position` bigint(20) NOT NULL default 0,
+	PRIMARY KEY (`post_id`),
+	KEY `topic_time` (`topic_id`, `post_time`),
+	KEY `poster_time` (`poster_id`, `post_time`),
+	KEY `post_time` (`post_time`),
+	FULLTEXT KEY `post_text` (`post_text`)
+) ENGINE = MYISAM;";
+
+// terms
+$bb_queries['terms'] = "CREATE TABLE IF NOT EXISTS `$bbdb->terms` (
+	`term_id` bigint(20) NOT NULL auto_increment,
+	`name` varchar(55) NOT NULL default '',
+	`slug` varchar(200) NOT NULL default '',
+	`term_group` bigint(10) NOT NULL default 0,
+	PRIMARY KEY (`term_id`),
+	UNIQUE KEY `slug` (`slug`),
+	KEY name (name)
+);";
+
+// term_relationships
+$bb_queries['term_relationships'] = "CREATE TABLE IF NOT EXISTS `$bbdb->term_relationships` (
+	`object_id` bigint(20) NOT NULL default 0,
+	`term_taxonomy_id` bigint(20) NOT NULL default 0,
+	`user_id` bigint(20) NOT NULL default 0,
+	`term_order` int(11) NOT NULL default 0,
+	PRIMARY KEY (`object_id`, `term_taxonomy_id`),
+	KEY `term_taxonomy_id` (`term_taxonomy_id`)
+);";
+
+// term_taxonomy
+$bb_queries['term_taxonomy'] = "CREATE TABLE IF NOT EXISTS `$bbdb->term_taxonomy` (
+	`term_taxonomy_id` bigint(20) NOT NULL auto_increment,
+	`term_id` bigint(20) NOT NULL default 0,
+	`taxonomy` varchar(32) NOT NULL default '',
+	`description` longtext NOT NULL,
+	`parent` bigint(20) NOT NULL default 0,
+	`count` bigint(20) NOT NULL default 0,
+	PRIMARY KEY (`term_taxonomy_id`),
+	UNIQUE KEY `term_id_taxonomy` (`term_id`, `taxonomy`),
+	KEY `taxonomy` (`taxonomy`)
+);";
+
+// topics
+$bb_queries['topics'] = "CREATE TABLE IF NOT EXISTS `$bbdb->topics` (
+	`topic_id` bigint(20) NOT NULL auto_increment,
+	`topic_title` varchar(100) NOT NULL default '',
+	`topic_slug` varchar(255) NOT NULL default '',
+	`topic_poster` bigint(20) NOT NULL default 0,
+	`topic_poster_name` varchar(40) NOT NULL default 'Anonymous',
+	`topic_last_poster` bigint(20) NOT NULL default 0,
+	`topic_last_poster_name` varchar(40) NOT NULL default '',
+	`topic_start_time` datetime NOT NULL default '0000-00-00 00:00:00',
+	`topic_time` datetime NOT NULL default '0000-00-00 00:00:00',
+	`forum_id` int(10) NOT NULL default 1,
+	`topic_status` tinyint(1) NOT NULL default 0,
+	`topic_open` tinyint(1) NOT NULL default 1,
+	`topic_last_post_id` bigint(20) NOT NULL default 1,
+	`topic_sticky` tinyint(1) NOT NULL default 0,
+	`topic_posts` bigint(20) NOT NULL default 0,
+	`tag_count` bigint(20) NOT NULL default 0,
+	PRIMARY KEY (`topic_id`),
+	KEY `topic_slug` (`topic_slug`),
+	KEY `forum_time` (`forum_id`, `topic_time`),
+	KEY `user_start_time` (`topic_poster`, `topic_start_time`),
+	KEY `stickies` (`topic_status`, `topic_sticky`, `topic_time`)
+);";
+
+if ( bb_get_option( 'wp_table_prefix' ) || ( defined( 'BB_SCHEMA_IGNORE_WP_USERS_TABLES' ) && BB_SCHEMA_IGNORE_WP_USERS_TABLES ) ) {
+	// Don't add user tables
+} else {
+	// users - 'user_login', 'user_nicename' and 'user_registered' indices are inconsistent with WordPress
+	$bb_queries['users'] = "CREATE TABLE IF NOT EXISTS `$bbdb->users` (
+		`ID` bigint(20) unsigned NOT NULL auto_increment,
+		`user_login` varchar(60) NOT NULL default '',
+		`user_pass` varchar(64) NOT NULL default '',
+		`user_nicename` varchar(50) NOT NULL default '',
+		`user_email` varchar(100) NOT NULL default '',
+		`user_url` varchar(100) NOT NULL default '',
+		`user_registered` datetime NOT NULL default '0000-00-00 00:00:00',
+		`user_status` int(11) NOT NULL default 0,
+		`display_name` varchar(250) NOT NULL default '',
+		PRIMARY KEY (`ID`),
+		UNIQUE KEY `user_login` (`user_login`),
+		UNIQUE KEY `user_nicename` (`user_nicename`),
+		KEY `user_registered` (`user_registered`)
+	);";
+
+	// usermeta
+	$bb_queries['usermeta'] = "CREATE TABLE IF NOT EXISTS `$bbdb->usermeta` (
+		`umeta_id` bigint(20) NOT NULL auto_increment,
+		`user_id` bigint(20) NOT NULL default 0,
+		`meta_key` varchar(255),
+		`meta_value` longtext,
+		PRIMARY KEY (`umeta_id`),
+		KEY `user_id` (`user_id`),
+		KEY `meta_key` (`meta_key`)
+	);";
+}
+
+$bb_queries = apply_filters( 'bb_schema_pre_charset', $bb_queries );
+
+// Set the charset and collation on each table
+foreach ($bb_queries as $_table_name => $_sql) {
+	// Skip SQL that isn't creating a table
+	if (!preg_match('@^\s*CREATE\s+TABLE\s+@im', $_sql)) {
+		continue;
+	}
+	
+	// Skip if the table's database doesn't support collation
+	if (!$bbdb->has_cap('collation', $bbdb->$_table_name)) {
+		continue;
+	}
+	
+	// Find out if the table has a custom database set
+	if (
+		isset($bbdb->db_tables) &&
+		is_array($bbdb->db_tables) &&
+		isset($bbdb->db_tables[$bbdb->$_table_name])
+	) {
+		// Set the database for this table
+		$_database = $bbdb->db_tables[$bbdb->$_table_name];
+	} else {
+		// Set the default global database
+		$_database = 'dbh_global';
+	}
+	
+	// Make sure the database exists
+	if (
+		isset($bbdb->db_servers) &&
+		is_array($bbdb->db_servers) &&
+		isset($bbdb->db_servers[$_database]) &&
+		is_array($bbdb->db_servers[$_database])
+	) {
+		$_charset_collate = '';
+		if (isset($bbdb->db_servers[$_database]['charset']) && !empty($bbdb->db_servers[$_database]['charset'])) {
+			// Add a charset if set
+			$_charset_collate .= ' DEFAULT CHARACTER SET \'' . $bbdb->db_servers[$_database]['charset'] . '\'';
+		}
+		if (isset($bbdb->db_servers[$_database]['collate']) && !empty($bbdb->db_servers[$_database]['collate'])) {
+			// Add a collation if set
+			$_charset_collate .= ' COLLATE \'' . $bbdb->db_servers[$_database]['collate'] . '\'';
+		}
+		if ($_charset_collate) {
+			// Modify the SQL
+			$bb_queries[$_table_name] = str_replace(';', $_charset_collate . ';', $_sql);
+		}
+	}
+	unset($_database, $_charset_collate);
+}
+unset($_table_name, $_sql);
+
+$bb_queries = apply_filters( 'bb_schema', $bb_queries );
+
+// These elements in the schema may need to be ignored when doing comparisons due to inconsistencies with WordPress
+if ( bb_get_option('wp_table_prefix') || ( defined( 'BB_SCHEMA_IGNORE_WP_USERS_KEYS' ) && BB_SCHEMA_IGNORE_WP_USERS_KEYS ) ) {
+	$bb_schema_ignore = array(
+		'tables' => array(),
+		'columns' => array(),
+		'indices' => array(
+			$bbdb->users => array(
+				'user_login',
+				'user_nicename',
+				'user_registered'
+			)
+		)
+	);
+} else {
+	$bb_schema_ignore = false;
+}
+
+if ( bb_get_option('wp_table_prefix') || ( defined( 'BB_SCHEMA_IGNORE_WP_USERS_TABLES' ) && BB_SCHEMA_IGNORE_WP_USERS_TABLES ) ) {
+	if ( $bb_schema_ignore ) {
+		$bb_schema_ignore['tables'] = array( $bbdb->users, $bbdb->usermeta );
+	} else {
+		$bb_schema_ignore = array(
+			'tables' => array( $bbdb->users, $bbdb->usermeta ),
+			'columns' => array(),
+			'indices' => array()
+		);
+	}
+}
+
+$bb_schema_ignore = apply_filters( 'bb_schema_ignore', $bb_schema_ignore );
+
+do_action( 'bb_schema_defined' );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-admin.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-admin.php	(revision 5057)
@@ -0,0 +1,1731 @@
+<?php
+
+function bb_get_admin_header()
+{
+	do_action( 'bb_admin-header.php' );
+	include( 'admin-header.php' );
+	do_action( 'bb_get_admin_header' );
+}
+
+function bb_get_admin_footer()
+{
+	do_action( 'bb_admin-footer.php' );
+	include( 'admin-footer.php' );
+}
+
+function bb_admin_notice( $message, $class = false )
+{
+	if ( is_string( $message ) ) {
+		$message = '<p>' . $message . '</p>';
+		$class = $class ? $class : 'updated';
+	} elseif ( is_wp_error( $message ) ) {
+		$errors = $message->get_error_messages();
+		switch ( count( $errors ) ) {
+			case 0:
+				return false;
+				break;
+			case 1:
+				$message = '<p>' . $errors[0] . '</p>';
+				break;
+			default:
+				$message = '<ul>' . "\n\t" . '<li>' . join( '</li>' . "\n\t" . '<li>', $errors ) . '</li>' . "\n" . '</ul>';
+				break;
+		}
+		$class = $class ? $class : 'error';
+	} else {
+		return false;
+	}
+
+	$message = '<div id="message" class="' . esc_attr( $class ) . '">' . $message . '</div>';
+	$message = str_replace( "'", "\'", $message );
+	$lambda = create_function( '', "echo '$message';" );
+	add_action( 'bb_admin_notices', $lambda );
+	return $lambda;
+}
+
+/* Menu */
+
+function bb_admin_menu_generator()
+{
+	global $bb_menu, $bb_submenu;
+	$bb_menu = array();
+	$bb_submenu = array();
+
+	// Dashboard menu items < 50
+	$bb_menu[0]  = array( __( 'Dashboard' ), 'moderate', 'index.php', '', 'bb-menu-dashboard' );
+		$bb_submenu['index.php'][5]   = array( __( 'Dashboard' ), 'moderate', 'index.php' );
+
+	// 50 < Plugin added menu items < 100
+
+	$bb_menu[100] = array( '', 'read', 'separator' );
+
+	// 100 < Plugin added menu items < 150
+
+	// 150 < First menu items < 200
+	$bb_menu[150] = array( __( 'Forums' ), 'manage_forums', 'forums.php', '', 'bb-menu-forums' );
+		$bb_submenu['forums.php'][5]   = array( __( 'Forums' ), 'manage_forums', 'forums.php' );
+	$bb_menu[155] = array( __( 'Topics' ), 'moderate', 'topics.php', '', 'bb-menu-topics' );
+		$bb_submenu['topics.php'][5]   = array( __( 'Topics' ), 'moderate', 'topics.php' );
+	$bb_menu[160] = array( __( 'Posts' ), 'moderate', 'posts.php', '', 'bb-menu-posts' );
+		$bb_submenu['posts.php'][5]   = array( __( 'Posts' ), 'moderate', 'posts.php' );
+
+	// 200 < Plugin added menu items < 250
+
+	$bb_menu[250] = array( '', 'read', 'separator' );
+
+	// 250 < Plugin added menu items < 300
+
+	// 300 < Second menu items < 350
+	$bb_menu[300] = array( __( 'Appearance' ), 'manage_themes', 'themes.php', '', 'bb-menu-appearance' );
+		$bb_submenu['themes.php'][5]   = array(__('Themes'), 'manage_themes', 'themes.php');
+	$bb_menu[305] = array( __( 'Plugins' ), 'use_keys', 'plugins.php', '', 'bb-menu-plugins' );
+		$bb_submenu['plugins.php'][5]  = array( __( 'Installed' ), 'manage_plugins', 'plugins.php' );
+	$bb_menu[310] = array( __( 'Users' ), 'moderate', 'users.php', '', 'bb-menu-users' );
+		$bb_submenu['users.php'][5]  = array( __( 'Users' ), 'moderate', 'users.php' );
+		$bb_submenu['users.php'][10]  = array( __( 'Add New' ), 'manage_options', 'user-add-new.php' );
+	$bb_menu[315] = array( __( 'Tools' ), 'recount', 'tools-recount.php', '', 'bb-menu-tools' );
+		$bb_submenu['tools-recount.php'][5] = array( __( 'Re-count' ), 'recount', 'tools-recount.php' );
+	$bb_menu[320] = array( __( 'Settings' ), 'manage_options', 'options-general.php', '', 'bb-menu-settings' );
+		$bb_submenu['options-general.php'][5]  = array( __( 'General' ), 'manage_options', 'options-general.php' );
+		//$bb_submenu['options-general.php'][10] = array( __( 'Date and Time' ), 'manage_options', 'options-time.php' );
+		$bb_submenu['options-general.php'][15] = array( __( 'Writing' ), 'manage_options', 'options-writing.php' );
+		$bb_submenu['options-general.php'][20] = array( __( 'Reading' ), 'manage_options', 'options-reading.php' );
+		$bb_submenu['options-general.php'][25] = array( __( 'Discussion' ), 'manage_options', 'options-discussion.php' );
+		$bb_submenu['options-general.php'][30] = array( __( 'Permalinks' ), 'manage_options', 'options-permalinks.php' );
+		$bb_submenu['options-general.php'][35] = array( __( 'WordPress Integration' ), 'manage_options', 'options-wordpress.php' );
+
+	// 350 < Plugin added menu items
+
+	do_action( 'bb_admin_menu_generator' );
+	ksort( $bb_menu );
+
+	$last_key = false;
+	foreach ( $bb_menu as $key => $m ) {
+		if ( $last_key === false || $bb_menu[$last_key][2] === 'separator' ) {
+			$bb_menu[$key][3] .= ' bb-menu-first';
+		}
+		if ( $bb_menu[$key][2] === 'separator' ) {
+			$bb_menu[$last_key][3] .= ' bb-menu-last';
+		}
+		$last_key = $key;
+		if ( isset( $bb_submenu[$m[2]] ) ) {
+			ksort( $bb_submenu[$m[2]] );
+		}
+	}
+	$bb_menu[$last_key][3] .= ' bb-menu-last';
+}
+
+function bb_admin_add_menu( $display_name, $capability, $file_name, $menu_position = false, $class = '', $id = '' )
+{
+	global $bb_menu;
+	global $bb_registered_plugin_callbacks;
+	if ( empty( $bb_registered_plugin_callbacks ) ) {
+		$bb_registered_plugin_callbacks = array();
+	}
+
+	if ( $display_name && $capability && $file_name ) {
+		// Get an array of the keys
+		$menu_keys = array_keys( $bb_menu );
+
+		if ( $menu_position ) {
+			if ( is_numeric( $menu_position ) ) {
+				if ( !isset( $bb_menu[$menu_position] ) ) {
+					$plugin_menu_next = $menu_position;
+				} else {
+					return bb_admin_add_menu( $display_name, $capability, $file_name, ( $menu_position + 1 ), $class, $id );
+				}
+			} else {
+				// Set the bounds for different menu groups (main or side)
+				switch ( $menu_position ) {
+					case 'dash':
+						$lower = 50;
+						$upper = 100;
+						break;
+					case 'main':
+						$lower = 200;
+						$upper = 250;
+						break;
+					default:
+						$lower = 350;
+						$upper = 500;
+						break;
+				}
+
+				// Get an array of all plugin added keys
+				$plugin_menu_keys = array_filter( $menu_keys, create_function( '$v', 'if ($v >= ' . $lower . ' && $v < ' . $upper . ') { return $v; }' ) );
+
+				// If there is an array of keys
+				if ( is_array( $plugin_menu_keys ) && count( $plugin_menu_keys ) ) {
+					// Get the highest key value and add one
+					$plugin_menu_next = max( $plugin_menu_keys ) + 1;
+				} else {
+					// It's the first one
+					$plugin_menu_next = $lower;
+				}
+			}
+		} else {
+			$plugin_menu_next = max( array_keys( $bb_menu ) ) + 1;
+			$bb_menu[$plugin_menu_next] = array( '', 'read', 'separator' );
+			$plugin_menu_next++;
+		}
+
+		if ( strpos( $file_name, '.php' ) === false ) {
+			$bb_registered_plugin_callbacks[] = $file_name;
+		}
+
+		// Add the menu item at the given key
+		$bb_menu[$plugin_menu_next] = array( $display_name, $capability, $file_name, $class, $id );
+
+		ksort( $bb_menu );
+
+		return $plugin_menu_next;
+	}
+
+	return false;
+}
+
+function bb_admin_add_submenu( $display_name, $capability, $file_name, $parent = 'plugins.php' )
+{
+	global $bb_submenu;
+	global $bb_registered_plugin_callbacks;
+	if ( empty( $bb_registered_plugin_callbacks ) ) {
+		$bb_registered_plugin_callbacks = array();
+	}
+
+	if ( $display_name && $capability && $file_name ) {
+		if ( strpos( $file_name, '.php' ) === false ) {
+			$bb_registered_plugin_callbacks[] = $file_name;
+		}
+		$bb_submenu[$parent][] = array( $display_name, $capability, $file_name );
+		ksort( $bb_submenu );
+	}
+}
+
+function bb_get_current_admin_menu()
+{
+	global $bb_menu, $bb_submenu, $bb_admin_page, $bb_current_menu, $bb_current_submenu;
+	foreach ( $bb_submenu as $m => $b ) {
+		foreach ( $b as $s ) {
+			if ( $s[2] == $bb_admin_page ) {
+				$bb_current_submenu = $s;
+				$bb_current_menu = $m;
+				break;
+			}
+		}
+	}
+	if ( !isset($bb_current_menu) ) {
+		$bb_current_menu = $bb_menu[0];
+		$bb_current_submenu = $bb_submenu['index.php'][5];
+	} else {
+		foreach ( $bb_menu as $m ) {
+			if ( $m[2] == $bb_current_menu ) {
+				$bb_current_menu = $m;
+				break;
+			}
+		}
+	}
+	if ( $bb_current_submenu && !bb_current_user_can( $bb_current_submenu[1] ) || !bb_current_user_can( $bb_current_menu[1] ) ) {
+		wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+		exit;
+	}
+}
+
+function bb_admin_title()
+{
+	global $bb_current_menu, $bb_current_submenu;
+
+	$title = $bb_current_menu[0] . ' &lsaquo; ' . bb_get_option( 'name' ) . ' &#8212; ' . __( 'bbPress' );
+
+	if ( $bb_current_submenu && $bb_current_submenu[0] !== $bb_current_menu[0] ) {
+		$title = $bb_current_submenu[0] . ' &lsaquo; ' . $title;
+	}
+
+	echo esc_html( $title );
+}
+
+function bb_admin_menu()
+{
+	global $bb_menu, $bb_submenu, $bb_current_menu, $bb_current_submenu;
+
+	if ( !is_array( $bb_menu ) || !count( $bb_menu ) ) {
+		return '';
+	}
+
+	$r = "\t\t\t" . '<ul id="bbAdminMenu">' . "\n";
+
+	foreach ( $bb_menu as $key => $m ) {
+		if ( !bb_current_user_can( $m[1] ) ) {
+			continue;
+		}
+		$class = 'bb-menu';
+		if ( isset( $m[3] ) ) {
+			$class .= ' ' . $m[3];
+		}
+		$id = '';
+		if ( isset( $m[4] ) ) {
+			$id .= ' id="' . $m[4] . '"';
+		}
+		$m[0] = esc_html( $m[0] );
+		if ( $m[2] === 'separator' ) {
+			if ( 'f' == bb_get_user_setting( 'fm' ) ) {
+				$href = '?foldmenu=0';
+			} else {
+				$href = '?foldmenu=1';
+			}
+			$m[0] = '<br />';
+			$class .= ' bb-menu-separator';
+		} elseif ( strpos( $m[2], 'http://' ) === 0 || strpos( $m[2], 'https://' ) === 0 ) {
+			$href = esc_url( $m[2] );
+			$class .= ' bb-menu-external';
+		} else {
+			$href = esc_url( bb_get_option( 'path' ) . 'bb-admin/' . bb_get_admin_tab_link( $m[2] ) );
+		}
+		if ( $m[2] == $bb_current_menu[2] ) {
+			$class .= ' bb-menu-current';
+		}
+
+		$sr = '';
+		if ( $m[2] !== 'separator' && isset( $bb_submenu[$m[2]] ) && is_array( $bb_submenu[$m[2]] ) && count( $bb_submenu[$m[2]] ) ) {
+			$sr .= "\t\t\t\t\t" . '<div class="bb-menu-sub-wrap"><span>' . $m[0] . '</span>' . "\n";
+			$sr .= "\t\t\t\t\t\t" . '<ul>' . "\n";
+			$sc = 0;
+			foreach ( $bb_submenu[$m[2]] as $skey => $sm ) {
+				if ( !bb_current_user_can( $sm[1] ) ) {
+					continue;
+				}
+				if ( $sc === 0 && $sm[2] === $m[2] ) {
+					$no_submenu = true;
+				}
+				if ( $sc > 0 ) {
+					$no_submenu = false;
+				}
+				$sc++;
+				$sclass = 'bb-menu-sub';
+				if ( isset( $sm[3] ) ) {
+					$sclass .= ' ' . $sm[3];
+				}
+				if ( strpos( $sm[2], 'http://' ) === 0 || strpos( $sm[2], 'https://' ) === 0 ) {
+					$shref = $sm[2];
+					$sclass .= ' bb-menu-external';
+				} else {
+					$shref = bb_get_option( 'path' ) . 'bb-admin/' . bb_get_admin_tab_link( $sm[2] );
+				}
+				if ( $sm[2] == $bb_current_submenu[2] ) {
+					$sclass .= ' bb-menu-sub-current';
+				}
+				$sr .= "\t\t\t\t\t\t\t" . '<li class="' . esc_attr( trim( $sclass ) ) . '"><a href="' . esc_url( $shref ) . '">' . esc_html( $sm[0] ) . '</a></li>' . "\n";
+			}
+			$sr .= "\t\t\t\t\t\t" . '</ul>' . "\n";
+			$sr .= "\t\t\t\t\t" . '</div>' . "\n";
+		}
+
+		if ( $sr && !$no_submenu ) {
+			$class .= ' bb-menu-has-submenu';
+			if ( $m[2] == $bb_current_menu[2] ) {
+				$class .= ' bb-menu-open';
+			}
+		}
+
+		$r .= "\t\t\t\t" . '<li' . $id . ' class="' . esc_attr( trim( $class ) ) . '"><a href="' . $href . '">';
+
+		if ( $m[2] !== 'separator' ) {
+			$r .= '<div class="bb-menu-icon"></div>';
+		}
+
+		$r .= '<span>' . $m[0] . '</span></a>' . "\n";
+
+		if ( $sr && !$no_submenu ) {
+			$r .= '<div class="bb-menu-toggle"></div>';
+			$r .= $sr;
+		}
+
+		$r .= "\t\t\t\t" . '</li>' . "\n";
+	}
+
+	$r .= "\t\t\t" . '</ul>' . "\n";
+
+	echo $r;
+}
+
+function bb_get_admin_tab_link( $tab )
+{
+	if ( is_array( $tab ) ) {
+		$tab = $tab[2];
+	}
+	if ( strpos( $tab, '.php' ) !== false ) {
+		return $tab;
+	} else {
+		return 'admin-base.php?plugin=' . $tab;
+	}
+}
+
+/* Stats */
+
+function bb_get_recently_moderated_objects( $num = 5 ) {
+	$post_query  = new BB_Query( 'post', array( 'per_page' => $num, 'post_status' => '-normal', 'topic_status' => 0 ) ); // post_time != moderation_time;
+	$topic_query = new BB_Query( 'topic', array( 'per_page' => $num, 'topic_status' => '-normal' ) ); // topic_time == topic_start_time != moderation_time;
+
+	$objects = array();
+	if ( $post_query->results )
+		foreach ( array_keys($post_query->results) as $key )
+			$objects[bb_gmtstrtotime($post_query->results[$key]->post_time)] = array('type' => 'post', 'data' => $post_query->results[$key]);
+	if ( $topic_query->results )
+		foreach ( array_keys($topic_query->results) as $key )
+			$objects[bb_gmtstrtotime($topic_query->results[$key]->topic_time)] = array('type' => 'topic', 'data' => $topic_query->results[$key]);
+	krsort($objects);
+	return array_slice($objects, 0, $num);
+}
+
+/* Users */
+
+function bb_manage_user_fields( $edit_user = '' ) {
+	global $wp_roles, $wp_users_object, $bbdb;
+
+	// Cap checks
+	$user_roles    = $wp_roles->role_names;
+	$can_keep_gate = bb_current_user_can( 'keep_gate' );
+
+	if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+
+		bb_check_admin_referer( 'user-manage' );
+
+		// Instantiate required vars
+		$_POST                   = stripslashes_deep( $_POST );
+		$create_user_errors      = new WP_Error;
+
+		// User login
+		$trimmed_user_login      = str_replace( ' ', '', $_POST['user_login'] );
+		$user_login              = sanitize_user( $_POST['user_login'], true );
+		$user_meta['first_name'] = $_POST['first_name'];
+		$user_meta['last_name']  = $_POST['last_name'];
+		$user_display_name       = $_POST['display_name'];
+		$user_email              = $_POST['user_email'];
+		$user_url                = $_POST['user_url'];
+		$user_meta['from']       = $_POST['from'];
+		$user_meta['occ']        = $_POST['occ'];
+		$user_meta['interest']   = $_POST['interest'];
+		$user_role               = $_POST['userrole'];
+		$user_meta['throttle']   = $_POST['throttle'];
+		$user_pass1              = $_POST['pass1'];
+		$user_pass2              = $_POST['pass2'];
+		$user_status             = 0;
+		$user_pass               = false;
+		$user_url                = $user_url ? bb_fix_link( $user_url ) : '';
+
+		// Check user_login
+		if ( !isset( $_GET['action'] ) && empty( $user_login ) ) {
+			$create_user_errors->add( 'user_login', __( 'Username is a required field.' ) );
+		} else {
+			if ( $user_login !== $trimmed_user_login ) {
+				$create_user_errors->add( 'user_login', sprintf( __( '%s is an invalid username. How\'s this one?' ), esc_html( $_POST['user_login'] ) ) );
+				$user_login = $trimmed_user_login;
+			}
+		}
+
+		// Check email
+		if ( isset( $user_email ) && empty( $user_email ) )
+			$create_user_errors->add( 'user_email', __( 'Email address is a required field.' ) );
+
+		// Password Sanity Check
+		if ( ( !empty( $user_pass1 ) || !empty( $user_pass2 ) ) && $user_pass1 !== $user_pass2 )
+			$create_user_errors->add( 'pass', __( 'You must enter the same password twice.' ) );
+		elseif ( !isset( $_GET['action'] ) && ( empty( $user_pass1 ) && empty( $user_pass2 ) ) )
+			$create_user_errors->add( 'pass', __( 'You must enter a password.' ) );
+		elseif ( isset( $_GET['action'] ) && ( empty( $user_pass1 ) && empty( $user_pass2 ) ) )
+			$user_pass = '';
+		else
+			$user_pass = $user_pass1;
+
+		// No errors
+		if ( !$create_user_errors->get_error_messages() ) {
+
+			// Create or udpate
+			switch ( $_POST['action'] ) {
+				case 'create' :
+					$goback = bb_get_uri( 'bb-admin/users.php', array( 'created' => 'true' ), BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN );
+					$user   = $wp_users_object->new_user( compact( 'user_login', 'user_email', 'user_url', 'user_nicename', 'user_status', 'user_pass' ) );
+
+					// Error handler
+					if ( is_wp_error( $user ) ) {
+						bb_admin_notice( $user );
+						unset( $goback );
+
+					// Update additional user data
+					} else {
+						// Update caps
+						bb_update_usermeta( $user['ID'], $bbdb->prefix . 'capabilities', array( $user_role => true ) );
+
+						// Update all user meta
+						foreach ( $user_meta as $key => $value )
+							bb_update_usermeta( $user['ID'], $key, $value );
+
+						// Don't send email if empty
+						if ( !empty( $user_pass ) )
+							bb_send_pass( $user['ID'], $user_pass );
+
+						do_action( 'bb_new_user',    $user['ID'], $user_pass );
+					}
+
+					break;
+
+				case 'update' :
+					$goback = bb_get_uri( 'bb-admin/users.php', array( 'updated' => 'true' ), BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN );
+					$user   = $wp_users_object->get_user( $_GET['user_id'], array( 'output' => ARRAY_A ) );
+					bb_update_user( $user['ID'], $user_email, $user_url, $user_display_name );
+
+					// Don't change PW if empty
+					if ( !empty( $user_pass ) )
+						bb_update_user_password( $user['ID'], $user_pass );
+
+					// Error handler
+					if ( is_wp_error( $user ) ) {
+						bb_admin_notice( $user );
+						unset( $goback );
+
+					// Update additional user data
+					} else {
+						// Update caps
+						bb_update_usermeta( $user['ID'], $bbdb->prefix . 'capabilities', array( $user_role => true ) );
+
+						// Update all user meta
+						foreach ( $user_meta as $key => $value )
+							bb_update_usermeta( $user['ID'], $key, $value );
+
+						// Don't send email if empty
+						if ( !empty( $user_pass ) )
+							bb_send_pass( $user['ID'], $user_pass );
+
+						do_action( 'bb_update_user', $user['ID'], $user_pass );
+					}
+
+					break;
+			}
+
+			// Redirect
+			if ( isset( $goback ) && !empty( $goback ) )
+				bb_safe_redirect( $goback );
+
+		// Error handler
+		} else {
+			bb_admin_notice( $create_user_errors );
+		}
+	} elseif ( isset( $_GET['action'] ) && $_GET['action'] == 'edit' ) {
+		if ( isset( $_GET['user_id'] ) && is_numeric( $_GET['user_id'] ) ) {
+			$disabled = true;
+
+			// Get the user
+			if ( empty( $edit_user ) )
+				$edit_user = bb_get_user( bb_get_user_id( $_GET['user_id'] ) );
+
+			// Instantiate required vars
+			$user_login              = $edit_user->user_login;
+			$user_meta['first_name'] = $edit_user->first_name;
+			$user_meta['last_name']  = $edit_user->last_name;
+			$user_display_name       = $edit_user->display_name;
+			$user_email              = $edit_user->user_email;
+			$user_url                = $edit_user->user_url;
+			$user_meta['from']       = $edit_user->from;
+			$user_meta['occ']        = $edit_user->occ;
+			$user_meta['interest']   = $edit_user->interest;
+			$user_role               = array_search( 'true', $edit_user->capabilities );
+			$user_meta['throttle']   = $edit_user->throttle;
+
+			// Keymasters can't demote themselves
+			if ( ( $edit_user->ID == bb_get_current_user_info( 'id' ) && $can_keep_gate ) || ( isset( $edit_user->capabilities ) && is_array( $edit_user->capabilities ) && array_key_exists( 'keymaster', $edit_user->capabilities ) && !$can_keep_gate ) )
+				$user_roles = array( 'keymaster' => $user_roles['keymaster'] );
+
+			// only keymasters can promote others to keymaster status
+			elseif ( !$can_keep_gate )
+				unset( $user_roles['keymaster'] );
+		}
+	}
+
+	// Load password strength checker
+	wp_enqueue_script( 'password-strength-meter' );
+	wp_enqueue_script( 'profile-edit' );
+
+	// Generate a few PW hints
+	$some_pass_hints = '';
+	for ( $l = 3; $l != 0; $l-- )
+		$some_pass_hints .= '<p>' . bb_generate_password() . '</p>';
+
+	// Create  the user fields
+	$user_fields = array(
+		'user_login' => array(
+			'title'    => __( 'Username' ),
+			'note'     => __( 'Required! Unique identifier for new user.' ),
+			'value'    => $user_login,
+			'disabled' => $disabled
+		),
+		'first_name' => array(
+			'title'    => __( 'First Name' ),
+			'value'    => $user_meta['first_name']
+		),
+		'last_name' => array(
+			'title'    => __( 'Last Name' ),
+			'value'    => $user_meta['last_name']
+		),
+		'display_name' => array(
+			'title'    => __( 'Display Name' ),
+			'value'    => $user_display_name
+		),
+		'user_email' => array(
+			'title'    => __( 'Email' ),
+			'note'     => __( 'Required! Will be used for notifications and profile settings changes.' ),
+			'value'    => $user_email
+		),
+		'user_url' => array(
+			'title'    => __( 'Website' ),
+			'class'    => array( 'long', 'code' ),
+			'note'     => __( 'The full URL of user\'s homepage or blog.' ),
+			'value'    => $user_url
+		),
+		'from' => array(
+			'title'    => __( 'Location' ),
+			'class'    => array( 'long' ),
+			'value'    => $user_meta['from']
+		),
+		'occ' => array(
+			'title'    => __( 'Occupation' ),
+			'class'    => array( 'long' ),
+			'value'    => $user_meta['occ']
+		),
+		'interest' => array(
+			'title'    => __( 'Interests' ),
+			'class'    => array( 'long' ),
+			'value'    => $user_meta['interest']
+		),
+		'userrole' => array(
+			'title'    => __( 'User Role' ),
+			'type'     => 'select',
+			'options'  => $user_roles,
+			'note'     => __( 'Allow user the above privileges.' ),
+			'value'    => $user_role,
+		),
+		'pass1' => array(
+			'title'    => __( 'New Password' ),
+			'type'     => 'password',
+			'class'    => array( 'short', 'text', 'code' ),
+			'note'     => __( 'Hints: ' ) . $some_pass_hints,
+			'value'    => $user_pass1,
+		),
+		'pass2' => array(
+			'title'    => __( 'Repeat New Password' ),
+			'type'     => 'password',
+			'class'    => array( 'short', 'text', 'code' ),
+			'note'     => __( 'If you ignore hints, remember: the password should be at least seven characters long. To make it stronger, use upper and lower case letters, numbers and symbols like ! " ? $ % ^ &amp; ).' ),
+			'value'    => $user_pass2,
+		),
+		'email_pass' => array(
+			'title'    => '',
+			'type'     => 'checkbox',
+			'options'  => array(
+				'1' => array(
+					'label'      => __( 'Email the new password.' ),
+					'attributes' => array( 'checked' => true )
+				)
+			),
+		),
+		'pass-strength-fake-input' => array(
+			'title' => __( 'Password Strength' ),
+			'type'  => 'hidden',
+		),
+	);
+
+	return apply_filters( 'bb_manage_user_fields', $user_fields );
+}
+
+// Not bbdb::prepared
+function bb_get_ids_by_role( $role = 'moderator', $sort = 0, $page = 1, $limit = 50 ) {
+	global $bbdb, $bb_last_countable_query;
+	$sort = $sort ? 'DESC' : 'ASC';
+	$key = $bbdb->escape( $bbdb->prefix . 'capabilities' );
+
+	if ( !$page = abs( (int) $page ) )
+		$page = 1;
+	$limit = abs( (int) $limit );
+
+	$limit = ($limit * ($page - 1)) . ", $limit";
+
+	$role = $bbdb->escape_deep($role);
+
+	if ( is_array($role) )
+		$and_where = "( meta_value LIKE '%" . join("%' OR meta_value LIKE '%", $role) . "%' )";
+	else
+		$and_where = "meta_value LIKE '%$role%'";
+	$bb_last_countable_query = "SELECT user_id FROM $bbdb->usermeta WHERE meta_key = '$key' AND $and_where ORDER BY user_id $sort LIMIT $limit";
+
+	$ids = false;
+
+	$_tuple = compact( 'ids', 'role', 'sort', 'page', 'key', 'limit', 'bb_last_countable_query' );
+	$_tuple = apply_filters( 'bb_get_ids_by_role', $_tuple );
+	extract( $_tuple, EXTR_OVERWRITE );
+
+	if ( !$ids ) {
+		$ids = (array) $bbdb->get_col( $bb_last_countable_query );
+	}
+
+	if ( $ids ) {
+		bb_cache_users( $ids );
+	}
+
+	return $ids;
+}
+
+function bb_user_row( $user, $role = '', $email = false ) {
+	$actions = "<a href='" . esc_attr( get_user_profile_link( $user->ID ) ) . "'>" . __('View') . "</a>";
+	$title = '';
+	if ( bb_current_user_can( 'edit_user', $user_id ) ) {
+		$actions .= " | <a href='" . esc_attr( bb_get_user_admin_link( $user->ID ) ) . "'>" . __('Edit') . "</a>";
+		$title = " title='" . esc_attr( sprintf( __( 'User ID: %d' ), $user->ID ) ) . "'";
+	}
+	$r  = "\t<tr id='user-$user->ID'" . get_alt_class("user-$role") . ">\n";
+	$r .= "\t\t<td class=\"user\">" . bb_get_avatar( $user->ID, 32 ) . "<span class=\"row-title\"><a href='" . get_user_profile_link( $user->ID ) . "'" . $title . ">" . get_user_name( $user->ID ) . "</a></span><div><span class=\"row-actions\">$actions</span>&nbsp;</div></td>\n";
+	$r .= "\t\t<td><a href='" . get_user_profile_link( $user->ID ) . "'>" . get_user_display_name( $user->ID ) . "</a></td>\n";
+	if ( $email ) {
+		$email = bb_get_user_email( $user->ID );
+		$r .= "\t\t<td><a href='mailto:$email'>$email</a></td>\n";
+	}
+	
+	$registered_time = bb_gmtstrtotime( $user->user_registered );
+	if ( $registered_time < ( time() - 86400 ) ) {
+		$time = date( 'Y/m/d\<\b\r \/\>H:i:s', bb_offset_time( $registered_time ) );
+	} else {
+		$time = sprintf( __( '%s ago' ), bb_since( $registered_time ) );
+	}
+	
+	$r .= "\t\t<td>" . $time . "</td>\n";
+	
+	if (
+		!isset($user->capabilities) ||
+		!is_array($user->capabilities) ||
+		empty($user->capabilities)
+	) {
+		$role = array( __('Inactive (no role)') );
+	} else {
+		global $wp_roles;
+		$_roles = $wp_roles->get_names();
+		$role = array();
+		foreach ( $user->capabilities as $cap => $cap_set ) {
+			if (!$cap_set) {
+				continue;
+			}
+			$role[] = $_roles[$cap];
+		}
+		if ( !count( $role ) ) {
+			$role[] = __('None');
+		}
+	}
+	
+	$r .= "\t\t<td>" . join(', ', $role) . "</td>\n\t</tr>";
+	return $r;
+}
+
+// BB_User_Search class
+// by Mark Jaquith
+
+class BB_User_Search {
+	var $results;
+	var $search_term;
+	var $page;
+	var $raw_page;
+	var $users_per_page = 50;
+	var $first_user;
+	var $last_user;
+	var $query_limit;
+	var $total_users_for_query = 0;
+	var $search_errors;
+	var $paging_text;
+	var $paging_text_bottom;
+
+	function BB_User_Search ($search_term = false, $page = 1, $roles = false ) { // constructor
+		$this->search_term = $search_term ? stripslashes($search_term) : false;
+		$this->raw_page = ( '' == $page ) ? false : (int) $page;
+		$page = (int) $page;
+		$this->page = $page < 2 ? 1 : $page;
+		$roles = (array) $roles;
+		$_roles = array();
+		foreach ( $roles as $role ) {
+			if ( false !== $role ) {
+				$_roles[] = stripslashes( $role );
+			}
+		}
+		$this->roles = empty( $_roles ) ? false : $_roles;
+
+		$this->prepare_query();
+		$this->query();
+		$this->prepare_vars_for_template_usage();
+		$this->do_paging();
+	}
+
+	function prepare_query() {
+		$this->first_user = ($this->page - 1) * $this->users_per_page;
+	}
+
+	function query() {
+		$users = bb_user_search( array(
+				'query' => $this->search_term,
+				'user_email' => true,
+				'users_per_page' => $this->users_per_page,
+				'page' => $this->page,
+				'roles' => $this->roles
+		) );
+
+		if ( is_wp_error($users) )
+			$this->search_errors = $users;
+		else if ( $users )
+			$this->results = $users;
+		//	foreach ( (array) $users as $user )
+		//		$this->results[] = $user->ID;
+
+		if ( $this->results )
+			$this->total_users_for_query = bb_count_last_query();
+		elseif ( !is_wp_error($this->search_errors) )
+			$this->search_errors = new WP_Error( 'no_matching_users_found', __( '<strong>No matching users were found!</strong>' ) );
+
+		if ( is_wp_error( $this->search_errors ) )
+			bb_admin_notice( $this->search_errors );
+	}
+
+	function prepare_vars_for_template_usage() {
+		$this->search_term = stripslashes($this->search_term); // done with DB, from now on we want slashes gone
+	}
+
+	function do_paging() {
+		global $bb_current_submenu;
+		$displaying_num = sprintf(
+			__( '%1$s to %2$s of %3$s' ),
+			bb_number_format_i18n( ( $this->page - 1 ) * $this->users_per_page + 1 ),
+			$this->page * $this->users_per_page < $this->total_users_for_query ? bb_number_format_i18n( $this->page * $this->users_per_page ) : '<span class="total-type-count">' . bb_number_format_i18n( $this->total_users_for_query ) . '</span>',
+			'<span class="total-type-count">' . bb_number_format_i18n( $this->total_users_for_query ) . '</span>'
+		);
+		$page_number_links = $this->total_users_for_query > $this->users_per_page ? get_page_number_links( $this->page, $this->total_users_for_query, $this->users_per_page, false ) : '';
+		$this->paging_text = "<div class='tablenav-pages'><span class='displaying-num'>$displaying_num</span><span class=\"displaying-pages\">$page_number_links</span><div class=\"clear\"></div></div>\n";
+		$this->paging_text_bottom = "<div class='tablenav-pages'><span class=\"displaying-pages\">$page_number_links</span><div class=\"clear\"></div></div>\n";
+	}
+
+	function get_results() {
+		return (array) $this->results;
+	}
+
+	function page_links() {
+		echo $this->paging_text;
+	}
+
+	function results_are_paged() {
+		if ( isset($this->paging_text) && $this->paging_text )
+			return true;
+		return false;
+	}
+
+	function is_search() {
+		if ( $this->search_term )
+			return true;
+		return false;
+	}
+
+	function display( $show_search = true, $show_email = false ) {
+		global $wp_roles;
+
+		$r = '';
+
+		if ( isset($this->title) )
+			$title = $this->title;
+		elseif ( $this->is_search() )
+			$title = sprintf(__('Users Matching "%s" by Role'), esc_html( $this->search_term ));
+
+		$h2_search = $this->search_term;
+		$h2_role   = $this->roles[0];
+
+		$roles = $wp_roles->get_names();
+		if ( in_array( $h2_role, array_keys( $roles ) ) ) {
+			$h2_role = $roles[$h2_role];
+		}
+
+		$h2_search = $h2_search ? ' ' . sprintf( __('containing &#8220;%s&#8221;'), esc_html( $h2_search ) ) : '';
+		$h2_role  = $h2_role  ? ' ' . sprintf( __('with role &#8220;%s&#8221;'), esc_html( $h2_role ) ) : '';
+
+		$h2_span = '<span class="subtitle">';
+		$h2_span .= apply_filters( 'bb_user_search_description', sprintf( __( '%1$s%2$s' ), $h2_search, $h2_role ), $h2_search, $h2_role, $this );
+		$h2_span .= '</span>';
+
+		echo "<h2 class=\"first\">" . apply_filters( 'bb_user_search_title', __('Users') ) . $h2_span . "</h2>\n";
+		do_action( 'bb_admin_notices' );
+
+		if ( $show_search ) {
+			$roles = apply_filters( 'bb_user_search_form_roles', $wp_roles->get_names() );
+			
+			$r .= "<form action='' method='get' id='search' class='search-form'>\n";
+			$r .= "<fieldset>\n";
+			$r .= "<div>\n";
+			$r .= "\t\t<label for='usersearch'>" . __('Search term') . "</label>";
+			$r .= "\t\t<div><input type='text' name='usersearch' id='usersearch' class='text-input' value='" . esc_html( $this->search_term, 1) . "' /></div>\n";
+			$r .= "</div>\n";
+			$r .= "<div>\n";
+			$r .= "\t\t<label for='userrole'>" . __('Role') . "</label>";
+			$r .= "\t\t<div><select name='userrole[]' id='userrole'>\n";
+			$r .= "\t\t\t<option value=''>" . _x( 'All', 'user roles' ) . "</option>\n";
+			
+			foreach ( $roles as $role => $display ) {
+				$selected = '';
+				if ( is_array( $this->roles ) && in_array( $role, $this->roles ) ) {
+					$selected = ' selected="selected"';
+				}
+				$value = esc_attr($role);
+				$display = esc_html(translate($display));
+				$r .= "\t\t\t<option value='$value'$selected>$display</option>\n";
+			}
+			
+			$r .= "\t\t</select></div>\n";
+			$r .= "</div>\n";
+			
+			$r = apply_filters( 'bb_user_search_form_inputs', $r, $this );
+			
+			$r .= "<div class=\"submit\">\n";
+			$r .= "\t\t<label class='hidden' for='submit'>" . __('Search') . "</label>";
+			$r .= "\t\t<div><input type='submit' id='submit' class='button submit-input' value='" . __('Filter') . "' /></div>\n";
+			$r .= "</div>\n";
+			$r .= "</fieldset>\n";
+			$r .= "</form>\n\n";
+		}
+
+		if ( $this->get_results() ) {
+			if ( $this->results_are_paged() )
+				$r .= "<div class='tablenav'>\n" . $this->paging_text . "</div><div class=\"clear\"></div>\n\n";
+
+			//foreach($roleclasses as $role => $roleclass) {
+				//ksort($roleclass);
+				//if ( !empty($role) )
+				//	$r .= "\t\t<h3>{$wp_roles->role_names[$role]}</h3>\n";
+				//else
+				//	$r .= "\t\t<h3><em>" . __('Users with no role in these forums') . "</h3>\n";
+				$r .= "<table class='widefat'>\n";
+				$r .= "<thead>\n";
+				$r .= "\t<tr>\n";
+				if ( $show_email ) {
+					$r .= "\t\t<th style='width:30%;'>" . __('Username') . "</th>\n";
+					$r .= "\t\t<th style='width:20%;'>" . __('Name') . "</th>\n";
+					$r .= "\t\t<th style='width:20%;'>" . __('E-mail') . "</th>\n";
+				} else {
+					$r .= "\t\t<th style='width:40%;'>" . __('Username') . "</th>\n";
+					$r .= "\t\t<th style='width:30%;'>" . __('Name') . "</th>\n";
+				}
+				$r .= "\t\t<th style='width:15%;'>" . __('Registered') . "</th>\n";
+				$r .= "\t\t<th style='width:15%;'>" . __('Role') . "</th>\n";
+				$r .= "\t</tr>\n";
+				$r .= "</thead>\n\n";
+				$r .= "<tfoot>\n";
+				$r .= "\t<tr>\n";
+				if ( $show_email ) {
+					$r .= "\t\t<th style='width:30%;'>" . __('Username') . "</th>\n";
+					$r .= "\t\t<th style='width:20%;'>" . __('Name') . "</th>\n";
+					$r .= "\t\t<th style='width:20%;'>" . __('E-mail') . "</th>\n";
+				} else {
+					$r .= "\t\t<th style='width:40%;'>" . __('Username') . "</th>\n";
+					$r .= "\t\t<th style='width:30%;'>" . __('Name') . "</th>\n";
+				}
+				$r .= "\t\t<th style='width:15%;'>" . __('Registered') . "</th>\n";
+				$r .= "\t\t<th style='width:15%;'>" . __('Role') . "</th>\n";
+				$r .= "\t</tr>\n";
+				$r .= "</tfoot>\n\n";
+
+				$r .= "<tbody id='role-$role'>\n";
+				foreach ( (array) $this->get_results() as $user_object )
+					$r .= bb_user_row($user_object, $role, $show_email);
+				$r .= "</tbody>\n";
+				$r .= "</table>\n\n";
+			//}
+
+			if ( $this->results_are_paged() )
+				$r .= "<div class='tablenav bottom'>\n" . $this->paging_text_bottom . "</div><div class=\"clear\"></div>\n\n";
+		}
+		echo $r;
+	}
+
+}
+
+class BB_Users_By_Role extends BB_User_Search {
+	var $role = '';
+	var $title = '';
+
+	function BB_Users_By_Role($role = '', $page = '') { // constructor
+		$this->role = $role ? $role : 'member';
+		$this->raw_page = ( '' == $page ) ? false : (int) $page;
+		$this->page = (int) ( '' == $page ) ? 1 : $page;
+
+		$this->prepare_query();
+		$this->query();
+		$this->do_paging();
+	}
+
+	function query() {
+		if ( $_results = bb_get_ids_by_role( $this->role, 0, $this->page, $this->users_per_page ) ) {
+			$this->results = bb_get_user($_results);
+			$this->total_users_for_query = bb_count_last_query();
+		} else
+			$this->search_errors = new WP_Error( 'no_matching_users_found', __( '<strong>No matching users were found!</strong>' ) );
+
+		if ( is_wp_error( $this->search_errors ) )
+			bb_admin_notice( $this->search_errors );
+	}
+}
+
+/* Forums */
+
+// Expects forum_name, forum_desc to be pre-escaped
+function bb_new_forum( $args ) {
+	global $bbdb;
+	if ( !bb_current_user_can( 'manage_forums' ) )
+		return false;
+
+	$defaults = array( 'forum_name' => '', 'forum_desc' => '', 'forum_parent' => 0, 'forum_order' => false, 'forum_is_category' => 0 );
+	$args = wp_parse_args( $args, $defaults );
+	if ( 1 < func_num_args() ) : // For back compat
+		$args['forum_name']  = func_get_arg(0);
+		$args['forum_desc']  = func_get_arg(1);
+		$args['forum_order'] = 2 < func_num_args() ? func_get_arg(2) : 0;
+	endif;
+
+	extract($args, EXTR_SKIP);
+
+	if ( !is_numeric($forum_order) )
+		$forum_order = (int) $bbdb->get_var("SELECT MAX(forum_order) FROM $bbdb->forums") + 1;
+
+	$forum_order = (int) $forum_order;
+	$forum_parent = (int) $forum_parent;
+	$forum_is_category = (int) $forum_is_category;
+
+	$forum_name = apply_filters( 'bb_pre_forum_name', stripslashes( wp_specialchars_decode( $forum_name, ENT_QUOTES ) ) );
+	$forum_desc = apply_filters( 'bb_pre_forum_desc', stripslashes($forum_desc) );
+
+	if ( strlen($forum_name) < 1 )
+		return false;
+
+	$forum_sql = "SELECT forum_slug FROM $bbdb->forums WHERE forum_slug = %s";
+
+	$forum_slug = $_forum_slug = bb_slug_sanitize($forum_name);
+	if ( strlen($_forum_slug) < 1 )
+		return false;
+
+	while ( is_numeric($forum_slug) || $existing_slug = $bbdb->get_var( $bbdb->prepare( $forum_sql, $forum_slug ) ) )
+		$forum_slug = bb_slug_increment($_forum_slug, $existing_slug);
+
+	$bbdb->insert( $bbdb->forums, compact( 'forum_name', 'forum_slug', 'forum_desc', 'forum_parent', 'forum_order' ) );
+	$forum_id = $bbdb->insert_id;
+	if ($forum_id && $forum_is_category)
+		bb_update_forummeta($forum_id, 'forum_is_category', $forum_is_category);
+	wp_cache_flush( 'bb_forums' );
+
+	return $forum_id;
+}
+
+// Expects forum_name, forum_desc to be pre-escaped
+function bb_update_forum( $args ) {
+	global $bbdb;
+	if ( !bb_current_user_can( 'manage_forums' ) )
+		return false;
+
+	$defaults = array( 'forum_id' => 0, 'forum_name' => '', 'forum_slug' => '', 'forum_desc' => '', 'forum_parent' => 0, 'forum_order' => 0, 'forum_is_category' => 0 );
+	$fields = array( 'forum_name', 'forum_desc', 'forum_parent', 'forum_order' );
+	$args = wp_parse_args( $args, $defaults );
+	if ( 1 < func_num_args() ) : // For back compat
+		$args['forum_id']    = func_get_arg(0);
+		$args['forum_name']  = func_get_arg(1);
+		$args['forum_desc']  = 2 < func_num_args() ? func_get_arg(2) : '';
+		$args['forum_order'] = 3 < func_num_args() && is_numeric(func_get_arg(3)) ? func_get_arg(3) : 0;
+	endif;
+
+	extract($args, EXTR_SKIP);
+
+	if ( !$forum_id = (int) $forum_id )
+		return false;
+	if ( !$forum = bb_get_forum( $forum_id ) )
+		return false;
+	$forum_order = (int) $forum_order;
+	$forum_parent = (int) $forum_parent;
+	$forum_is_category = (int) $forum_is_category;
+
+	$forum_name = apply_filters( 'bb_pre_forum_name', stripslashes( wp_specialchars_decode( $forum_name, ENT_QUOTES ) ), $forum_id );
+	$forum_desc = apply_filters( 'bb_pre_forum_desc', stripslashes($forum_desc), $forum_id );
+
+	if ( strlen($forum_name) < 1 )
+		return false;
+
+	// Slug is not changing, don't update it
+	if ( !$forum_slug || $forum_slug == $forum->forum_slug ) {
+		// [sic]
+	} else {
+		$forum_slug = $_forum_slug = bb_slug_sanitize($forum_slug);
+		if ( strlen($_forum_slug) < 1 )
+			return false;
+
+		$forum_sql = "SELECT forum_slug FROM $bbdb->forums WHERE forum_slug = %s";
+
+		while ( is_numeric($forum_slug) || $existing_slug = $bbdb->get_var( $bbdb->prepare( $forum_sql, $forum_slug ) ) )
+			$forum_slug = bb_slug_increment($_forum_slug, $existing_slug);
+
+		$fields[] = 'forum_slug';
+	}
+
+	wp_cache_delete( $forum_id, 'bb_forum' );
+	wp_cache_flush( 'bb_forums' );
+
+	$update_result = $bbdb->update( $bbdb->forums, compact( $fields ), compact( 'forum_id' ) );
+
+	if ($forum_is_category)
+		bb_update_forummeta($forum_id, 'forum_is_category', $forum_is_category);
+	else
+		bb_delete_forummeta($forum_id, 'forum_is_category');
+
+	return $update_result;
+}
+
+// When you delete a forum, you delete *everything*
+// NOT bbdb::prepared
+function bb_delete_forum( $forum_id ) {
+	global $bbdb;
+	if ( !bb_current_user_can( 'delete_forum', $forum_id ) )
+		return false;
+	if ( !$forum_id = (int) $forum_id )
+		return false;
+
+	if ( !$forum = bb_get_forum( $forum_id ) )
+		return false;
+
+	if ( $topic_ids = $bbdb->get_col( $bbdb->prepare( "SELECT topic_id FROM $bbdb->topics WHERE forum_id = %d", $forum_id ) ) ) {
+		foreach ($topic_ids as $topic_id) {
+			bb_remove_topic_tags( $topic_id );
+		}
+		$_topic_ids = join(',', array_map('intval', $topic_ids));
+		$bbdb->query("DELETE FROM $bbdb->posts WHERE topic_id IN ($_topic_ids) AND topic_id != 0");
+		$bbdb->query("DELETE FROM $bbdb->meta WHERE object_type = 'bb_topic' AND object_id IN ($_topic_ids)");
+		$bbdb->query( $bbdb->prepare( "DELETE FROM $bbdb->topics WHERE forum_id = %d", $forum_id ) );
+	}
+	
+	$bbdb->update( $bbdb->forums, array( 'forum_parent' => $forum->forum_parent ), array( 'forum_parent' => $forum_id ) );
+
+	$return = $bbdb->query( $bbdb->prepare( "DELETE FROM $bbdb->forums WHERE forum_id = %d", $forum_id ) );
+
+	wp_cache_flush( 'bb_post' );
+
+	if ( $topic_ids )
+		foreach ( $topic_ids as $topic_id ) {
+			// should maybe just flush these groups instead
+			wp_cache_delete( $topic_id, 'bb_topic' );
+			wp_cache_delete( $topic_id, 'bb_thread' );
+		}
+
+	wp_cache_delete( $forum_id, 'bb_forum' );
+	wp_cache_flush( 'bb_forums' );
+
+	return $return;
+}
+
+function bb_forum_row( $forum_id = 0, $echo = true, $close = false ) {
+	global $forum, $forums_count;
+	if ( $forum_id )
+		$_forum = bb_get_forum( $forum_id );
+	else
+		$_forum =& $forum;
+
+	if ( !$_forum )
+		return;
+
+	$description = get_forum_description( $_forum->forum_id );
+
+	$r  = '';
+	if ( $close )
+		$r .= "\t<li id='forum-$_forum->forum_id'" . get_alt_class( 'forum', 'forum clear list-block' ) . ">\n";
+	$r .= "\t\t<div class='list-block posrel'>\n";
+	$r .= "\t\t\t<div class=\"row-title\">" . get_forum_name( $_forum->forum_id ) . "</div>\n";
+	if ( $description )
+		$r .= "\t\t\t<p class=\"row-description\">" . get_forum_description( $_forum->forum_id ) . "</p>\n";
+	$r .= "\t\t\t<div class=\"row-actions\"><span>\n";
+		$r .= "\t\t\t\t<a class='edit' href='" . get_forum_link() . "'>" . __('View') . "</a>\n";
+	if ( bb_current_user_can( 'manage_forums' ) )
+		$r .= "\t\t\t\t| <a class='edit' href='" . esc_attr( bb_get_uri('bb-admin/forums.php', array('action' => 'edit', 'id' => $_forum->forum_id), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN) ) . "'>" . __('Edit') . "</a>\n";
+	if ( bb_current_user_can( 'delete_forum', $_forum->forum_id ) && 1 < $forums_count )
+		$r .= "\t\t\t\t| <a class='delete' href='" . esc_attr( bb_get_uri('bb-admin/forums.php', array('action' => 'delete', 'id' => $_forum->forum_id), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN) ) . "'>" . __('Delete') . "</a>\n";
+	$r .= "\t\t\t</span>&nbsp;</div>\n";
+	$r .= "\t\t</div>\n";
+	if ( $close )
+		$r .= "\t</li>\n";
+
+	if ( $echo )
+		echo $r;
+	return $r;
+}
+
+function bb_forum_form( $forum_id = 0 ) {
+	$forum_id = (int) $forum_id;
+	if ( $forum_id && !$forum = bb_get_forum( $forum_id ) ) {
+		return;
+	}
+
+	$forum_name = '';
+	$forum_slug = '';
+	$forum_description = '';
+	$forum_position = '';
+
+	if ( $forum_id ) {
+		$forum_name = get_forum_name( $forum_id );
+	    	$forum_slug = apply_filters('editable_slug', $forum->forum_slug); 
+		$forum_description = get_forum_description( $forum_id );
+		$forum_position = get_forum_position( $forum_id );
+		$legend = __( 'Edit Forum' );
+		$submit = __( 'Save Changes' );
+		$action = 'update';
+	} else {
+		$legend = __( 'Add Forum' );
+		$submit = __( 'Add Forum' );
+		$action = 'add';
+	}
+
+	$forum_options = array(
+		'forum_name' => array(
+			'title' => __( 'Name' ),
+			'value' => $forum_name
+		),
+		'forum_slug' => array(
+			'title' => __( 'Slug' ),
+			'value' => $forum_slug
+		),
+		'forum_desc' => array(
+			'title' => __( 'Description' ),
+			'value' => $forum_description,
+			'class' => 'long'
+		),
+		'forum_parent' => array(
+			'title' => __( 'Parent' ),
+			'type' => 'select',
+			'options' => bb_get_forum_dropdown( array(
+				'cut_branch' => $forum_id,
+				'id' => 'forum_parent',
+				'none' => true,
+				'selected' => $forum_id ? get_forum_parent( $forum_id ) : 0,
+				'disable_categories' => 0,
+				'options_only' => true
+			) )
+		),
+		'forum_order' => array(
+			'title' => __( 'Position' ),
+			'value' => $forum_position,
+			'class' => 'short'
+		),
+		'forum_is_category' => array(
+			'title' => __( 'Category' ),
+			'type' => 'checkbox',
+			'options' => array(
+				1 => array(
+					'label' => __( 'Make this forum a category' ),
+					'value' => bb_get_forum_is_category( $forum_id ),
+				)
+			),
+			'note' => __( 'Categories are forums where new topics cannot be created. Categories usually contain a group of sub-forums.' )
+		)
+	);
+	
+	if ( !$forum_id ) {
+		unset( $forum_options['forum_slug'] );
+		unset( $forum_options['forum_order'] );
+	}
+	
+?>
+<form class="settings" method="post" id="<?php echo $action; ?>-forum" action="<?php bb_uri('bb-admin/bb-forum.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>" class="add:forum-list: forum-form">
+	<fieldset>
+		<legend><?php echo $legend; ?></legend>
+<?php
+foreach ( $forum_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+?>
+		<fieldset class="submit">
+<?php if ( $forum_id ) : ?>
+			<input type="hidden" name="forum_id" value="<?php echo $forum_id; ?>" />
+<?php endif; ?>
+			<?php bb_nonce_field( 'order-forums', 'order-nonce' ); ?>
+			<?php bb_nonce_field( $action . '-forum' ); ?>
+			<input type="hidden" name="action" value="<?php echo $action; ?>" />
+			<input class="submit" type="submit" name="submit" value="<?php echo $submit; ?>" />
+		</fieldset>
+	</fieldset>
+</form>
+<?php
+}
+
+class BB_Walker_ForumAdminlistitems extends BB_Walker {
+	var $tree_type = 'forum';
+	var $db_fields = array ('parent' => 'forum_parent', 'id' => 'forum_id'); //TODO: decouple this
+	
+	function start_lvl($output, $depth) {
+		$indent = str_repeat("\t", $depth) . '    ';
+		$output .= $indent . "<ul id='forum-root-$this->forum_id' class='list-block holder'>\n";
+		return $output;
+	}
+	
+	function end_lvl($output, $depth) {
+		$indent = str_repeat("\t", $depth) . '    ';
+		$output .= $indent . "</ul>\n";
+		return $output;
+	}
+	
+	function start_el($output, $forum, $depth) {
+		$this->forum_id = $forum->forum_id;
+		$indent = str_repeat("\t", $depth + 1);
+		$output .= $indent . "<li id='forum-$this->forum_id'" . get_alt_class( 'forum', 'forum clear list-block' ) . ">\n";
+
+		return $output;
+	}
+	
+	function end_el($output, $forum, $depth) {
+		$indent = str_repeat("\t", $depth + 1);
+		$output .= $indent . "</li>\n";
+		return $output;
+	}
+}
+
+
+
+/* Topics */
+
+function bb_move_forum_topics( $from_forum_id, $to_forum_id ) {
+	global $bbdb;
+	
+	$from_forum_id = (int) $from_forum_id ;
+	$to_forum_id = (int) $to_forum_id;
+	
+	add_filter('get_forum_where', 'bb_no_where'); // Just in case
+	
+	$from_forum = bb_get_forum( $from_forum_id );
+	if ( !$to_forum = bb_get_forum( $to_forum_id ) )
+		return false;
+
+	$posts = $to_forum->posts + ( $from_forum ? $from_forum->posts : 0 );
+	$topics = $to_forum->topics + ( $from_forum ? $from_forum->topics : 0 );
+	
+	$bbdb->update( $bbdb->forums, compact( 'topics', 'posts' ), array( 'forum_id' => $to_forum_id ) );
+	$bbdb->update( $bbdb->forums, array( 'topics' => 0, 'posts' => 0 ), array( 'forum_id' => $from_forum_id ) );
+	$bbdb->update( $bbdb->posts, array( 'forum_id' => $to_forum_id ), array( 'forum_id' => $from_forum_id ) );
+	$topic_ids = $bbdb->get_col( $bbdb->prepare( "SELECT topic_id FROM $bbdb->topics WHERE forum_id = %d", $from_forum_id ) );
+	$return = $bbdb->update( $bbdb->topics, array( 'forum_id' => $to_forum_id ), array( 'forum_id' => $from_forum_id ) );
+
+	wp_cache_flush( 'bb_post' );
+
+	if ( $topic_ids )
+		foreach ( $topic_ids as $topic_id ) {
+			// should maybe just flush these groups
+			wp_cache_delete( $topic_id, 'bb_topic' );
+			wp_cache_delete( $topic_id, 'bb_thread' );
+		}
+
+	wp_cache_delete( $from_forum_id, 'bb_forum' );
+	wp_cache_delete( $to_forum_id, 'bb_forum' );
+	wp_cache_flush( 'bb_forums' );
+	
+	return $return;
+}
+
+/* Posts */
+
+function bb_admin_list_posts() {
+	global $bb_posts, $bb_post;
+	
+	if ( !$bb_posts ) {
+?>
+<p class="no-results"><?php _e('No posts found.'); ?></p>
+<?php
+	} else {
+?>
+<table id="posts-list" class="widefat" cellspacing="0" cellpadding="0">
+<thead>
+	<tr>
+		<th scope="col" class="check-column"><input type="checkbox" /></th>
+		<th scope="col"><?php _e( 'Post' ); ?></th>
+		<th scope="col"><?php _e( 'Author' ); ?></th>
+		<th scope="col"><?php _e( 'Topic' ); ?></th>
+		<th scope="col"><?php _e( 'Date' ); ?></th>
+	</tr>
+</thead>
+<tfoot>
+	<tr>
+		<th scope="col" class="check-column"><input type="checkbox" /></th>
+		<th scope="col"><?php _e( 'Post' ); ?></th>
+		<th scope="col"><?php _e( 'Author' ); ?></th>
+		<th scope="col"><?php _e( 'Topic' ); ?></th>
+		<th scope="col"><?php _e( 'Date' ); ?></th>
+	</tr>
+</tfoot>
+<tbody>
+<?php
+		foreach ( $bb_posts as $bb_post ) {
+?>
+	<tr id="post-<?php post_id(); ?>"<?php alt_class('post', post_del_class()); ?>>
+		<td class="check-column"><input type="checkbox" name="post[]" value="<?php post_id(); ?>" /></td>
+		<td class="post">
+			<?php post_text(); ?>
+			<div>
+				<span class="row-actions">
+					<a href="<?php echo esc_url( get_post_link() ); ?>"><?php _e( 'View' ); ?></a>
+<?php
+	bb_post_admin( array(
+		'before_each' => ' | ',
+		'each' => array(
+			'undelete' => array(
+				'before' => ' '
+			)
+		),
+		'last_each' => array(
+			'before' => ' | '
+		)
+	) );
+?>
+				</span>&nbsp;
+			</div>
+		</td>
+
+		<td class="author">
+			<?php if ( get_post_author_id() ) : ?>
+
+				<a href="<?php user_profile_link( get_post_author_id() ); ?>">
+					<?php post_author_avatar( '16' ); ?>
+					<?php post_author(); ?>
+				</a>
+
+			<?php else : ?>
+
+				<span>
+					<?php post_author_avatar( '16' ); ?>
+					<?php post_author(); ?>
+				</span>
+
+			<?php endif; ?>
+		</td>
+
+		<td class="topic">
+			<a href="<?php topic_link( $bb_post->topic_id ); ?>"><?php topic_title( $bb_post->topic_id ); ?></a>
+		</td>
+		
+		<td class="date">
+<?php
+	if ( bb_get_post_time( 'U' ) < ( time() - 86400 ) ) {
+		bb_post_time( 'Y/m/d\<\b\r \/\>H:i:s' );
+	} else {
+		printf( __( '%s ago' ), bb_get_post_time( 'since' ) );
+	}
+?>
+		</td>
+	</tr>
+<?php 
+		}
+?>
+</tbody>
+</table>
+<?php
+	}
+}
+
+/* Recounts */
+
+function bb_recount_list()
+{
+	global $recount_list;
+	$recount_list = array(
+		5  => array( 'topic-posts', __( 'Count posts of every topic' ) ),
+		6  => array( 'topic-voices', __( 'Count voices of every topic' ) ),
+		10 => array( 'topic-deleted-posts', __( 'Count deleted posts on every topic' ) ),
+		15 => array( 'forums', __( 'Count topics and posts in every forum' ) ),
+		20 => array( 'topics-replied', __( 'Count topics to which each user has replied' ) ),
+		25 => array( 'topic-tag-count', __( 'Count tags for every topic' ) ),
+		30 => array( 'tags-tag-count', __( 'Count topics for every tag' ) ),
+		35 => array( 'tags-delete-empty', __( 'Delete tags with no topics' ) ),
+		40 => array( 'clean-favorites', __( 'Remove deleted topics from users\' favorites' ) )
+	);
+	do_action( 'bb_recount_list' );
+	ksort( $recount_list );
+	return $recount_list;
+}
+
+/* Themes */
+
+function bb_get_current_theme_data( $property = 'all' ) {
+	if (!$property) {
+		$property = 'all';
+	}
+	$directory = bb_get_active_theme_directory();
+	$stylesheet = $directory . 'style.css';
+	if (file_exists($stylesheet)) {
+		$data = bb_get_theme_data($stylesheet);
+	}
+	if ($property == 'all') {
+		return $data;
+	} elseif (isset($data[$property])) {
+		return $data[$property];
+	} else {
+		return false;
+	}
+}
+
+// Output sanitized for display
+function bb_get_theme_data( $theme_file )
+{
+	if ( strpos($theme_file, '#') !== false ) {
+		$theme_file = bb_get_theme_directory( $theme_file ) . 'style.css';
+	}
+	$theme_code = implode( '', file( $theme_file ) );
+	$theme_code = str_replace ( '\r', '\n', $theme_code );
+	// Grab just the first commented area from the file
+	preg_match( '|/\*(.*)\*/|msU', $theme_code, $theme_block );
+	$theme_data = trim( $theme_block[1] );
+	preg_match( '|Theme Name:(.*)|i', $theme_data, $theme_name );
+	preg_match( '|Theme URI:(.*)|i', $theme_data, $theme_uri );
+	preg_match( '|Description:(.*)|i', $theme_data, $description );
+	preg_match( '|Author:(.*)|i', $theme_data, $author_name );
+	preg_match( '|Author URI:(.*)|i', $theme_data, $author_uri );
+	preg_match( '|Ported By:(.*)|i', $theme_data, $porter_name );
+	preg_match( '|Porter URI:(.*)|i', $theme_data, $porter_uri );
+//	preg_match( '|Template:(.*)|i', $theme_data, $template );
+	if ( preg_match( '|Version:(.*)|i', $theme_data, $version ) )
+		$version = esc_html( trim( $version[1] ) );
+	else
+		$version ='';
+	if ( preg_match('|Status:(.*)|i', $theme_data, $status) )
+		$status = esc_html( trim($status[1]) );
+	else
+		$status = 'publish';
+
+	$description = trim($description[1]);
+	$description = bb_encode_bad( $description );
+	$description = bb_code_trick( $description );
+	$description = force_balance_tags( $description );
+	$description = bb_filter_kses( $description );
+	$description = bb_autop( $description );
+
+	$name = $theme_name[1];
+	$name = esc_html( trim($name) );
+	$theme = $name;
+
+	if ( $author_name || $author_uri ) {
+		if ( empty($author_uri[1]) ) {
+			$author = bb_filter_kses( trim($author_name[1]) );
+		} else {
+			$author = '<a href="' . esc_url( trim($author_uri[1]) ) . '" title="' . esc_attr__( 'Visit author homepage' ) . '">' . bb_filter_kses( trim($author_name[1]) ) . '</a>';
+		}
+	} else {
+		$author = '';
+	}
+
+	if ( $porter_name || $porter_uri ) {
+		if ( empty($porter_uri[1]) ) {
+			$porter = bb_filter_kses( trim($porter_name[1]) );
+		} else {
+			$porter = '<a href="' . esc_url( trim($porter_uri[1]) ) . '" title="' . esc_attr__( 'Visit porter homepage' ) . '">' . bb_filter_kses( trim($porter_name[1]) ) . '</a>';
+		}
+	} else {
+		$porter = '';
+	}
+
+	global $bb;
+
+	// Normalise the path to the theme
+	$theme_file = str_replace( '\\', '/', $theme_file );
+
+	foreach ( $bb->theme_locations as $_name => $_data ) {
+		$_directory = str_replace( '\\', '/', $_data['dir'] );
+		if ( 0 === strpos( $theme_file, $_directory ) ) {
+			$location = $_name;
+			break;
+		}
+	}
+
+	return array(
+		'Location' => $location,
+		'Name' => $name,
+		'Title' => $theme,
+		'Description' => $description,
+		'Author' => $author,
+		'Porter' => $porter,
+		'Version' => $version,
+//		'Template' => $template[1],
+		'Status' => $status,
+		'URI' => esc_url( $theme_uri[1] )
+	);
+}
+
+if ( !function_exists( 'checked' ) ) :
+function checked( $checked, $current) {
+	if ( $checked == $current)
+		echo ' checked="checked"';
+}
+endif;
+
+if ( !function_exists( 'selected' ) ) :
+function selected( $selected, $current) {
+	if ( $selected === $current)
+		echo ' selected="selected"';
+}
+endif;
+
+/* Options */
+
+function bb_option_form_element( $name = 'name', $args = null ) {
+	global $bb_hardcoded;
+
+	$defaults = array(
+		'title' => 'title',
+		'type' => 'text',
+		'value' => false,
+		'options' => false,
+		'message' => false,
+		'class' => false,
+		'default' => false,
+		'before' => '',
+		'after' => '',
+		'note' => false,
+		'attributes' => false,
+		'disabled' => false,
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+
+	$id = str_replace( array( '_', '[', ']' ), array( '-', '-', '' ), $name );
+	if ( false !== strpos( $name, '[' ) ) {
+		list( $option_name, $option_key ) = preg_split( '/[\[\]]/', $name, -1, PREG_SPLIT_NO_EMPTY );
+		$option = bb_get_option( $option_name );
+		$value = false === $args['value'] ? esc_attr( $option[$option_key] ) : esc_attr( $args['value'] );
+		$hardcoded = isset( $bb_hardcoded[$option_name][$option_key] );
+	} else {
+		$value = false === $args['value'] ? bb_get_form_option( $name ) : esc_attr( $args['value'] );
+		$hardcoded = isset( $bb_hardcoded[$name] );
+	}
+
+	$class = $args['class'] ? (array) $args['class'] : array();
+	array_unshift( $class, $args['type'] );
+
+	if ( $hardcoded || $args['disabled'] )
+		$disabled = ' disabled="disabled"';
+	else
+		$disabled = false;
+
+	if ( $args['attributes'] ) {
+		$attributes = array();
+		foreach ( $args['attributes'] as $k => $v )
+			$attributes[] = "$k='$v'";
+		$attributes = ' ' . join( ' ', $attributes );
+	} else {
+		$attributes = '';
+	}
+
+?>
+
+		<div id="option-<?php echo $id; ?>"<?php if ( !empty( $disabled ) ) echo ' class="disabled"'; ?>>
+<?php
+			switch ( $args['type'] ) {
+				case 'radio' :
+				case 'checkbox' :
+				case 'message' :
+?>
+			<div class="label">
+				<?php echo $args['title']; ?>
+			</div>
+
+<?php
+					break;
+				case 'select' :
+				default :
+?>
+			<label for="<?php echo $id; ?>">
+				<?php echo $args['title']; ?>
+			</label>
+
+<?php
+					break;
+			}
+?>
+			<div class="inputs">
+
+<?php
+			if ( $args['before'] ) {
+				echo '<span class="before">' . $args['before'] . '</span>';
+			}
+			switch ( $args['type'] ) {
+				case 'select' :
+					echo "<select$disabled class='" . join( ' ', $class ) . "' name='$name' id='$id'$attributes>\n";
+					if ( is_array( $args['options'] ) ) {
+						foreach ( $args['options'] as $option => $label )
+							echo "\t<option value='$option'" . ( $value == $option ? " selected='selected'" : '' ) . ">$label</option>\n";
+					} elseif ( is_string( $args['options'] ) ) {
+						echo $args['options'] . "\n";
+					}
+					echo "</select>\n";
+					break;
+				case 'radio' :
+				case 'checkbox' :
+					if ( is_array( $args['options'] ) ) {
+						$_id = 0;
+						if ( 'radio' === $args['type'] && !in_array( $value, array_keys( $args['options'] ) ) && empty( $value ) ) {
+							$use_first_value = true;
+						}
+						$type = $args['type'];
+						foreach ( $args['options'] as $option => $label ) {
+							if ( $use_first_value ) {
+								$use_first_value = false;
+								$value = $option;
+							}
+							if ( is_array( $label ) ) {
+								if ( isset( $label['attributes'] ) ) {
+									$attributes = array();
+									foreach ( $label['attributes'] as $k => $v )
+										$attributes[] = "$k='$v'";
+									$attributes = ' ' . join( ' ', $attributes );
+								} else {
+									$attributes = '';
+								}
+								if ( isset( $label['name'] ) ) {
+									$name = $label['name'];
+									$id = str_replace( array( '_', '[', ']' ), array( '-', '-', '' ), $name );
+									$hardcoded = isset( $bb_hardcoded[$name] );
+								}
+								if ( isset( $label['value'] ) ) {
+									$_value = $label['value'];
+								} else {
+									$_value = $args['value'];
+								}
+								$value = false === $_value ? bb_get_form_option( $name ) : esc_attr( $_value );
+								$label = $label['label'];
+							}
+							echo "<label class=\"{$type}s\"><input$disabled type='$type' class='" . join( ' ', $class ) . "' name='$name' id='$id-$_id' value='$option'" . ( $value == $option ? " checked='checked'" : '' ) . "{$attributes} /> $label</label>\n";
+							$_id++;
+						}
+					} elseif ( is_string( $args['options'] ) ) {
+						echo $args['options'] . "\n";
+					}
+					break;
+				case 'message' :
+					if ( $args['message'] ) {
+						echo $args['message'];
+					}
+					break;
+				default :
+					echo "<input$disabled type='$args[type]' class='" . join( ' ', $class ) . "' name='$name' id='$id' value='$value'$attributes />\n";
+					break;
+			}
+			if ( $args['after'] ) {
+				echo '<span class="after">' . $args['after'] . '</span>';
+			}
+
+			if ( $args['note'] ) {
+				foreach ( (array) $args['note'] as $note ) {
+?>
+
+				<p><?php echo $note; ?></p>
+
+<?php
+				}
+			}
+?>
+
+			</div>
+		</div>
+
+<?php
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-plugin.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-plugin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-plugin.php	(revision 5057)
@@ -0,0 +1,441 @@
+<?php
+
+function bb_get_plugins_callback( $type = 'normal', $path, $filename )
+{
+	if ( '.php' != substr( $filename, -4 ) ) {
+		return false;
+	}
+
+	$data = array( 'autoload' => 0 );
+
+	if ( $has_underscore = '_' === substr( $filename, 0, 1 ) ) {
+		switch ( $type ) {
+			case 'all':
+			case 'autoload':
+				$data['autoload'] = 1;
+				break;
+			case 'normal':
+			default:
+				return false;
+				break;
+		}
+	} elseif ( 'autoload' === $type ) {
+		return false;
+	}
+
+	if ( $_data = bb_get_plugin_data( $path ) ) {
+		return array_merge( $_data , $data );
+	}
+
+	return false;
+}
+
+function bb_get_plugins( $location = 'all', $type = 'normal', $status = 'all' )
+{
+	static $plugin_cache = array();
+
+	if ( !in_array( $type, array( 'all', 'autoload', 'normal' ) ) ) {
+		$type = 'normal';
+	}
+
+	if ( 'autoload' === $type || !in_array( $status, array( 'all', 'active', 'inactive' ) ) ) {
+		$status = 'all';
+	}
+
+	if ( isset( $plugin_cache[$location][$type][$status] ) ) {
+		return $plugin_cache[$location][$type][$status];
+	}
+
+	global $bb;
+	$directories = array();
+	if ( 'all' === $location ) {
+		foreach ( $bb->plugin_locations as $_data ) {
+			$directories[] = $_data['dir'];
+		}
+	} elseif ( isset( $bb->plugin_locations[$location]['dir'] ) ) {
+		$directories[] = $bb->plugin_locations[$location]['dir'];
+	}
+
+	require_once( BB_PATH . BB_INC . 'class.bb-dir-map.php' );
+
+	$plugin_arrays = array();
+	foreach ( $directories as $directory ) {
+		$dir_map = new BB_Dir_Map(
+			$directory,
+			array(
+				'callback' => 'bb_get_plugins_callback',
+				'callback_args' => array( $type ),
+				'recurse' => 1
+			)
+		);
+		$dir_plugins = $dir_map->get_results();
+		$dir_plugins = is_wp_error( $dir_plugins ) ? array() : $dir_plugins;
+		$plugin_arrays[] = $dir_plugins;
+		unset($dir_map, $dir_plugins);
+	}
+	
+	$plugins = array();
+	foreach ($plugin_arrays as $plugin_array) {
+		$plugins = array_merge($plugins, $plugin_array);
+	}
+
+	$active_plugins = (array) bb_get_option( 'active_plugins' );
+	
+	$adjusted_plugins = array();
+	foreach ($plugins as $plugin => $plugin_data) {
+		$_id = $plugin_data['location'] . '#' . $plugin;
+		$plugin_data['active'] = 0;
+		if ( 'autoload' === $type || in_array( $_id, $active_plugins ) ) {
+			$plugin_data['active'] = 1;
+		}
+		if (
+			'active' === $status && $plugin_data['active'] ||
+			'inactive' === $status && !$plugin_data['active'] ||
+			'all' === $status
+		) {
+			$adjusted_plugins[$_id] = $plugin_data;
+		}
+	}
+
+	uasort( $adjusted_plugins, 'bb_plugins_sort' );
+
+	$plugin_cache[$location][$type][$status] = $adjusted_plugins;
+
+	return $adjusted_plugins;
+}
+
+function bb_plugins_sort( $a, $b )
+{
+	return strnatcasecmp( $a['name'], $b['name'] );
+}
+
+function bb_get_plugin_counts()
+{
+	$all_plugins = bb_get_plugins( 'all', 'all' );
+	$active_plugins = (array) bb_get_option( 'active_plugins' );
+	$counts = array(
+		'plugin_count_all' => count( $all_plugins ),
+		'plugin_count_active' => count( $active_plugins ),
+		'plugin_count_inactive' => 0,
+		'plugin_count_autoload' => 0
+	);
+	foreach ( $all_plugins as $id => $all_plugin ) {
+		if ( $all_plugin['autoload'] ) {
+			$counts['plugin_count_autoload']++;
+		} elseif ( !in_array( $id, $active_plugins ) ) {
+			$counts['plugin_count_inactive']++;
+		}
+	}
+	return $counts;
+}
+
+/**
+ * Parse the plugin contents to retrieve plugin's metadata.
+ *
+ * The metadata of the plugin's data searches for the following in the plugin's
+ * header. All plugin data must be on its own line. For plugin description, it
+ * must not have any newlines or only parts of the description will be displayed
+ * and the same goes for the plugin data. The below is formatted for printing.
+ *
+ * <code>
+ * /*
+ * Plugin Name: Name of Plugin
+ * Plugin URI: Link to plugin information
+ * Description: Plugin Description
+ * Author: Plugin author's name
+ * Author URI: Link to the author's web site
+ * Version: Must be set
+ * Requires at least: Optional.  Minimum bbPress version this plugin requires
+ * Tested up to: Optional. Maximum bbPress version this plugin has been tested with
+ * Text Domain: Optional. Unique identifier, should be same as the one used in
+ *		bb_load_plugin_textdomain()
+ * Domain Path: Optional. Only useful if the translations are located in a
+ *		folder above the plugin's base path. For example, if .mo files are
+ *		located in the locale folder then Domain Path will be "/locale/" and
+ *		must have the first slash. Defaults to the base folder the plugin is
+ *		located in.
+ *  * / # You must remove the space to close comment (the space is here only for documentation purposes).
+ * </code>
+ *
+ * Plugin data returned array contains the following:
+ *		'location' - Location of plugin file
+ *		'name' - Name of the plugin, must be unique.
+ *		'uri' - Plugin's web site.
+ *		'plugin_link' - Title of plugin linked to plugin's web site.
+ *		'description' - Description of what the plugin does and/or notes
+ *		from the author.
+ *		'author' - The author's name
+ *		'author_uri' - The author's web site address.
+ *		'author_link' - The author's name linked to the author's web site.
+ *		'version' - The plugin version number.
+ *		'requires' - Minimum bbPress version plugin requires
+ *		'tested' - Maximum bbPress version plugin has been tested with
+ *		'text_domain' - Plugin's text domain for localization.
+ *		'domain_path' - Plugin's relative directory path to .mo files.
+ *
+ * Some users have issues with opening large files and manipulating the contents
+ * for want is usually the first 1kiB or 2kiB. This function stops pulling in
+ * the plugin contents when it has all of the required plugin data.
+ *
+ * The first 8kiB of the file will be pulled in and if the plugin data is not
+ * within that first 8kiB, then the plugin author should correct their plugin
+ * and move the plugin data headers to the top.
+ *
+ * The plugin file is assumed to have permissions to allow for scripts to read
+ * the file. This is not checked however and the file is only opened for
+ * reading.
+ *
+ * @link http://trac.wordpress.org/ticket/5651 Previous Optimizations.
+ * @link http://trac.wordpress.org/ticket/7372 Further and better Optimizations.
+ * @since 1.5.0
+ *
+ * @param string $plugin_file Path to the plugin file
+ * @param bool $markup If the returned data should have HTML markup applied
+ * @param bool $translate If the returned data should be translated
+ * @return array See above for description.
+ */
+function bb_get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
+	global $bb;
+
+	if ( preg_match( '/^([a-z0-9_-]+)#((?:[a-z0-9\/\\_-]+.)+)(php)$/i', $plugin_file, $_matches ) ) {
+		$plugin_file = $bb->plugin_locations[$_matches[1]]['dir'] . $_matches[2] . $_matches[3];
+		
+		$_directory = $bb->plugin_locations[$_matches[1]]['dir'];
+		$_plugin = $_matches[2] . $_matches[3];
+
+		if ( !$_plugin ) {
+			// Not likely
+			return false;
+		}
+
+		if ( validate_file( $_plugin ) ) {
+			// $plugin has .., :, etc.
+			return false;
+		}
+
+		$plugin_file = $_directory . $_plugin;
+		unset( $_matches, $_directory, $_plugin );
+	}
+
+	if ( !file_exists( $plugin_file ) ) {
+		// The plugin isn't there
+		return false;
+	}
+
+	// We don't need to write to the file, so just open for reading.
+	$fp = fopen($plugin_file, 'r');
+
+	// Pull only the first 8kiB of the file in.
+	$plugin_code = fread( $fp, 8192 );
+
+	// PHP will close file handle, but we are good citizens.
+	fclose($fp);
+
+	// Grab just the first commented area from the file
+	if ( !preg_match( '|/\*(.*?Plugin Name:.*?)\*/|ims', $plugin_code, $plugin_block ) )
+		return false;
+        $plugin_data = trim( $plugin_block[1] );
+
+	preg_match( '|Plugin Name:(.*)$|mi', $plugin_data, $name );
+	preg_match( '|Plugin URI:(.*)$|mi', $plugin_data, $uri );
+	preg_match( '|Version:(.*)|i', $plugin_data, $version );
+	preg_match( '|Description:(.*)$|mi', $plugin_data, $description );
+	preg_match( '|Author:(.*)$|mi', $plugin_data, $author );
+	preg_match( '|Author URI:(.*)$|mi', $plugin_data, $author_uri );
+	preg_match( '|Text Domain:(.*)$|mi', $plugin_data, $text_domain );
+	preg_match( '|Domain Path:(.*)$|mi', $plugin_data, $domain_path );
+	preg_match( '|Requires at least:(.*)$|mi', $plugin_data, $requires );
+	preg_match( '|Tested up to:(.*)$|mi', $plugin_data, $tested );
+
+	// Normalise the path to the plugin
+	$plugin_file = str_replace( '\\', '/', $plugin_file );
+
+	foreach ( $bb->plugin_locations as $_name => $_data ) {
+		$_directory = str_replace( '\\', '/', $_data['dir'] );
+		if ( 0 === strpos( $plugin_file, $_directory ) ) {
+			$location = array( 1 => $_name );
+			break;
+		}
+	}
+
+	$plugins_allowedtags = array('a' => array('href' => array(),'title' => array()),'abbr' => array('title' => array()),'acronym' => array('title' => array()),'code' => array(),'em' => array(),'strong' => array());
+
+	$fields = array(
+		'location' => '',
+		'name' => 'html',
+		'uri' => 'url',
+		'version' => 'text',
+		'description' => 'html',
+		'author' => 'html',
+		'author_uri' => 'url',
+		'text_domain' => '',
+		'domain_path' => '',
+		'requires' => 'text',
+		'tested' => 'text',
+	);
+	foreach ( $fields as $field => $san ) {
+		if ( !empty( ${$field} ) ) {
+			${$field} = trim(${$field}[1]);
+			switch ( $san ) {
+			case 'html' :
+				${$field} = bb_filter_kses( ${$field} );
+				break;
+			case 'text' :
+				${$field} = esc_html(  ${$field} );
+				break;
+			case 'url' :
+				${$field} = esc_url( ${$field} );
+				break;
+			}
+		} else {
+			${$field} = '';
+		}
+	}
+
+	$plugin_data = compact( array_keys( $fields ) );
+
+	if ( $translate )
+		$plugin_data = _bb_get_plugin_data_translate( $plugin_data, $plugin_file );
+
+	if ( $markup )
+		$plugin_data['description'] = bb_autop( preg_replace( '/[\r\n]+/', "\n", trim( $plugin_data['description'] ) ) );
+
+	$plugin_data['plugin_link'] = ( $plugin_data['uri'] ) ?
+		"<a href='{$plugin_data['uri']}' title='" . esc_attr__( 'Visit plugin site' ) . "'>{$plugin_data['name']}</a>" :
+		$plugin_data['name'];
+	$plugin_data['author_link'] = ( $plugin_data['author'] && $plugin_data['author_uri'] ) ?
+		"<a href='{$plugin_data['author_uri']}' title='" . esc_attr__( 'Visit author homepage' ) . "'>{$plugin_data['author']}</a>" :
+		$plugin_data['author'];
+
+	return $plugin_data;
+}
+
+function _bb_get_plugin_data_translate( $plugin_data, $plugin_file ) {
+	//Translate fields
+	if( !empty($plugin_data['text_domain']) ) {
+		if( ! empty( $plugin_data['domain_path'] ) )
+			bb_load_plugin_textdomain($plugin_data['text_domain'], dirname($plugin_file). $plugin_data['domain_path']);
+		else
+			bb_load_plugin_textdomain($plugin_data['text_domain'], dirname($plugin_file));
+
+		foreach ( array('name', 'plugin_url', 'description', 'author', 'author_uri', 'version') as $field )
+			$plugin_data[$field] = translate($plugin_data[$field], $plugin_data['text_domain']);
+	}
+
+	return $plugin_data;
+}
+
+/**
+ * Attempts activation of plugin in a "sandbox" and redirects on success.
+ *
+ * A plugin that is already activated will not attempt to be activated again.
+ *
+ * The way it works is by setting the redirection to the error before trying to
+ * include the plugin file. If the plugin fails, then the redirection will not
+ * be overwritten with the success message. Also, the options will not be
+ * updated and the activation hook will not be called on plugin error.
+ *
+ * It should be noted that in no way the below code will actually prevent errors
+ * within the file. The code should not be used elsewhere to replicate the
+ * "sandbox", which uses redirection to work.
+ *
+ * If any errors are found or text is outputted, then it will be captured to
+ * ensure that the success redirection will update the error redirection.
+ *
+ * @since 1.0
+ *
+ * @param string $plugin Plugin path to main plugin file with plugin data.
+ * @param string $redirect Optional. URL to redirect to.
+ * @return WP_Error|null WP_Error on invalid file or null on success.
+ */
+function bb_activate_plugin( $plugin, $redirect = '' ) {
+	$active_plugins = (array) bb_get_option( 'active_plugins' );
+	$plugin = bb_plugin_basename( trim( $plugin ) );
+
+	$valid_path = bb_validate_plugin( $plugin );
+	if ( is_wp_error( $valid_path ) )
+		return $valid_path;
+
+	if ( in_array( $plugin, $active_plugins ) ) {
+		return false;
+	}
+
+	if ( !empty( $redirect ) ) {
+		// We'll override this later if the plugin can be included without fatal error
+		wp_redirect( add_query_arg( '_scrape_nonce', bb_create_nonce( 'scrape-plugin_' . $plugin ), $redirect ) ); 
+	}
+
+	ob_start();
+	@include( $valid_path );
+	// Add to the active plugins array
+	$active_plugins[] = $plugin;
+	ksort( $active_plugins );
+	bb_update_option( 'active_plugins', $active_plugins );
+	do_action( 'bb_activate_plugin_' . $plugin );
+	ob_end_clean();
+
+	return $valid_path;
+}
+
+/**
+ * Deactivate a single plugin or multiple plugins.
+ *
+ * The deactivation hook is disabled by the plugin upgrader by using the $silent
+ * parameter.
+ *
+ * @since unknown
+ *
+ * @param string|array $plugins Single plugin or list of plugins to deactivate.
+ * @param bool $silent Optional, default is false. Prevent calling deactivate hook.
+ */
+function bb_deactivate_plugins( $plugins, $silent = false ) {
+	$active_plugins = (array) bb_get_option( 'active_plugins' );
+
+	if ( !is_array( $plugins ) ) {
+		$plugins = array( $plugins );
+	}
+
+	foreach ( $plugins as $plugin ) {
+		$plugin = bb_plugin_basename( trim( $plugin ) );
+		if ( !in_array( $plugin, $active_plugins ) ) {
+			continue;
+		}
+		// Remove the deactivated plugin
+		array_splice( $active_plugins, array_search( $plugin, $active_plugins ), 1 );
+		if ( !$silent ) {
+			do_action( 'bb_deactivate_plugin_' . $plugin );
+		}
+	}
+
+	bb_update_option( 'active_plugins', $active_plugins );
+}
+
+/**
+ * Validate the plugin path.
+ *
+ * Checks that the file exists and is valid file.
+ *
+ * @since 1.0
+ * @uses validate_file() to check the passed plugin identifier isn't malformed
+ * @uses bb_get_plugin_path() to get the full path of the plugin
+ * @uses bb_get_plugins() to get the plugins that actually exist
+ *
+ * @param string $plugin Plugin Path
+ * @param string $location The location of plugin, one of 'user', 'core' or 'all'
+ * @param string $type The type of plugin, one of 'all', 'autoload' or 'normal'
+ * @return WP_Error|int 0 on success, WP_Error on failure.
+ */
+function bb_validate_plugin( $plugin, $location = 'all', $type = 'all' ) {
+	if ( validate_file( trim( $plugin ) ) ) {
+		return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) );
+	}
+	$path = bb_get_plugin_path( trim( $plugin ) );
+	if ( !file_exists( $path ) ) {
+		return new WP_Error( 'plugin_not_found', __( 'Plugin file does not exist.' ) );
+	}
+	if ( !in_array( trim( $plugin ), array_keys( bb_get_plugins( $location, $type ) ) ) ) {
+		return new WP_Error( 'plugin_not_available', __( 'That type of plugin is not available in the specified location.' ) );
+	}
+
+	return $path;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-recount.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-recount.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-recount.php	(revision 5057)
@@ -0,0 +1,461 @@
+<?php
+
+function bb_recount_topic_posts()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of posts in each topic&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql = "INSERT INTO `$bbdb->topics` (`topic_id`, `topic_posts`) (SELECT `topic_id`, COUNT(`post_status`) as `topic_posts` FROM `$bbdb->posts` WHERE `post_status` = '0' GROUP BY `topic_id`) ON DUPLICATE KEY UPDATE `topic_posts` = VALUES(`topic_posts`);";
+	if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_topic_voices()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of voices in each topic&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql_delete = "DELETE FROM `$bbdb->meta` WHERE `object_type` = 'bb_topic' AND `meta_key` = 'voices_count';";
+	if ( is_wp_error( $bbdb->query( $sql_delete ) ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$sql = "INSERT INTO `$bbdb->meta` (`object_type`, `object_id`, `meta_key`, `meta_value`) (SELECT 'bb_topic', `topic_id`, 'voices_count', COUNT(DISTINCT `poster_id`) as `meta_value` FROM `$bbdb->posts` WHERE `post_status` = '0' GROUP BY `topic_id`);";
+	if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_topic_deleted_posts()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of deleted posts in each topic&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql_delete = "DELETE FROM `$bbdb->meta` WHERE `object_type` = 'bb_topic' AND `meta_key` = 'deleted_posts';";
+	if ( is_wp_error( $bbdb->query( $sql_delete ) ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$sql = "INSERT INTO `$bbdb->meta` (`object_type`, `object_id`, `meta_key`, `meta_value`) (SELECT 'bb_topic', `topic_id`, 'deleted_posts', COUNT(`post_status`) as `meta_value` FROM `$bbdb->posts` WHERE `post_status` != '0' GROUP BY `topic_id`);";
+	if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_forum_topics()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of topics in each forum&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql = "INSERT INTO `$bbdb->forums` (`forum_id`, `topics`) (SELECT `forum_id`, COUNT(`topic_status`) as `topics` FROM `$bbdb->topics` WHERE `topic_status` = '0' GROUP BY `forum_id`) ON DUPLICATE KEY UPDATE `topics` = VALUES(`topics`);";
+	if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_forum_posts()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of posts in each forum&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql = "INSERT INTO `$bbdb->forums` (`forum_id`, `posts`) (SELECT `forum_id`, COUNT(`post_status`) as `posts` FROM `$bbdb->posts` WHERE `post_status` = '0' GROUP BY `forum_id`) ON DUPLICATE KEY UPDATE `posts` = VALUES(`posts`);";
+	if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_user_topics_replied()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of topics to which each user has replied&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$sql_select = "SELECT `poster_id`, COUNT(DISTINCT `topic_id`) as `_count` FROM `$bbdb->posts` WHERE `post_status` = '0' GROUP BY `poster_id`;";
+	$insert_rows = $bbdb->get_results( $sql_select );
+
+	if ( is_wp_error( $insert_rows ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$meta_key = $bbdb->prefix . 'topics_replied';
+
+	$insert_values = array();
+	foreach ( $insert_rows as $insert_row ) {
+		$insert_values[] = "('$insert_row->poster_id', '$meta_key', '$insert_row->_count')";
+	}
+
+	if ( !count( $insert_values ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+
+	$sql_delete = "DELETE FROM `$bbdb->usermeta` WHERE `meta_key` = '$meta_key';";
+	if ( is_wp_error( $bbdb->query( $sql_delete ) ) ) {
+		return array( 3, sprintf( $statement, $result ) );
+	}
+
+	$insert_values = array_chunk( $insert_values, 10000 );
+	foreach ( $insert_values as $chunk ) {
+		$chunk = "\n" . join( ",\n", $chunk );
+		$sql_insert = "INSERT INTO `$bbdb->usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;";
+
+		if ( is_wp_error( $bbdb->query( $sql_insert ) ) ) {
+			return array( 4, sprintf( $statement, $result ) );
+		}
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+// This function bypasses the taxonomy API
+function bb_recount_topic_tags()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of topic tags in each topic&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	// Delete empty tags
+	$delete = bb_recount_tag_delete_empty();
+	if ( $delete[0] > 0 ) {
+		$result = __( 'Could not delete empty tags.' );
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	// Get all tags
+	$sql_terms = "SELECT
+		`$bbdb->term_relationships`.`object_id`,
+		`$bbdb->term_taxonomy`.`term_id`
+	FROM `$bbdb->term_relationships`
+	JOIN `$bbdb->term_taxonomy`
+		ON `$bbdb->term_taxonomy`.`term_taxonomy_id` = `$bbdb->term_relationships`.`term_taxonomy_id`
+	WHERE
+		`$bbdb->term_taxonomy`.`taxonomy` = 'bb_topic_tag'
+	ORDER BY
+		`$bbdb->term_relationships`.`object_id`,
+		`$bbdb->term_taxonomy`.`term_id`;";
+
+	$terms = $bbdb->get_results( $sql_terms );
+	if ( is_wp_error( $terms ) || !is_array( $terms ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+	if ( empty( $terms ) ) {
+		$result = __( 'No topic tags found.' );
+		return array( 3, sprintf( $statement, $result ) );
+	}
+
+	// Count the tags in each topic
+	$topics = array();
+	foreach ( $terms as $term ) {
+		if ( !isset( $topics[$term->object_id] ) ) {
+			$topics[$term->object_id] = 1;
+		} else {
+			$topics[$term->object_id]++;
+		}
+	}
+	if ( empty( $topics ) ) {
+		return array( 4, sprintf( $statement, $result ) );
+	}
+
+	// Build the values to insert into the SQL statement
+	$values = array();
+	foreach ($topics as $topic_id => $tag_count) {
+		$values[] = '(' . $topic_id . ', ' . $tag_count . ')';
+	}
+	if ( empty( $values ) ) {
+		return array( 5, sprintf( $statement, $result ) );
+	}
+
+	// Update the topics with the new tag counts
+	$values = array_chunk( $values, 10000 );
+	foreach ($values as $chunk) {
+		$sql = "INSERT INTO `$bbdb->topics` (`topic_id`, `tag_count`) VALUES " . implode(", ", $chunk) . " ON DUPLICATE KEY UPDATE `tag_count` = VALUES(`tag_count`);";
+		if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+			return array( 6, sprintf( $statement, $result ) );
+		}
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+// This function bypasses the taxonomy API
+function bb_recount_tag_topics()
+{
+	global $bbdb;
+
+	$statement = __( 'Counting the number of topics in each topic tag&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	// Delete empty tags
+	$delete = bb_recount_tag_delete_empty();
+	if ( $delete[0] > 0 ) {
+		$result = __( 'Could not delete empty tags.' );
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	// Get all tags
+	$sql_terms = "SELECT
+		`$bbdb->term_taxonomy`.`term_taxonomy_id`,
+		`$bbdb->term_relationships`.`object_id`
+	FROM `$bbdb->term_relationships`
+	JOIN `$bbdb->term_taxonomy`
+		ON `$bbdb->term_taxonomy`.`term_taxonomy_id` = `$bbdb->term_relationships`.`term_taxonomy_id`
+	WHERE
+		`$bbdb->term_taxonomy`.`taxonomy` = 'bb_topic_tag'
+	ORDER BY
+		`$bbdb->term_taxonomy`.`term_taxonomy_id`,
+		`$bbdb->term_relationships`.`object_id`;";
+
+	$terms = $bbdb->get_results( $sql_terms );
+	if ( is_wp_error( $terms ) || !is_array( $terms ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+	if ( empty( $terms ) ) {
+		$result = __( 'No topic tags found.' );
+		return array( 3, sprintf( $statement, $result ) );
+	}
+	
+	// Count the topics in each tag
+	$tags = array();
+	foreach ( $terms as $term ) {
+		if ( !isset( $tags[$term->term_taxonomy_id] ) ) {
+			$tags[$term->term_taxonomy_id] = 1;
+		} else {
+			$tags[$term->term_taxonomy_id]++;
+		}
+	}
+	if ( empty( $tags ) ) {
+		return array( 4, sprintf( $statement, $result ) );
+	}
+	
+	// Build the values to insert into the SQL statement
+	$values = array();
+	foreach ($tags as $term_taxonomy_id => $count) {
+		$values[] = '(' . $term_taxonomy_id . ', ' . $count . ')';
+	}
+	if ( empty( $values ) ) {
+		return array( 5, sprintf( $statement, $result ) );
+	}
+	
+	// Update the terms with the new tag counts
+	$values = array_chunk( $values, 10000 );
+	foreach ($values as $chunk) {
+		$sql = "INSERT INTO `$bbdb->term_taxonomy` (`term_taxonomy_id`, `count`) VALUES " . implode(", ", $chunk) . " ON DUPLICATE KEY UPDATE `count` = VALUES(`count`);";
+		if ( is_wp_error( $bbdb->query( $sql ) ) ) {
+			return array( 6, sprintf( $statement, $result ) );
+		}
+	}
+
+	if ($return_boolean) {
+		return true;
+	}
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+// This function bypasses the taxonomy API
+function bb_recount_tag_delete_empty()
+{
+	global $bbdb;
+
+	$statement = __( 'Deleting topic tags with no topics&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	static $run_once;
+	if ( isset( $run_once ) ) {
+		if ($run_once > 0) {
+			$exit = sprintf( __( 'failure (returned code %s)' ), $run_once );
+		} else {
+			$exit = __( 'success' );
+		}
+		$result = sprintf( __( 'Already run with %s.' ), $exit );
+		return array( $run_once, sprintf( $statement, $result ) );
+	}
+
+	// Get all topic ids
+	$sql_topics = "SELECT `topic_id` FROM $bbdb->topics ORDER BY `topic_id`;";
+	$topics = $bbdb->get_results( $sql_topics );
+	if ( is_wp_error( $topics ) ) {
+		$result = __('No topics found.');
+		$run_once = 1;
+		return array( 1, sprintf( $statement, $result ) );
+	}
+	$topic_ids = array();
+	foreach ($topics as $topic) {
+		$topic_ids[] = $topic->topic_id;
+	}
+
+	// Get all topic tag term relationships without a valid topic id
+	$in_topic_ids = implode(', ', $topic_ids);
+	$sql_bad_term_relationships = "SELECT
+		`$bbdb->term_taxonomy`.`term_taxonomy_id`,
+		`$bbdb->term_taxonomy`.`term_id`,
+		`$bbdb->term_relationships`.`object_id`
+	FROM `$bbdb->term_relationships`
+	JOIN `$bbdb->term_taxonomy`
+		ON `$bbdb->term_taxonomy`.`term_taxonomy_id` = `$bbdb->term_relationships`.`term_taxonomy_id`
+	WHERE
+		`$bbdb->term_taxonomy`.`taxonomy` = 'bb_topic_tag' AND
+		`$bbdb->term_relationships`.`object_id` NOT IN ($in_topic_ids)
+	ORDER BY
+		`$bbdb->term_relationships`.`object_id`,
+		`$bbdb->term_taxonomy`.`term_id`,
+		`$bbdb->term_taxonomy`.`term_taxonomy_id`;";
+
+	$bad_term_relationships = $bbdb->get_results( $sql_bad_term_relationships );
+	if ( is_wp_error( $bad_term_relationships ) || !is_array( $bad_term_relationships ) ) {
+		$run_once = 2;
+		return array( 2, sprintf( $statement, $result ) );
+	}
+
+	// Delete those bad term relationships
+	if ( !empty( $bad_term_relationships ) ) {
+		$values = array();
+		foreach ( $bad_term_relationships as $bad_term_relationship ) {
+			$values[] = '(`object_id` = ' . $bad_term_relationship->object_id . ' AND `term_taxonomy_id` = ' . $bad_term_relationship->term_taxonomy_id . ')';
+		}
+		if ( !empty( $values ) ) {
+			$values = join(' OR ', $values);
+			$sql_bad_term_relationships_delete = "DELETE
+			FROM `$bbdb->term_relationships`
+			WHERE $values;";
+			if ( is_wp_error( $bbdb->query( $sql_bad_term_relationships_delete ) ) ) {
+				$run_once = 3;
+				return array( 3, sprintf( $statement, $result ) );
+			}
+		}
+	}
+
+	// Now get all term taxonomy ids with term relationships
+	$sql_term_relationships = "SELECT `term_taxonomy_id` FROM $bbdb->term_relationships ORDER BY `term_taxonomy_id`;";
+	$term_taxonomy_ids = $bbdb->get_col($sql_term_relationships);
+	if ( is_wp_error( $term_taxonomy_ids ) ) {
+		$run_once = 4;
+		return array( 4, sprintf( $statement, $result ) );
+	}
+	$term_taxonomy_ids = array_unique( $term_taxonomy_ids );
+
+	// Delete topic tags that don't have any term relationships
+	if ( !empty( $term_taxonomy_ids ) ) {
+		$in_term_taxonomy_ids = implode(', ', $term_taxonomy_ids);
+		$sql_delete_term_relationships = "DELETE
+		FROM $bbdb->term_taxonomy
+		WHERE
+			`taxonomy` = 'bb_topic_tag' AND
+			`term_taxonomy_id` NOT IN ($in_term_taxonomy_ids);";
+		if ( is_wp_error( $bbdb->query( $sql_delete_term_relationships ) ) ) {
+			$run_once = 5;
+			return array( 5, sprintf( $statement, $result ) );
+		}
+	}
+
+	// Get all valid term ids
+	$sql_terms = "SELECT `term_id` FROM $bbdb->term_taxonomy ORDER BY `term_id`;";
+	$term_ids = $bbdb->get_col($sql_terms);
+	if ( is_wp_error( $term_ids ) ) {
+		$run_once = 6;
+		return array( 6, sprintf( $statement, $result ) );
+	}
+	$term_ids = array_unique( $term_ids );
+
+	// Delete terms that don't have any associated term taxonomies
+	if ( !empty( $term_ids ) ) {
+		$in_term_ids = implode(', ', $term_ids);
+		$sql_delete_terms = "DELETE
+		FROM $bbdb->terms
+		WHERE
+			`term_id` NOT IN ($in_term_ids);";
+		if ( is_wp_error( $bbdb->query( $sql_delete_terms ) ) ) {
+			$run_once = 7;
+			return array( 7, sprintf( $statement, $result ) );
+		}
+	}
+
+	$result = __( 'Complete!' );
+	$run_once = 0;
+	return array( 0, sprintf( $statement, $result ) );
+}
+
+function bb_recount_clean_favorites()
+{
+	global $bbdb;
+
+	$statement = __( 'Removing deleted topics from user favorites&hellip; %s' );
+	$result = __( 'Failed!' );
+
+	$meta_key = $bbdb->prefix . 'favorites';
+
+	$users = $bbdb->get_results( "SELECT `user_id`, `meta_value` AS `favorites` FROM `$bbdb->usermeta` WHERE `meta_key` = '$meta_key';" );
+	if ( is_wp_error( $users ) ) {
+		return array( 1, sprintf( $statement, $result ) );
+	}
+
+	$topics = $bbdb->get_col( "SELECT `topic_id` FROM `$bbdb->topics` WHERE `topic_status` = '0';" );
+
+	if ( is_wp_error( $topics ) ) {
+		return array( 2, sprintf( $statement, $result ) );
+	}
+
+	$values = array();
+	foreach ( $users as $user ) {
+		if ( empty( $user->favorites ) || !is_string( $user->favorites ) ) {
+			continue;
+		}
+		$favorites = explode( ',', $user->favorites );
+		if ( empty( $favorites ) || !is_array( $favorites ) ) {
+			continue;
+		}
+		$favorites = join( ',', array_intersect( $topics, $favorites ) );
+		$values[] = "('$user->user_id', '$meta_key', '$favorites')";
+	}
+
+	if ( !count( $values ) ) {
+		$result = __( 'Nothing to remove!' );
+		return array( 0, sprintf( $statement, $result ) );
+	}
+
+	$sql_delete = "DELETE FROM `$bbdb->usermeta` WHERE `meta_key` = '$meta_key';";
+	if ( is_wp_error( $bbdb->query( $sql_delete ) ) ) {
+		return array( 4, sprintf( $statement, $result ) );
+	}
+
+	$values = array_chunk( $values, 10000 );
+	foreach ( $values as $chunk ) {
+		$chunk = "\n" . join( ",\n", $chunk );
+		$sql_insert = "INSERT INTO `$bbdb->usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;";
+		if ( is_wp_error( $bbdb->query( $sql_insert ) ) ) {
+			return array( 5, sprintf( $statement, $result ) );
+		}
+	}
+
+	$result = __( 'Complete!' );
+	return array( 0, sprintf( $statement, $result ) );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-upgrade.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-upgrade.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/includes/functions.bb-upgrade.php	(revision 5057)
@@ -0,0 +1,533 @@
+<?php
+function bb_install() {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	require_once( BB_PATH . 'bb-admin/includes/defaults.bb-schema.php' );
+	$alterations = BP_SQL_Schema_Parser::delta( $bbdb, $bb_queries, $bb_schema_ignore );
+
+	bb_update_db_version();
+
+	return array_filter($alterations);
+}
+
+function bb_upgrade_all()
+{
+	if ( !ini_get( 'safe_mode' ) ) {
+		set_time_limit(600);
+	}
+
+	$_do_user_operations = true;
+	if ( bb_get_option( 'wp_table_prefix' ) || ( defined( 'BB_SCHEMA_IGNORE_WP_USERS_TABLES' ) && BB_SCHEMA_IGNORE_WP_USERS_TABLES ) ) {
+		$_do_user_operations = false;
+	}
+
+	$bb_upgrade = array();
+
+	// Pre DB Delta
+	if ( $_do_user_operations ) {
+		$bb_upgrade['messages'][] = bb_upgrade_160(); // Break blocked users
+		$bb_upgrade['messages'][] = bb_upgrade_170(); // Escaping in usermeta
+		$bb_upgrade['messages'][] = bb_upgrade_180(); // Delete users for real
+	}
+	$bb_upgrade['messages'][] = bb_upgrade_190(); // Move topic_resolved to topicmeta
+	$bb_upgrade['messages'][] = bb_upgrade_200(); // Indices
+	$bb_upgrade['messages'][] = bb_upgrade_210(); // Convert text slugs to varchar slugs
+	$bb_upgrade['messages'][] = bb_upgrade_220(); // remove bb_tagged primary key, add new column and primary key
+
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	require_once( BB_PATH . 'bb-admin/includes/defaults.bb-schema.php' );
+	$delta = BP_SQL_Schema_Parser::delta( $bbdb, $bb_queries, $bb_schema_ignore );
+	if ( is_array( $delta ) ) {
+		$bb_upgrade['messages'] = array_merge($bb_upgrade['messages'], $delta['messages']);
+		$bb_upgrade['errors'] = $delta['errors'];
+	} else {
+		$bb_upgrade['errors'] = array();
+	}
+
+	// Post DB Delta
+	$bb_upgrade['messages'][] = bb_upgrade_1000(); // Make forum and topic slugs
+	$bb_upgrade['messages'][] = bb_upgrade_1010(); // Make sure all forums have a valid parent
+	if ( $_do_user_operations ) {
+		$bb_upgrade['messages'][] = bb_upgrade_1020(); // Add a user_nicename to existing users
+	}
+	$bb_upgrade['messages'][] = bb_upgrade_1030(); // Move admin_email option to from_email
+	$bb_upgrade['messages'][] = bb_upgrade_1040(); // Activate Akismet and bozo plugins and convert active plugins to new convention on upgrade only
+	$bb_upgrade['messages'][] = bb_upgrade_1050(); // Update active theme if present
+	$bb_upgrade['messages'][] = bb_upgrade_1070(); // trim whitespace from raw_tag
+	$bb_upgrade['messages'][] = bb_upgrade_1080(); // Convert tags to taxonomy
+	if ( $_do_user_operations ) {
+		$bb_upgrade['messages'][] = bb_upgrade_1090(); // Add display names
+	}
+	$bb_upgrade['messages'][] = bb_upgrade_1100(); // Replace forum_stickies index with stickies (#876)
+	$bb_upgrade['messages'][] = bb_upgrade_1110(); // Create plugin directory (#1083)
+	$bb_upgrade['messages'][] = bb_upgrade_1120(); // Create theme directory (#1083)
+	$bb_upgrade['messages'][] = bb_upgrade_1130(); // Add subscriptions option and set it to true (#1268)
+
+	bb_update_db_version();
+	wp_cache_flush();
+
+	$bb_upgrade['messages'] = array_filter($bb_upgrade['messages']);
+	$bb_upgrade['errors'] = array_filter($bb_upgrade['errors']);
+
+	return $bb_upgrade;
+}
+
+function bb_upgrade_process_all_slugs() {
+	global $bbdb;
+	// Forums
+
+	$forums = (array) $bbdb->get_results("SELECT forum_id, forum_name FROM $bbdb->forums ORDER BY forum_order ASC" );
+
+	$slugs = array();
+	foreach ( $forums as $forum ) :
+		$slug = bb_slug_sanitize( wp_specialchars_decode( $forum->forum_name, ENT_QUOTES ) );
+		$slugs[$slug][] = $forum->forum_id;
+	endforeach;
+
+	foreach ( $slugs as $slug => $forum_ids ) :
+		foreach ( $forum_ids as $count => $forum_id ) :
+			$_slug = $slug;
+			$count = - $count; // madness
+			if ( is_numeric($slug) || $count )
+				$_slug = bb_slug_increment( $slug, $count );
+			$bbdb->query("UPDATE $bbdb->forums SET forum_slug = '$_slug' WHERE forum_id = '$forum_id';");
+		endforeach;
+	endforeach;
+	unset($forums, $forum, $slugs, $slug, $_slug, $forum_ids, $forum_id, $count);
+
+	// Topics
+
+	$topics = (array) $bbdb->get_results("SELECT topic_id, topic_title FROM $bbdb->topics ORDER BY topic_start_time ASC" );
+
+	$slugs = array();
+	foreach ( $topics as $topic) :
+		$slug = bb_slug_sanitize( wp_specialchars_decode( $topic->topic_title, ENT_QUOTES ) );
+		$slugs[$slug][] = $topic->topic_id;
+	endforeach;
+
+	foreach ( $slugs as $slug => $topic_ids ) :
+		foreach ( $topic_ids as $count => $topic_id ) :
+			$_slug = $slug;
+			$count = - $count;
+			if ( is_numeric($slug) || $count )
+				$_slug = bb_slug_increment( $slug, $count );
+			$bbdb->query("UPDATE $bbdb->topics SET topic_slug = '$_slug' WHERE topic_id = '$topic_id';");
+		endforeach;
+	endforeach;
+	unset($topics, $topic, $slugs, $slug, $_slug, $topic_ids, $topic_id, $count);
+}
+
+// Reversibly break passwords of blocked users.
+function bb_upgrade_160() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 535 )
+		return;
+
+	require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+	$blocked = bb_get_ids_by_role( 'blocked' );
+	foreach ( $blocked as $b )
+		bb_break_password( $b );
+	return 'Done reversibly breaking passwords: ' . __FUNCTION__;
+}
+
+function bb_upgrade_170() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 536 )
+		return;
+
+	global $bbdb;
+	foreach ( (array) $bbdb->get_results("SELECT * FROM $bbdb->usermeta WHERE meta_value LIKE '%&quot;%' OR meta_value LIKE '%&#039;%'") as $meta ) {
+		$value = str_replace(array('&quot;', '&#039;'), array('"', "'"), $meta->meta_value);
+		$value = stripslashes($value);
+		bb_update_usermeta( $meta->user_id, $meta->meta_key, $value);
+	}
+	bb_update_option( 'bb_db_version', 536 );
+	return 'Done updating usermeta: ' . __FUNCTION__;
+}
+
+function bb_upgrade_180() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 559 )
+		return;
+
+	global $bbdb;
+
+	foreach ( (array) $bbdb->get_col("SELECT ID FROM $bbdb->users WHERE user_status = 1") as $user_id )
+		bb_delete_user( $user_id );
+	bb_update_option( 'bb_db_version', 559 );
+	return 'Done clearing deleted users: ' . __FUNCTION__;
+}
+
+function bb_upgrade_190() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 630 )
+		return;
+
+	global $bbdb;
+
+	$exists = false;
+	foreach ( (array) $bbdb->get_col("DESC $bbdb->topics") as $col )
+		if ( 'topic_resolved' == $col )
+			$exists = true;
+	if ( !$exists )
+		return;
+
+	$topics = (array) $bbdb->get_results("SELECT topic_id, topic_resolved FROM $bbdb->topics" );
+	foreach ( $topics  as $topic )
+		bb_update_topicmeta( $topic->topic_id, 'topic_resolved', $topic->topic_resolved );
+	unset($topics,$topic);
+
+	$bbdb->query("ALTER TABLE $bbdb->topics DROP topic_resolved");
+
+	bb_update_option( 'bb_db_version', 630 );
+
+	return 'Done converting topic_resolved: ' . __FUNCTION__;
+}
+
+function bb_upgrade_200() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 845 )
+		return;
+
+	global $bbdb;
+
+	$bbdb->hide_errors();
+	$bbdb->query( "DROP INDEX tag_id_index ON $bbdb->tagged" );
+	$bbdb->query( "DROP INDEX user_id ON $bbdb->topicmeta" );
+	$bbdb->query( "DROP INDEX forum_id ON $bbdb->topics" );
+	$bbdb->query( "DROP INDEX topic_time ON $bbdb->topics" );
+	$bbdb->query( "DROP INDEX topic_start_time ON $bbdb->topics" );
+	$bbdb->query( "DROP INDEX tag_id_index ON $bbdb->tagged" );
+	$bbdb->query( "DROP INDEX topic_id ON $bbdb->posts" );
+	$bbdb->query( "DROP INDEX poster_id ON $bbdb->posts" );
+	$bbdb->show_errors();
+
+	bb_update_option( 'bb_db_version', 845 );
+
+	return 'Done removing old indices: ' . __FUNCTION__;
+}
+
+// 210 converts text slugs to varchar(255) width slugs (upgrading from alpha version - fires before dbDelta)
+// 1000 Gives new slugs (upgrading from previous release - fires after dbDelta)
+function bb_upgrade_210() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 846 )
+		return;
+
+	global $bbdb;
+
+	$bbdb->hide_errors();
+	if ( !$bbdb->get_var("SELECT forum_slug FROM $bbdb->forums ORDER BY forum_order ASC LIMIT 1" ) )
+		return; // Wait till after dbDelta
+	$bbdb->show_errors();
+
+	bb_upgrade_process_all_slugs();
+
+	bb_update_option( 'bb_db_version', 846 );
+	
+	return 'Done adding slugs: ' . __FUNCTION__;
+}
+
+function bb_upgrade_220() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1051 )
+		return;
+
+	global $bbdb;
+
+	$bbdb->query( "ALTER TABLE $bbdb->tagged DROP PRIMARY KEY" );
+	$bbdb->query( "ALTER TABLE $bbdb->tagged ADD tagged_id bigint(20) unsigned NOT NULL auto_increment PRIMARY KEY FIRST" );
+
+	return "Done removing key from $bbdb->tagged: " . __FUNCTION__;
+}
+
+function bb_upgrade_1000() { // Give all topics and forums slugs
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 846 )
+		return;
+
+	bb_upgrade_process_all_slugs();
+
+	bb_update_option( 'bb_db_version', 846 );
+	
+	return 'Done adding slugs: ' . __FUNCTION__;;
+}
+
+// Make sure all forums have a valid parent
+function bb_upgrade_1010() {
+	global $bbdb;
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 952 )
+		return;
+
+	$forums = (array) $bbdb->get_results( "SELECT forum_id, forum_parent FROM $bbdb->forums" );
+	$forum_ids = (array) $bbdb->get_col( '', 0 );
+
+	foreach ( $forums as $forum ) {
+		if ( $forum->forum_parent && !in_array( $forum->forum_parent, $forum_ids ) )
+			$bbdb->query( "UPDATE $bbdb->forums SET forum_parent = 0 WHERE forum_id = '$forum->forum_id'" );
+	}
+
+	bb_update_option( 'bb_db_version', 952 );
+	
+	return 'Done re-parenting orphaned forums: ' . __FUNCTION__;
+}
+
+// Add a nicename for existing users if they don't have one already
+function bb_upgrade_1020() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 977 )
+		return;
+	
+	global $bbdb;
+	
+	$users = $bbdb->get_results( "SELECT ID, user_login, user_nicename FROM $bbdb->users WHERE user_nicename IS NULL OR user_nicename = ''" );
+	
+	if ( $users ) {
+		foreach ( $users as $user ) {
+			$user_nicename = $_user_nicename = bb_user_nicename_sanitize( $user->user_login );
+			while ( is_numeric($user_nicename) || $existing_user = bb_get_user_by_nicename( $user_nicename ) )
+				$user_nicename = bb_slug_increment($_user_nicename, $existing_user->user_nicename, 50);
+			
+			$bbdb->query( "UPDATE $bbdb->users SET user_nicename = '$user_nicename' WHERE ID = $user->ID;" );
+		}
+	}
+	
+	bb_update_option( 'bb_db_version', 977 );
+	
+	return 'Done adding nice-names to existing users: ' . __FUNCTION__;
+}
+
+// Move admin_email option to from_email
+function bb_upgrade_1030() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1058 )
+		return;
+	
+	$admin_email = bb_get_option('admin_email');
+	if ($admin_email) {
+		bb_update_option('from_email', $admin_email);
+	}
+	bb_delete_option('admin_email');
+	
+	bb_update_option( 'bb_db_version', 1058 );
+	
+	return 'Done moving admin_email to from_email: ' . __FUNCTION__;
+}
+
+// Activate Akismet and bozo plugins and convert active plugins to new convention on upgrade only
+function bb_upgrade_1040() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1230 )
+		return;
+	
+	// Only do this when upgrading
+	if ( defined( 'BB_UPGRADING' ) && BB_UPGRADING ) {
+		$plugins = bb_get_option('active_plugins');
+		if ( bb_get_option('akismet_key') && !in_array('core#akismet.php', $plugins) ) {
+			$plugins[] = 'core#akismet.php';
+		}
+		if ( !in_array('core#bozo.php', $plugins) ) {
+			$plugins[] = 'core#bozo.php';
+		}
+		
+		$new_plugins = array();
+		foreach ($plugins as $plugin) {
+			if (substr($plugin, 0, 5) != 'core#') {
+				if ($plugin != 'akismet.php' && $plugin != 'bozo.php') {
+					$new_plugins[] = 'user#' . $plugin;
+				}
+			} else {
+				$new_plugins[] = $plugin;
+			}
+		}
+		
+		bb_update_option( 'active_plugins', $new_plugins );
+	}
+	
+	bb_update_option( 'bb_db_version', 1230 );
+	
+	return 'Done activating Akismet and Bozo plugins and converting active plugins to new convention on upgrade only: ' . __FUNCTION__;
+}
+
+// Update active theme if present
+function bb_upgrade_1050() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1234 )
+		return;
+	
+	// Only do this when upgrading
+	if ( defined( 'BB_UPGRADING' ) && BB_UPGRADING ) {
+		if ( $theme = bb_get_option( 'bb_active_theme' ) ) {
+			bb_update_option( 'bb_active_theme', bb_theme_basename( $theme ) );
+		}
+	}
+	
+	bb_update_option( 'bb_db_version', 1234 );
+	
+	return 'Done updating active theme if present: ' . __FUNCTION__;
+}
+
+function bb_upgrade_1070() {
+	global $bbdb;
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1467 )
+		return;
+
+	$bbdb->query( "UPDATE `$bbdb->tags` SET `raw_tag` = TRIM(`raw_tag`)" );
+
+	bb_update_option( 'bb_db_version', 1467 );
+
+	return 'Whitespace trimmed from raw_tag: ' . __FUNCTION__;
+}
+
+function bb_upgrade_1080() {
+	global $bbdb, $wp_taxonomy_object;
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1526 )
+		return;
+
+	$offset = 0;
+	while ( $tags = (array) $bbdb->get_results( "SELECT * FROM $bbdb->tags LIMIT $offset, 100" ) ) {
+		if ( !ini_get('safe_mode') ) set_time_limit(600);
+		$wp_taxonomy_object->defer_term_counting(true);
+		for ( $i = 0; isset($tags[$i]); $i++ ) {
+			$bbdb->insert( $bbdb->terms, array( 
+				'name' => $tags[$i]->raw_tag,
+				'slug' => $tags[$i]->tag
+			) );
+			$term_id = $bbdb->insert_id;
+			$bbdb->insert( $bbdb->term_taxonomy, array(
+				'term_id' => $term_id,
+				'taxonomy' => 'bb_topic_tag',
+				'description' => ''
+			) );
+			$term_taxonomy_id = $bbdb->insert_id;
+			$topics = (array) $bbdb->get_results( $bbdb->prepare( "SELECT user_id, topic_id FROM $bbdb->tagged WHERE tag_id = %d", $tags[$i]->tag_id ) );
+			for ( $j = 0; isset($topics[$j]); $j++ ) {
+				$bbdb->insert( $bbdb->term_relationships, array(
+					'object_id' => $topics[$j]->topic_id,
+					'term_taxonomy_id' => $term_taxonomy_id,
+					'user_id' => $topics[$j]->user_id
+				) );
+			}
+			$wp_taxonomy_object->update_term_count( array( $term_taxonomy_id ), 'bb_topic_tag' );
+		}
+		$wp_taxonomy_object->defer_term_counting(false);
+		$offset += 100;
+	}
+
+	bb_update_option( 'bb_db_version', 1526 );
+
+	return 'Tags copied to taxonomy tables: ' . __FUNCTION__;
+}
+
+function bb_upgrade_1090() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1589 )
+		return;
+
+	global $bbdb;
+
+	$users = (array) $bbdb->get_results( "SELECT `ID`, `user_login` FROM $bbdb->users WHERE `display_name` = '' OR `display_name` IS NULL;" );
+
+	if ($users) {
+		foreach ($users as $user) {
+			$bbdb->query( "UPDATE $bbdb->users SET `display_name` = '" . $user->user_login . "' WHERE ID = " . $user->ID . ";" );
+		}
+		unset($user, $users);
+	}
+
+	bb_update_option( 'bb_db_version', 1589 );
+
+	return 'Display names populated: ' . __FUNCTION__;
+}
+
+function bb_upgrade_1100() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 1638 )
+		return;
+
+	global $bbdb;
+
+	$bbdb->query( "DROP INDEX forum_stickies ON $bbdb->topics" );
+
+	bb_update_option( 'bb_db_version', 1638 );
+
+	return 'Index forum_stickies dropped: ' . __FUNCTION__;
+}
+
+function bb_upgrade_1110() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 2077 )
+		return;
+
+	// No matter what happens, update the db version
+	bb_update_option( 'bb_db_version', 2077 );
+
+	if ( !defined( 'BB_PLUGIN_DIR' ) ) {
+		return;
+	}
+
+	if ( !BB_PLUGIN_DIR ) {
+		return;
+	}
+
+	if ( file_exists( BB_PLUGIN_DIR ) ) {
+		return;
+	}
+
+	// Just suppress errors as this is not critical
+	if ( @mkdir( BB_PLUGIN_DIR, 0755 ) ) {
+		return 'Making plugin directory at ' . BB_PLUGIN_DIR . ': ' . __FUNCTION__;
+	}
+
+	return;
+}
+
+function bb_upgrade_1120() {
+	if ( ( $dbv = bb_get_option_from_db( 'bb_db_version' ) ) && $dbv >= 2078 ) {
+		return;
+	}
+
+	// No matter what happens, update the db version
+	bb_update_option( 'bb_db_version', 2078 );
+
+	if ( !defined( 'BB_THEME_DIR' ) ) {
+		return;
+	}
+
+	if ( !BB_THEME_DIR ) {
+		return;
+	}
+
+	if ( file_exists( BB_THEME_DIR ) ) {
+		return;
+	}
+
+	// Just suppress errors as this is not critical
+	if ( @mkdir( BB_THEME_DIR, 0755 ) ) {
+		return 'Making theme directory at ' . BB_THEME_DIR . ': ' . __FUNCTION__;
+	}
+
+	return;
+}
+
+// Subscription Option
+function bb_upgrade_1130() {
+	if ( $dbv = bb_get_option_from_db( 'bb_db_version' ) && $dbv >= 2471 )
+		return;
+	
+	// If the option is already there, then return
+	if ( bb_get_option( 'enable_subscriptions' ) )
+		return;
+	
+	bb_update_option( 'enable_subscriptions', 1 );
+	
+	bb_update_option( 'bb_db_version', 2471 );
+	
+	return 'Added subscriptions option and set its value to true: ' . __FUNCTION__;
+}
+
+function bb_deslash($content) {
+    // Note: \\\ inside a regex denotes a single backslash.
+
+    // Replace one or more backslashes followed by a single quote with
+    // a single quote.
+    $content = preg_replace("/\\\+'/", "'", $content);
+
+    // Replace one or more backslashes followed by a double quote with
+    // a double quote.
+    $content = preg_replace('/\\\+"/', '"', $content);
+
+    // Replace one or more backslashes with one backslash.
+    $content = preg_replace("/\\\+/", "\\", $content);
+
+    return $content;
+}
+
+function bb_update_db_version() {
+	bb_update_option( 'bb_db_version', bb_get_option( 'bb_db_version' ) );
+}
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/index.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/index.php	(revision 5057)
@@ -0,0 +1,149 @@
+<?php
+
+// Get started
+require_once('admin.php');
+require_once( BB_PATH . BB_INC . 'functions.bb-statistics.php' );
+
+// Get counts and format numbers
+$rn_forums             = number_format( get_total_forums() );
+$rn_forums             = sprintf(__ngettext('<span>%s</span> forum', '<span>%s</span> forums', $rn_forums), $rn_forums );
+
+$rn_topics             = number_format( get_total_topics() );
+$rn_topics             = sprintf(__ngettext('<span>%s</span> topic', '<span>%s</span> topics', $rn_topics), $rn_topics );
+
+$rn_posts              = number_format( get_total_posts() );
+$rn_posts              = sprintf(__ngettext('<span>%s</span> post', '<span>%s</span> posts', $rn_posts), $rn_posts );
+
+$rn_users              = number_format( bb_get_total_users() );
+$rn_users              = sprintf(__ngettext('<span>%s</span> user', '<span>%s</span> users', $rn_users), $rn_users );
+
+$rn_topic_tags         = number_format( bb_get_total_topic_tags() );
+$rn_topic_tags         = sprintf(__ngettext('<span>%s</span> tag', '<span>%s</span> tags', $rn_topic_tags), $rn_topic_tags );
+
+$rn_topics_average     = number_format( get_topics_per_day() );
+$rn_topics_average     = sprintf(__ngettext('<span>%s</span> topic', '<span>%s</span> topics', $rn_topics_average), $rn_topics_average );
+
+$rn_posts_average      = number_format( get_posts_per_day() );
+$rn_posts_average      = sprintf(__ngettext('<span>%s</span> post', '<span>%s</span> posts', $rn_posts_average), $rn_posts_average );
+
+$rn_users_average      = number_format( get_registrations_per_day() );
+$rn_users_average      = sprintf(__ngettext('<span>%s</span> user', '<span>%s</span> users', $rn_users_average), $rn_users_average );
+
+$rn_topic_tags_average = number_format( bb_get_topic_tags_per_day() );
+$rn_topic_tags_average = sprintf(__ngettext('<span>%s</span> tag', '<span>%s</span> tags', $rn_topic_tags_average), $rn_topic_tags_average );
+
+// Filter the numbers
+$rn = apply_filters( 'bb_admin_right_now', array(
+	'forums'     => array( $rn_forums, '-' ),
+	'topics'     => array( $rn_topics, $rn_topics_average ),
+	'posts'      => array( $rn_posts, $rn_posts_average ),
+	'topic_tags' => array( $rn_topic_tags, $rn_topic_tags_average ),
+	'users'      => array( $rn_users, $rn_users_average )
+) );
+
+$bb_admin_body_class = ' bb-admin-dashboard';
+
+bb_get_admin_header(); ?>
+
+<div class="wrap">
+	<h2><?php _e('Dashboard'); ?></h2>
+
+	<?php do_action( 'bb_admin_notices' ); ?>
+
+	<div id="dashboard-right-now" class="dashboard">
+		<h3><?php _e('Right Now'); ?></h3>
+		<div class="table">
+			<table cellpadding="0" cellspacing="0">
+				<thead>
+					<tr>
+						<th><?php _e( 'Totals' ); ?></th>
+						<th><?php _e( 'Per Day' ); ?></th>
+					</tr>
+				</thead>
+
+<?php if ( !empty( $rn ) && is_array( $rn ) ) { ?>
+
+				<tbody>
+
+	<?php foreach ( $rn as $rn_row ) { ?>
+
+					<tr>
+						<td><?php echo $rn_row[0]; ?></td>
+						<td><?php echo $rn_row[1]; ?></td>
+					</tr>
+	<?php } ?>
+
+				</tbody>
+
+<?php } ?>
+
+			</table>
+		</div>
+
+		<div class="versions">
+			<p class="theme"><a class="button" href="<?php bb_uri( 'bb-admin/themes.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ); ?>"><?php _e( 'Change Theme' ); ?></a><?php printf ( __( 'Theme <a href="%1$s">%2$s</a>' ), bb_get_uri( 'bb-admin/themes.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ), bb_get_current_theme_data( 'Name' ) ); ?></p>
+			<p class="bbpress"><?php printf( __( 'You are using <span class="b">bbPress %s</span>' ), bb_get_option( 'version' ) ); ?></p>
+		</div>
+	</div>
+
+	<div id="dashboard-moderated" class="dashboard">
+		<h3><?php _e('Recently Moderated Items'); ?></h3>
+		<?php if ( $objects = bb_get_recently_moderated_objects() ) : ?>
+		<ul class="posts">
+			<?php add_filter( 'get_topic_where', 'bb_no_where' ); foreach ( $objects as $object ) : ?>
+			<?php if ( 'post' == $object['type'] ) : global $bb_post; $bb_post = $object['data']; ?>
+			<li>
+			<?php
+					if ( $bb_post->poster_id ) {
+						printf(
+							__( '<a href="%1$s">Post</a> on <a href="%2$s">%3$s</a> by <a href="%4$s">%5$s</a>' ),
+							esc_attr( add_query_arg( 'view', 'all', get_post_link() ) ),
+							get_topic_link( $bb_post->topic_id ),
+							get_topic_title( $bb_post->topic_id ),
+							get_user_profile_link( $bb_post->poster_id ),
+							get_post_author()
+						);
+					} else {
+						printf(
+							__( '<a href="%1$s">Post</a> on <a href="%2$s">%3$s</a> by %4$s' ),
+							esc_attr( add_query_arg( 'view', 'all', get_post_link() ) ),
+							get_topic_link( $bb_post->topic_id ),
+							get_topic_title( $bb_post->topic_id ),
+							get_post_author()
+						);
+					}
+			?>
+			</li>
+			<?php elseif ( 'topic' == $object['type'] ) : global $topic; $topic = $object['data']; ?>
+			<li>
+			<?php
+					if ( $topic->topic_poster ) {
+						printf(
+							__( 'Topic titled <a href="%1$s">%2$s</a> started by <a href="%3$s">%4$s</a>' ),
+							esc_attr( add_query_arg( 'view', 'all', get_topic_link() ) ),
+							get_topic_title( $topic->topic_id ),
+							get_user_profile_link( $topic->topic_poster ),
+							get_topic_author( $topic->topic_id )
+						);
+					} else {
+						printf(
+							__( 'Topic titled <a href="%1$s">%2$s</a> started by %3$s' ),
+							esc_attr( add_query_arg( 'view', 'all', get_topic_link() ) ),
+							get_topic_title( $topic->topic_id ),
+							get_topic_author( $topic->topic_id )
+						);
+					}
+			?>
+			</li>
+			<?php endif; endforeach; remove_filter( 'get_topic_where', 'bb_no_where' ); ?>
+		</ul>
+		<?php else : ?>
+		<p>
+			<?php _e('No moderated posts or topics&#8230; you must have very well behaved members.'); ?>
+		</p>
+		<?php endif; ?>
+	</div>
+	<div class="clear"></div>
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install-rtl.css	(revision 5057)
@@ -0,0 +1,100 @@
+body {
+	font-family: Tahoma, "Times New Roman";
+}
+
+div#container {
+	text-align: right;
+}
+
+dl {
+	border-left-width: 0;
+	border-right: 1px solid rgb(125, 125, 125);
+	margin-right: 2.5em;
+	margin-left: 0;
+}
+
+dt {
+	padding-right: 0.6em;
+	padding-left: 0;
+}
+
+dd {
+	padding-right: 3.6em;
+	padding-left: 0;
+}
+
+p.status {
+	right: auto;
+	left: 0;
+	text-shadow: none;
+}
+
+h2 {
+	text-shadow: none;
+}
+
+label a.note-toggle {
+	float: right;
+	padding-left: 0;
+	width: 18px;
+	margin-left: 0;
+	margin-right: 6px;
+}
+
+label.has-note.for-textarea a.note-toggle {
+	margin-left: 0;
+	margin-right: 6px;
+}
+
+label.has-note.for-select a.note-toggle {
+	margin-left: 0;
+	margin-right: 4px;
+}
+
+label.has-note.for-toggle a.note-toggle {
+	margin-left: 0;
+	margin-right: 6px;
+}
+
+label.for-textarea span,
+label.for-toggle span {
+	float: right;
+}
+
+label span.error {
+	margin-right: 3em;
+	margin-left: 0.4em;
+}
+
+fieldset.buttons label.forward {
+	float: left;
+}
+
+fieldset.buttons label.back {
+	float: right;
+}
+
+p.note {
+	margin-right: 2.5em;
+	margin-left: 0;
+	border-left-width: 0;
+	border-right: 1px solid rgb(50, 140, 0);
+}
+
+input.text {
+	font-family: Tahoma, "Times New Roman";
+	float: right;
+}
+
+select {
+	font-family: Tahoma, "Times New Roman";
+	float: right;
+}
+
+textarea {
+	font-family: Tahoma, "Times New Roman";
+}
+
+input.button {
+	font-family: Tahoma, "Times New Roman";
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.css	(revision 5057)
@@ -0,0 +1,433 @@
+/*
+Start with some basic resets
+*/
+
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}
+table{border-collapse:collapse;border-spacing:0;}
+fieldset,img{border:0;}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}
+del,ins{text-decoration:none;}
+li{list-style:none;}
+caption,th{text-align:left;}
+h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
+q:before,q:after{content:'';}
+abbr,acronym{border:0;font-variant:normal;}
+sup{vertical-align:baseline;}
+sub{vertical-align:baseline;}
+legend{color:#000;}
+input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}
+input,button,textarea,select{*font-size:100%;}
+body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
+select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}
+table{font-size:inherit;font:100%;}
+pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
+
+/*
+Now for our own code
+*/
+
+html {
+	background-color: rgb(247, 247, 247);
+}
+
+body {
+	text-align: center;
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 1em;
+	color: rgb(255, 255, 255);
+}
+
+div#container {
+	margin: 2em auto 1em auto;
+	padding: 1em 32px;
+	width: 700px;
+	background-color: rgb(255, 255, 255);
+	text-align: left;
+	-moz-border-radius: 11px;
+	-khtml-border-radius: 11px;
+	-webkit-border-radius: 11px;
+	border-radius: 11px;
+	border: 1px solid rgb(223, 223, 223);
+	color: rgb(51, 51, 51);
+}
+
+a {
+	color: rgb(50, 140, 0);
+	text-decoration: none;
+}
+
+a:hover {
+	text-decoration: underline;
+}
+
+strong {
+	font-weight: bold;
+}
+
+em {
+	font-style: italic;
+}
+
+div.logo img {
+	margin: 12px 0 27px 0;
+	width: 200px;
+	height: 57px;
+	display: block;
+}
+
+h1 {
+	margin: 0 0 2em 0;
+	font-size: 0.75em;
+	font-weight: bold;
+}
+
+p {
+	margin: 0 0 1em 0;
+	font-size: 0.75em;
+	line-height: 1.4em;
+}
+
+dl {
+	border-left: 1px solid rgb(125, 125, 125);
+	margin: 1.5em 0 1.5em 2.5em;
+	font-size: 0.75em;
+}
+
+dl code {
+	font-size: 1.4em;
+}
+
+dt {
+	font-size: 1em;
+	padding: 0.6em 0 0.2em 0.6em;
+}
+
+dd {
+	font-size: 1em;
+	padding: 0 0 0.6em 3.6em;
+}
+
+p.status {
+	position: absolute;
+	right: 0;
+	top: -2.4em;
+	margin: 0;
+	padding: 0.5em 1em;
+	line-height: 1.4em;
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	color: rgb(70, 70, 70);
+}
+
+p code {
+	font-size: 1.25em;
+}
+
+div.open div p.error.last,
+div.open div p.message.last {
+	margin-bottom: 1.4em;
+}
+
+h2 {
+	font-size: 0.75em;
+	background-color: rgb(236, 236, 236);
+	padding: 0.5em 1em;
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	color: rgb(70, 70, 70);
+	font-weight: bold;
+	line-height: 1.4em;
+}
+
+div.open,
+div.closed {
+	border: 1px solid rgb(223, 223, 223);
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	margin-bottom: 1em;
+	background-color: rgb(247, 247, 247);
+}
+
+div.open div {
+	padding: 1em;
+	margin: 1px 0;
+}
+
+div.closed div {
+	position: relative;
+}
+
+p.error {
+	border: 1px solid rgb(204, 0, 0);
+	background-color: rgb(255, 235, 232);
+	padding: 0.6em;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+p.error a {
+	color: rgb(204, 0, 0);
+}
+
+dl.error {
+	margin-top: -1em;
+}
+
+p.message {
+	border: 1px solid rgb(230, 219, 85);
+	background-color: rgb(255, 251, 204);
+	padding: 0.6em;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+div.open div.toggle {
+	margin: 0;
+	padding: 0;
+}
+
+div.open h2 {
+	color: rgb(0, 0, 0);
+}
+
+form {
+	clear: both;
+	border: 1px solid rgb(223, 223, 223);
+	background-color: rgb(255, 255, 255);
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 1em 1em 1em 1em;
+}
+
+legend {
+	font-size: 0.9em;
+	font-weight: bold;
+	padding-bottom: 0.5em;
+}
+
+label {
+	display: block;
+	margin: 0 0 1.8em 0;
+	font-size: 0.75em;
+	color: rgb(34, 34, 34);
+	cursor: pointer;
+	clear: both;
+}
+
+label span {
+	display: block;
+	margin-bottom: 5px;
+}
+
+label.error {
+	border: 1px solid rgb(204, 0, 0);
+	background-color: rgb(255, 235, 232);
+	padding: 0.6em;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+label.forward {
+	clear: none;
+}
+
+label a.note-toggle {
+	background-color: rgb(223, 223, 223);
+	color: rgb(255, 255, 255);
+	display: block;
+	text-align: center;
+	float: left;
+	font-weight: bold;
+	font-size: 1.2em;
+	padding-top: 1px;
+	padding-left: 1px;
+	width: 18px;
+	height: 18px;
+	line-height: 18px;
+	margin-left: 6px;
+	margin-top: 4px;
+	-moz-border-radius: 9px;
+	-khtml-border-radius: 9px;
+	-webkit-border-radius: 9px;
+	border-radius: 9px;
+}
+
+label a.note-toggle:hover {
+	text-decoration: none;
+	background-color: rgb(50, 140, 0);
+}
+
+label.has-note.for-textarea a.note-toggle {
+	margin-top: -2px;
+	margin-left: 6px;
+}
+
+label.has-note.for-select a.note-toggle {
+	margin-top: 3px;
+	margin-left: 4px;
+}
+
+label.has-note.for-toggle a.note-toggle {
+	margin-top: -2px;
+	margin-left: 6px;
+}
+
+label.for-textarea span,
+label.for-toggle span {
+	float: left;
+}
+
+label.for-toggle span {
+	margin-bottom: 0;
+}
+
+label span.error {
+	color: rgb(204, 0, 0);
+	display: block;
+	margin: 0.4em 0.4em 0.4em 3em;
+}
+
+label code {
+	font-size: 1.25em;
+}
+
+fieldset.buttons label.forward {
+	margin: 0;
+	float: right;
+}
+
+fieldset.buttons label.back {
+	margin: 0;
+	float: left;
+}
+
+p.note {
+	margin: 0 0 0 2.5em;
+	color: rgb(102, 102, 102);
+	border-left: 1px solid rgb(50, 140, 0);
+	padding: 0.6em;
+	font-size: 0.9em;
+	font-style: italic;
+	clear: both;
+}
+
+input.text {
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 1.2em;
+	padding: 4px;
+	border: 1px solid rgb(187, 187, 187);
+	width: 400px;
+	display: block;
+	float: left;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	clear: both;
+}
+
+select {
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 1.2em;
+	padding: 0;
+	border: 1px solid rgb(187, 187, 187);
+	display: block;
+	float: left;
+}
+
+textarea {
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.9em;
+	padding: 0;
+	border: 1px solid rgb(187, 187, 187);
+	width: 630px;
+	max-width: 630px;
+	height: 38em;
+	display: block;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	clear: both;
+}
+
+textarea#config {
+	text-align: left;
+}
+
+textarea.short,
+textarea#error_log {
+	height: 15em;
+}
+
+textarea#error_log {
+	border-color: rgb(153, 0, 0);
+	color: rgb(153, 0, 0);
+}
+
+input.checkbox {
+	font-size: 1.25em;
+}
+
+input.button {
+	background: url('images/white-grad.png') repeat-x 0 0 rgb(242, 242, 242);
+	-moz-border-radius: 15px;
+	-khtml-border-radius: 15px;
+	-webkit-border-radius: 15px;
+	border-radius: 15px;
+	border: 1px solid rgb(187, 187, 187);
+	color: rgb(70, 70, 70);
+	cursor: pointer;
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 1.2em;
+	line-height: 1.2em;
+	padding: 0.45em 0.9em;
+	text-decoration: none;
+}
+
+input.button:hover {
+	border-color: rgb(102, 102, 102);
+	color: rgb(0, 0, 0);
+}
+
+input.button:active {
+	background-image: url('images/white-grad-active.png');
+}
+
+label#label-toggle_4 {
+	margin-bottom: 0;
+}
+
+label#label-toggle_4 span {
+	margin-bottom: 0.4em;
+}
+
+label#label-error_log,
+label#label-installation_log {
+	margin-top: 1.8em;
+	margin-bottom: 0;
+}
+
+div.clear {
+	clear: both;
+	height: 0;
+	line-height: 0;
+	font-size: 0;
+	margin: 0 !important;
+	padding: 0 !important;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/install.php	(revision 5057)
@@ -0,0 +1,401 @@
+<?php
+// Modify error reporting levels
+error_reporting(E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING);
+
+// Let everyone know we are installing
+define('BB_INSTALLING', true);
+
+// Load bbPress
+require_once('../bb-load.php');
+
+// Instantiate the install class
+require_once( BB_PATH . 'bb-admin/includes/class.bb-install.php' );
+$bb_install = new BB_Install(__FILE__);
+
+$bb_install->header();
+?>
+		<script type="text/javascript" charset="utf-8">
+			function toggleNote(target) {
+				var targetObj = document.getElementById(target);
+				if (targetObj.style.display == 'none') {
+					targetObj.style.display = 'block';
+				} else {
+					targetObj.style.display = 'none';
+				}
+			}
+			function toggleBlock(toggleObj, target) {
+				var targetObj = document.getElementById(target);
+				if (toggleObj.checked) {
+					targetObj.style.display = 'block';
+				} else {
+					targetObj.style.display = 'none';
+				}
+			}
+			function toggleValue(toggleObj, target, offValue, onValue) {
+				var targetObj = document.getElementById(target);
+				if (toggleObj.checked) {
+					targetObj.value = onValue;
+				} else {
+					targetObj.value = offValue;
+				}
+			}
+		</script>
+<?php
+switch ($bb_install->step) {
+	case -1:
+		$bb_install->messages();
+		$bb_install->intro();
+		break;
+
+	default:
+		$bb_install->step_header(0);
+
+		if ($bb_install->step === 0) {
+?>
+				<form action="install.php" method="post">
+<?php
+			$bb_install->messages();
+			$bb_install->get_language_selector();
+			$bb_install->input_buttons('forward_0_0', false, 1);
+?>
+				</form>
+<?php
+		} else {
+			$bb_install->sanitize_form_data();
+		}
+		
+		$bb_install->step_footer();
+		
+		$bb_install->step_header(1);
+		
+		if ($bb_install->step === 1) {
+			
+			switch($bb_install->step_status[1]) {
+				case 'incomplete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+?>
+					<fieldset>
+<?php
+					$bb_install->input_text('bbdb_name');
+					$bb_install->input_text('bbdb_user');
+					$bb_install->input_text('bbdb_password');
+					$bb_install->input_text('bbdb_host');
+					$bb_install->select_language();
+					$bb_install->input_toggle('toggle_1');
+?>
+						<div class="toggle" id="toggle_1_target" style="<?php echo esc_attr( 'display:' . $bb_install->data[$bb_install->step]['form']['toggle_1']['display'] ); ?>;">
+<?php
+					$bb_install->input_text('bbdb_charset');
+					$bb_install->input_text('bbdb_collate');
+					//$bb_install->input_text('bb_auth_key');
+					//$bb_install->input_text('bb_secure_auth_key');
+					//$bb_install->input_text('bb_logged_in_key');
+					//$bb_install->input_text('bb_nonce_key');
+					$bb_install->input_text('bb_table_prefix', 'ltr');
+?>
+						</div>
+					</fieldset>
+<?php
+					$bb_install->input_buttons('forward_1_0');
+?>
+				</form>
+<?php
+					break;
+				
+				case 'manual':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+					$bb_install->hidden_step_inputs();
+?>
+					<fieldset>
+<?php
+					$bb_install->textarea('config', 'ltr');
+?>
+					</fieldset>
+<?php
+					$bb_install->input_buttons('forward_1_1', 'back_1_1');
+?>
+				</form>
+<?php
+					break;
+				
+				case 'complete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+					$bb_install->input_buttons('forward_1_2', false, 2);
+?>
+				</form>
+<?php
+					break;
+			}
+		}
+		
+		$bb_install->step_footer();
+		
+		$bb_install->step_header(2);
+		
+		if ($bb_install->step === 2) {
+			
+			switch ($bb_install->step_status[2]) {
+				case 'incomplete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+?>
+					<fieldset>
+<?php
+					bb_nonce_field('bbpress-installer');
+					$bb_install->input_toggle('toggle_2_0');
+?>
+					</fieldset>
+					<div class="toggle" id="toggle_2_0_target" style="<?php echo esc_attr( 'display:' . $bb_install->data[$bb_install->step]['form']['toggle_2_0']['display'] ); ?>;">
+						<fieldset>
+<?php
+					$bb_install->input_toggle('toggle_2_1');
+?>
+						</fieldset>
+						<div class="toggle" id="toggle_2_1_target" style="<?php echo esc_attr( 'display:' . $bb_install->data[$bb_install->step]['form']['toggle_2_1']['display'] ); ?>;">
+							<fieldset>
+								<legend><?php _e('Cookies'); ?></legend>
+								<p><?php _e('Integrating cookies allows you and your users to login to either your bbPress or your WordPress site and be automatically logged into both.'); ?></p>
+								<p><?php _e('You may need to make changes to your WordPress configuration once installation is complete. See the "WordPress Integration" section of the bbPress administration area when you are done.'); ?></p>
+<?php
+					$bb_install->input_text('wp_siteurl', 'ltr');
+					$bb_install->input_text('wp_home', 'ltr');
+					$bb_install->input_text('wp_auth_key');
+					$bb_install->input_text('wp_auth_salt');
+					$bb_install->input_text('wp_secure_auth_key');
+					$bb_install->input_text('wp_secure_auth_salt');
+					$bb_install->input_text('wp_logged_in_key');
+					$bb_install->input_text('wp_logged_in_salt');
+?>
+							</fieldset>
+						</div>
+						<fieldset>
+<?php
+					$bb_install->input_toggle('toggle_2_2');
+?>
+						</fieldset>
+						<div class="toggle" id="toggle_2_2_target" style="<?php echo esc_attr( 'display:' . $bb_install->data[$bb_install->step]['form']['toggle_2_2']['display'] ); ?>;">
+							<fieldset>
+								<legend><?php _e('User database'); ?></legend>
+								<p><?php _e('Integrating your WordPress database user tables allows you to store user data in one location, instead of having separate user data for both bbPress and WordPress.'); ?></p>
+<?php
+					$bb_install->input_text('wp_table_prefix', 'ltr');
+					$bb_install->input_text('wordpress_mu_primary_blog_id', 'ltr');
+					$bb_install->input_toggle('toggle_2_3');
+?>
+							</fieldset>
+							<div class="toggle" id="toggle_2_3_target" style="<?php echo esc_attr( 'display:' . $bb_install->data[$bb_install->step]['form']['toggle_2_3']['display'] ); ?>;">
+								<fieldset>
+									<legend><?php _e('Separate user database settings'); ?></legend>
+									<p><?php _e('Most of the time these settings are <em>not</em> required. Look before you leap!'); ?></p>
+									<p><?php _e('If required, then all settings except for the character set must be specified.'); ?></p>
+<?php
+					$bb_install->input_text('user_bbdb_name');
+					$bb_install->input_text('user_bbdb_user');
+					$bb_install->input_text('user_bbdb_password');
+					$bb_install->input_text('user_bbdb_host');
+					$bb_install->input_text('user_bbdb_charset');
+					$bb_install->input_text('user_bbdb_collate');
+?>
+								</fieldset>
+								<fieldset>
+									<legend><?php _e('Custom user tables'); ?></legend>
+									<p><?php _e('Only set these options if your integrated user tables do not fit the usual mould of <em>wp_user</em> and <em>wp_usermeta</em>.'); ?></p>
+<?php
+					$bb_install->input_text('custom_user_table');
+					$bb_install->input_text('custom_user_meta_table');
+?>
+								</fieldset>
+							</div>
+						</div>
+					</div>
+<?php
+					$bb_install->input_buttons('forward_2_0');
+?>
+				</form>
+				<script type="text/javascript" charset="utf-8">
+					function updateWordPressOptionURL () {
+						var siteURLInputValue = document.getElementById('wp_siteurl').value;
+						if (siteURLInputValue && siteURLInputValue.substr(-1,1) != '/') {
+							siteURLInputValue += '/';
+						}
+						var authSaltAnchor = document.getElementById('getAuthSaltOption');
+						var secureAuthSaltAnchor = document.getElementById('getSecureAuthSaltOption');
+						var loggedInSaltAnchor = document.getElementById('getLoggedInSaltOption');
+						if (siteURLInputValue) {
+							authSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+							secureAuthSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+							loggedInSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+						} else {
+							authSaltAnchor.href = '';
+							secureAuthSaltAnchor.href = '';
+							loggedInSaltAnchor.href = '';
+						}
+					}
+					var siteURLInput = document.getElementById('wp_siteurl');
+					if (siteURLInput.value) {
+						updateWordPressOptionURL();
+					}
+					siteURLInput.onkeyup = updateWordPressOptionURL;
+					siteURLInput.onblur = updateWordPressOptionURL;
+					siteURLInput.onclick = updateWordPressOptionURL;
+					siteURLInput.onchange = updateWordPressOptionURL;
+				</script>
+<?php
+					break;
+				
+				case 'complete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+?>
+					<fieldset>
+<?php
+					bb_nonce_field('bbpress-installer');
+?>
+					</fieldset>
+<?php
+					$bb_install->hidden_step_inputs();
+					$bb_install->input_buttons('forward_2_1', 'back_2_1', 3);
+?>
+				</form>
+<?php
+					break;
+			}
+		}
+		
+		$bb_install->step_footer();
+		
+		$bb_install->step_header(3);
+		
+		if ($bb_install->step === 3) {
+			
+			switch($bb_install->step_status[3]) {
+				case 'incomplete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+?>
+					<fieldset>
+<?php
+					bb_nonce_field('bbpress-installer');
+?>
+					</fieldset>
+<?php
+					$bb_install->hidden_step_inputs(2);
+?>
+					<fieldset>
+<?php
+					$bb_install->input_text('name');
+					$bb_install->input_text('uri', 'ltr');
+
+					if ($bb_install->populate_keymaster_user_login_from_user_tables()) {
+						echo $bb_install->strings[3]['scripts']['changeKeymasterEmail'];
+						$bb_install->select('keymaster_user_login');
+						$bb_install->input_hidden('keymaster_user_email');
+					} else {
+						$bb_install->input_text('keymaster_user_login');
+						$bb_install->input_text('keymaster_user_email', 'ltr');
+					}
+					$bb_install->input_hidden('keymaster_user_type');
+
+					if (!$bb_install->database_tables_are_installed()) {
+						$bb_install->input_text('forum_name');
+					}
+?>
+					</fieldset>
+<?php
+					$bb_install->input_buttons('forward_3_0');
+?>
+				</form>
+<?php
+					break;
+				
+				case 'complete':
+?>
+				<form action="install.php" method="post">
+<?php
+					$bb_install->messages();
+?>
+					<fieldset>
+<?php
+					bb_nonce_field('bbpress-installer');
+?>
+					</fieldset>
+<?php
+					$bb_install->hidden_step_inputs(2);
+					$bb_install->hidden_step_inputs(); // The current step (3) is assumed here
+					$bb_install->input_buttons('forward_3_1', 'back_3_1', 4);
+?>
+				</form>
+<?php
+					break;
+			}
+		}
+		
+		$bb_install->step_footer();
+		
+		if ($bb_install->step === 4) {
+		
+			$bb_install->step_header(4);
+			$bb_install->messages();
+
+			if ($bb_install->step_status[4] == 'complete') {
+?>
+				<p><?php _e('You can now log in with the following details:'); ?></p>
+				<dl>
+					<dt><?php _e('Username:'); ?></dt>
+					<dd><code><?php echo esc_html( $bb_install->data[3]['form']['keymaster_user_login']['value'] ); ?></code></dd>
+					<dt><?php _e('Password:'); ?></dt>
+					<dd><code><?php echo esc_html( $bb_install->data[4]['form']['keymaster_user_password']['value'] ); ?></code></dd>
+					<dt><?php _e('Site address:'); ?></dt>
+					<dd dir="ltr"><a href="<?php bb_uri(); ?>"><?php bb_uri(null, null, BB_URI_CONTEXT_TEXT); ?></a></dd>
+				</dl>
+<?php
+				if ($bb_install->data[3]['form']['keymaster_user_type']['value'] == 'bbPress') {
+?>
+				<p><?php _e('<strong><em>Note that password</em></strong> carefully! It is a <em>random</em> password that was generated just for you. If you lose it, you will have to delete the tables from the database yourself, and re-install bbPress.'); ?></p>
+<?php
+				}
+			}
+?>
+				<form action="<?php bb_uri(null, null, BB_URI_CONTEXT_FORM_ACTION); ?>">
+					<fieldset>
+<?php
+			$bb_install->input_toggle('toggle_4');
+?>
+						<div class="toggle" id="toggle_4_target" style="display:none;">
+<?php
+			if ($bb_install->data[4]['form']['error_log']['value']) {
+				$bb_install->textarea('error_log');
+			}
+			$bb_install->textarea('installation_log');
+?>
+						</div>
+					</fieldset>
+				</form>
+<?php
+			$bb_install->step_footer();
+			
+		} else {
+//? >
+//			<div id="step4" class="closed"></div>
+//<?php
+		}
+		
+		break;
+}
+$bb_install->footer();
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/admin-forums.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/admin-forums.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/admin-forums.js	(revision 5057)
@@ -0,0 +1,140 @@
+jQuery( function($) { // In here $ is jQuery
+
+bbSortForums = {
+	handleText: 'drag',
+	handle: '',
+	sortCfg: {
+		accept: 'forum',
+		handle: 'img.sort-handle',
+		opacity: .3,
+		helperclass: 'helper',
+		onStop: function() {
+			bbSortForums.place = null;
+			bbSortForums.recolor();
+		}
+	},
+	editText: 'Edit Forum Order',
+	saveText: 'Save Forum Order',
+	place: null,  // The id of the list item it's currently hovering before
+	placed: null, // The id of the list item it's been made a child of
+	rtl: 'rtl' == $('html').attr( 'dir' ),
+
+	recolor: function() {
+		$('#forum-list li:gt(0)').css( 'background-color', '' ).filter(':even').removeClass('alt').end().filter(':odd').addClass('alt');
+	},
+
+	checkHover: function(el, doit) {
+		if ( this.place == el.id && doit )
+			return;
+
+		if ( !doit ) {
+			this.place = null;
+			return;
+		}
+
+		this.place = el.id;
+		if ( $('#' + this.place).children('ul:has(li:visible)').size() ) // Don't shift over if there's already a UL with stuff in it
+			return;
+
+		var id = this.place.split('-')[1];
+		$('#' + this.place).not(':has(ul)').append("<ul id='forum-root-" + id + "' class='list-block holder'></ul>").end().children('ul').append(jQuery.iSort.helper.get(0)); // Place in shifted box
+		this.placed = 'forum-' + id;
+	},
+
+	serialize: function () {
+		h = '';
+		$('#forum-list, #forum-list ul').each( function() {
+			var i = this.id;
+			$('#' + i + '> .forum').each( function () {
+				if (h.length > 0)
+					h += '&';
+				var root = 'forum-list' == i ? 0 : i.split('-')[2];
+				h += 'root[' + root + '][]=' + this.id.split('-')[1];
+			} );
+		} );
+		return h;
+	},
+
+	init: function() {
+		this.handle = "<img class='sort-handle' src='images/drag.gif' alt='" + this.handleText +  "' />";
+		var div = document.createElement('div');
+		div.innerHTML = this.saveText; // Save the raquo!
+		this.saveText = div.childNodes[0].nodeValue;
+		div.innerHTML = this.editText; // Save the raquo!
+		this.editText = div.childNodes[0].nodeValue;
+		div = null;
+		$('#forum-list').after("<form class='settings' action='' onsubmit='return false;'><fieldset class='submit'><input class='submit' type='submit' name='submit' id='forum-order-edit' value='" + this.editText + "' /></fieldset></form>");
+
+		$('#forum-order-edit').toggle( function() {
+			$(this).val(bbSortForums.saveText);
+			$('#forum-list li:gt(0) div.row-title').before(bbSortForums.handle);
+			$('#forum-list').Sortable( bbSortForums.sortCfg );
+			$('body').addClass('sorting');
+		}, function() {
+			$(this).val(bbSortForums.editText);
+			$('.sort-handle').remove();
+
+			var hash = bbSortForums.serialize();
+			hash += '&' + $.SortSerialize('forum-list').hash.replace(/forum-list/g, 'order').replace(/forum-/g, '')
+			$('#forum-list').SortableDestroy();
+			$('body').removeClass('sorting');
+
+			$.post(
+				'admin-ajax.php',
+				'action=order-forums&_ajax_nonce=' +  $('#add-forum input[name=order-nonce]').val() + '&' + hash
+			);
+		} );
+	}
+}
+
+// overwrite with more advanced function
+jQuery.iSort.checkhover = function(e,o) {
+	if (!jQuery.iDrag.dragged)
+		return;
+
+	if ( e.dropCfg.el.size() > 0 ) {
+		var bottom = jQuery.grep(e.dropCfg.el, function(i) { // All the list items whose bottom edges are inside the draggable
+			var x = bbSortForums.rtl ? i.pos.x + i.pos.wb > jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.wb : i.pos.x < jQuery.iDrag.dragged.dragCfg.nx;
+			return i.pos.y + i.pos.hb > jQuery.iDrag.dragged.dragCfg.ny && i.pos.y + i.pos.hb < jQuery.iDrag.dragged.dragCfg.ny + 30 && x;
+		} );
+
+		if ( bottom.length > 0 ) { // Use the lowest one one the totem pole
+			var x = bbSortForums.rtl ? bottom[bottom.length-1].pos.x + bottom[bottom.length-1].pos.wb - 30 > jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.wb : bottom[bottom.length-1].pos.x + 30 < jQuery.iDrag.dragged.dragCfg.nx;
+			if ( bbSortForums.placed != bottom[bottom.length-1].id || !x ) { // Testing to see if still placed in shifted box
+				bbSortForums.placed = null;
+				jQuery(bottom[bottom.length-1]).after(jQuery.iSort.helper.get(0));
+			}
+			bbSortForums.checkHover(bottom[bottom.length-1], x); // If far enough right, shift it over
+			return;
+		}
+
+		// Didn't find anything by checking bottems.  Look at tops
+		var top = jQuery.grep(e.dropCfg.el, function(i) { // All the list items whose top edges are inside the draggable
+			var x = bbSortForums.rtl ? i.pos.x + i.pos.wb > jQuery.iDrag.dragged.dragCfg.nx : i.pos.x < jQuery.iDrag.dragged.dragCfg.nx;
+			return i.pos.y > jQuery.iDrag.dragged.dragCfg.ny && i.pos.y < jQuery.iDrag.dragged.dragCfg.ny + 30 && x;
+		} );
+
+		if ( top.length ) { // Use the highest one (should be only one)
+			jQuery(top[0]).before(jQuery.iSort.helper.get(0));
+			bbSortForums.checkHover(top[0], false);
+			return;
+		}
+	}
+	jQuery.iSort.helper.get(0).style.display = 'block';
+}
+
+if ( 'undefined' != typeof bbSortForumsL10n )
+	$.extend( bbSortForums, bbSortForumsL10n );
+
+bbSortForums.init();
+
+var options = $('#forum-parent').get(0).options;
+var addAfter = function( r, settings ) {
+	var name = $("<span>" + $('name', r).text() + "</span>").html();
+	var id = $('forum', r).attr('id');
+	options[options.length] = new Option(name, id);
+}
+
+$('#forum-list').wpList( { addAfter: addAfter } );
+
+} );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/common.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/common.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/common.js	(revision 5057)
@@ -0,0 +1,93 @@
+var showNotice, adminMenu, columns;
+(function($){
+// sidebar admin menu
+adminMenu = {
+
+	init : function() {
+		$('ul#bbAdminMenu li.bb-menu div.bb-menu-toggle').each( function() {
+			if ( $(this).siblings('div.bb-menu-sub-wrap').length )
+				$(this).click(function(){ adminMenu.toggle( $(this).siblings('div.bb-menu-sub-wrap') ); });
+			else
+				$(this).hide();
+		});
+
+		$('#bbAdminMenu li.bb-menu.bb-menu-separator a').click(function(){
+			if ( $('body').hasClass('bb-menu-folded') ) {
+				adminMenu.fold(1);
+				deleteUserSetting( 'fm' );
+			} else {
+				adminMenu.fold();
+				setUserSetting( 'fm', 'f' );
+			}
+			return false;
+		});
+
+		if ( $('body').hasClass('bb-menu-folded') ) {
+			this.fold();
+		}
+		this.restoreMenuState();
+	},
+
+	restoreMenuState : function() {
+		$('ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu').each(function(i, e) {
+			var v = getUserSetting( 'm'+i );
+			if ( $(e).hasClass('bb-menu-current') ) return true; // leave the current parent open
+
+			if ( 'o' == v ) $(e).addClass('bb-menu-open');
+			else if ( 'c' == v ) $(e).removeClass('bb-menu-open');
+		});
+	},
+
+	toggle : function(el) {
+		el['slideToggle'](150, function(){el.css('display','');}).parent().toggleClass( 'bb-menu-open' );
+
+		$('ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu').each(function(i, e) {
+			var v = $(e).hasClass('bb-menu-open') ? 'o' : 'c';
+			setUserSetting( 'm'+i, v );
+		});
+
+		return false;
+	},
+
+	fold : function(off) {
+		if (off) {
+			$('body').removeClass('bb-menu-folded');
+			$('#bbAdminMenu li.bb-menu.bb-menu-has-submenu').unbind();
+		} else {
+			$('body').addClass('bb-menu-folded');
+			$('#bbAdminMenu li.bb-menu.bb-menu-has-submenu').hoverIntent({
+				over: function(e){
+					var m, b, h, o, f;
+					m = $(this).find('div.bb-menu-sub-wrap');
+					b = m.parent().offset().top + m.height() + 1; // Bottom offset of the menu
+					h = $('#bbWrap').height(); // Height of the entire page
+					o = 60 + b - h;
+					f = $(window).height() + $('body').scrollTop() - 15; // The fold
+					if (f < (b - o)) {
+						o = b - f;
+					}
+					if (o > 1) {
+						m.css({'marginTop':'-'+o+'px'});
+					} else if ( m.css('marginTop') ) {
+						m.css({'marginTop':''});
+					}
+					m.addClass('bb-menu-sub-open');
+				},
+				out: function(){ $(this).find('div.bb-menu-sub-wrap').removeClass('bb-menu-sub-open').css({'marginTop':''}); },
+				timeout: 220,
+				sensitivity: 8,
+				interval: 100
+			});
+		}
+	}
+};
+
+$(document).ready(function(){
+	adminMenu.init();
+	$('thead .check-column :checkbox, tfoot .check-column :checkbox').click( function() {
+		$(this).parents( 'table' ).find( '.check-column :checkbox' ).attr( 'checked', $(this).is( ':checked' ) ? 'checked' : false );
+	} );
+});
+
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/utils.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/utils.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/js/utils.js	(revision 5057)
@@ -0,0 +1,166 @@
+// utility functions
+function convertEntities(o) {
+	var c, v;
+	c = function(s) {
+		if (/&[^;]+;/.test(s)) {
+			var e = document.createElement("div");
+			e.innerHTML = s;
+			return !e.firstChild ? s : e.firstChild.nodeValue;
+		}
+		return s;
+	}
+
+	if ( typeof o === 'string' ) {
+		return c(o);
+	} else if ( typeof o === 'object' ) {
+		for (v in o) {
+			if ( typeof o[v] === 'string' ) {
+				o[v] = c(o[v]);
+			}
+		}
+	}
+	return o;
+}
+
+var wpCookies = {
+// The following functions are from Cookie.js class in TinyMCE, Moxiecode, used under LGPL.
+
+	each : function(o, cb, s) {
+		var n, l;
+
+		if (!o)
+			return 0;
+
+		s = s || o;
+
+		if (typeof(o.length) != 'undefined') {
+			for (n=0, l = o.length; n<l; n++) {
+				if (cb.call(s, o[n], n, o) === false)
+					return 0;
+			}
+		} else {
+			for (n in o) {
+				if (o.hasOwnProperty(n)) {
+					if (cb.call(s, o[n], n, o) === false) {
+						return 0;
+					}
+				}
+			}
+		}
+		return 1;
+	},
+
+	getHash : function(n) {
+		var v = this.get(n), h;
+
+		if (v) {
+			this.each(v.split('&'), function(v) {
+				v = v.split('=');
+				h = h || {};
+				h[v[0]] = v[1];
+			});
+		}
+		return h;
+	},
+
+	setHash : function(n, v, e, p, d, s) {
+		var o = '';
+
+		this.each(v, function(v, k) {
+			o += (!o ? '' : '&') + k + '=' + v;
+		});
+
+		this.set(n, o, e, p, d, s);
+	},
+
+	get : function(n) {
+		var c = document.cookie, e, p = n + "=", b;
+
+		if (!c)
+			return;
+
+		b = c.indexOf("; " + p);
+
+		if (b == -1) {
+			b = c.indexOf(p);
+
+			if (b != 0)
+				return null;
+
+		} else {
+			b += 2;
+		}
+
+		e = c.indexOf(";", b);
+
+		if (e == -1)
+			e = c.length;
+
+		return decodeURIComponent(c.substring(b + p.length, e));
+	},
+
+	set : function(n, v, e, p, d, s) {
+		document.cookie = n + "=" + encodeURIComponent(v) +
+			((e) ? "; expires=" + e.toGMTString() : "") +
+			((p) ? "; path=" + p : "") +
+			((d) ? "; domain=" + d : "") +
+			((s) ? "; secure" : "");
+	},
+
+	remove : function(n, p) {
+		var d = new Date();
+
+		d.setTime(d.getTime() - 1000);
+
+		this.set(n, '', d, p, d);
+	}
+};
+
+// Returns the value as string. Second arg or empty string is returned when value is not set.
+function getUserSetting( name, def ) {
+	var o = getAllUserSettings();
+
+	if ( o.hasOwnProperty(name) )
+		return o[name];
+
+	if ( typeof def != 'undefined' )
+		return def;
+
+	return '';
+}
+
+// Both name and value must be only ASCII letters, numbers or underscore
+// and the shorter, the better (cookies can store maximum 4KB). Not suitable to store text.
+function setUserSetting( name, value, del ) {
+	if ( 'object' !== typeof userSettings )
+		return false;
+	
+	var c = 'bb-user-settings-' + userSettings.uid, o = wpCookies.getHash(c) || {}, d = new Date(), p,
+	n = name.toString().replace(/[^A-Za-z0-9_]/, ''), v = value.toString().replace(/[^A-Za-z0-9_]/, '');
+
+	if ( del ) {
+		delete o[n];
+	} else {
+		o[n] = v;
+	}
+
+	d.setTime( d.getTime() + 31536000000 );
+	p = userSettings.url;
+
+	wpCookies.setHash(c, o, d, p);
+	wpCookies.set('bb-user-settings-time-'+userSettings.uid, userSettings.time, d, p);
+	
+	return name;
+}
+
+function deleteUserSetting( name ) {
+	return setUserSetting( name, '', 1 );
+}
+
+// Returns all settings as js object.
+function getAllUserSettings() {
+	if ( 'object' !== typeof userSettings )
+		return {};
+
+	return wpCookies.getHash('bb-user-settings-' + userSettings.uid) || {};
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-discussion.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-discussion.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-discussion.php	(revision 5057)
@@ -0,0 +1,156 @@
+<?php
+
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update') {
+	
+	bb_check_admin_referer( 'options-discussion-update' );
+	
+	// Deal with pingbacks checkbox when it isn't checked
+	if ( !isset( $_POST['enable_pingback'] ) ) {
+		$_POST['enable_pingback'] = false;
+	}
+
+	if ( !isset( $_POST['enable_loginless'] ) ) {
+		$_POST['enable_loginless'] = false;
+	}
+	
+	if ( !isset( $_POST['enable_subscriptions'] ) ) {
+		$_POST['enable_subscriptions'] = false;
+	}
+	
+	// Deal with avatars checkbox when it isn't checked
+	if ( !isset( $_POST['avatars_show'] ) ) {
+		$_POST['avatars_show'] = false;
+	}
+	
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array( '_wpnonce', '_wp_http_referer', 'action', 'submit' ) ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+	
+	$goback = add_query_arg('updated', 'true', wp_get_referer());
+	bb_safe_redirect($goback);
+	exit;
+}
+
+if ( !empty($_GET['updated']) ) {
+	bb_admin_notice( __( '<strong>Settings saved.</strong>' ) );
+}
+
+$general_options = array(
+	'enable_pingback' => array(
+		'title' => __( 'Enable Pingbacks' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => __( 'Allow link notifications from other sites.' )
+		)
+	),
+
+	'enable_loginless' => array(
+		'title' => __( 'Enable Login-less Posting' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => __( 'Allow users to create topics and posts without logging in.' )
+		),
+	),
+	
+	'enable_subscriptions' => array(
+		'title' => __( 'Enable Subscriptions' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => __( 'Allow users to subscribe to topics and receive new posts via email.' )
+		),
+	),
+);
+
+$bb_get_option_avatars_show = create_function( '$a', 'return 1;' );
+add_filter( 'bb_get_option_avatars_show', $bb_get_option_avatars_show );
+$avatar_options = array(
+	'avatars_show' => array(
+		'title' => __( 'Avatar display' ),
+		'type' => 'radio',
+		'options' => array(
+			0 => __( 'Don&#8217;t show avatars' ),
+			1 => __( 'Show avatars' )
+		)
+	),
+	'avatars_rating' => array(
+		'title' => __( 'Maximum rating' ),
+		'type' => 'radio',
+		'options' => array(
+			'g' => __( 'G &#8212; Suitable for all audiences' ),
+			'pg' => __( 'PG &#8212; Possibly offensive, usually for audiences 13 and above' ),
+			'r' => __( 'R &#8212; Intended for adult audiences above 17' ),
+			'x' => __( 'X &#8212; Even more mature than above' )
+		)
+	),
+	'avatars_default' => array(
+		'title' => __( 'Default avatar' ),
+		'type' => 'radio',
+		'options' => array(
+			'default'   => bb_get_avatar( '',             32, 'default' )   . ' ' . __( 'Mystery Man' ),
+			'blank'     => bb_get_avatar( '',             32, 'blank' )     . ' ' . __( 'Blank' ),
+			'logo'      => bb_get_avatar( '',             32, 'logo' )      . ' ' . __( 'Gravatar Logo' ),
+			'identicon' => bb_get_avatar( rand( 0, 999 ), 32, 'identicon' ) . ' ' . __( 'Identicon (Generated)' ),
+			'wavatar'   => bb_get_avatar( rand( 0, 999 ), 32, 'wavatar' )   . ' ' . __( 'Wavatar (Generated)' ),
+			'monsterid' => bb_get_avatar( rand( 0, 999 ), 32, 'monsterid' ) . ' ' . __( 'MonsterID  (Generated)' ),
+			'retro'     => bb_get_avatar( rand( 0, 999 ), 32, 'retro' )     . ' ' . __( 'Retro  (Generated)' )
+		),
+		'note' => array(
+			__( 'For users without a custom avatar of their own, you can either display a generic logo or a generated one based on their email address.' )
+		),
+	)
+);
+remove_filter( 'bb_get_option_avatars_show', $bb_get_option_avatars_show );
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e('Discussion Settings'); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-discussion.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+<?php
+foreach ( $general_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+?>
+	</fieldset>
+	<fieldset>
+		<legend><?php _e('Avatars'); ?></legend>
+		<p>
+			<?php _e('bbPress includes built-in support for <a href="http://gravatar.com/">Gravatars</a>. A Gravatar is an image that follows you from site to site, appearing beside your name when you comment on Gravatar enabled sites. Here you can enable the display of Gravatars on your site.'); ?>
+		</p>
+<?php
+foreach ( $avatar_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-discussion-update' ); ?>
+		<input type="hidden" name="action" value="update" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+
+</div>
+
+<?php
+
+bb_get_admin_footer();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-general.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-general.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-general.php	(revision 5057)
@@ -0,0 +1,244 @@
+<?php
+
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update' ) {
+	
+	bb_check_admin_referer( 'options-general-update' );
+	
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array( '_wpnonce', '_wp_http_referer', 'action', 'submit' ) ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( $option == 'uri' && !empty( $value ) ) {
+				$value = rtrim( $value, " \t\n\r\0\x0B/" ) . '/';
+			}
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+	
+	$goback = add_query_arg( 'updated', 'true', wp_get_referer() );
+	bb_safe_redirect( $goback );
+	exit;
+}
+
+if ( !empty( $_GET['updated'] ) ) {
+	bb_admin_notice( __( '<strong>Settings saved.</strong>' ) );
+}
+
+$general_options = array(
+	'name' => array(
+		'title' => __( 'Site title' ),
+		'class' => 'long',
+	),
+	'description' => array(
+		'title' => __( 'Tagline' ),
+		'class' => 'long',
+		'note' => __( 'In a few words, explain what this site is about.' )
+	),
+	'uri' => array(
+		'title' => __( 'bbPress address (URL)' ),
+		'class' => array('long', 'code'),
+		'note' => __( 'The full URL of your bbPress install.' ),
+	),
+	'from_email' => array(
+		'title' => __( 'E-mail address' ),
+		'note' => __( 'This address is used for admin purposes, like new user notification.' ),
+	)
+);
+
+$time_options = array(
+	'gmt_offset' => array(
+		'title' => __( 'Time zone' ),
+		'type' => 'select',
+		'options' => array(
+			'-12'   => '-12:00',
+			'-11.5' => '-11:30',
+			'-11'   => '-11:00',
+			'-10.5' => '-10:30',
+			'-10'   => '-10:00',
+			'-9.5'  => '-9:30',
+			'-9'    => '-9:00',
+			'-8.5'  => '-8:30',
+			'-8'    => '-8:00',
+			'-7.5'  => '-7:30',
+			'-7'    => '-7:00',
+			'-6.5'  => '-6:30',
+			'-6'    => '-6:00',
+			'-5.5'  => '-5:30',
+			'-5'    => '-5:00',
+			'-4.5'  => '-4:30',
+			'-4'    => '-4:00',
+			'-3.5'  => '-3:30',
+			'-3'    => '-3:00',
+			'-2.5'  => '-2:30',
+			'-2'    => '-2:00',
+			'-1.5'  => '-1:30',
+			'-1'    => '-1:00',
+			'-0.5'  => '-0:30',
+			'0'     => '',
+			'0.5'   => '+0:30',
+			'1'     => '+1:00',
+			'1.5'   => '+1:30',
+			'2'     => '+2:00',
+			'2.5'   => '+2:30',
+			'3'     => '+3:00',
+			'3.5'   => '+3:30',
+			'4'     => '+4:00',
+			'4.5'   => '+4:30',
+			'5'     => '+5:00',
+			'5.5'   => '+5:30',
+			'5.75'  => '+5:45',
+			'6'     => '+6:00',
+			'6.5'   => '+6:30',
+			'7'     => '+7:00',
+			'7.5'   => '+7:30',
+			'8'     => '+8:00',
+			'8.5'   => '+8:30',
+			'8.75'  => '+8:45',
+			'9'     => '+9:00',
+			'9.5'   => '+9:30',
+			'10'    => '+10:00',
+			'10.5'  => '+10:30',
+			'11'    => '+11:00',
+			'11.5'  => '+11:30',
+			'12'    => '+12:00',
+			'12.75' => '+12:45',
+			'13'    => '+13:00',
+			'13.75' => '+13:45',
+			'14'    => '+14:00'
+		),
+		'after' => __( 'hours' )
+	),
+	'datetime_format' => array(
+		'title' => __( 'Date and time format' ),
+		'class' => 'short',
+		'value' => bb_get_datetime_formatstring_i18n(),
+		'after' => bb_datetime_format_i18n( bb_current_time() ),
+		'note' => array(
+			__( '<a href="http://codex.wordpress.org/Formatting_Date_and_Time">Documentation on date formatting</a>.' ),
+			__( 'Click "Save Changes" to update sample output.' )
+		)
+	),
+	'date_format' => array(
+		'title' => __( 'Date format' ),
+		'class' => 'short',
+		'value' => bb_get_datetime_formatstring_i18n( 'date' ),
+		'after' => bb_datetime_format_i18n( bb_current_time(), 'date' )
+	)
+);
+
+if ( !$gmt_offset = bb_get_option( 'gmt_offset' ) ) {
+	$gmt_offset = 0;
+}
+
+if ( wp_timezone_supported() ) {
+	unset( $time_options['gmt_offset'] );
+
+	if ( !$timezone_string = bb_get_option( 'timezone_string' ) ) {
+		// set the Etc zone if no timezone string exists
+		$_gmt_offset = (integer) round( $gmt_offset );
+		if ( $_gmt_offset === 0 ) {
+			$timezone_string = 'Etc/UTC';
+		} elseif ( $_gmt_offset > 0 ) {
+			// Zoneinfo has these signed backwards to common convention
+			$timezone_string = 'Etc/GMT-' . abs( $_gmt_offset );
+		} else {
+			// Zoneinfo has these signed backwards to common convention
+			$timezone_string = 'Etc/GMT+' . abs( $_gmt_offset );
+		}
+		unset( $_gmt_offset );
+	}
+
+	// Build the new selector
+	$_time_options = array(
+		'timezone_string' => array(
+			'title' => __( 'Time zone' ),
+			'type' => 'select',
+			'options' => wp_timezone_choice( $timezone_string ), // This passes a string of html, which gets used verbatim
+			'note' => array(
+				__( 'Choose a city in the same time zone as you.' ),
+				sprintf( __( '<abbr title="Coordinated Universal Time">UTC</abbr> time is <code>%s</code>' ), bb_gmdate_i18n( bb_get_datetime_formatstring_i18n(), bb_current_time() ) ),
+				sprintf( __( 'Local time is <code>%s</code>' ), bb_datetime_format_i18n( bb_current_time() ) )
+			)
+		)
+	);
+
+	$_now = localtime( bb_current_time(), true );
+	if ( $now['tm_isdst'] ) {
+		$_time_options['timezone_string']['note'][] = __( 'This time zone is currently in daylight savings time.' );
+	} else {
+		$_time_options['timezone_string']['note'][] = __( 'This time zone is currently in standard time.' );
+	}
+
+	if ( function_exists( 'timezone_transitions_get' ) ) {
+		$timezone_object = new DateTimeZone( $timezone_string );
+		$found_transition = false;
+		foreach ( timezone_transitions_get( $timezone_object ) as $timezone_transition ) {
+			if ( $timezone_transition['ts'] > time() ) {
+				$note = $timezone_transition['isdst'] ? __('Daylight savings time begins on <code>%s</code>') : __('Standard time begins on <code>%s</code>');
+				$_time_options['timezone_string']['note'][] = sprintf( $note, bb_gmdate_i18n( bb_get_datetime_formatstring_i18n(), $timezone_transition['ts'], false ) );
+				break;
+			}
+		}
+	}
+
+	$time_options = array_merge( $_time_options, $time_options );
+
+} else {
+	// Tidy up the old style dropdown
+	$time_options['gmt_offset']['note'] = array(
+		1 => sprintf( __( '<abbr title="Coordinated Universal Time">UTC</abbr> %s is <code>%s</code>' ), $time_options['gmt_offset']['options'][$gmt_offset], bb_datetime_format_i18n( bb_current_time() ) ),
+		2 => __( 'Unfortunately, you have to manually update this for Daylight Savings Time.' )
+	);
+
+	if ( $gmt_offset ) {
+		$time_options['gmt_offset']['note'][0] = sprintf( __( '<abbr title="Coordinated Universal Time">UTC</abbr> time is <code>%s</code>' ), bb_gmdate_i18n( bb_get_datetime_formatstring_i18n(), bb_current_time(), true ) );
+		ksort($time_options['gmt_offset']['note']);
+	}
+
+	foreach ( $time_options['gmt_offset']['options'] as $_key => $_value ) {
+		$time_options['gmt_offset']['options'][$_key] = sprintf( __( 'UTC %s' ), $_value );
+	}
+}
+
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e('General Settings'); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-general.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+<?php
+foreach ( $general_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+foreach ( $time_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-general-update' ); ?>
+		<input type="hidden" name="action" value="update" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+
+</div>
+
+<?php
+
+bb_get_admin_footer();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-permalinks.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-permalinks.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-permalinks.php	(revision 5057)
@@ -0,0 +1,188 @@
+<?php
+
+require_once('admin.php');
+
+$file_source = BB_PATH . 'bb-admin/includes/defaults.bb-htaccess.php';
+$file_target = BB_PATH . '.htaccess';
+include( $file_source );
+$file_source_rules = $_rules; // This is a string
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update') {
+
+	bb_check_admin_referer( 'options-permalinks-update' );
+
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array('_wpnonce', '_wp_http_referer', 'action', 'submit') ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+
+	$mod_rewrite = (string) bb_get_option( 'mod_rewrite' );
+
+	$goback = remove_query_arg( array( 'updated', 'notapache', 'notmodrewrite' ), wp_get_referer() );
+
+	// Make sure mod_rewrite is possible on the server
+	if ( !$is_apache ) {
+		bb_delete_option( 'mod_rewrite_writable' );
+		$goback = add_query_arg( 'notapache', 'true', $goback );
+		bb_safe_redirect( $goback );
+		exit;
+	} elseif ( '0' !== $mod_rewrite && !apache_mod_loaded( 'mod_rewrite', true ) ) {
+		bb_delete_option( 'mod_rewrite_writable' );
+		bb_update_option( 'mod_rewrite', '0' );
+		$goback = add_query_arg( 'notmodrewrite', 'true', $goback );
+		bb_safe_redirect( $goback );
+		exit;
+	}
+
+	$file_target_rules = array();
+
+	$file_target_exists = false;
+	$file_target_writable = true;
+	if ( file_exists( $file_target ) ) {
+		if ( is_readable( $file_target ) ) {
+			$file_target_rules = explode( "\n", implode( '', file(  $file_target ) ) );
+		}
+		$file_target_exists = true;
+		if ( !is_writable( $file_target ) ) {
+			$file_target_writable = false;
+		}
+	} else {
+		$file_target_dir = dirname( $file_target );
+		if ( file_exists( $file_target_dir ) ) {
+			if ( !is_writable( $file_target_dir ) || !is_dir( $file_target_dir ) ) {
+				$file_target_writable = false;
+			}
+		} else {
+			$file_target_writable = false;
+		}
+	}
+
+	// Strip out existing bbPress rules
+	$_keep_rule = true;
+	$_kept_rules = array();
+	foreach ( $file_target_rules as $_rule ) {
+		if ( false !== strpos( $_rule, '# BEGIN bbPress' ) ) {
+			$_keep_rule = false;
+			continue;
+		} elseif ( false !== strpos( $_rule, '# END bbPress' ) ) {
+			$_keep_rule = true;
+			continue;
+		}
+		if ( $_keep_rule ) {
+			$_kept_rules[] = $_rule;
+		}
+	}
+
+	$file_target_rules = join( "\n", $_kept_rules ) . "\n" . $file_source_rules;
+	
+	$file_target_written = 0;
+	if ( $file_target_writable ) {
+		// Open the file for writing - rewrites the whole file
+		if ( $file_target_handle = fopen( $file_target, 'w' ) ) {
+			if ( fwrite( $file_target_handle, $file_target_rules ) ) {
+				$file_target_written = 1;
+			}
+			// Close the file
+			fclose( $file_target_handle );
+			@chmod( $file_target, 0666 );
+		}
+	}
+
+	bb_update_option( 'mod_rewrite_writable', $file_target_writable );
+	$goback = add_query_arg( 'updated', 'true', $goback );
+	bb_safe_redirect( $goback );
+	exit;
+}
+
+if ( $is_apache && bb_get_option( 'mod_rewrite' ) && !bb_get_option( 'mod_rewrite_writable' ) ) {
+	$manual_instructions = true;
+}
+
+if ( !empty( $_GET['notmodrewrite'] ) ) {
+	$manual_instructions = false;
+	bb_admin_notice( __( '<strong>It appears that your server does not support custom permalink structures.</strong>' ), 'error' );
+}
+
+if ( !empty( $_GET['notapache'] ) ) {
+	$manual_instructions = false;
+	bb_admin_notice( __( '<strong>Rewriting on webservers other than Apache using mod_rewrite is currently unsupported, but we won&#8217;t stop you from trying.</strong>' ), 'error' );
+}
+
+if ( !empty( $_GET['updated'] ) ) {
+	if ( $manual_instructions ) {
+		bb_admin_notice( __( '<strong>You should update your .htaccess now.</strong>' ) );
+	} else {
+		bb_admin_notice( __( '<strong>Permalink structure updated.</strong>' ) );
+	}
+}
+
+$permalink_options = array(
+	'mod_rewrite' => array(
+		'title' => __( 'Permalink type' ),
+		'type' => 'radio',
+		'options' => array(
+			'0' => sprintf( __( '<span>None</span> <code>%s</code>' ), bb_get_uri( 'forum.php', array( 'id' => 1 ), BB_URI_CONTEXT_TEXT ) ),
+			'1' => sprintf( __( '<span>Numeric</span> <code>%s</code>' ), bb_get_uri( 'forum/1', null, BB_URI_CONTEXT_TEXT ) ),
+			'slugs' => sprintf( __( '<span>Name based</span> <code>%s</code>' ), bb_get_uri( '/forum/first-forum', null, BB_URI_CONTEXT_TEXT ) )
+		)
+	)
+);
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'Permalink Settings' ); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-permalinks.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+		<p>
+			<?php _e( 'By default bbPress uses web URLs which have question marks and lots of numbers in them, however bbPress offers you the ability to choose an alternative URL structure for your permalinks. This can improve the aesthetics, usability, and forward-compatibility of your links.' ); ?>
+		</p>
+<?php
+foreach ( $permalink_options as $option => $args ) { 
+	bb_option_form_element( $option, $args );
+}
+?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-permalinks-update' ); ?>
+		<input type="hidden" name="action" value="update" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+
+<?php
+if ( $manual_instructions ) {
+?>
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-permalinks.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+		<p>
+			<?php _e( 'If your <code>.htaccess</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so these are the mod_rewrite rules you should have in your <code>.htaccess</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all.' ); ?>
+		</p>
+		<textarea dir="ltr" id="rewrite-rules" class="readonly" readonly="readonly" rows="6"><?php echo esc_html( trim( $file_source_rules ) ); ?></textarea>
+	</fieldset>
+</form>
+
+<?php
+}
+?>
+
+</div>
+
+<?php
+
+bb_get_admin_footer();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-reading.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-reading.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-reading.php	(revision 5057)
@@ -0,0 +1,77 @@
+<?php
+
+require_once( 'admin.php' );
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update') {
+	
+	bb_check_admin_referer( 'options-reading-update' );
+	
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array('_wpnonce', '_wp_http_referer', 'action', 'submit') ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+	
+	$goback = add_query_arg('updated', 'true', wp_get_referer());
+	bb_safe_redirect($goback);
+	exit;
+}
+
+if ( !empty( $_GET['updated'] ) )
+	bb_admin_notice( '<strong>' . __( 'Settings saved.' ) . '</strong>' );
+
+$reading_options = array(
+	'page_topics' => array(
+		'title' => __( 'Items per page' ),
+		'class' => 'short',
+		'note' => __( 'Number of topics, posts or tags to show per page.' )
+	),
+	'name_link_profile' => array(
+		'title' => __( 'Link name to' ),
+		'type' => 'radio',
+		'options' => array(
+			0 => __( 'Website' ),
+			1 => __( 'Profile' )
+		),
+		'note' => __( 'What should the user\'s name link to on the topic page? The user\'s title would automatically get linked to the option you don\'t choose. By default, the user\'s name is linked to his/her website.' )
+	)
+);
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'Reading Settings' ); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-reading.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+<?php
+foreach ( $reading_options as $option => $args ) {
+	bb_option_form_element( $option, $args );
+}
+?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-reading-update' ); ?>
+		<input type="hidden" name="action" value="update" />
+		<input class="submit" type="submit" name="submit" value="<?php _e( 'Save Changes' ) ?>" />
+	</fieldset>
+</form>
+
+</div>
+
+<?php
+
+bb_get_admin_footer();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-wordpress.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-wordpress.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-wordpress.php	(revision 5057)
@@ -0,0 +1,313 @@
+<?php
+
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) )
+	$action = @$_POST['action'];
+else
+	$action = false;
+
+if ( in_array( $action, array('update-users', 'update-options') ) ) {
+	bb_check_admin_referer( 'options-wordpress-' . $action );
+	
+	// Deal with advanced user database checkbox when it isn't checked
+	if (!isset($_POST['user_bbdb_advanced'])) {
+		$_POST['user_bbdb_advanced'] = false;
+	}
+	
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array('_wpnonce', '_wp_http_referer', 'action', 'submit') ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( ( $option == 'wp_siteurl' || $option == 'wp_home' ) && !empty( $value ) ) {
+				$value = rtrim( $value, " \t\n\r\0\x0B/" ) . '/';
+			}
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+	
+	if ($action == 'update-users') {
+		bb_apply_wp_role_map_to_orphans();
+	}
+	
+	$goback = add_query_arg('updated', $action, wp_get_referer());
+	bb_safe_redirect($goback);
+	exit;
+}
+
+switch (@$_GET['updated']) {
+	case 'update-users':
+		bb_admin_notice( __( '<strong>User role mapping saved.</strong>' ) );
+		break;
+	case 'update-options':
+		bb_admin_notice( __( '<strong>User integration settings saved.</strong>' ) );
+		break;
+}
+
+
+
+$bb_role_names[''] = _c( 'none|no bbPress role' );
+$bb_role_names = array_merge( $bb_role_names, array_map( create_function( '$a', 'return sprintf( _c( "bbPress %s|bbPress role" ), $a );' ), $wp_roles->get_names() ) );
+
+$wpRoles = array(
+	'administrator' => __('WordPress Administrator'),
+	'editor'        => __('WordPress Editor'),
+	'author'        => __('WordPress Author'),
+	'contributor'   => __('WordPress Contributor'),
+	'subscriber'    => __('WordPress Subscriber')
+);
+
+$wpRoles = apply_filters( 'role_map_wp_roles', $wpRoles );
+
+$cookie_options = array(
+	'wp_siteurl' => array(
+		'title' => __( 'WordPress address (URL)' ),
+		'class' => 'long',
+		'note' => __( 'This value should exactly match the <strong>WordPress address (URL)</strong> setting in your WordPress general settings.' )
+	),
+	'wp_home' => array(
+		'title' => __( 'Blog address (URL)' ),
+		'class' => 'long',
+		'note' => __( 'This value should exactly match the <strong>Blog address (URL)</strong> setting in your WordPress general settings.' )
+	),
+	'bb_auth_salt' => array(
+		'title' => __( 'WordPress "auth" cookie salt' ),
+		'note' => __( 'This must match the value of the WordPress setting named "auth_salt" in your WordPress site. Look for the option labeled "auth_salt" in <a href="#" id="getAuthSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>.' )
+	),
+	'bb_secure_auth_salt' => array(
+		'title' => __( 'WordPress "secure auth" cookie salt' ),
+		'note' => __( 'This must match the value of the WordPress setting named "secure_auth_salt" in your WordPress site. Look for the option labeled "secure_auth_salt" in <a href="#" id="getSecureAuthSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>. Sometimes this value is not set in WordPress, in that case you can leave this setting blank as well.' )
+	),
+	'bb_logged_in_salt' => array(
+		'title' => __( 'WordPress "logged in" cookie salt' ),
+		'note' => __( 'This must match the value of the WordPress setting named "logged_in_salt" in your WordPress site. Look for the option labeled "logged_in_salt" in <a href="#" id="getLoggedInSaltOption" onclick="window.open(this.href); return false;">this WordPress admin page</a>.' )
+	)
+);
+
+foreach ( array( 'bb_auth_salt', 'bb_secure_auth_salt', 'bb_logged_in_salt' ) as $salt_constant ) {
+	if ( defined( strtoupper( $salt_constant ) ) ) {
+		$cookie_options[$salt_constant]['note'] = array(
+			sprintf( __( 'You have defined the "%s" constant which locks this setting.' ), strtoupper( $salt_constant ) ),
+			$cookie_options[$salt_constant]['note'],
+		);
+		$cookie_options[$salt_constant]['value'] = constant( strtoupper( $salt_constant ) );
+		$bb_hardcoded[$salt_constant] = true;
+	}
+}
+
+$user_db_options = array(
+	'wp_table_prefix' => array(
+		'title' => __( 'User database table prefix' ),
+		'note'  => __( 'If your bbPress and WordPress sites share the same database, then this is the same value as <code>$table_prefix</code> in your WordPress <code>wp-config.php</code> file. It is usually <strong>wp_</strong>.' )
+	),
+	'wordpress_mu_primary_blog_id' => array(
+		'title' => __( 'WordPress MU primary blog ID' ),
+		'note'  => __( 'If you are integrating with a WordPress MU site you need to specify the primary blog ID for that site. It is usually <strong>1</strong>. You should probably leave this blank if you are integrating with a standard WordPress site' )
+	),
+	'user_bbdb_advanced' => array(
+		'title' => __( 'Show advanced database settings' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => array(
+				'label' => __( 'If your bbPress and WordPress site do not share the same database, then you will need to add advanced settings.' ),
+				'attributes' => array( 'onclick' => 'toggleAdvanced(this);' )
+			)
+		)
+	)
+);
+
+$advanced_user_db_options = array(
+	'user_bbdb_name' => array(
+		'title' => __( 'User database name' ),
+		'note' => __( 'The name of the database in which your user tables reside.' )
+	),
+	'user_bbdb_user' => array(
+		'title' => __( 'User database user' ),
+		'note' => __( 'The database user that has access to that database.' )
+	),
+	'user_bbdb_password' => array(
+		'title' => __( 'User database password' ),
+		'note' => __( 'That database user\'s password.' )
+	),
+	'user_bbdb_host' => array(
+		'title' => __( 'User database host' ),
+		'note' => __( 'The domain name or IP address of the server where the database is located. If the database is on the same server as the web site, then this probably should be <strong>localhost</strong>.' )
+	),
+	'user_bbdb_charset' => array(
+		'title' => __( 'User database character set' ),
+		'note' => __( 'The best choice is <strong>utf8</strong>, but you will need to match the character set which you created the database with.' )
+	),
+	'user_bbdb_collate' => array(
+		'title' => __( 'User database character collation' ),
+		'note' => __( 'The character collation value set when the user database was created.' )
+	)
+);
+
+$custom_table_options = array(
+	'custom_user_table' => array(
+		'title' => __( 'User database "user" table' ),
+		'note' => __( 'The complete table name, including any prefix.' ),
+	),
+	'custom_user_meta_table' => array(
+		'title' => __( 'User database "user meta" table' ),
+		'note' => __( 'The complete table name, including any prefix.' ),
+	),
+);
+
+$advanced_display = bb_get_option( 'user_bbdb_advanced' ) ? 'block' : 'none';
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'WordPress Integration Settings' ); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri('bb-admin/options-wordpress.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>">
+	<fieldset>
+		<legend><?php _e('User Role Map'); ?></legend>
+		<p><?php _e('Here you can match WordPress roles to bbPress roles.'); ?></p>
+		<p><?php _e('This will have no effect until your user tables are integrated below. Only standard WordPress roles are supported. Changes do not affect users with existing roles in both WordPress and bbPress.'); ?></p>
+<?php foreach ( $wpRoles as $wpRole => $wpRoleName ) bb_option_form_element( "wp_roles_map[$wpRole]", array( 'title' => $wpRoleName, 'type' => 'select', 'options' => $bb_role_names ) ); ?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-wordpress-update-users' ); ?>
+		<input type="hidden" name="action" value="update-users" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+
+<hr class="settings" />
+
+<div class="settings">
+	<h3><?php _e('User Integration'); ?></h3>
+	<p><?php _e('Usually, you will have to specify both cookie integration and user database integration settings. Make sure you have a "User role map" setup above before trying to add user integration.'); ?></p>
+	<p><?php _e('<em><strong>Note:</strong> changing the settings below may cause you to be logged out!</em>'); ?></p>
+</div>
+
+<form class="settings" method="post" action="<?php bb_uri('bb-admin/options-wordpress.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>">
+	<fieldset>
+		<legend><?php _e('Cookies'); ?></legend>
+		<p><?php _e('Cookie sharing allows users to log in to either your bbPress or your WordPress site, and have access to both.'); ?></p>
+	<?php foreach ( $cookie_options as $option => $args ) bb_option_form_element( $option, $args ); ?>
+		<script type="text/javascript" charset="utf-8">
+/* <![CDATA[ */
+			function updateWordPressOptionURL () {
+				var siteURLInputValue = document.getElementById('wp-siteurl').value;
+				if (siteURLInputValue && siteURLInputValue.substr(-1,1) != '/') {
+					siteURLInputValue += '/';
+				}
+				var authSaltAnchor = document.getElementById('getAuthSaltOption');
+				var secureAuthSaltAnchor = document.getElementById('getSecureAuthSaltOption');
+				var loggedInSaltAnchor = document.getElementById('getLoggedInSaltOption');
+				if (siteURLInputValue) {
+					authSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+					secureAuthSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+					loggedInSaltAnchor.href = siteURLInputValue + 'wp-admin/options.php';
+				} else {
+					authSaltAnchor.href = '';
+					secureAuthSaltAnchor.href = '';
+					loggedInSaltAnchor.href = '';
+				}
+			}
+			var siteURLInput = document.getElementById('wp-siteurl');
+			if (siteURLInput.value) {
+				updateWordPressOptionURL();
+			}
+			siteURLInput.onkeyup = updateWordPressOptionURL;
+			siteURLInput.onblur = updateWordPressOptionURL;
+			siteURLInput.onclick = updateWordPressOptionURL;
+			siteURLInput.onchange = updateWordPressOptionURL;
+/* ]]> */
+		</script>
+<?php
+$cookie_settings = array(
+	'cookiedomain' => 'COOKIE_DOMAIN',
+	'cookiepath' => 'COOKIEPATH'
+);
+$wp_settings = '';
+foreach ($cookie_settings as $bb_setting => $wp_setting) {
+	if ( isset($bb->$bb_setting) ) {
+		$wp_settings .= 'define(\'' . $wp_setting . '\', \'' . $bb->$bb_setting . '\');' . "\n";
+	}
+}
+?>
+		<p><?php printf(__('To complete cookie integration, you will need to add some settings to your <code>wp-config.php</code> file in the root directory of your WordPress installation. To get those settings, you will need to install and configure the <a href="%s">"bbPress Integration" plugin for WordPress</a>.'), 'http://wordpress.org/extend/plugins/bbpress-integration/'); ?></p>
+		<p><?php _e('You will also have to manually ensure that the following constants are equivalent in WordPress\' and bbPress\' respective config files.'); ?></p>
+		<div class="table">
+			<table>
+				<tr>
+					<th><?php _e('WordPress'); ?></th>
+					<td></td>
+					<th><?php _e('bbPress'); ?></th>
+				</tr>
+				<tr>
+					<td>AUTH_KEY</td>
+					<td>&lt;=&gt;</td>
+					<td>BB_AUTH_KEY</td>
+				</tr>
+				<tr>
+					<td>SECURE_AUTH_KEY</td>
+					<td>&lt;=&gt;</td>
+					<td>BB_SECURE_AUTH_KEY</td>
+				</tr>
+				<tr>
+					<td>LOGGED_IN_KEY</td>
+					<td>&lt;=&gt;</td>
+					<td>BB_LOGGED_IN_KEY</td>
+				</tr>
+			</table>
+		</div>
+	</fieldset>
+
+	<fieldset>
+		<legend><?php _e('User database'); ?></legend>
+		<p><?php _e('User database sharing allows you to store user data in your WordPress database.'); ?></p>
+		<p><?php _e('You should setup a "User role map" before'); ?></p>
+		<script type="text/javascript" charset="utf-8">
+			function toggleAdvanced(checkedObj) {
+				var advanced1 = document.getElementById('advanced1');
+				var advanced2 = document.getElementById('advanced2');
+				if (checkedObj.checked) {
+					advanced1.style.display = 'block';
+					advanced2.style.display = 'block';
+				} else {
+					advanced1.style.display = 'none';
+					advanced2.style.display = 'none';
+				}
+			}
+		</script>
+	<?php foreach ( $user_db_options as $option => $args ) bb_option_form_element( $option, $args ); ?>
+	</fieldset>
+	<fieldset id="advanced1" style="display:<?php echo $advanced_display; ?>">
+		<legend><?php _e('Separate user database settings'); ?></legend>
+		<p><?php _e('Most of the time these settings are <em>not</em> required. Look before you leap!'); ?></p>
+		<p><?php _e('All settings except for the character set must be specified.'); ?></p>
+	<?php foreach ( $advanced_user_db_options as $option => $args ) bb_option_form_element( $option, $args ); ?>
+	</fieldset>
+	<fieldset id="advanced2" style="display:<?php echo $advanced_display; ?>">
+		<legend><?php _e('Custom user tables'); ?></legend>
+		<p><?php _e('Only set these values if your user tables differ from the default WordPress naming convention.'); ?></p>
+	<?php foreach ( $custom_table_options as $option => $args ) bb_option_form_element( $option, $args ); ?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-wordpress-update-options' ); ?>
+		<input type="hidden" name="action" value="update-options" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+
+</div>
+
+<?php
+bb_get_admin_footer();
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-writing.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-writing.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/options-writing.php	(revision 5057)
@@ -0,0 +1,92 @@
+<?php
+
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update' ) {
+	
+	bb_check_admin_referer( 'options-writing-update' );
+	
+	// Deal with xmlrpc checkbox when it isn't checked
+	if ( !isset( $_POST['enable_xmlrpc'] ) ) {
+		$_POST['enable_xmlrpc'] = false;
+	}
+	
+	foreach ( (array) $_POST as $option => $value ) {
+		if ( !in_array( $option, array( '_wpnonce', '_wp_http_referer', 'action', 'submit' ) ) ) {
+			$option = trim( $option );
+			$value = is_array( $value ) ? $value : trim( $value );
+			$value = stripslashes_deep( $value );
+			if ( $value ) {
+				bb_update_option( $option, $value );
+			} else {
+				bb_delete_option( $option );
+			}
+		}
+	}
+	
+	$goback = add_query_arg( 'updated', 'true', wp_get_referer() );
+	bb_safe_redirect( $goback );
+	exit;
+}
+
+if ( !empty($_GET['updated']) ) {
+	bb_admin_notice( __( '<strong>Settings saved.</strong>' ) );
+}
+
+$general_options = array(
+	'edit_lock' => array(
+		'title' => __( 'Lock post editing after' ),
+		'class' => 'short',
+		'after' => __( 'minutes' ),
+		'note' => __( 'A user can edit a post for this many minutes after submitting.' ),
+	),
+	'throttle_time' => array(
+		'title' => __( 'Throttle time' ),
+		'class' => 'short',
+		'after' => __( 'seconds' ),
+		'note' => __( 'Users must wait this many seconds between posts. By default, moderators, administrators and keymasters are not throttled.' )
+	)
+);
+
+$remote_options = array(
+	'enable_xmlrpc' => array(
+		'title' => __( 'XML-RPC' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => __( 'Enable the bbPress XML-RPC publishing protocol.' )
+		)
+	)
+);
+
+$bb_admin_body_class = ' bb-admin-settings';
+
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'Writing Settings' ); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/options-writing.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset><?php foreach ( $general_options as $option => $args ) bb_option_form_element( $option, $args ); ?></fieldset>
+	<fieldset>
+		<legend><?php _e( 'Remote Publishing' ); ?></legend>
+		<p>
+			<?php _e( 'To interact with bbPress from a desktop client or remote website that uses the XML-RPC publishing interface you must enable it below.' ); ?>
+		</p>
+<?php		foreach ( $remote_options as $option => $args ) bb_option_form_element( $option, $args ); ?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-writing-update' ); ?>
+		<input type="hidden" name="action" value="update" />
+		<input class="submit" type="submit" name="submit" value="<?php _e( 'Save Changes' ); ?>" />
+	</fieldset>
+</form>
+
+</div>
+
+<?php
+
+bb_get_admin_footer();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/plugins.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/plugins.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/plugins.php	(revision 5057)
@@ -0,0 +1,268 @@
+<?php
+require_once( 'admin.php' );
+
+require_once( 'includes/functions.bb-plugin.php' );
+
+$plugin_request = 'all';
+
+if ( isset( $_GET['plugin_request'] ) ) {
+	$plugin_request = (string) $_GET['plugin_request'];
+}
+
+switch ( $plugin_request ) {
+	case 'active':
+		$_plugin_type = 'normal';
+		$_plugin_status = 'active';
+		break;
+	case 'inactive':
+		$_plugin_type = 'normal';
+		$_plugin_status = 'inactive';
+		break;
+	case 'autoload':
+		$_plugin_type = 'autoload';
+		$_plugin_status = 'all';
+		break;
+	default:
+		$plugin_request = 'all'; // For sanitisation
+		$_plugin_type = 'all';
+		$_plugin_status = 'all';
+		break;
+}
+
+$plugin_nav_class = array(
+	'all' => '',
+	'active' => '',
+	'inactive' => '',
+	'autoload' => ''
+);
+$plugin_nav_class[$plugin_request] = ' class="current"';
+
+// Get plugin counts
+extract( bb_get_plugin_counts() );
+
+// Get requested plugins
+$requested_plugins = bb_get_plugins( 'all', $_plugin_type, $_plugin_status );
+
+// Get currently active 
+$active_plugins = (array) bb_get_option( 'active_plugins' );
+
+// Check for missing plugin files and remove them from the active plugins array
+$update = false;
+foreach ( $active_plugins as $index => $plugin ) {
+	if ( !file_exists( bb_get_plugin_path( $plugin ) ) ) {
+		$update = true;
+		unset( $active_plugins[$index] );
+	}
+}
+if ( $update ) {
+	bb_update_option( 'active_plugins', $active_plugins );
+}
+unset( $update, $index, $plugin );
+
+// Set the action
+$action = '';
+if( isset( $_GET['action'] ) && !empty( $_GET['action'] ) ) {
+	$action = trim( $_GET['action'] );
+}
+
+// Set the plugin
+$plugin = isset( $_GET['plugin'] ) ? trim( stripslashes( $_GET['plugin'] ) ) : '';
+
+// Deal with user actions
+if ( !empty( $action ) ) {
+	switch ( $action ) {
+		case 'activate':
+			// Activation
+			bb_check_admin_referer( 'activate-plugin_' . $plugin );
+
+			$result = bb_activate_plugin( $plugin, 'plugins.php?message=error&plugin=' . urlencode( $plugin ) );
+			if ( is_wp_error( $result ) )
+				bb_die( $result );
+
+			// Overrides the ?message=error one above
+			wp_redirect( 'plugins.php?plugin_request=' . $plugin_request . '&message=activate&plugin=' . urlencode( $plugin ) );
+			break;
+
+		case 'deactivate':
+			// Deactivation
+			bb_check_admin_referer( 'deactivate-plugin_' . $plugin );
+
+			// Remove the deactivated plugin
+			bb_deactivate_plugins( $plugin );
+
+			// Redirect
+			wp_redirect( 'plugins.php?plugin_request=' . $plugin_request . '&message=deactivate&plugin=' . urlencode( $plugin ) );
+			break;
+
+		case 'scrape':
+			// Scrape php errors from the plugin
+			bb_check_admin_referer('scrape-plugin_' . $plugin);
+
+			$valid_path = bb_validate_plugin( $plugin );
+			if ( is_wp_error( $valid_path ) )
+				bb_die( $valid_path );
+
+			// Pump up the errors and output them to screen
+			error_reporting( E_ALL ^ E_NOTICE );
+			@ini_set( 'display_errors', true );
+
+			include( $valid_path );
+			break;
+	}
+
+	// Stop processing
+	exit;
+}
+
+// Display notices
+if ( isset($_GET['message']) ) {
+	switch ( $_GET['message'] ) {
+		case 'error' :
+			bb_admin_notice( __( '<strong>Plugin could not be activated, it produced a Fatal Error</strong>. The error is shown below.' ), 'error' );
+			break;
+		case 'activate' :
+			$plugin_data = bb_get_plugin_data( $plugin );
+			bb_admin_notice( sprintf( __( '<strong>"%s" plugin activated</strong>' ), esc_attr( $plugin_data['name'] ) ) );
+			break;
+		case 'deactivate' :
+			$plugin_data = bb_get_plugin_data( $plugin );
+			bb_admin_notice( sprintf( __( '<strong>"%s" plugin deactivated</strong>' ), esc_attr( $plugin_data['name'] ) ) );
+			break;
+	}
+}
+
+if ( isset( $bb->safemode ) && $bb->safemode === true ) {
+	bb_admin_notice( __( '<strong>"Safe mode" is on, all plugins are disabled even if they are listed as active.</strong>' ), 'error' );
+}
+
+$bb_admin_body_class = ' bb-admin-plugins';
+
+bb_get_admin_header();
+?>
+
+<div class="wrap">
+
+	<h2><?php _e( 'Manage Plugins' ); ?></h2>
+	<?php do_action( 'bb_admin_notices' ); ?>
+
+<?php
+if ( bb_verify_nonce( $_GET['_scrape_nonce'], 'scrape-plugin_' . $plugin ) ) {
+	$scrape_src = esc_attr(
+		bb_nonce_url(
+			bb_get_uri(
+				'bb-admin/plugins.php',
+				array(
+					'action' => 'scrape',
+					'plugin' => urlencode( $plugin )
+				),
+				BB_URI_CONTEXT_IFRAME_SRC + BB_URI_CONTEXT_BB_ADMIN
+			),
+			'scrape-plugin_' . $plugin
+		)
+	);
+?>
+
+	<div class="plugin-error"><iframe src="<?php echo $scrape_src; ?>"></iframe></div>
+
+<?php
+}
+?>
+
+	<div class="table-filter">
+		<a<?php echo $plugin_nav_class['all']; ?> href="<?php bb_uri( 'bb-admin/plugins.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ); ?>"><?php printf( __( 'All <span class="count">(%d)</span>' ), $plugin_count_all ); ?></a> |
+		<a<?php echo $plugin_nav_class['active']; ?> href="<?php bb_uri( 'bb-admin/plugins.php', array( 'plugin_request' => 'active' ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ); ?>"><?php printf( __( 'Active <span class="count">(%d)</span>' ), $plugin_count_active ); ?></a> |
+		<a<?php echo $plugin_nav_class['inactive']; ?> href="<?php bb_uri( 'bb-admin/plugins.php', array( 'plugin_request' => 'inactive' ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ); ?>"><?php printf( __( 'Inactive <span class="count">(%d)</span>' ), $plugin_count_inactive ); ?></a> |
+		<a<?php echo $plugin_nav_class['autoload']; ?> href="<?php bb_uri( 'bb-admin/plugins.php', array( 'plugin_request' => 'autoload' ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN ); ?>"><?php printf( __( 'Autoloaded <span class="count">(%d)</span>' ), $plugin_count_autoload ); ?></a>
+	</div>
+
+<?php
+if ( $requested_plugins ) :
+?> 
+
+	<table id="plugins-list" class="widefat">
+		<thead>
+			<tr>
+				<th><?php _e( 'Plugin' ); ?></th>
+				<th><?php _e( 'Description' ); ?></th>
+			</tr>
+		</thead>
+		<tfoot>
+			<tr>
+				<th><?php _e( 'Plugin' ); ?></th>
+				<th><?php _e( 'Description' ); ?></th>
+			</tr>
+		</tfoot>
+		<tbody>
+
+<?php
+	foreach ( $requested_plugins as $plugin => $plugin_data ) :
+		$class =  ' class="inactive"';
+		$action = 'activate';
+		$action_class = 'edit';
+		$action_text = __( 'Activate' );
+		if ( $plugin_data['autoload'] ) {
+			$class =  ' class="autoload"';
+		} elseif ( in_array( $plugin, $active_plugins ) ) {
+			$class =  ' class="active"';
+			$action = 'deactivate';
+			$action_class = 'delete';
+			$action_text = __( 'Deactivate' );
+		}
+		$href = esc_attr(
+			bb_nonce_url(
+				bb_get_uri(
+					'bb-admin/plugins.php',
+					array(
+						'plugin_request' => $plugin_request,
+						'action' => $action,
+						'plugin' => urlencode($plugin)
+					),
+					BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN
+				),
+				$action . '-plugin_' . $plugin
+			)
+		);
+		$meta = array();
+		if ( $plugin_data['version'] ) $meta[] = sprintf( __( 'Version %s' ), $plugin_data['version'] );
+		if ( $plugin_data['author_link'] ) $meta[] = sprintf( __( 'By %s' ), $plugin_data['author_link'] );
+		if ( $plugin_data['uri'] ) $meta[] = '<a href="' . $plugin_data['uri'] . '">' . esc_html__( 'Visit plugin site' ) . '</a>';
+		if ( count( $meta ) ) {
+			$meta = '<p class="meta">' . join( ' | ', $meta ) . '</p>';
+		} else {
+			$meta = '';
+		}
+?>
+
+			<tr<?php echo $class; ?>>
+				<td class="plugin-name">
+					<span class="row-title"><?php echo $plugin_data['name']; ?></span>
+					<div><span class="row-actions"><?php if ( !$plugin_data['autoload'] ) : ?><a class="<?php echo $action_class; ?>" href="<?php echo $href; ?>"><?php echo $action_text; ?></a><?php else : ?><span class="note"><?php _e( 'Autoloaded' ); ?></span><?php endif; ?></span>&nbsp;</div>
+				</td>
+				<td class="plugin-description">
+					<?php echo $plugin_data['description']; ?>
+					<?php echo $meta; ?>
+				</td>
+			</tr>
+
+<?php
+	endforeach;
+?>
+
+		</tbody>
+	</table>
+
+<?php
+else :
+?>
+
+	<p class="no-results"><?php _e( 'No plugins found.' ); ?></p>
+
+<?php
+endif;
+?>
+
+</div>
+
+<?php
+bb_get_admin_footer();
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/posts.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/posts.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/posts.php	(revision 5057)
@@ -0,0 +1,195 @@
+<?php
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+	bb_check_admin_referer( 'post-bulk' );
+
+	$post_ids = array_map( 'absint', $_POST['post'] );
+
+	$count = 0;
+
+	$action = trim( $_POST['action'] );
+
+	switch ( $action ) {
+	case 'delete' :
+		foreach ( $post_ids as $post_id ) {
+			$count += (int) (bool) bb_delete_post( $post_id, 1 );
+		}
+		$query_vars = array( 'message' => 'deleted', 'count' => $count );
+		break;
+	case 'undelete' :
+		foreach ( $post_ids as $post_id ) {
+			$count += (int) (bool) bb_delete_post( $post_id, 0 );
+		}
+		$query_vars = array( 'message' => 'undeleted', 'count' => $count );
+		break;
+	default :
+		if ( $action )
+			$query_vars = apply_filters( "bulk_post__$action", array(), $post_ids, $action );
+		break;
+	}
+
+	bb_safe_redirect( add_query_arg( $query_vars ) );
+	exit;
+}
+
+if ( !empty( $_GET['message'] ) ) {
+	$message_count = isset( $_GET['count'] ) ? (int) $_GET['count'] : 1;
+
+	switch ( (string) $_GET['message'] ) {
+		case 'undeleted':
+			bb_admin_notice( sprintf( _n( '<strong>Post undeleted.</strong>', '<strong>%s posts undeleted.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'deleted':
+			bb_admin_notice( sprintf( _n( '<strong>Post deleted.</strong>', '<strong>%s posts deleted.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'spammed':
+			bb_admin_notice( sprintf( _n( '<strong>Post spammed.</strong>', '<strong>%s posts spammed.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'unspammed-normal':
+			bb_admin_notice( sprintf( _n( '<strong>Post removed from spam.</strong> It is now a normal post.', '<strong>%s posts removed from spam.</strong> They are now normal posts.', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'unspammed-deleted':
+			bb_admin_notice( sprintf( _n( '<strong>Post removed from spam.</strong> It is now a deleted post.', '<strong>%s posts removed from spam.</strong> They are nowdeleted posts.', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+	}
+}
+
+$ip_available = false;
+if ( bb_current_user_can( 'view_by_ip' ) ) {
+	$ip_available = true;
+} elseif (isset($_GET['poster_ip'])) {
+	unset( $_GET['poster_ip'] );
+}
+
+$bb_admin_body_class = ' bb-admin-posts';
+
+bb_get_admin_header();
+
+if ( !bb_current_user_can('browse_deleted') )
+	die(__("Now how'd you get here?  And what did you think you'd being doing?")); //This should never happen.
+add_filter( 'get_topic_where', 'bb_no_where' );
+add_filter( 'get_topic_link', 'bb_make_link_view_all' );
+add_filter( 'post_edit_uri', 'bb_make_link_view_all' );
+$post_query = new BB_Query_Form( 'post', array( 'post_status' => 'normal', 'count' => true, 'per_page' => 20 ) );
+$bb_posts =& $post_query->results;
+$total = $post_query->found_rows;
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'Posts' ); ?>
+<?php
+$h2_search = $post_query->get( 'post_text' );
+$h2_forum  = $post_query->get( 'forum_id' );
+$h2_tag    = $post_query->get( 'tag_id' );
+$h2_author = $post_query->get( 'post_author_id' );
+
+$h2_search = $h2_search ? ' ' . sprintf( __('containing &#8220;%s&#8221;'), esc_html( $h2_search ) ) : '';
+$h2_forum  = $h2_forum  ? ' ' . sprintf( __('in &#8220;%s&#8221;')      , get_forum_name( $h2_forum ) ) : '';
+$h2_tag    = $h2_tag    ? ' ' . sprintf( __('with tag &#8220;%s&#8221;'), esc_html( bb_get_tag_name( $h2_tag ) ) ) : '';
+$h2_author = $h2_author ? ' ' . sprintf( __('by %s')                    , esc_html( get_user_name( $h2_author ) ) ) : '';
+
+if ($ip_available) {
+	$h2_ip = $post_query->get( 'poster_ip' );
+	$h2_ip = $h2_ip ? ' ' . sprintf( __('from IP address %s'), esc_html( $h2_ip ) ) : '';
+} else {
+	$h2_ip = '';
+}
+
+if ( $h2_search || $h2_forum || $h2_tag || $h2_author || $h2_ip ) {
+	echo '<span class="subtitle">';
+	
+	printf( __( '%1$s%2$s%3$s%4$s%5$s' ), $h2_search, $h2_forum, $h2_tag, $h2_author, $h2_ip );
+	
+	echo '</span>';
+}
+?>
+</h2>
+<?php
+
+do_action( 'bb_admin_notices' );
+
+$post_query->form( array( 'poster_ip' => $ip_available, 'tag' => true, 'post_author' => true, 'post_status' => true, 'submit' => __( 'Filter' ) ) );
+
+$bulk_actions = array(
+	'delete' => __( 'Delete' ),
+	'undelete' => __( 'Undelete' ),
+);
+
+if ( is_numeric( $bulk_action = $post_query->get( 'post_status' ) ) ) {
+	switch ( $bulk_action ) {
+	case 0 :
+		unset( $bulk_actions['undelete'] );
+		break;
+	case 1 : 
+		unset( $bulk_actions['delete'] );
+	}
+}
+
+unset( $bulk_action );
+
+do_action_ref_array( 'bulk_post_actions', array( &$bulk_actions, &$post_query ) );
+
+?>
+
+<div class="clear"></div>
+
+<form class="table-form bulk-form" method="post" action="">
+
+<fieldset>
+	<select name="action">
+		<option><?php _e( 'Bulk Actions' ); ?></option>
+<?php	foreach ( $bulk_actions as $value => $label ) : ?>
+
+		<option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
+<?php	endforeach; ?>
+	</select>
+	<input type="submit" value="<?php esc_attr_e( 'Apply' ); ?>" class="button submit-input" />
+	<?php bb_nonce_field( 'post-bulk' ); ?>
+</fieldset>
+
+<div class="tablenav">
+<?php if ( $total ) : ?>
+	<div class="tablenav-pages">
+		<span class="displaying-num"><?php echo $displaying_num = sprintf(
+			__( '%1$s to %2$s of %3$s' ),
+			bb_number_format_i18n( ( $page - 1 ) * $post_query->get( 'per_page' ) + 1 ),
+			$page * $post_query->get( 'per_page' ) < $total ? bb_number_format_i18n( $page * $post_query->get( 'per_page' ) ) : '<span class="total-type-count">' . bb_number_format_i18n( $total ) . '</span>',
+			'<span class="total-type-count">' . bb_number_format_i18n( $total ) . '</span>'
+		); ?></span><span class="displaying-pages">
+<?php
+$_page_link_args = array(
+	'page' => $page,
+	'total' => $total,
+	'per_page' => $post_query->get( 'per_page' ),
+	'mod_rewrite' => false,
+	'prev_text' => __( '&laquo;' ),
+	'next_text' => __( '&raquo;' )
+);
+echo $page_number_links = get_page_number_links( $_page_link_args );
+?></span>
+		<div class="clear"></div>
+	</div>
+<?php endif; ?>
+</div>
+
+<div class="clear"></div>
+
+<?php bb_admin_list_posts(); ?>
+
+</form>
+
+<div class="tablenav bottom">
+<?php if ( $total ) : ?>
+	<div class="tablenav-pages">
+		<span class="displaying-pages"><?php echo $page_number_links; ?></span>
+		<div class="clear"></div>
+	</div>
+<?php endif; ?>
+</div>
+<div class="clear"></div>
+
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/rewrite-rules.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/rewrite-rules.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/rewrite-rules.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+require('admin-action.php');
+
+wp_redirect( bb_get_uri('bb-admin/options-permalinks.php', null, BB_URI_CONTEXT_BB_ADMIN + BB_URI_CONTEXT_HEADER) );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/sticky.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/sticky.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/sticky.php	(revision 5057)
@@ -0,0 +1,29 @@
+<?php
+require('admin-action.php');
+
+$topic_id = (int) $_GET['id'];
+$topic    =  get_topic ( $topic_id );
+$super = ( isset($_GET['super']) && 1 == (int) $_GET['super'] ) ? 1 : 0;
+
+if ( !$topic )
+	bb_die(__('There is a problem with that topic, pardner.'));
+
+if ( !bb_current_user_can( 'stick_topic', $topic_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	exit;
+}
+
+bb_check_admin_referer( 'stick-topic_' . $topic_id );
+
+if ( topic_is_sticky( $topic_id ) )
+	bb_unstick_topic ( $topic_id );
+else
+	bb_stick_topic   ( $topic_id, $super );
+
+if ( !$redirect = wp_get_referer() )
+	$redirect = get_topic_link( $topic_id );
+
+bb_safe_redirect( $redirect );
+exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style-rtl.css	(revision 5057)
@@ -0,0 +1,465 @@
+/*
+Start with some basic resets
+*/
+
+caption,th{text-align:right;}
+
+
+body {
+	font-family: Tahoma, "Times New Roman";
+}
+
+
+
+/* Header */
+
+div#bbHead h1 {
+	padding-right: 39px;
+	padding-left: 0;
+	margin-right: 16px;
+	margin-left: 0;
+	background-position: 100% 7px;
+	font-family: "Times New Roman", Tahoma;
+}
+
+div#bbHead h1 a em {
+	margin-left: auto;
+	margin-right: 5px;
+}
+
+div#bbUserInfo {
+	right: auto;
+	left: 15px;
+}
+
+
+
+/* Body */
+
+div#bbBody {
+	margin-left: 0;
+	margin-right: 175px;
+	padding-right: 0;
+	padding-left: 15px;
+}
+
+body.bb-menu-folded div#bbBody {
+	margin-left: auto;
+	margin-right: 59px;
+}
+
+h2 {
+	padding-right: 0;
+	padding-left: 15px;
+	font-family: "Times New Roman", Tahoma;
+}
+
+body.bb-admin-dashboard h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -172px;
+}
+
+body.bb-admin-forums h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -802px;
+}
+
+body.bb-admin-topics h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -802px;
+}
+
+body.bb-admin-posts h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -802px;
+}
+
+body.bb-admin-appearance h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% 8px;
+}
+
+body.bb-admin-plugins h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -532px;
+}
+
+body.bb-admin-users h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -891px;
+}
+
+body.bb-admin-tools h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -621px;
+}
+
+body.bb-admin-settings h2 {
+	padding-left: auto;
+	padding-right: 47px;
+	background-position: 100% -712px;
+}
+
+
+
+
+
+/* Menu */
+ul#bbAdminMenu {
+	float: right;
+	margin-right: -160px;
+	margin-left: 5px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu {
+	margin-left: 5px;
+	margin-right: -44px;
+}
+
+ul#bbAdminMenu li.bb-menu a {
+	font-family: "Times New Roman", Tahoma;
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-toggle {
+	float: left;
+	background-image: url('images/menu-bits-rtl.gif');
+	background-position: 100% -110px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu.bb-menu-current div.bb-menu-toggle {
+	background-position: 100% -208px;
+}
+
+ul#bbAdminMenu li.bb-menu a div.bb-menu-icon {
+	float: right;
+	background-position: -331px -39px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li.bb-menu a:hover div.bb-menu-icon {
+	background-position: -331px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-dashboard a div.bb-menu-icon {
+	background-position: -62px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-dashboard.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-dashboard a:hover div.bb-menu-icon {
+	background-position: -62px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-forums a div.bb-menu-icon {
+	background-position: -272px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-forums.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-forums a:hover div.bb-menu-icon {
+	background-position: -272px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-topics a div.bb-menu-icon {
+	background-position: -272px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-topics.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-topics a:hover div.bb-menu-icon {
+	background-position: -272px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-posts a div.bb-menu-icon {
+	background-position: -272px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-posts.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-posts a:hover div.bb-menu-icon {
+	background-position: -272px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-appearance a div.bb-menu-icon {
+	background-position: -1px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-appearance.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-appearance a:hover div.bb-menu-icon {
+	background-position: -1px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-plugins a div.bb-menu-icon {
+	background-position: -181px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-plugins.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-plugins a:hover div.bb-menu-icon {
+	background-position: -181px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-users a div.bb-menu-icon {
+	background-position: -303px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-users.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-users a:hover div.bb-menu-icon {
+	background-position: -303px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-tools a div.bb-menu-icon {
+	background-position: -212px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-tools.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-tools a:hover div.bb-menu-icon {
+	background-position: -212px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-settings a div.bb-menu-icon {
+	background-position: -241px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-settings.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-settings a:hover div.bb-menu-icon {
+	background-position: -241px -7px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-separator a {
+	background-position: -55px -34px;
+	cursor: e-resize;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-separator a {
+	background-position: 0 5px;
+	cursor: w-resize;
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap {
+	border-left-width: 1px;
+	border-right-width: 0;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap {
+	left: auto;
+	right: 36px;
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap span {
+	font-family: "Times New Roman", Tahoma;
+	padding-right: 10px;
+	padding-left: 5px;
+}
+
+ul#bbAdminMenu li.bb-menu li.bb-menu-sub a {
+	font-family: Tahoma, "Times New Roman";
+	padding-right:12px;
+	padding-left: 5px;
+	background-image: url('images/menu-bits-rtl.gif');
+	background-position: 100% -306px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu li.bb-menu-sub a {
+	background-image: url('images/menu-bits-rtl.gif');
+	background-position: 100% -306px;
+	border-right-width: 0 !important;
+	border-left-width: 1px !important;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub a {
+	background-image: url('images/menu-dark-rtl.gif');
+	background-position: 100% -20px !important;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub a {
+	background-image: url('images/menu-dark-rtl.gif');
+	background-position: 100% -20px !important;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub.bb-menu-sub-current a {
+	background-position: 100% 0 !important;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub.bb-menu-sub-current a {
+	background-position: 100% 0 !important;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-open li.bb-menu-sub a {
+	background-position: 100% -306px;
+}
+
+
+
+/* Dashboard */
+
+div.dashboard div.versions p.theme a.button {
+	float: left;
+}
+
+
+
+/* Search forms */
+
+form.search-form fieldset, form.bulk-form fieldset {
+	clear: right;
+	float: right;
+}
+
+form.search-form fieldset div {
+	float: right;
+	margin-right: 0;
+	margin-left: 5px;
+}
+
+
+
+/* Table nav */
+
+div.tablenav {
+	float: left;
+}
+
+span.displaying-num {
+	text-align: left;
+}
+
+span.displaying-pages {
+	float: left;
+}
+
+span.displaying-pages span,
+span.displaying-pages a {
+	float: right;
+}
+
+
+
+/* Tabled info */
+
+table#plugins-list.widefat tr.autoload td.plugin-name {
+	background-position: 0 0;
+	padding-right: 7px;
+	padding-left: 20px;
+}
+
+table.widefat .check-column {
+	padding-right: 7px;
+	padding-left: 0;
+}
+
+/* Forums */
+
+ul#forum-list li ul li {
+	border-left-width: 0;
+	border-right: 1px dashed rgb(223, 223, 223);
+}
+
+ul#forum-list li ul.list-block {
+	padding: 0 30px 0 0;
+}
+
+ul#forum-list li img.sort-handle {
+	float: right;
+	margin: 0 -4px 0 3px;
+}
+
+#dragHelper img.sort-handle {
+	float: right;
+	margin: 0 0 0 3px;
+}
+
+#dragHelper div.row-description {
+	margin: 0 4px 0 0;
+}
+
+
+
+/* Themes */
+
+table.theme-list td {
+	border-left: 1px solid rgb(221, 221, 221);
+	border-right-width: 0;
+}
+
+table.theme-list td.position-3 {
+	border-left: none;
+}
+
+table.theme-list-active td div.screen-shot {
+	float: right;
+	margin-right: 0;
+	margin-left: 15px;
+}
+
+
+
+/* Options */
+
+form.settings div {
+	margin-right: 0;
+	margin-left: 10px;
+}
+
+form.settings div label,
+form.settings div div.label {
+	float: right;
+}
+
+form.settings div.disabled label,
+form.settings div.disabled div.label {
+	background-position: 0 0;
+	padding-right: 0;
+	padding-left: 20px;
+}
+
+form.settings div div.inputs {
+	margin: 0 210px 0 0;
+}
+
+form.settings div.table {
+	margin: 0 220px 0 0;
+}
+
+form.settings div p {
+	margin: 0 3em 0 0;
+	border-left-width: 0;
+	border-right: 1px solid rgb(223, 223, 223);
+}
+
+form.settings a.cancel {
+	float: right;
+	margin-right: 0;
+	margin-left: 6px;
+}
+
+form.settings select#timezone-string option {
+	margin-left: auto;
+	margin-right: 1em;
+}
+
+
+
+/* Footer */
+
+p#bbShowOff {
+	margin-right: 0;
+	margin-left: 8px;
+	float: left;
+	font-family: "Times New Roman", Tahoma;
+}
+
+div#bbFoot {
+	font-family: "Times New Roman", Tahoma;
+}
+
+p#bbThanks {
+	float: right;
+}
+
+p#bbVersion {
+	text-align: left;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/style.css	(revision 5057)
@@ -0,0 +1,1932 @@
+/*
+Start with some basic resets
+*/
+
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}
+table{border-collapse:collapse;border-spacing:0;}
+fieldset,img{border:0;}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}
+del,ins{text-decoration:none;}
+li{list-style:none;}
+caption,th{text-align:left;}
+h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
+q:before,q:after{content:'';}
+abbr,acronym{border:0;font-variant:normal;}
+sup{vertical-align:baseline;}
+sub{vertical-align:baseline;}
+legend{color:#000;}
+input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}
+input,button,textarea,select{*font-size:100%;}
+body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
+select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}
+table{font-size:inherit;font:100%;}
+pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
+
+
+html {
+	height: 100%;
+	background-color: rgb(249, 249, 249);
+}
+
+body {
+	height: 100%;
+	min-width: 785px;
+	color: rgb(51, 51, 51);
+	line-height: 1.4em;
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.8em;
+}
+
+a {
+	outline: none;
+}
+
+div#bbWrap {
+	height: auto;
+	min-height: 100%;
+	width: 100%;
+}
+
+div#bbContent {
+	height: 100%;
+	padding-bottom: 45px;
+}
+
+
+pre {
+	/* http://www.longren.org/2006/09/27/wrapping-text-inside-pre-tags/ */
+	white-space: pre-wrap; /* css-3 */
+	white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+	white-space: -pre-wrap; /* Opera 4-6 */
+	white-space: -o-pre-wrap; /* Opera 7 */
+	word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+/* Header */
+
+div#bbHead {
+	position: relative;
+	background-color: rgb(70, 70, 70);
+}
+
+div#bbHead h1 {
+	padding: 10px 0 5px 39px;
+	margin-left: 16px;
+	min-height: 31px;
+	background: transparent url('images/admin-header-logo.gif') no-repeat 0 8px;
+	font: normal normal normal 22px/normal Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	color: white;
+}
+
+div#bbHead h1 a {
+	text-decoration: none;
+}
+
+div#bbHead h1 a span {
+	color: rgb(255, 255, 255);
+}
+
+div#bbHead h1 a:hover span {
+	text-decoration: underline;
+}
+
+div#bbHead h1 a em {
+	background: rgb(88, 88, 88) url('images/visit-site-button-grad.gif') repeat-x 0 0;
+	color: rgb(170, 170, 170);
+	text-shadow: rgb(63, 63, 63) 0px -1px 0px;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	cursor: pointer;
+	display: inline-block;
+	font-size: 0.5em;
+	font-style: normal;
+	line-height: 17px;
+	margin-left: 5px;
+	padding: 0px 6px;
+	vertical-align: middle;
+}
+
+div#bbHead h1 a:hover em {
+	color: rgb(255, 255, 255);
+}
+
+div#bbHead h1 a:active em {
+	background-position: 0 -27px;
+}
+
+div#bbUserInfo {
+	position: absolute;
+	top: 14px;
+	right: 15px;
+	font-size: 0.95em;
+	color: rgb(153, 153, 153);
+}
+
+div#bbUserInfo a {
+	color: rgb(204, 204, 204);
+	text-decoration: none;
+}
+
+div#bbUserInfo a:hover {
+	color: rgb(255, 255, 255);
+	text-decoration: underline;
+}
+
+
+
+
+/* Body */
+
+div#bbBody {
+	margin-left: 180px;
+	padding-right: 15px;
+}
+
+body.bb-menu-folded div#bbBody {
+	margin-left: 65px;
+}
+
+h2 {
+	display: block;
+	color: rgb(70, 70, 70);
+	font: italic normal normal 1.9em/1.45em Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	padding: 14px 15px 3px 0;
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	background: transparent none no-repeat 0 0;
+	margin-left: -5px;
+}
+
+h2 span.subtitle {
+	font-size: 0.75em;
+	padding-left: 25px;
+}
+
+body.bb-admin-dashboard h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -172px;
+}
+
+body.bb-admin-forums h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -981px;
+}
+
+body.bb-admin-topics h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -1071px;
+}
+
+body.bb-admin-posts h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -1161px;
+}
+
+body.bb-admin-appearance h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 8px;
+}
+
+body.bb-admin-plugins h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -532px;
+}
+
+body.bb-admin-users h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -891px;
+}
+
+body.bb-admin-user-manage h2 {
+       padding-left: 47px;
+       background-image: url('images/icons32.png');
+       background-position: 0 -891px;
+}
+
+body.bb-admin-tools h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -621px;
+}
+
+body.bb-admin-settings h2 {
+	padding-left: 47px;
+	background-image: url('images/icons32.png');
+	background-position: 0 -712px;
+}
+
+
+
+
+
+/* Menu */
+ul#bbAdminMenu * {
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-khtml-user-select: none;
+	user-select: none;
+}
+
+ul#bbAdminMenu {
+	float: left;
+	margin: 15px 5px 15px -165px;
+	position: relative;
+	width: 145px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu {
+	margin-left: -50px;
+	width: auto;
+}
+
+ul#bbAdminMenu li.bb-menu {
+	
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu {
+	position: relative;
+}
+
+ul#bbAdminMenu li.bb-menu a {
+	display: block;
+	color: rgb(0, 102, 0);
+	background: rgb(241, 241, 241) url('images/menu-bits.gif') repeat-x 0 -379px;
+	border: 1px solid rgb(227, 227, 227);
+	width: 133px;
+	padding: 5px;
+	font: normal normal normal 1em/1.4em Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	text-decoration: none;
+	border-bottom-width: 0;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu a {
+	height: 17px;
+	width: 17px;
+	background-image: none;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu a span {
+	display: none;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current a {
+	background-position: 0 0;
+	border-color: rgb(109, 109, 109);
+	color: rgb(255, 255, 255);
+	text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current a {
+	background-color: rgb(230, 230, 230);
+	background-image: none;
+	border-color: rgb(227, 227, 227);
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current a:hover {
+	border-color: rgb(181, 181, 181);
+	color: rgb(255, 255, 255);
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-first a {
+	-moz-border-radius-topleft: 6px;
+	-moz-border-radius-topright: 6px;
+	-khtml-border-top-left-radius: 6px;
+	-khtml-border-top-right-radius: 6px;
+	-webkit-border-top-left-radius: 6px;
+	-webkit-border-top-right-radius: 6px;
+	border-top-left-radius: 6px;
+	border-top-right-radius: 6px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-last a {
+	-moz-border-radius-bottomleft: 6px;
+	-moz-border-radius-bottomright: 6px;
+	-khtml-border-bottom-left-radius: 6px;
+	-khtml-border-bottom-right-radius: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	-webkit-border-bottom-right-radius: 6px;
+	border-bottom-left-radius: 6px;
+	border-bottom-right-radius: 6px;
+	border-bottom-width: 1px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-open.bb-menu-has-submenu a,
+ul#bbAdminMenu li.bb-menu.bb-menu-open a {
+	-moz-border-radius-bottomleft: 0;
+	-moz-border-radius-bottomright: 0;
+	-khtml-border-bottom-left-radius: 0;
+	-khtml-border-bottom-right-radius: 0;
+	-webkit-border-bottom-left-radius: 0;
+	-webkit-border-bottom-right-radius: 0;
+	border-bottom-left-radius: 0;
+	border-bottom-right-radius: 0;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-last a {
+	-moz-border-radius-bottomleft: 6px;
+	-moz-border-radius-bottomright: 6px;
+	-khtml-border-bottom-left-radius: 6px;
+	-khtml-border-bottom-right-radius: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	-webkit-border-bottom-right-radius: 6px;
+	border-bottom-left-radius: 6px;
+	border-bottom-right-radius: 6px;
+	border-bottom-width: 1px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-open a {
+	border-bottom-width: 0;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current.bb-menu-has-submenu a:hover {
+	border-color: rgb(109, 109, 109);
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current.bb-menu-has-submenu a:hover {
+	border-color: rgb(227, 227, 227);
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-toggle {
+	float: right;
+	height: 27px;
+	width: 24px;
+	display: none;
+	background-image: url('images/menu-bits.gif');
+	background-repeat: no-repeat;
+	background-position: 0 -110px;
+	margin-top: -27px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-last div.bb-menu-toggle {
+	margin-top: -28px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-last.bb-menu-open div.bb-menu-toggle {
+	margin-top: -27px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu:hover div.bb-menu-toggle,
+ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu.bb-menu-open div.bb-menu-toggle {
+	display: block;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu:hover div.bb-menu-toggle,
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu.bb-menu-open div.bb-menu-toggle {
+	display: none;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-has-submenu.bb-menu-current div.bb-menu-toggle {
+	background-position: 0 -208px;
+}
+
+ul#bbAdminMenu li.bb-menu a:hover,
+ul#bbAdminMenu li.bb-menu a:active,
+ul#bbAdminMenu li.bb-menu a:focus {
+	color: rgb(213, 78, 33);
+}
+
+ul#bbAdminMenu li.bb-menu a div.bb-menu-icon {
+	height: 22px;
+	width: 22px;
+	float: left;
+	background: transparent url('images/menu.png') no-repeat -337px -39px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li.bb-menu a:hover div.bb-menu-icon {
+	background-position: -337px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-dashboard a div.bb-menu-icon {
+	background-position: -67px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-dashboard.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-dashboard a:hover div.bb-menu-icon {
+	background-position: -67px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-forums a div.bb-menu-icon {
+	background-position: -367px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-forums.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-forums a:hover div.bb-menu-icon {
+	background-position: -367px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-topics a div.bb-menu-icon {
+	background-position: -397px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-topics.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-topics a:hover div.bb-menu-icon {
+	background-position: -397px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-posts a div.bb-menu-icon {
+	background-position: -427px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-posts.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-posts a:hover div.bb-menu-icon {
+	background-position: -427px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-appearance a div.bb-menu-icon {
+	background-position: -6px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-appearance.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-appearance a:hover div.bb-menu-icon {
+	background-position: -6px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-plugins a div.bb-menu-icon {
+	background-position: -186px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-plugins.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-plugins a:hover div.bb-menu-icon {
+	background-position: -186px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-users a div.bb-menu-icon {
+	background-position: -308px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-users.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-users a:hover div.bb-menu-icon {
+	background-position: -308px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-tools a div.bb-menu-icon {
+	background-position: -217px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-tools.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-tools a:hover div.bb-menu-icon {
+	background-position: -217px -7px;
+}
+
+ul#bbAdminMenu li#bb-menu-settings a div.bb-menu-icon {
+	background-position: -246px -39px;
+}
+
+ul#bbAdminMenu li#bb-menu-settings.bb-menu-current a div.bb-menu-icon,
+ul#bbAdminMenu li#bb-menu-settings a:hover div.bb-menu-icon {
+	background-position: -246px -7px;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-separator {
+	
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-separator a {
+	background: transparent url('images/menu-arrows.gif') no-repeat 0 5px;
+	border-width: 0;
+	padding: 0;
+	height: 21px;
+	cursor: w-resize;
+	width: 145px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-separator a {
+	background: transparent url('images/menu-arrows.gif') no-repeat -171px -34px;
+	height: 21px;
+	cursor: e-resize;
+	width: 29px;
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap {
+	display: none;
+	border: 1px solid rgb(227, 227, 227);
+	border-bottom-width: 0;
+	border-left-width: 0;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap {
+	display: none;
+	position: absolute;
+	left: 36px;
+	top: 0;
+	width: 145px;
+	z-index: 1000;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current div.bb-menu-sub-wrap {
+	border-color: rgb(170, 170, 170);
+	border-bottom-width: 1px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap {
+	border-width: 0;
+	border-color: rgb(227, 227, 227);
+	border-style: solid;
+	border-bottom-width: 1px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current div.bb-menu-sub-wrap {
+	border-color: rgb(170, 170, 170);
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-open div.bb-menu-sub-wrap {
+	display: block;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-last div.bb-menu-sub-wrap {
+	border-bottom-width: 1px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap.bb-menu-sub-open {
+	display: block;
+	border-bottom-width: 1px;
+}
+
+ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap span {
+	display: none;
+	font: normal normal normal 1em/1.45em Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	background-color: rgb(241, 241, 241);
+	border: 1px solid rgb(227, 227, 227);
+	-moz-border-radius-topleft: 6px;
+	-moz-border-radius-topright: 6px;
+	-khtml-border-top-left-radius: 6px;
+	-khtml-border-top-right-radius: 6px;
+	-webkit-border-top-left-radius: 6px;
+	-webkit-border-top-right-radius: 6px;
+	border-top-left-radius: 6px;
+	border-top-right-radius: 6px;
+	padding: 5px 5px 4px 10px;
+	cursor: default;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current div.bb-menu-sub-wrap span {
+	background-color: rgb(234, 234, 234);
+	border-color: rgb(170, 170, 170);
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu div.bb-menu-sub-wrap span {
+	display: block;
+}
+
+ul#bbAdminMenu li.bb-menu li.bb-menu-sub a {
+	width: 127px !important;
+	-moz-border-radius: 0 !important;
+	-khtml-border-radius: 0 !important;
+	-webkit-border-radius: 0 !important;
+	border-radius: 0 !important;
+	border-width: 0 !important;
+	font: normal normal normal 0.85em/1.7em 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	padding: 1px 5px 3px 12px;
+	color: rgb(0, 102, 0);
+	text-shadow: none;
+	background: rgb(255, 255, 255) url('images/menu-bits.gif') no-repeat 0 -306px;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu li.bb-menu-sub a {
+	height: auto;
+	background: rgb(255, 255, 255) url('images/menu-bits.gif') no-repeat 0 -306px;
+	border-right-width: 1px !important;
+}
+
+ul#bbAdminMenu li.bb-menu li.bb-menu-sub a:hover {
+	background-color: rgb(222, 236, 225) !important;
+	color: rgb(51, 51, 51);
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub a {
+	background-image: url('images/menu-dark.gif');
+	background-position: 0 -20px !important;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub a {
+	background-image: url('images/menu-dark.gif');
+	background-position: 0 -20px !important;
+	border-color: rgb(170, 170, 170) !important;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub.bb-menu-sub-current a {
+	color: rgb(51, 51, 51);
+	font-weight: bold;
+	background-color: rgb(245, 245, 245) !important;
+	background-position: 0 0 !important;
+}
+
+body.bb-menu-folded ul#bbAdminMenu li.bb-menu.bb-menu-current li.bb-menu-sub.bb-menu-sub-current a {
+	background-position: 0 0 !important;
+}
+
+ul#bbAdminMenu li.bb-menu.bb-menu-open li.bb-menu-sub a {
+	background-position: 0 -306px;
+}
+
+div.wrap {
+	float: left;
+	width: 100%;
+}
+
+div#message {
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	border-style: solid;
+	border-width: 1px;
+	margin: 5px 0 2px 0;
+	padding: 0 0.65em;
+	font-size: 0.9em;
+}
+
+div#message.updated {
+	background-color: rgb(255, 251, 204);
+	border-color: rgb(230, 219, 85);
+}
+
+div#message.error {
+	background-color: rgb(255, 235, 232);
+	border-color: rgb(204, 0, 0);
+}
+
+div#message p {
+	line-height: 1;
+	margin: 0.6em 0;
+	padding: 2px;
+}
+
+div#message p strong {
+	font-weight: bold;
+}
+
+div#message a {
+	color: rgb(0, 102, 0);
+	text-decoration: none;
+}
+
+div#message a:hover {
+	color: rgb(213, 78, 33);
+}
+
+div.plugin-error {
+	background-color: rgb(255, 235, 232);
+	border-color: rgb(204, 0, 0);
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	border-style: solid;
+	border-width: 1px;
+	margin: 5px 0 2px 0;
+	padding: 0;
+	height: 160px;
+}
+
+div.plugin-error iframe {
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	display: block;
+	width: 100%;
+	height: 100%;
+	margin: 0;
+	padding: 0;
+	border-width: 0;
+}
+
+
+/* Dashboard */
+
+div.dashboard {
+	background-color: rgb(255, 255, 255);
+	border: 1px solid rgb(223, 223, 223);
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	margin: 10px 5px 20px 5px;
+}
+
+div.dashboard a {
+	color: rgb(0, 102, 0);
+}
+
+div.dashboard a:hover {
+	color: rgb(213, 78, 33);
+}
+
+div.dashboard h3 {
+	-moz-border-radius-topleft: 6px;
+	-moz-border-radius-topright: 6px;
+	-khtml-border-top-left-radius: 6px;
+	-khtml-border-top-right-radius: 6px;
+	-webkit-border-top-left-radius: 6px;
+	-webkit-border-top-right-radius: 6px;
+	border-top-left-radius: 6px;
+	border-top-right-radius: 6px;
+	background: url('images/gray-grad.png') repeat-x rgb(223, 223, 223);
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	color: rgb(70, 70, 70);
+	font-size: 0.9em;
+	font-weight: bold;
+	line-height: 1;
+	margin: 0px;
+	padding: 7px 9px;
+}
+
+div.dashboard div.table {
+	border-bottom: 1px solid rgb(236, 236, 236);
+	background: rgb(249, 249, 249);
+}
+
+div.dashboard div.table table {
+	width: 100%;
+}
+
+div.dashboard div.table thead th {
+	color: rgb(119, 119, 119);
+	font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	font-size: 1em;
+	font-style: italic;
+	padding: 3px 8px 4px;
+	background: rgb(255, 255, 255);
+	width: 50%;
+}
+
+div.dashboard div.table tbody td {
+	border-top: 1px solid rgb(236, 236, 236);
+	padding: 4px 16px;
+	white-space: nowrap;
+	font-size: 0.9em;
+	color: rgb(70, 70, 70);
+}
+
+div.dashboard div.table tbody td span {
+	font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	font-size: 1.6em;
+	padding-right: 6px;
+}
+
+div.dashboard p {
+	color: rgb(51, 51, 51);
+	font-size: 0.85em;
+	margin: 8px;
+}
+
+div.dashboard div.versions p span.b {
+	font-weight: bold;
+}
+
+div.dashboard div.versions p a {
+	text-decoration: none;
+	font-weight: bold;
+}
+
+div.dashboard div.versions p.theme a.button {
+	display: block;
+	float: right;
+	font-weight: normal;
+	-moz-border-radius: 10px;
+	-khtml-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	border-radius: 10px;
+	padding: 4px 10px;
+	background: url('images/white-grad.png') repeat-x scroll rgb(242, 242, 242);
+	border: 1px solid rgb(187, 187, 187);
+	color: rgb(70, 70, 70);
+	cursor: pointer;
+	font-size: 1em;
+	line-height: 1.2em;
+}
+
+div.dashboard div.versions p.theme a.button:hover {
+	border-color: rgb(102, 102, 102);
+	color: rgb(0, 0, 0);
+}
+
+div.dashboard div.versions p.theme a.button:active {
+	background: url('images/white-grad-active.png') repeat-x scroll rgb(238, 238, 238);
+}
+
+div.dashboard ul li {
+	color: rgb(51, 51, 51);
+	font-size: 0.85em;
+	margin: 8px;
+}
+
+div.dashboard ul li a {
+	text-decoration: none;
+	font-weight: bold;
+}
+
+
+
+/* Search forms */
+
+form.search-form fieldset, form.bulk-form fieldset {
+	margin: 10px 0 0 0;
+	clear: left;
+	float: left;
+}
+
+form.search-form fieldset div {
+	float: left;
+	margin-right: 5px;
+	margin-top: 3px;
+}
+
+form.search-form fieldset div div {
+	float: none;
+	margin: 0;
+}
+
+form.search-form fieldset div label {
+	display: block;
+	line-height: 1;
+	padding: 0 0 3px 0;
+	font-size: 0.8em;
+	color: rgb(70, 70, 70);
+}
+
+form.search-form fieldset div div input.text-input {
+	width: 100px;
+	padding: 3px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	border: 1px solid rgb(223, 223, 223);
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.85em;
+	line-height: 1.3em;
+}
+
+form.search-form fieldset div div input.checkbox-input {
+	margin: 4px;
+}
+
+form.search-form select, form.bulk-form select {
+	height: 2.1em;
+	margin: 0;
+	padding: 3px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	border: 1px solid rgb(223, 223, 223);
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.85em;
+}
+
+form.search-form fieldset div div option {
+	padding: 3px;
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.9em;
+}
+
+form.search-form div.submit label {
+	width: 0;
+	overflow: hidden;
+}
+
+form.search-form div.submit input.button, form.bulk-form input.button {
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	-moz-border-radius: 10px;
+	-khtml-border-radius: 10px;
+	-webkit-border-radius: 10px;
+	border-radius: 10px;
+	padding: 4px 9px 5px;
+	background: url('images/white-grad.png') repeat-x scroll rgb(242, 242, 242);
+	border: 1px solid rgb(187, 187, 187);
+	color: rgb(70, 70, 70);
+	cursor: pointer;
+	line-height: 1.1em;
+	font-size: 0.85em;
+}
+
+form.search-form div.submit input.button:hover, form.bulk-form input.button:hover {
+	border-color: rgb(102, 102, 102);
+	color: rgb(0, 0, 0);
+}
+
+form.search-form div.submit input.button:active, form.bulk-form input.button:active {
+	background: url('images/white-grad-active.png') repeat-x scroll rgb(238, 238, 238);
+}
+
+
+
+/* Table nav */
+
+div.tablenav {
+	margin-top: -1em;
+	float: right;
+}
+
+div.bottom {
+	margin-top: 0;
+}
+
+div.tablenav a {
+	color: rgb(0, 102, 0);
+}
+
+div.tablenav a:hover {
+	color: rgb(213, 78, 33);
+}
+
+div.tablenav-pages {
+	margin: 13px 0 0 0;
+}
+
+div.tablenav.bottom div.tablenav-pages {
+	margin: 0 0 16px 0;
+}
+
+span.displaying-num {
+	display: block;
+	text-align: right;
+	line-height: 1;
+	font-size: 0.8em;
+	color: rgb(70, 70, 70);
+	padding: 0 0 3px 0;
+}
+
+span.displaying-pages {
+	display: block;
+	float: right;
+	cursor: default;
+	color: rgb(85, 85, 85);
+}
+
+span.displaying-pages span,
+span.displaying-pages a {
+	display: block;
+	float: left;
+	-moz-border-radius: 5px;
+	-khtml-border-radius: 5px;
+	-webkit-border-radius: 5px;
+	border-radius: 5px;
+	border: 1px solid rgb(211, 211, 211);
+	padding: 3px 7px;
+	text-decoration: none;
+	background-color: rgb(223, 223, 223);
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 0.85em;
+	line-height: 1.3em;
+	margin-left: 3px;
+}
+
+span.displaying-pages span.dots {
+	border-width: 0;
+	background-color: transparent;
+	padding: 4px 0 0 0;
+	font-weight: bold;
+}
+
+span.displaying-pages a {
+	font-weight: bold;
+	background: url('images/menu-bits.gif') repeat-x 0 -379px rgb(238, 238, 238);
+	border-color: rgb(227, 227, 227);
+}
+
+span.displaying-pages a:hover {
+	border-color: rgb(213, 78, 33);
+}
+
+span.displaying-pages a:active {
+	color: rgb(255, 255, 255);
+}
+
+div.table-filter {
+	color: rgb(102, 102, 102);
+	margin: 8px 0px 5px;
+	font-size: 0.85em;
+}
+
+div.table-filter a {
+	color: rgb(0, 102, 0);
+	text-decoration: none;
+}
+
+div.table-filter a.current {
+	color: rgb(0, 0, 0);
+	font-weight: bold;
+}
+
+div.table-filter a:hover {
+	color: rgb(213, 78, 33);
+}
+
+div.table-filter a span.count {
+	color: rgb(153, 153, 153);
+	font-weight: normal;
+}
+
+
+
+
+/* Tabled info */
+
+p.no-results {
+	margin: 10px 0;
+	font-size: 0.85em;
+}
+
+table.widefat {
+	width: 100%;
+	border: 1px solid rgb(223, 223, 223);
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	border-collapse: separate;
+	margin: 10px 0;
+}
+
+table.widefat a {
+	color: rgb(0, 102, 0);
+	text-decoration: none;
+}
+
+table.widefat a:hover {
+	color: rgb(213, 78, 33);
+}
+
+table.widefat tr td {
+	width: 18%;
+	vertical-align: top;
+	padding: 3px 7px;
+	font-size: 0.85em;
+	border-bottom: 1px solid rgb(223, 223, 223);
+}
+
+table.widefat tr.alt td {
+	background-color: rgb(255, 255, 255);
+}
+
+table.widefat tr.deleted td {
+	background-color: rgb(245, 224, 224);
+}
+
+table.widefat tr.deleted.alt td {
+	background-color: rgb(248, 233, 233);
+}
+
+table.widefat tr.spam td {
+	background-color: rgb(238, 218, 204);
+}
+
+table.widefat tr.spam.alt td {
+	background-color: rgb(243, 228, 218);
+}
+
+table#plugins-list.widefat tr.inactive td {
+	background-color: rgb(238, 238, 238);
+}
+
+table#plugins-list.widefat tr.active td {
+	background-color: rgb(255, 255, 255);
+}
+
+table#plugins-list.widefat tr.autoload td {
+	background-color: rgb(249, 249, 249);
+}
+
+table#plugins-list.widefat tr.autoload td.plugin-name {
+	background-image: url('images/input-lock.png');
+	background-repeat: no-repeat;
+	background-position: 100% 0;
+	padding-right: 20px;
+}
+
+table.widefat tr th {
+	background: url('images/gray-grad.png') repeat-x rgb(223, 223, 223);
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	color: rgb(51, 51, 51);
+	font-weight: bold;
+	vertical-align: top;
+	padding: 7px 7px 8px;
+	font-size: 0.85em;
+	line-height: 1.3em;
+	border-bottom: 1px solid rgb(223, 223, 223);
+}
+
+table.widefat tfoot tr th {
+	border-bottom-width: 0;
+}
+
+table.widefat .check-column {
+	width: 2.2em;
+	padding-right: 0;
+}
+	
+table#posts-list.widefat tr td.post {
+	width: 46%;
+	font-size: 0.9em;
+}
+
+table#topics-list.widefat tr td.topic {
+	width: 28%;
+	font-size: 0.9em;
+}
+
+table#plugins-list.widefat tr td.plugin-name {
+	width: 25%;
+	font-size: 0.9em;
+}
+
+table#plugins-list.widefat tr td.plugin-description {
+	width: 75%;
+}
+
+table.widefat tr td.author img.avatar {
+	vertical-align: top;
+	border: 1px solid rgb(223, 223, 223);
+}
+
+table.widefat tr td.post p {
+	margin-bottom: 0.4em;
+}
+
+table.widefat tr td.plugin-description p {
+	margin-bottom: 0.8em;
+}
+
+table.widefat tr td.plugin-description p.meta {
+	margin-bottom: 0;
+}
+
+table.widefat tr td.topic span.row-title,
+table.widefat tr td.user span.row-title {
+	margin-bottom: 0.4em;
+	display: block;
+	font-weight: bold;
+}
+
+table.widefat tr td.user img.avatar {
+	display: block;
+	float: left;
+	margin: 3px 8px 3px -1px;
+	border: 1px solid rgb(223, 223, 223);
+}
+
+table.widefat tr td.plugin-name span.row-title {
+	margin-bottom: 0.8em;
+	display: block;
+	font-weight: bold;
+}
+
+table.widefat tr td.post div,
+table.widefat tr td.topic div,
+table.widefat tr td.plugin-name div {
+	font-size: 0.85em;
+}
+
+table.widefat tr span.row-actions {
+	display: none;
+}
+
+table#plugins-list.widefat tr span.row-actions {
+	display: inline;
+}
+
+table.widefat tr:hover span.row-actions {
+	display: inline;
+}
+
+table.widefat tr span.row-actions span.note {
+	color: rgb(102, 102, 102);
+}
+
+table.widefat tr a.post-undelete-link {
+	display: none;
+}
+
+table.widefat tr.deleted a.post-undelete-link {
+	display: inline;
+}
+
+table.widefat tr.deleted a.post-delete-link {
+	display: none;
+}
+
+table.widefat tr td em  {
+	font-style: italic;
+}
+
+table.widefat tr td strong  {
+	font-weight: bold;
+}
+
+table.widefat tr td code,
+table.widefat tr td kbd {
+	font-family: Consolas, Monaco, Courier, monospace;
+	font-size: 1.2em;
+	line-height: 1.4;
+	background: rgb(234, 234, 234);
+	margin: 0px 1px;
+	padding: 1px 3px;
+	color: rgb(51, 51, 51);
+}
+
+
+
+/* Forums */
+
+body.sorting {
+	overflow-x: hidden;
+}
+
+ul#forum-list {
+	xwidth: 100%;
+	border: 1px solid rgb(223, 223, 223);
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	margin: 10px 0;
+}
+
+ul#forum-list a {
+	color: rgb(0, 102, 0);
+	text-decoration: none;
+}
+
+ul#forum-list a:hover {
+	color: rgb(213, 78, 33);
+}
+
+ul#forum-list li {
+	border-top: 1px solid rgb(223, 223, 223);
+	background-color: rgb(249, 249, 249);
+}
+
+ul#forum-list li.alt {
+	background-color: rgb(255, 255, 255);
+}
+
+ul#forum-list li ul li {
+	border-left: 1px dashed rgb(223, 223, 223);
+}
+
+ul#forum-list li ul.list-block {
+	padding: 0 0 0 30px;
+}
+
+ul#forum-list li.list-block {
+	padding: 3px 0 0 0;
+}
+
+ul#forum-list li div.list-block {
+	padding: 0 7px 3px 7px;
+}
+
+ul#forum-list li.thead {
+	background: url('images/gray-grad.png') repeat-x rgb(223, 223, 223);
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+	color: rgb(51, 51, 51);
+	font-weight: bold;
+	vertical-align: top;
+	padding: 7px 7px 8px;
+	font-size: 0.85em;
+	line-height: 1.3em;
+	border-top-width: 0;
+}
+
+ul#forum-list li img.sort-handle {
+	float: left;
+	margin: 0 3px 0 -4px;
+	cursor: move;
+}
+
+ul#forum-list li div.row-title {
+	font-size: 0.9em;
+	font-weight: bold;
+}
+
+ul#forum-list li p.row-description {
+	font-size: 0.85em;
+}
+
+ul#forum-list li div.list-block div.row-actions {
+	font-size: 0.8em;
+	margin-top: 0.4em;
+}
+
+ul#forum-list li div.list-block div.row-actions span {
+	display: none;
+}
+
+ul#forum-list li div.list-block:hover div.row-actions span {
+	display: inline;
+}
+
+ul#forum-list.sorting li div.list-block div.row-actions span {
+	display: none;
+}
+
+ul#forum-list.sorting li div.list-block div.row-actions span {
+	display: none;
+}
+
+.helper {
+	background-color: rgb(200, 250, 200);
+	outline: 1px dashed #777;
+	width: 100% !important;
+	padding: 3px 0;
+}
+
+#dragHelper {
+	xwidth: auto !important;
+	padding: 3px;
+}
+
+#dragHelper img.sort-handle {
+	float: left;
+	margin: 0 3px 0 0;
+}
+
+#dragHelper div.row-description {
+	margin: 0 0 0 4px;
+}
+
+#dragHelper div.row-actions span {
+	display: none;
+}
+
+
+
+/* Themes */
+
+h3.themes {
+	display: block;
+	color: rgb(51, 51, 51);
+	font-size: 1.17em;
+	font-weight: bold;
+	padding: 0 0 1em 0;
+	margin-top: 10px;
+}
+
+table.theme-list-active,
+table.theme-list {
+	margin: 0 auto 40px;
+	width: 100%;
+	border-collapse: collapse;
+}
+
+table.theme-list {
+	margin-bottom: 20px;
+	width: 100%;
+	border-top: 1px solid rgb(221, 221, 221);
+	border-bottom: 1px solid rgb(221, 221, 221);
+	border-collapse: collapse;
+}
+
+table.theme-list td {
+	width: 240px;
+	padding: 20px;
+	border-bottom: 1px solid rgb(221, 221, 221);
+	border-right: 1px solid rgb(221, 221, 221);
+	vertical-align: top;
+}
+
+table.theme-list td.position-3 {
+	border-right: none;
+}
+
+table.theme-list td h3.themes,
+table.theme-list-active td h3.themes {
+	margin-top: 0;
+	font-size: 1em;
+}
+
+table.theme-list td div.screen-shot {
+	border: 1px solid rgb(204, 204, 204);
+	width: 240px;
+	height: 180px;
+	margin-bottom: 10px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+table.theme-list td div.screen-shot img {
+	width: 240px;
+	height: 180px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+table.theme-list-active td div.screen-shot {
+	border: 1px solid rgb(153, 153, 153);
+	width: 150px;
+	height: 112px;
+	float: left;
+	margin-right: 15px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+table.theme-list-active td div.screen-shot img {
+	width: 150px;
+	height: 112px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+table.theme-list td div.description p,
+table.theme-list-active td div.description p {
+	color: rgb(102, 102, 102);
+	font-style: italic;
+	margin: 0 0 1em 0;
+	font-size: 0.9em;
+}
+
+table.theme-list td div.description div.actions {
+	margin: 0 0 1em 0;
+	font-size: 1em;
+}
+
+table.theme-list td div.description p.location,
+table.theme-list-active td div.description p.location {
+	color: inherit;
+	font-style: normal;
+	margin: 0 0 0 0;
+}
+
+table.theme-list td a,
+table.theme-list-active td a {
+	color: rgb(0, 102, 0);
+}
+
+table.theme-list td a:hover,
+table.theme-list-active td a:hover {
+	color: rgb(213, 78, 33);
+}
+
+
+/* Users */
+
+.bb-admin-user-manage form {
+	width: 550px;
+}
+
+#option-pass-strength-fake-input {
+	clear: none;
+	width: 200px;
+}
+.bb-admin-user-manage #pass-strength-result {
+	clear: left;
+	float: left;
+	margin-top: -25px;
+}
+
+#pass-strength-result {
+	padding: 2px;
+	text-align: center;
+	width: 280px;
+	border: 1px solid #ccc;
+	background-color: #e3e3e3;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
+#pass-strength-result.bad {
+	background-color: #ffeff7;
+	border-color: #c69;
+}
+
+#pass-strength-result.good {
+	background-color: #effff4;
+	border-color: #66cc87;
+}
+
+#pass-strength-result.short {
+	background-color: #e3e3e3;
+}
+
+#pass-strength-result.strong {
+	background-color: #59ef86;
+	border-color: #319f52;
+}
+
+
+/* Options */
+
+form.settings fieldset {
+	margin-top: 10px;
+	margin-bottom: 20px;
+}
+
+form.settings legend,
+div.settings h3 {
+	display: block;
+	color: rgb(51, 51, 51);
+	font-size: 1.17em;
+	font-weight: bold;
+	padding: 0 0 1em 0;
+}
+
+div.settings {
+	margin-bottom: 20px;
+}
+
+hr.settings {
+	height: 1px;
+	background-color: rgb(221, 221, 221);
+	border-width: 0;
+	margin-bottom: 20px;
+}
+
+form.settings p,
+div.settings p {
+	float: none;
+	display: block;
+	color: rgb(51, 51, 51);
+	font-size: 0.9em;
+	margin: 0 0 1em 0;
+}
+
+form.settings ul,
+div.settings ul {
+	float: none;
+	display: block;
+	color: rgb(51, 51, 51);
+	font-size: 0.9em;
+	margin: 0 4em 1em 4em;
+}
+
+form.settings ul li,
+div.settings ul li {
+	list-style: disc outside none;
+}
+
+form.settings strong,
+div.settings strong {
+	font-weight: bold;
+}
+
+form.settings em,
+div.settings em {
+	font-style: italic;
+}
+
+form.settings code,
+form.settings kbd {
+	font-family: Consolas, Monaco, Courier, monospace;
+	font-size: 1.2em;
+	background: rgb(234, 234, 234);
+	margin: 0px 1px;
+	padding: 1px 3px;
+	color: rgb(51, 51, 51);
+}
+
+form.settings a {
+	color: rgb(0, 102, 0);
+}
+
+form.settings a:hover {
+	color: rgb(213, 78, 33);
+}
+
+form.settings div {
+	clear: both;
+	padding: 10px;
+	margin-right: 10px;
+}
+
+form.settings div label {
+	float: left;
+	display: block;
+	padding: 3px;
+	width: 200px;
+	color: rgb(34, 34, 34);
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+}
+
+form.settings div div.label {
+	float: left;
+	display: block;
+	padding: 3px;
+	width: 190px;
+	color: rgb(34, 34, 34);
+	text-shadow: rgb(255, 255, 255) 0px 1px 0px;
+}
+
+form.settings div.disabled label,
+form.settings div.disabled div.label {
+	background-image: url('images/input-lock.png');
+	background-repeat: no-repeat;
+	background-position: 100% 0;
+	padding-right: 20px;
+	width: 180px;
+}
+
+form.settings div.disabled div.label {
+	width: 170px;
+}
+
+form.settings div div.inputs {
+	clear: none;
+	padding: 0;
+	margin: 0 0 0 210px;
+}
+
+form.settings div div.inputs label {
+	float: none;
+	clear: none;
+	display: block;
+	padding: 0;
+	margin: 3px 0 6px 0;
+	width: auto;
+	color: rgb(51, 51, 51);
+	text-shadow: none;
+	font-size: 0.85em;
+}
+
+form.settings div.disabled div.inputs label {
+	background-image: none;
+}
+
+form.settings div div.inputs label.radios img {
+	margin: -2px 0 0 0;
+	vertical-align: middle;
+	border: 1px solid rgb(223, 223, 223);
+}
+
+form.settings input.text,
+form.settings textarea {
+	width: 300px;
+	padding: 3px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	border: 1px solid rgb(223, 223, 223);
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	font-size: 1em;
+}
+
+form.settings div.disabled input.text,
+form.settings div.disabled textarea {
+	background-color: rgb(221, 221, 221);
+}
+
+form.settings div input.text.short {
+	width: 100px;
+}
+
+form.settings div input.text.code {
+	font-family: Consolas, Monaco, Courier, monospace;
+	font-size: 1.2em;
+}
+
+form.settings input.readonly,
+form.settings textarea.readonly {
+	background-color: rgb(221, 221, 221);
+}
+
+form.settings div.table {
+	margin: 0 0 0 220px;
+	padding: 3px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	border: 1px solid rgb(223, 223, 223);
+	background-color: rgb(221, 221, 221);
+	width: 400px;
+}
+
+form.settings div.table table {
+	width: 100%;
+	font-family: Consolas, Monaco, Courier, monospace;
+	font-size: 1.2em;
+	border-collapse: collapse;
+}
+
+form.settings div.table table th {
+	border-bottom: 1px dotted rgb(70, 70, 70);
+	padding: 6px;
+}
+
+form.settings div.table table td {
+	padding: 6px;
+}
+
+form.settings div span.after {
+	font-size: 0.85em;
+}
+
+form.settings div p {
+	color: rgb(102, 102, 102);
+	font-style: italic;
+	margin: 0 0 0 3em;
+	padding: 0.3em;
+	border-left: 1px solid rgb(223, 223, 223);
+}
+
+form.settings input.submit {
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	-moz-border-radius: 11px;
+	-khtml-border-radius: 11px;
+	-webkit-border-radius: 11px;
+	border-radius: 11px;
+	border: 1px solid rgb(63, 163, 50);
+	cursor: pointer;
+	font-size: 11px;
+	line-height: 16px;
+	padding: 2px 8px;
+	background: url('images/button-grad.png') 0 0 rgb(33, 117, 155);
+	color: rgb(255, 255, 255);
+	font-weight: bold;
+	text-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0;
+}
+
+form.settings input.submit:hover {
+	border-color: rgb(0, 102, 0);
+}
+
+form.settings input.submit:active {
+	background-position: 0 30px;
+}
+
+form.settings a.cancel {
+	display: block;
+	float: left;
+	font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;
+	-moz-border-radius: 11px;
+	-khtml-border-radius: 11px;
+	-webkit-border-radius: 11px;
+	border-radius: 11px;
+	border: 1px solid rgb(187, 187, 187);
+	cursor: pointer;
+	font-size: 11px;
+	line-height: 16px;
+	padding: 2px 8px;
+	background: url('images/white-grad.png') repeat-x scroll rgb(242, 242, 242);
+	color: rgb(70, 70, 70);
+	cursor: pointer;
+	font-weight: bold;
+	text-shadow: rgba(255, 255, 255, 0.7) 0 -1px 0;
+	margin-right: 6px;
+	text-decoration: none;
+}
+
+form.settings a.cancel:hover {
+	border-color: rgb(153, 51, 51);
+	color: rgb(204, 51, 51);
+}
+
+form.settings a.cancel:active {
+	background: url('images/white-grad-active.png') repeat-x scroll rgb(238, 238, 238);
+}
+
+form.settings div#option-mod-rewrite div.inputs label.radios span {
+	display: inline-block;
+	width: 7em;
+}
+
+form.settings div#option-mod-rewrite div.inputs label.radios code {
+	white-space: nowrap;
+}
+
+form.settings textarea#rewrite-rules {
+	width: 99%;
+}
+
+form.settings select#timezone-string option {
+	margin-left: 1em;
+}
+
+
+/* Footer */
+
+p#bbShowOff {
+	margin: 0 8px 8px 0;
+	padding: 6px 8px;
+	color: rgb(255, 255, 255);
+	background-color: rgb(170, 170, 170);
+	font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	font-size: 0.9em;
+	font-style: italic;
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	float: right;
+}
+
+div#bbFoot {
+	background-color: rgb(70, 70, 70);
+	color: rgb(153, 153, 153);
+	margin-top: -45px;
+	clear: both;
+	position: relative;
+	width: 100%;
+	font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;
+	font-size: 0.9em;
+	font-style: italic;
+}
+
+div#bbFoot p {
+	line-height: 15px;
+	padding: 15px;
+}
+
+div#bbFoot p a {
+	color: rgb(204, 204, 204);
+	text-decoration: none;
+}
+
+p#bbThanks {
+	float: left;
+}
+
+p#bbVersion {
+	text-align: right;
+}
+
+div.clear {
+	clear: both !important;
+	height: 0;
+	line-height: 0;
+	font-size: 0;
+}
+
+
+
+
+
+
+
+
+/* Layout classes */
+/*
+.absleft {
+	position: absolute;
+	left: 0;
+}
+
+.absright {
+	position: absolute;
+	right: 0;
+}
+
+.abstop {
+	position: absolute;
+	top: 0;
+}
+
+.posrel {
+	position: relative;
+}
+
+.alignleft {
+	float: left;
+}
+
+.alignright {
+	float: right;
+}
+*/
+/*
+.alternate, .alt {
+	background: #f1f1f1;
+}
+*/
+/*
+.active {
+	background-color: rgb(200, 250, 200);
+}
+*/
+
+
+
+
+/* List Blocks */
+
+
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-destroy.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-destroy.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-destroy.php	(revision 5057)
@@ -0,0 +1,20 @@
+<?php
+
+require( 'admin.php' );
+
+if ( !bb_current_user_can( 'manage_tags' ) )
+	bb_die( __( 'You are not allowed to manage tags.' ) );
+
+$tag_id = (int) $_POST['id' ];
+
+bb_check_admin_referer( 'destroy-tag_' . $tag_id );
+
+if ( !$old_tag = bb_get_tag( $tag_id ) )
+	bb_die( __( 'Tag not found.' ) );
+
+if ( bb_destroy_tag( $tag_id ) )
+	bb_die( __( 'That tag was successfully destroyed' ) );
+else
+	bb_die( printf( __( "Something odd happened when attempting to destroy that tag.<br />\n<a href=\"%s\">Try Again?</a>" ), wp_get_referer() ) ) ;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-merge.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-merge.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-merge.php	(revision 5057)
@@ -0,0 +1,29 @@
+<?php
+
+require( 'admin.php' );
+
+if ( !bb_current_user_can( 'manage_tags' ) )
+	bb_die( __( 'You are not allowed to manage tags.' ) );
+
+$old_id = (int) $_POST['id' ];
+$tag    =       $_POST['tag'];
+
+bb_check_admin_referer( 'merge-tag_' . $old_id );
+
+if ( ! $tag = bb_get_tag( $tag ) )
+	bb_die( __( 'The destination tag you specified could not be found.' ) );
+
+if ( ! bb_get_tag( $old_id ) )
+	bb_die( __( 'The original tag could not be found.' ) );
+
+if ( $merged = bb_merge_tags( $old_id, $tag->tag_id ) )
+	bb_die(
+		sprintf( __( "Number of topics from which the old tag was removed: %d <br />\n"),  $merged['old_count'] ) .
+		sprintf( __( "Number of topics to which the new tag was added: %d <br />\n"),$merged['diff_count'] ) .
+		sprintf( __( "Number of rows deleted from tags table:%d <br />\n"),$merged['destroyed']['tags'] ) .
+		sprintf( __( '<a href="%s">View Results of Merge</a>'), bb_get_tag_link() )
+	);
+else
+	bb_die( printf( __( "Something odd happened when attempting to merge those tags.<br />\n<a href=\"%s\">Try Again?</a>" ), wp_get_referer() ) );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-rename.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-rename.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tag-rename.php	(revision 5057)
@@ -0,0 +1,23 @@
+<?php
+
+require( 'admin.php' );
+
+if ( !bb_current_user_can( 'manage_tags' ) )
+	bb_die( __( 'You are not allowed to manage tags.' ) );
+
+$tag_id = (int) $_POST['id' ];
+$tag    = stripslashes( $_POST['tag'] );
+
+bb_check_admin_referer( 'rename-tag_' . $tag_id );
+
+if ( !$old_tag = bb_get_tag( $tag_id ) )
+	bb_die( __( 'Tag not found.' ) );
+
+if ( $tag = bb_rename_tag( $tag_id, $tag ) )
+	wp_redirect( bb_get_tag_link() );
+else
+	bb_die( printf( __( 'There already exists a tag by that name or the name is invalid. <a href="%s">Try Again</a>'), wp_get_referer() ) );
+
+exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/themes.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/themes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/themes.php	(revision 5057)
@@ -0,0 +1,159 @@
+<?php
+require_once('admin.php');
+
+$themes = bb_get_themes();
+
+$activetheme = bb_get_option('bb_active_theme');
+if (!$activetheme) {
+	$activetheme = BB_DEFAULT_THEME;
+}
+
+if ( isset($_GET['theme']) ) {
+	if ( !bb_current_user_can( 'manage_themes' ) ) {
+		wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+		exit;
+	}
+	
+	bb_check_admin_referer( 'switch-theme' );
+	do_action( 'bb_deactivate_theme_' . $activetheme );
+	
+	$theme = stripslashes($_GET['theme']);
+	$theme_data = bb_get_theme_data( $theme );
+	if ($theme_data['Name']) {
+		$name = $theme_data['Name'];
+	} else {
+		$name = preg_replace( '/^([a-z0-9_-]+#)/i', '', $theme);
+	}
+	if ($theme == BB_DEFAULT_THEME) {
+		bb_delete_option( 'bb_active_theme' );
+	} else {
+		bb_update_option( 'bb_active_theme', $theme );
+	}
+	do_action( 'bb_activate_theme_' . $theme );
+	wp_redirect( bb_get_uri('bb-admin/themes.php', array('activated' => 1, 'name' => urlencode( $name ) ), BB_URI_CONTEXT_HEADER + BB_URI_CONTEXT_BB_ADMIN ) );
+	exit;
+}
+
+if ( isset($_GET['activated']) )
+	$theme_notice = bb_admin_notice( sprintf( __( '<strong>Theme "%s" activated</strong>' ), esc_attr($_GET['name'])) );
+
+if ( !in_array($activetheme, $themes) ) {
+	if ($activetheme == BB_DEFAULT_THEME) {
+		remove_action( 'bb_admin_notices', $theme_notice );
+		bb_admin_notice( __( '<strong>Default theme is missing.</strong>' ), 'error' );
+	} else {
+		bb_delete_option( 'bb_active_theme' );
+		remove_action( 'bb_admin_notices', $theme_notice );
+		bb_admin_notice( __( '<strong>Theme not found.  Default theme applied.</strong>' ), 'error' );
+	}
+}
+
+function bb_admin_theme_row( $theme, $position ) {
+	$theme_directory = bb_get_theme_directory( $theme );
+	$theme_data = file_exists( $theme_directory . 'style.css' ) ? bb_get_theme_data( $theme ) : false;
+	$screen_shot = file_exists( $theme_directory . 'screenshot.png' ) ? esc_url( bb_get_theme_uri( $theme ) . 'screenshot.png' ) : false;
+	$activation_url = bb_get_uri('bb-admin/themes.php', array('theme' => urlencode($theme)), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$activation_url = esc_url( bb_nonce_url( $activation_url, 'switch-theme' ) );
+
+	if ( 1 === $position || 0 === $position ) {
+		echo '<tr>';
+	}
+?>
+	<td class="position-<?php echo( (int) $position ); ?>">
+		<div class="screen-shot"><?php if ( $screen_shot ) : ?><a href="<?php echo $activation_url; ?>" title="<?php echo esc_attr( sprintf( __( 'Activate "%s"' ), $theme_data['Title'] ) ); ?>"><img alt="<?php echo esc_attr( $theme_data['Title'] ); ?>" src="<?php echo $screen_shot; ?>" /></a><?php endif; ?></div>
+		<div class="description">
+			<h3 class="themes">
+<?php
+	printf(
+		__( '%1$s %2$s by <cite>%3$s</cite>' ),
+		$theme_data['Title'],
+		$theme_data['Version'],
+		$theme_data['Author']
+	);
+?>
+			</h3>
+			
+<?php
+	if ( $theme_data['Porter'] ) {
+?>
+			<p>
+<?php
+	printf(
+		__( 'Ported by <cite>%s</cite>' ),
+		$theme_data['Porter']
+	);
+?>
+			</p>
+<?php
+	}
+?>
+			
+			<?php echo $theme_data['Description']; // Description is autop'ed ?>
+<?php
+	if ( 0 !== $position ) {
+?>
+			<div class="actions">
+				<a href="<?php echo $activation_url; ?>" title="<?php echo esc_attr( sprintf( __( 'Activate "%s"' ), $theme_data['Title'] ) ); ?>"><?php _e( 'Activate' ); ?></a>
+			</div>
+<?php
+	}
+?>
+			<p class="location"><?php printf(__('All of this theme\'s files are located in the "%s" themes directory.'), $theme_data['Location']); ?></p>
+		</div>
+	</td>
+<?php
+
+	if ( 3 === $position || 0 === $position ) {
+		echo '</tr>';
+	}
+}
+
+if ( isset( $bb->safemode ) && $bb->safemode === true ) {
+	bb_admin_notice( __( '<strong>"Safe mode" is on, the default theme will be used instead of the active theme indicated below.</strong>' ), 'error' );
+}
+
+$bb_admin_body_class = ' bb-admin-appearance';
+
+bb_get_admin_header();
+?>
+
+<h2><?php _e('Manage Themes'); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<h3 class="themes"><?php _e('Current Theme'); ?></h3>
+<div>
+<table class="theme-list-active">
+<?php bb_admin_theme_row( $themes[$activetheme], 0 ); unset($themes[$activetheme] ); ?>
+</table>
+</div>
+
+<?php if ( !empty($themes) ) : ?>
+
+<h3 class="themes"><?php _e('Available Themes'); ?></h3>
+<div>
+<table class="theme-list">
+<?php
+$i = 0;
+foreach ( $themes as $theme ) {
+	$position = 1 + ( $i % 3 );
+
+	bb_admin_theme_row( $theme, $position );
+
+	$i++;
+}
+
+switch ( $position ) {
+	case 1:
+		echo '<td class="position-2"></td><td class="position-3"></td></tr>';
+		break;
+	case 2:
+		echo '<td class="position-3"></td></tr>';
+		break;
+	case 3:
+		break;
+}
+?>
+</table>
+</div>
+
+<?php endif; bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tools-recount.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tools-recount.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/tools-recount.php	(revision 5057)
@@ -0,0 +1,121 @@
+<?php
+require_once( 'admin.php' );
+require_once( 'includes/functions.bb-recount.php' );
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+	bb_check_admin_referer( 'do-counts' );
+
+	// Stores messages
+	$messages = array();
+
+	if ( !empty( $_POST['topic-posts'] ) ) {
+		$message = bb_recount_topic_posts();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['topic-voices'] ) ) {
+		$message = bb_recount_topic_voices();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['topic-deleted-posts'] ) ) {
+		$message = bb_recount_topic_deleted_posts();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['forums'] ) ) {
+		$message = bb_recount_forum_topics();
+		$messages[] = $message[1];
+		$message = bb_recount_forum_posts();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['topics-replied'] ) ) {
+		$message = bb_recount_user_topics_replied();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['topic-tag-count'] ) ) {
+		$message = bb_recount_topic_tags();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['tags-tag-count'] ) ) {
+		$message = bb_recount_tag_topics();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['tags-delete-empty'] ) ) {
+		$message = bb_recount_tag_delete_empty();
+		$messages[] = $message[1];
+	}
+
+	if ( !empty( $_POST['clean-favorites'] ) ) {
+		$message = bb_recount_clean_favorites();
+		$messages[] = $message[1];
+	}
+
+	bb_recount_list();
+	foreach ( (array) $recount_list as $item ) {
+		if ( isset($item[2]) && isset($_POST[$item[0]]) && 1 == $_POST[$item[0]] && is_callable($item[2]) ) {
+			$message = call_user_func( $item[2] );
+			if ( is_array( $message ) ) {
+				$messages[] = $message[1];
+			} else {
+				$messages[] = $message;
+			}
+		}
+	}
+
+	wp_cache_flush();
+
+	if ( count( $messages ) ) {
+		$messages = join( '</p>' . "\n" . '<p>', $messages );
+		bb_admin_notice( $messages );
+	}
+}
+
+
+$bb_admin_body_class = ' bb-admin-tools';
+
+bb_get_admin_header();
+?>
+<h2><?php _e('Tools') ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri('bb-admin/tools-recount.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>">
+	<fieldset>
+		<legend><?php _e( 'Re-count' ) ?></legend>
+		<p><?php _e( 'To minimize database queries, bbPress keeps it\'s own count of various items like posts in each topic and topics in each forum. Occasionally these internal counters may become incorrect, you can manually re-count these items using this form.' ) ?></p>
+		<p><?php _e( 'You can also clean out some stale items here, like empty tags.' ) ?></p>
+<?php
+bb_recount_list();
+if ( $recount_list ) {
+?>
+		<div id="option-counts">
+			<div class="label">
+				<?php _e( 'Items to re-count' ); ?>
+			</div>
+			<div class="inputs">
+<?php
+	foreach ( $recount_list as $item ) {
+		echo '<label class="checkboxs"><input type="checkbox" class="checkbox" name="' . esc_attr( $item[0] ) . '" id="' . esc_attr( str_replace( '_', '-', $item[0] ) ) . '" value="1" /> ' . esc_html( $item[1] ) . '</label>' . "\n";
+	}
+?>
+			</div>
+		</div>
+<?php
+} else {
+?>
+		<p><?php _e( 'There are no re-count tools available.' ) ?></p>
+<?php
+}
+?>
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'do-counts' ); ?>
+		<input class="submit" type="submit" name="submit" value="<?php _e('Recount Items') ?>" />
+	</fieldset>
+</form>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-move.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-move.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-move.php	(revision 5057)
@@ -0,0 +1,30 @@
+<?php
+require_once('admin-action.php');
+
+$topic_id = absint( $_POST['topic_id'] );
+$forum_id = absint( $_POST['forum_id'] );
+
+if ( !is_numeric($topic_id) || !is_numeric($forum_id) )
+	bb_die(__('Invalid topic or forum.'));
+
+if ( !bb_current_user_can( 'move_topic', $topic_id, $forum_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	exit;
+}
+
+bb_check_admin_referer( 'move-topic_' . $topic_id );
+
+$topic = get_topic( $topic_id );
+$forum = bb_get_forum( $forum_id );
+
+if ( !$topic || !$forum )
+	bb_die(__('Your topic or forum caused all manner of confusion'));
+
+bb_move_topic( $topic_id, $forum_id );
+
+if ( !$redirect = wp_get_referer() )
+	$redirect = get_topic_link( $topic_id );
+
+bb_safe_redirect( $redirect );
+exit;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-toggle.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-toggle.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topic-toggle.php	(revision 5057)
@@ -0,0 +1,33 @@
+<?php
+require('admin-action.php');
+
+$topic_id = (int) $_GET['id'];
+$topic    =  get_topic ( $topic_id );
+
+if ( !$topic )
+	bb_die(__('There is a problem with that topic, pardner.'));
+
+if ( !bb_current_user_can( 'close_topic', $topic_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	exit;
+}
+
+bb_check_admin_referer( 'close-topic_' . $topic_id );
+
+if ( topic_is_open( $topic_id ) ) {
+	bb_close_topic( $topic_id );
+	$message = 'closed';
+} else {
+	bb_open_topic ( $topic_id );
+	$message = 'opened';
+}
+
+if ( $sendto = wp_get_referer() ) {
+	$sendto = remove_query_arg( 'message', $sendto );
+	$sendto = add_query_arg( 'message', $message, $sendto );
+} else {
+	$sendto = get_topic_link( $topic_id );
+}
+
+bb_safe_redirect( $sendto );
+exit;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topics.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topics.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/topics.php	(revision 5057)
@@ -0,0 +1,267 @@
+<?php
+require_once('admin.php');
+
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+	bb_check_admin_referer( 'topic-bulk' );
+
+	$topic_ids = array_map( 'absint', $_POST['topic'] );
+
+	$affected = 0;
+
+	$action = trim( $_POST['action'] );
+
+	switch ( $action ) {
+	case 'close' :
+		foreach ( $topic_ids as $topic_id ) {
+			$affected += bb_close_topic( $topic_id );
+		}
+		$query_vars = array( 'message' => 'closed', 'count' => $affected );
+		break;
+	case 'open' :
+		foreach ( $topic_ids as $topic_id ) {
+			$affected += bb_open_topic( $topic_id );
+		}
+		$query_vars = array( 'message' => 'opened', 'count' => $affected );
+		break;
+	case 'delete' :
+		foreach ( $topic_ids as $topic_id ) {
+			$affected += (int) (bool) bb_delete_topic( $topic_id, 1 );
+		}
+		$query_vars = array( 'message' => 'deleted', 'count' => $affected );
+		break;
+	case 'undelete' :
+		foreach ( $topic_ids as $topic_id ) {
+			$affected += (int) (bool) bb_delete_topic( $topic_id, 0 );
+		}
+		$query_vars = array( 'message' => 'undeleted', 'count' => $affected );
+		break;
+	default :
+		if ( $action )
+			$query_vars = apply_filters( "bulk_topic__$action", array(), $topic_ids );
+		break;
+	}
+
+	bb_safe_redirect( add_query_arg( $query_vars ) );
+	exit;
+}
+
+if ( !empty( $_GET['message'] ) ) {
+	$message_count = isset( $_GET['count'] ) ? (int) $_GET['count'] : 1;
+
+	switch ( (string) $_GET['message'] ) {
+		case 'undeleted':
+			bb_admin_notice( sprintf( _n( '<strong>Topic undeleted.</strong>', '<strong>%s topics undeleted.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'deleted':
+			bb_admin_notice( sprintf( _n( '<strong>Topic deleted.</strong>', '<strong>%s topics deleted.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'opened':
+			bb_admin_notice( sprintf( _n( '<strong>Topic opened.</strong>', '<strong>%s topics opened.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+		case 'closed':
+			bb_admin_notice( sprintf( _n( '<strong>Topic closed.</strong>', '<strong>%s topics closed.</strong>', $message_count ), bb_number_format_i18n( $message_count ) ) );
+			break;
+	}
+}
+
+$bb_admin_body_class = ' bb-admin-topics';
+
+bb_get_admin_header();
+
+if ( !bb_current_user_can('browse_deleted') )
+	die(__("Now how'd you get here?  And what did you think you'd being doing?")); //This should never happen.
+add_filter( 'topic_link', 'bb_make_link_view_all' );
+add_filter( 'topic_last_post_link', 'bb_make_link_view_all' );
+$topic_query_vars = array( 'topic_status' => 'normal', 'open' => 'open', 'count' => true, 'per_page' => 20 );
+if ( isset($_POST['search']) && $_POST['search'] ) {
+	$topic_query_vars['post_status'] = 'all';
+} elseif ( isset($_GET['search']) && $_GET['search'] ) {
+	$topic_query_vars['post_status'] = 'all';
+}
+$topic_query = new BB_Query_Form( 'topic', $topic_query_vars );
+$topics = $topic_query->results;
+?>
+
+<div class="wrap">
+
+<h2><?php _e( 'Topics' ); ?>
+<?php
+$h2_search = $topic_query->get( 'search' );
+$h2_forum  = $topic_query->get( 'forum_id' );
+$h2_tag    = $topic_query->get( 'tag_id' );
+$h2_author = $topic_query->get( 'topic_author_id' );
+
+$h2_search = $h2_search ? ' ' . sprintf( __('containing &#8220;%s&#8221;'), esc_html( $h2_search ) ) : '';
+$h2_forum  = $h2_forum  ? ' ' . sprintf( __('in &#8220;%s&#8221;')      , get_forum_name( $h2_forum ) ) : '';
+$h2_tag    = $h2_tag    ? ' ' . sprintf( __('with tag &#8220;%s&#8221;'), esc_html( bb_get_tag_name( $h2_tag ) ) ) : '';
+$h2_author = $h2_author ? ' ' . sprintf( __('by %s')                    , esc_html( get_user_name( $h2_author ) ) ) : '';
+
+if ( $h2_search || $h2_forum || $h2_tag || $h2_author ) {
+	echo '<span class="subtitle">';
+	
+	printf( __( '%1$s%2$s%3$s%4$s' ), $h2_search, $h2_forum, $h2_tag, $h2_author );
+	
+	echo '</span>';
+}
+?>
+</h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<?php $topic_query->form( array('tag' => true, 'topic_author' => true, 'topic_status' => true, 'open' => true, 'submit' => __('Filter')) ); ?>
+
+<div class="clear"></div>
+
+<?php if ( !$topics ) : ?>
+
+<p class="no-results"><?php _e('No topics found.'); ?></p>
+
+<?php
+
+else :
+	bb_cache_first_posts( $topics ); bb_cache_last_posts( $topics );
+
+	$bulk_actions = array(
+		'delete' => __( 'Delete' ),
+		'undelete' => __( 'Undelete' ),
+		'open' => __( 'Open' ),
+		'close' => __( 'Close' ),
+	);
+
+	if ( is_numeric( $bulk_action = $topic_query->get( 'topic_status' ) ) )
+		unset( $bulk_actions[ $bulk_action ? 'delete' : 'undelete' ] );
+
+
+	if ( is_numeric( $bulk_action = $topic_query->get( 'open' ) ) )
+		unset( $bulk_actions[ $bulk_action ? 'open' : 'close' ] );
+
+	unset( $bulk_action );
+
+	do_action_ref_array( 'bulk_topic_actions', array( &$bulk_actions, $topic_query ) );
+?>
+
+<form class="table-form bulk-form" method="post" action="">
+<fieldset>
+	<select name="action">
+		<option><?php _e( 'Bulk Actions' ); ?></option>
+<?php	foreach ( $bulk_actions as $value => $label ) : ?>
+
+		<option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
+<?php	endforeach; ?>
+	</select>
+	<input type="submit" value="<?php esc_attr_e( 'Apply' ); ?>" class="button submit-input" />
+	<?php bb_nonce_field( 'topic-bulk' ); ?>
+</fieldset>
+
+<div class="tablenav">
+<?php if ( $topic_query->found_rows ) : ?>
+	<div class="tablenav-pages">
+		<span class="displaying-num"><?php echo $displaying_num = sprintf(
+			__( '%1$s to %2$s of %3$s' ),
+			bb_number_format_i18n( ( $page - 1 ) * $topic_query->get( 'per_page' ) + 1 ),
+			$page * $topic_query->get( 'per_page' ) < $topic_query->found_rows ? bb_number_format_i18n( $page * $topic_query->get( 'per_page' ) ) : '<span class="total-type-count">' . bb_number_format_i18n( $topic_query->found_rows ) . '</span>',
+			'<span class="total-type-count">' . bb_number_format_i18n( $topic_query->found_rows ) . '</span>'
+		); ?></span><span class="displaying-pages">
+<?php
+$_page_link_args = array(
+	'page' => $page,
+	'total' => $topic_query->found_rows,
+	'per_page' => $topic_query->get( 'per_page' ),
+	'mod_rewrite' => false,
+	'prev_text' => __( '&laquo;' ),
+	'next_text' => __( '&raquo;' )
+);
+echo $page_number_links = get_page_number_links( $_page_link_args );
+?></span>
+		<div class="clear"></div>
+	</div>
+<?php endif; ?>
+</div>
+
+<div class="clear"></div>
+
+<table id="topics-list" class="widefat">
+<thead>
+<tr>
+	<th scope="col" class="check-column"><input type="checkbox" /></th>
+	<th scope="col"><?php _e('Topic') ?></th>
+	<th scope="col"><?php _e('Author') ?></th>
+	<th scope="col"><?php _e('Posts') ?></th>
+	<th scope="col"><?php _e('Date') ?></th>
+	<th scope="col"><?php _e('Freshness') ?></th>
+</tr>
+</thead>
+<tfoot>
+<tr>
+	<th scope="col" class="check-column"><input type="checkbox" /></th>
+	<th scope="col"><?php _e('Topic') ?></th>
+	<th scope="col"><?php _e('Author') ?></th>
+	<th scope="col"><?php _e('Posts') ?></th>
+	<th scope="col"><?php _e('Date') ?></th>
+	<th scope="col"><?php _e('Freshness') ?></th>
+</tr>
+</thead>
+
+<tbody>
+<?php foreach ( $topics as $topic ) : $first_post = bb_get_first_post( $topic ); ?>
+<tr id="topic-<?php echo $topic->topic_id; ?>"<?php topic_class(); ?>>
+	<td class="check-column"><input type="checkbox" name="topic[]" value="<?php echo $topic->topic_id; ?>" /></td>
+	<td class="topic">
+		<span class="row-title"><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></span>
+		<div>
+		<span class="row-actions">
+			<a href="<?php topic_link(); ?>"><?php _e( 'View' ); ?></a> |
+			<?php if ( $first_post ) : post_edit_link( $first_post->post_id ); ?> |
+			<?php endif; topic_close_link( array( 'id' => $topic->topic_id, 'before' => '', 'after' => '', 'close_text' => __( 'Close' ), 'open_text' => _x( 'Open', 'action' ) ) ); ?> |
+			<?php topic_delete_link( array( 'id' => $topic->topic_id, 'before' => '', 'after' => '', 'delete_text' => __( 'Delete' ), 'undelete_text' => __( 'Undelete' ) ) ); ?>
+		</span>&nbsp;
+		</div>
+	</td>
+	<td class="author">
+		<a class="author-link" href="<?php user_profile_link( $topic->topic_poster ); ?>">
+			<?php echo bb_get_avatar( $topic->topic_poster, '16' ); ?>
+			<?php topic_author(); ?>
+		</a>
+	</td>
+	<td class="posts num"><?php topic_posts(); ?></td>
+	<td class="date num">
+<?php
+	if ( get_topic_start_time( 'U' ) < ( time() - 86400 ) ) {
+		topic_start_time( 'Y/m/d\<\b\r \/\>H:i:s' );
+	} else {
+		printf( __( '%s ago' ), get_topic_start_time( 'since' ) );
+	}
+?>
+	</td>
+	<td class="freshness num"><a href="<?php topic_last_post_link(); ?>" title="<?php echo esc_attr( sprintf( __( 'Last post by %s' ), get_topic_last_poster() ) ); ?>">
+<?php
+	if ( get_topic_time( 'U' ) < ( time() - 86400 ) ) {
+		topic_time( 'Y/m/d\<\b\r \/\>H:i:s' );
+	} else {
+		printf( __( '%s ago' ), get_topic_time( 'since' ) );
+	}
+?>
+	
+	<?php //topic_time( bb_get_datetime_formatstring_i18n() ); ?>
+	
+	
+	</a></td>
+</tr>
+<?php endforeach; ?>
+</tbody>
+</table>
+</form>
+<?php endif; ?>
+
+<div class="tablenav bottom">
+<?php if ( $topic_query->found_rows ) : ?>
+	<div class="tablenav-pages">
+		<span class="displaying-pages"><?php echo $page_number_links; ?></span>
+		<div class="clear"></div>
+	</div>
+<?php endif; ?>
+</div>
+<div class="clear"></div>
+
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/upgrade.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/upgrade.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/upgrade.php	(revision 5057)
@@ -0,0 +1,210 @@
+<?php
+// Remove these lines if you want to upgrade and are using safe mode
+if ( ini_get('safe_mode') )
+	die("You're running in safe mode which does not allow this upgrade
+	script to set a running time limit.  Depending on the size of your
+	database and on which parts of the script you are running, the script
+	can take quite some time to run (or it could take just a few seconds).
+	To throw caution to the wind and run the script in safe mode anyway,
+	remove the first few lines of code in the <code>bb-admin/upgrade.php</code>
+	file. Backups are always a good idea.");
+// Stop removing lines
+
+// Very old (pre 0.7) installs may need further upgrade utilities.
+// Post to http://lists.bbpress.org/mailman/listinfo/bbdev if needed
+
+require('../bb-load.php');
+require( BB_PATH . 'bb-admin/includes/functions.bb-upgrade.php' );
+
+$step = 'unrequired';
+
+$forced = false;
+if ( isset( $_POST['force'] ) && 1 == $_POST['force'] ) {
+	$forced = true;
+} elseif ( isset( $_GET['force'] ) && 1 == $_GET['force'] ) {
+	$forced = true;
+}
+
+if ( bb_get_option( 'bb_db_version' ) > bb_get_option_from_db( 'bb_db_version' ) || $forced ) {
+	
+	$forced_input = '';
+	if ( $forced ) {
+		$forced_input = '<input type="hidden" name="force" value="1" />';
+	}
+	
+	$step = 'required';
+	
+	if ( strtolower( $_SERVER['REQUEST_METHOD']) == 'post' ) {
+		
+		bb_check_admin_referer( 'bbpress-upgrader' );
+		
+		define('BB_UPGRADING', true);
+		
+		$bbdb->hide_errors();
+		
+		$messages = bb_upgrade_all();
+		
+		$bbdb->show_errors();
+		
+		$upgrade_log = array(__('Beginning upgrade&hellip;'));
+		if (is_array($messages['messages'])) {
+			$upgrade_log = array_merge($upgrade_log, $messages['messages']);
+		}
+		$upgrade_log[] = '>>> ' . __('Done');
+		
+		$error_log = array();
+		if (is_array($messages['errors'])) {
+			$error_log = $messages['errors'];
+		}
+		
+		if ( bb_get_option( 'bb_db_version' ) === bb_get_option_from_db( 'bb_db_version' ) && !count($error_log) ) {
+			$step = 'complete';
+		} else {
+			$step = 'error';
+		}
+		
+		wp_cache_flush();
+	}
+	
+}
+
+bb_install_header( __('bbPress database upgrade'), false, true );
+?>
+		<script type="text/javascript" charset="utf-8">
+			function toggleAdvanced(toggle, target) {
+				var toggleObj = document.getElementById(toggle);
+				var targetObj = document.getElementById(target);
+				if (toggleObj.checked) {
+					targetObj.style.display = 'block';
+				} else {
+					targetObj.style.display = 'none';
+				}
+			}
+		</script>
+<?php
+switch ($step) {
+	case 'unrequired':
+?>
+		<p class="last">
+			<?php printf( __('Nothing to upgrade.  <a href="%s">Get back to work!</a>'), bb_get_uri('bb-admin/', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN) ); ?>
+		</p>
+<?php
+		break;
+	
+	case 'required'
+?>
+		<div class="open">
+			<h2><?php _e('Database upgrade required'); ?></h2>
+			<div>
+				<form action="<?php bb_uri('bb-admin/upgrade.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>" method="post">
+					<p class="error">
+						<?php _e('It looks like your database is out-of-date. You can upgrade it here.'); ?>
+					</p>
+					<fieldset class="buttons">
+						<?php bb_nonce_field( 'bbpress-upgrader' ); ?>
+						<?php echo $forced_input; ?>
+						<label for="upgrade_next" class="forward">
+							<input class="button" id="upgrade_next" type="submit" value="<?php _e( 'Upgrade database' ); ?>" />
+						</label>
+					</fieldset>
+				</form>
+			</div>
+		</div>
+<?php
+		break;
+	
+	case 'complete':
+?>
+		<div class="open">
+			<h2><?php _e('Database upgrade complete'); ?></h2>
+			<div>
+				<form action="<?php bb_uri('bb-admin/', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>" method="get">
+					<p class="message">
+						<?php _e('Your database has been successfully upgraded, enjoy!'); ?>
+					</p>
+					<fieldset>
+						<label class="has-label for-toggle" for="upgrade_log_container_toggle">
+							<span>
+								<?php _e('Show upgrade messages'); ?>
+								<input class="checkbox" type="checkbox" id="upgrade_log_container_toggle" value="1" onclick="toggleAdvanced('upgrade_log_container_toggle', 'upgrade_log_container');" />
+							</span>
+							<div class="clear"></div>
+						</label>
+					</fieldset>
+					<div class="toggle" id="upgrade_log_container" style="display:none;">
+						<fieldset>
+							<label class="has-label for-textarea" for="upgrade_log">
+								<span><?php _e('Upgrade log'); ?></span>
+								<textarea id="upgrade_log" class="short"><?php echo(join("\n", $upgrade_log)); ?></textarea>
+							</label>
+						</fieldset>
+					</div>
+					<fieldset class="buttons">
+						<label for="upgrade_next" class="back">
+							<input class="button" id="upgrade_back" type="button" value="<?php _e( '&laquo; Go back to forums' ); ?>" onclick="location.href='<?php echo esc_js( bb_get_uri() ); ?>'; return false;" />
+						</label>
+						<label for="upgrade_next" class="forward">
+							<input class="button" id="upgrade_next" type="submit" value="<?php _e( 'Go to admin' ); ?>" />
+						</label>
+					</fieldset>
+				</form>
+			</div>
+		</div>
+<?php
+		break;
+	
+	case 'error':
+?>
+		<div class="open">
+			<h2><?php _e('Database upgrade failed'); ?></h2>
+			<div>
+				<form action="<?php bb_uri('bb-admin/upgrade.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>" method="post">
+					<p class="error">
+						<?php _e('The upgrade process seems to have failed. Check the upgrade messages below for more information.<br /><br />Attempting to go to the admin area without resolving the listed errors will return you to this upgrade page.'); ?>
+					</p>
+					<fieldset>
+						<?php bb_nonce_field( 'bbpress-upgrader' ); ?>
+						<?php echo $forced_input; ?>
+						<label class="has-label for-toggle" for="upgrade_log_container_toggle" style="margin-bottom: 1.9em;">
+							<span>
+								<?php _e('Show upgrade messages'); ?>
+								<input class="checkbox" type="checkbox" id="upgrade_log_container_toggle" value="1" onclick="toggleAdvanced('upgrade_log_container_toggle', 'upgrade_log_container');" />
+							</span>
+							<div class="clear"></div>
+						</label>
+					</fieldset>
+					<div class="toggle" id="upgrade_log_container" style="display:none;">
+						<fieldset>
+<?php
+		if (count($error_log)) {
+?>
+							<label class="has-label for-textarea" for="error_log">
+								<span><?php _e('Error log'); ?></span>
+								<textarea id="error_log" class="short"><?php echo(join("\n", $error_log)); ?></textarea>
+							</label>
+<?php
+		}
+?>
+							<label class="has-label for-textarea" for="upgrade_log">
+								<span><?php _e('Upgrade log'); ?></span>
+								<textarea id="upgrade_log" class="short"><?php echo(join("\n", $upgrade_log)); ?></textarea>
+							</label>
+						</fieldset>
+					</div>
+					<fieldset class="buttons">
+						<label for="upgrade_next" class="back">
+							<input class="button" id="upgrade_back" type="button" value="<?php _e( '&laquo; Go back to forums' ); ?>" onclick="location.href='<?php echo esc_js( bb_get_uri() ); ?>'; return false;" />
+						</label>
+						<label for="upgrade_next" class="forward">
+							<input class="button" id="upgrade_next" type="submit" value="<?php _e( 'Try again' ); ?>" />
+						</label>
+					</fieldset>
+				</form>
+			</div>
+		</div>
+<?php
+		break;
+}
+
+bb_install_footer();
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/user-add-new.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/user-add-new.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/user-add-new.php	(revision 5057)
@@ -0,0 +1,49 @@
+<?php
+require_once('admin.php');
+
+$edit_user   = bb_get_user( bb_get_user_id( $_GET['user_id'] ) );
+$user_fields = bb_manage_user_fields( $edit_user );
+
+// Let it rip!
+
+// Header
+$bb_admin_body_class = 'bb-admin-user-manage';
+bb_get_admin_header();
+
+?>
+
+<div class="wrap">
+	<h2><?php _e( 'Add a new user' ); ?></h2>
+
+	<?php do_action( 'bb_admin_notices' ); ?>
+
+	<form class="settings" method="post" action="">
+		<fieldset>
+			<?php
+			foreach ( $user_fields as $field => $args ) {
+				bb_option_form_element( $field, $args );
+			}
+			?>
+			<noscript>
+				<?php _e( 'Disabled (requires JavaScript)' ); ?>
+			</noscript>
+			<script type="text/javascript" charset="utf-8">
+				if (typeof jQuery != 'undefined') {
+					jQuery('#user-login').attr( 'id', 'user_login' );
+					var meter = ('<div id="pass-strength-result">' + pwsL10n.short + '</div>');
+					jQuery('#option-pass-strength-fake-input div.inputs input').before( meter );
+				} else {
+					document.writeln('<?php echo str_replace( "'", "\'", __( 'Disabled.' ) ); ?>')
+				}
+			</script>
+		</fieldset>
+		<fieldset class="submit">
+			<?php bb_nonce_field( 'user-manage' ); ?>
+
+			<input type="hidden" name="action" value="create" />
+			<input class="submit" type="submit" name="submit" value="<?php _e( 'Create user' ); ?>" />
+		</fieldset>
+	</form>
+</div>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/users.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/users.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-admin/users.php	(revision 5057)
@@ -0,0 +1,68 @@
+<?php
+require_once('admin.php');
+
+// Get all errors and print as notice
+if ( isset( $_GET['created'] ) )
+	bb_admin_notice( __( '<strong>User Created.</strong>' ) );
+
+if ( isset( $_GET['updated'] ) )
+	bb_admin_notice( __( '<strong>User Updated.</strong>' ) );
+
+if ( isset( $_GET['action'] ) || isset( $_GET['user_id'] ) && ( 'edit' == $_GET['action'] || 'create' == $_GET['action'] ) ) {
+
+	$edit_user   = bb_get_user( bb_get_user_id( $_GET['user_id'] ) );
+	$user_fields = bb_manage_user_fields( $edit_user );
+
+	$bb_admin_body_class = 'bb-admin-user-manage';
+	bb_get_admin_header(); ?>
+
+	<div class="wrap">
+		<h2><?php _e( 'Edit user' ); ?></h2>
+
+		<?php do_action( 'bb_admin_notices' ); ?>
+
+		<form class="settings" method="post" action="">
+			<fieldset>
+				<?php
+				foreach ( $user_fields as $field => $args ) {
+					bb_option_form_element( $field, $args );
+				}
+				?>
+				<noscript>
+					<?php _e( 'Disabled (requires JavaScript)' ); ?>
+				</noscript>
+				<script type="text/javascript" charset="utf-8">
+					if (typeof jQuery != 'undefined') {
+						jQuery('#user-login').attr( 'id', 'user_login' );
+						var meter = ('<div id="pass-strength-result">' + pwsL10n.short + '</div>');
+						jQuery('#option-pass-strength-fake-input div.inputs input').before( meter );
+					} else {
+						document.writeln('<?php echo str_replace( "'", "\'", __( 'Disabled.' ) ); ?>')
+					}
+				</script>
+			</fieldset>
+			<fieldset class="submit">
+				<?php bb_nonce_field( 'user-manage' ); ?>
+
+				<input type="hidden" name="action" value="update" />
+				<input class="submit" type="submit" name="submit" value="<?php _e( 'Update user' ); ?>" />
+			</fieldset>
+		</form>
+	</div>
+
+<?php } else {
+	// Query the users
+	$bb_user_search = new BB_User_Search( @$_GET['usersearch'], @$_GET['page'], @$_GET['userrole'] );
+
+	$bb_admin_body_class = ' bb-admin-users';
+	bb_get_admin_header(); ?>
+
+	<div class="wrap">
+
+		<?php $bb_user_search->display( true, bb_current_user_can( 'edit_users' ) ); ?>
+
+	</div>
+
+<?php } ?>
+
+<?php bb_get_admin_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-config-sample.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-config-sample.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-config-sample.php	(revision 5057)
@@ -0,0 +1,63 @@
+<?php
+/** 
+ * The base configurations of bbPress.
+ *
+ * This file has the following configurations: MySQL settings, Table Prefix,
+ * Secret Keys and bbPress Language. You can get the MySQL settings from your
+ * web host.
+ *
+ * This file is used by the installer during installation.
+ *
+ * @package bbPress
+ */
+
+// ** MySQL settings - You can get this info from your web host ** //
+/** The name of the database for bbPress */
+define( 'BBDB_NAME', 'bbpress' );
+
+/** MySQL database username */
+define( 'BBDB_USER', 'username' );
+
+/** MySQL database password */
+define( 'BBDB_PASSWORD', 'password' );
+
+/** MySQL hostname */
+define( 'BBDB_HOST', 'localhost' );
+
+/** Database Charset to use in creating database tables. */
+define( 'BBDB_CHARSET', 'utf8' );
+
+/** The Database Collate type. Don't change this if in doubt. */
+define( 'BBDB_COLLATE', '' );
+
+/**#@+
+ * Authentication Unique Keys.
+ *
+ * Change these to different unique phrases!
+ * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/bbpress/ WordPress.org secret-key service}
+ *
+ * @since 1.0
+ */
+define( 'BB_AUTH_KEY', 'put your unique phrase here' );
+define( 'BB_SECURE_AUTH_KEY', 'put your unique phrase here' );
+define( 'BB_LOGGED_IN_KEY', 'put your unique phrase here' );
+define( 'BB_NONCE_KEY', 'put your unique phrase here' );
+/**#@-*/
+
+/**
+ * bbPress Database Table prefix.
+ *
+ * You can have multiple installations in one database if you give each a unique
+ * prefix. Only numbers, letters, and underscores please!
+ */
+$bb_table_prefix = 'bb_';
+
+/**
+ * bbPress Localized Language, defaults to English.
+ *
+ * Change this to localize bbPress. A corresponding MO file for the chosen
+ * language must be installed to a directory called "my-languages" in the root
+ * directory of bbPress. For example, install de.mo to "my-languages" and set
+ * BB_LANG to 'de' to enable German language support.
+ */
+define( 'BB_LANG', '' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-cron.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-cron.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-cron.php	(revision 5057)
@@ -0,0 +1,54 @@
+<?php
+/**
+ * bbPress Cron Implementation for hosts, which do not offer CRON or for which
+ * the user has not setup a CRON job pointing to this file.
+ *
+ * The HTTP request to this file will not slow down the visitor who happens to
+ * visit when the cron job is needed to run.
+ *
+ * @package bbPress
+ */
+
+ignore_user_abort( true );
+
+if ( !empty( $_POST ) || defined( 'DOING_AJAX' ) || defined( 'DOING_CRON' ) ) {
+	die();
+}
+
+/**
+ * Tell bbPress we are doing the CRON task.
+ *
+ * @var bool
+ */
+define( 'DOING_CRON', true );
+
+/** Setup bbPress environment */
+require_once( './bb-load.php' );
+
+if ( false === $crons = _get_cron_array() ) {
+	die();
+}
+
+$keys = array_keys( $crons );
+$local_time = time();
+
+if ( !is_array( $crons ) || ( isset($keys[0]) && $keys[0] > $local_time ) ) {
+	die();
+}
+
+foreach ( $crons as $timestamp => $cronhooks ) {
+	if ( $timestamp > $local_time ) {
+		break;
+	}
+	foreach ( $cronhooks as $hook => $keys ) {
+		foreach ( $keys as $key => $args ) {
+			$schedule = $args['schedule'];
+			if ( $schedule != false ) {
+				$new_args = array( $timestamp, $schedule, $hook, $args['args'] );
+				call_user_func_array( 'wp_reschedule_event' , $new_args );
+			}
+			wp_unschedule_event( $timestamp, $hook, $args['args'] );
+ 			do_action_ref_array( $hook, $args['args'] );
+		}
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-edit.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-edit.php	(revision 5057)
@@ -0,0 +1,68 @@
+<?php
+require('./bb-load.php');
+
+bb_auth('logged_in');
+
+$post_id = (int) $_POST['post_id'];
+
+$bb_post = bb_get_post( $post_id );
+
+if ( !$bb_post ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	die();
+}
+
+if ( !bb_current_user_can( 'edit_post', $post_id ) )
+	bb_die(__('Sorry, post is too old.'));
+
+bb_check_admin_referer( 'edit-post_' . $post_id );
+
+if ( 0 != $bb_post->post_status && 'all' == $_GET['view'] ) // We're trying to edit a deleted post
+	add_filter('bb_is_first_where', 'bb_no_where');
+
+// Check possible anonymous user data
+$post_author = $post_email = $post_url = '';
+
+if ( !bb_get_user( get_post_author_id( $post_id ) ) ) {
+	if ( !$post_author = sanitize_user( trim( $_POST['author'] ) ) )
+		bb_die( __( 'Every post needs an author name!' ) );
+	elseif ( !$post_email = sanitize_email( trim( $_POST['email'] ) ) )
+		bb_die( __( 'Every post needs a valid email address!' ) );
+
+	if ( !empty( $_POST['url'] ) )
+		$post_url = esc_url( trim( $_POST['url'] ) );
+}
+
+// Loop through possible anonymous post data
+foreach( array('post_author', 'post_email', 'post_url') as $field ) {
+	if ( ! empty( $$field ) ) {
+		$post_data[$field] = $$field;
+	}
+}
+
+// Setup topic data
+if ( bb_is_first( $bb_post->post_id ) && bb_current_user_can( 'edit_topic', $bb_post->topic_id ) ) {
+
+	$post_data['topic_title'] = stripslashes( $_POST['topic'] );
+	$post_data['topic_id']    = $bb_post->topic_id;
+
+	bb_insert_topic( $post_data );
+}
+
+// Setup post data
+$post_data['post_text'] = stripslashes( $_POST['post_content'] );
+$post_data['post_id']   = $post_id;
+
+bb_insert_post( $post_data );
+
+if ( $post_id ) {
+	if ( $_REQUEST['view'] === 'all' ) {
+		add_filter( 'get_post_link', 'bb_make_link_view_all' );
+	}
+	$post_link = get_post_link( $post_id );
+	wp_redirect( $post_link );
+} else {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+}
+exit;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/action.subscribe.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/action.subscribe.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/action.subscribe.php	(revision 5057)
@@ -0,0 +1,29 @@
+<?php
+
+if ( !isset( $_GET['doit'] ) || 'bb-subscribe' != $_GET['doit'] ) // sanity check
+	bb_die( __( 'What are you trying to do, exactly?' ) );
+
+if ( !bb_is_subscriptions_active() )
+	bb_die( __( 'You can not subscribe to topics.' ) );
+
+if ( !isset( $_GET['topic_id'] ) )
+	bb_die( __( 'Missing topic ID!' ) );
+
+bb_auth( 'logged_in' );
+
+$topic_id = (int) $_GET['topic_id'];
+
+$topic = get_topic( $topic_id );
+if ( !$topic )
+	bb_die( __( 'Topic not found! What are you subscribing to?' ) );
+
+bb_check_admin_referer( 'toggle-subscribe_' . $topic_id );
+
+// Okay, we should be covered now
+
+if ( in_array( $_GET['and'], array( 'add', 'remove' ) ) )
+	bb_subscription_management( $topic->topic_id, $_GET['and'] );
+
+wp_redirect( get_topic_link( $topic_id, 1 ) );
+
+exit;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-log.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-log.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-log.php	(revision 5057)
@@ -0,0 +1,550 @@
+<?php
+
+
+/**
+ * BackPress logging level constants
+ */
+define('BP_LOG_NONE',    0);
+define('BP_LOG_FAIL',    1);
+define('BP_LOG_ERROR',   2);
+define('BP_LOG_WARNING', 4);
+define('BP_LOG_NOTICE',  8);
+define('BP_LOG_DEBUG',   16);
+
+/**
+ * Combination of all errors (excluding none and debug)
+ */
+define('BP_LOG_ALL', BP_LOG_FAIL + BP_LOG_ERROR + BP_LOG_WARNING + BP_LOG_NOTICE);
+
+
+
+/**
+ * Provides easy and abstracted logging facilities
+ *
+ * @package BackPress
+ */
+class BP_Log
+{
+	/**
+	 * The logging level
+	 *
+	 * @var integer
+	 */
+	var $level = BP_LOG_NONE;
+
+	/**
+	 * The type of logging
+	 *
+	 * @var string
+	 */
+	var $type = 'php';
+	
+	/**
+	 * The types of logging available
+	 *
+	 * @var array
+	 */
+	var $types = array('php', 'file', 'display', 'console');
+
+	/**
+	 * The filename of the file to log messages to when type is "file"
+	 *
+	 * @var string
+	 */
+	var $filename = '';
+
+	/**
+	 * Whether or not the javascript functions are available
+	 *
+	 * @var boolean
+	 **/
+	var $console_javascript_loaded = false;
+
+	/**
+	 * Console log messages which are queued up to be displayed on page load
+	 *
+	 * @var array
+	 **/
+	var $console_javascript_onloads = array();
+
+	/**
+	 * Initialises the logging
+	 *
+	 * @return void
+	 */
+	function BP_log($level = false, $type = false, $filename = false)
+	{
+		$this->set_level($level);
+		$this->set_type($type);
+		$this->set_filename($filename);
+	}
+
+	/**
+	 * Sets the logging level
+	 *
+	 * @return integer|boolean The old level on success or false
+	 * @uses BP_LOG_LEVEL
+	 */
+	function set_level($level)
+	{
+		$old_level = $this->level;
+
+		if (is_integer($level)) {
+			$this->level = $level;
+		} elseif (defined('BP_LOG_LEVEL') && is_integer(BP_LOG_LEVEL)) {
+			$this->level = BP_LOG_LEVEL;
+		} else {
+			return false;
+		}
+
+		return $old_level;
+	}
+
+	/**
+	 * Sets the logging type
+	 *
+	 * @return string|false The old type on success or false
+	 * @uses BP_LOG_TYPE
+	 */
+	function set_type($type)
+	{
+		$old_type = $this->type;
+		$type = strtolower($type);
+
+		if (in_array($type, $this->types)) {
+			$this->type = $type;
+		} elseif (defined('BP_LOG_TYPE') && in_array(BP_LOG_TYPE, $this->types)) {
+			$this->type = BP_LOG_TYPE;
+		} else {
+			return false;
+		}
+
+		return $old_type;
+	}
+
+	/**
+	 * Sets the logging filename
+	 *
+	 * @return string|boolean The old filename on success or false
+	 * @uses BP_LOG_FILENAME
+	 */
+	function set_filename($filename)
+	{
+		$old_filename = $this->filename;
+
+		if (is_string($filename)) {
+			$_filename = $filename;
+		} elseif (defined('BP_LOG_FILENAME') && is_string(BP_LOG_FILENAME)) {
+			$_filename = BP_LOG_FILENAME;
+		} else {
+			return false;
+		}
+
+		if (isset($_filename) && file_exists($_filename) && is_file($_filename) && is_writable($_filename)) {
+			$this->filename = $_filename;
+		} else {
+			return false;
+		}
+
+		return $old_filename;
+	}
+
+	/**
+	 * Sends a message to the log
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function send($message = '', $level = BP_LOG_DEBUG, $type = false, $prefix = false)
+	{
+		// Make sure the level of this message is set to be logged
+		if (($level & $this->level) === 0) {
+			return;
+		}
+
+		// Format the message into an array of lines to be logged
+		$lines = $this->format_message($message, $level, $prefix);
+
+		// Do some validation on the type
+		if ($type && in_array($type, $this->types)) {
+			$_type = $type;
+		} else {
+			$_type = $this->type;
+		}
+
+		// Get a name for the level
+		if ($level) {
+			$_level = $this->get_level_from_integer($level);
+		}
+
+		// Setup strings to prepend to some of the types
+		$prepend = $_level . ': ';
+		if ($prefix) {
+			$prepend .= $prefix . ': ';
+		}
+		$pad = str_repeat(' ', strlen($prepend) - 2) . '| ';
+
+		// Switch over the four types
+		switch ($_type) {
+			case 'php':
+				$php_fail = false;
+
+				// Check that the error_log() function is available
+				if (function_exists('error_log') && is_callable('error_log')) {
+					foreach ($lines as $key => $line) {
+						if ($key === 0) {
+							$_prepend = $prepend;
+						} else {
+							$_prepend = $pad;
+						}
+						if (!error_log($_prepend . $line, 0)) {
+							$php_fail = true;
+							break;
+						}
+					}
+				} else {
+					$php_fail = true;
+				}
+
+				if ($php_fail) {
+					// The PHP error log process failed, path of least resistance is to write to display
+					$this->send($message, $level, 'display', $prefix);
+					return;
+				}
+				break;
+
+			case 'file':
+				$file_fail = false;
+
+				// We've already done the prerequisite checks on the file by now so just write to it
+				if (!$file_handle = fopen($this->filename, 'a')) {
+					$file_fail = true;
+				} else {
+					// Prepare a string to write
+					$_lines = array(
+						'[' . date('c') . ']',
+						'[client ' . $_SERVER['REMOTE_ADDR'] . ']',
+						$prepend,
+						join("\n", $lines)
+					);
+				}
+				if (fwrite($file_handle, join(' ', $_lines) . "\n") === false) {
+					$file_fail = true;
+				}
+				if ($file_handle) {
+					fclose($file_handle);
+				}
+				if ($file_fail) {
+					// Writing to file failed, output to display
+					$this->send($message, $level, 'display', $prefix);
+					return;
+				}
+				break;
+
+			case 'display':
+				$_lines = array();
+				foreach ($lines as $key => $line) {
+					if ($key === 0) {
+						$_lines[] = $prepend . $line;
+					} else {
+						$_lines[] = $pad . $line;
+					}
+				}
+				echo '<div class="bplog_message bplog_level_' . strtolower($_level) . '"><pre>' . join("\n", $_lines) . '</pre></div>' . "\n";
+				break;
+
+			case 'console':
+				$_lines = array();
+				foreach ($lines as $key => $line) {
+					if ($key === 0 && $prefix) {
+						$_lines[] = $prefix . ': ' . $line;
+					} else {
+						$_lines[] = $line;
+					}
+				}
+				
+				$_lines = $ident . $_level . ' ~\n' . str_replace('\'', '\\\'', join('\n', $_lines));
+				
+				if (!$this->console_javascript_loaded) {
+					// Queue it for logging onload
+					$this->console_javascript_onloads[] = array('message' => $_lines, 'level' => $level, 'time' => date('c'));
+				} else {
+					// Log it now
+					echo '<script type="text/javascript" charset="utf-8">bp_log_add(\'' . $this->_esc_js_log( $_lines ) . '\', ' . $this->_esc_js_log( $level ) . ', \'' . $this->_esc_js_log( date('c') ) . '\');</script>' . "\n";
+				}
+				break;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Gets the name of the log level from an integer
+	 *
+	 * @return string The logging level
+	 */
+	function get_level_from_integer($integer)
+	{
+		switch ($integer) {
+			case BP_LOG_NONE:
+				return 'BP_LOG_NONE';
+				break;
+			case BP_LOG_FAIL:
+				return 'BP_LOG_FAIL';
+				break;
+			case BP_LOG_ERROR:
+				return 'BP_LOG_ERROR';
+				break;
+			case BP_LOG_WARNING:
+				return 'BP_LOG_WARNING';
+				break;
+			case BP_LOG_NOTICE:
+				return 'BP_LOG_NOTICE';
+				break;
+			case BP_LOG_DEBUG:
+				return 'BP_LOG_DEBUG';
+				break;
+			default:
+				return 'BP_LOG_UNDEFINED';
+				break;
+		}
+	}
+
+	/**
+	 * Formats a message for output to a log file
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function format_message($message, $level = BP_LOG_DEBUG, $prefix = false, $tabs = 0)
+	{
+		$lines = array();
+		
+		if (is_null($message)) {
+			$lines[] = 'null (' . var_export($message, true) . ')';
+			return $lines;
+		}
+		
+		if (is_bool($message)) {
+			$lines[] = 'bool (' . var_export($message, true) . ')';
+			return $lines;
+		}
+
+		if (is_string($message)) {
+			if ($level === BP_LOG_DEBUG || $message === '') {
+				$lines[] = 'string(' . strlen($message) . ') ("' . $message . '")';
+			} else {
+				$lines[] = $message;
+			}
+			return $lines;
+		}
+
+		if (is_array($message) || is_object($message)) {
+			if (is_array($message)) {
+				$lines[] = 'array(' . count($message) . ') (';
+			} else {
+				$lines[] = 'object(' . get_class($message) . ') (';
+			}
+			$tabs++;
+			foreach ($message as $key => $value) {
+				$array = $this->format_message($value, $level, false, $tabs);
+				if (is_array($array)) {
+					$array[0] = str_repeat('    ', $tabs) . $key . ' => ' . $array[0];
+					$lines = array_merge($lines, $array);
+				} else {
+					$lines[] = str_repeat('    ', $tabs) . $key . ' => ' . $array;
+				}
+			}
+			$tabs--;
+			$lines[] = str_repeat('    ', $tabs) . ')';
+			return $lines;
+		}
+
+		if (is_int($message)) {
+			$lines[] = 'int (' . $message . ')';
+			return $lines;
+		}
+
+		if (is_float($message)) {
+			$lines[] = 'float (' . $message . ')';
+			return $lines;
+		}
+
+		if (is_resource($message)) {
+			$lines[] = 'resource (' . get_resource_type($message) . ')';
+			return $lines;
+		}
+
+		$lines[] = 'unknown (' . $message . ')';
+		return $lines;
+	}
+
+	/**
+	 * Send a debug message
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function debug($message, $prefix = false)
+	{
+		$this->send($message, BP_LOG_DEBUG, false, $prefix);
+	}
+
+	/**
+	 * Send a notice message
+	 *
+	 * If the message is an array, then it sends each index as a separate message
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function notice($message)
+	{
+		if (is_array($message)) {
+			foreach ($message as $value) {
+				$this->send($value, BP_LOG_NOTICE);
+			}
+		} else {
+			$this->send($message, BP_LOG_NOTICE);
+		}
+	}
+
+	/**
+	 * Send a warning message
+	 *
+	 * If the message is an array, then it sends each index as a separate message
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function warning($message)
+	{
+		if (is_array($message)) {
+			foreach ($message as $value) {
+				$this->send($value, BP_LOG_WARNING);
+			}
+		} else {
+			$this->send($message, BP_LOG_WARNING);
+		}
+	}
+
+	/**
+	 * Send an error message
+	 *
+	 * If the message is an array, then it sends each index as a separate message
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function error($message)
+	{
+		if (is_array($message)) {
+			foreach ($message as $value) {
+				$this->send($value, BP_LOG_ERROR);
+			}
+		} else {
+			$this->send($message, BP_LOG_ERROR);
+		}
+	}
+
+	/**
+	 * Send an error message and die
+	 *
+	 * If the message is an array, then it sends each index as a separate message
+	 *
+	 * @return boolean True on success, false on failure
+	 */
+	function fail($message)
+	{
+		if (is_array($message)) {
+			foreach ($message as $value) {
+				$this->send($value, BP_LOG_FAIL);
+			}
+		} else {
+			$this->send($message, BP_LOG_FAIL);
+		}
+
+		die();
+	}
+
+	/**
+	 * Outputs javascript functions for the head of the html document
+	 *
+	 * Must be included in the head of the debug document somehow when using 'console' type.
+	 *
+	 * @return void
+	 **/
+	function console_javascript()
+	{
+		if ($this->type !== 'console') {
+			return;
+		}
+
+		$this->console_javascript_loaded = true;
+?>
+
+	<script type="text/javascript" charset="utf-8">
+		var BP_LOG_NONE    = 0;
+		var BP_LOG_FAIL    = 1;
+		var BP_LOG_ERROR   = 2;
+		var BP_LOG_WARNING = 4;
+		var BP_LOG_NOTICE  = 8;
+		var BP_LOG_DEBUG   = 16;
+		
+		function bp_log_send(message, level, time) {
+			if (window.console) {
+				// Works in later Safari and Firefox with Firebug
+				switch (level) {
+					case BP_LOG_NONE:
+						// This shouldn't happen really
+						break;
+					case BP_LOG_FAIL:
+					case BP_LOG_ERROR:
+						window.console.error("[" + time + "] " + message);
+						break;
+					case BP_LOG_WARNING:
+						window.console.warn("[" + time + "] " + message);
+						break;
+					case BP_LOG_NOTICE:
+						window.console.info("[" + time + "] " + message);
+						break;
+					case BP_LOG_DEBUG:
+						window.console.log("[" + time + "] " + message);
+						break;
+					default:
+						break;
+				}
+			}
+		}
+
+		var bp_log_queue = new Array();
+
+		function bp_log_add(message, level, time) {
+			bp_log_queue.push(new Array(message, level, time));
+		}
+
+		function bp_log_process() {
+			while (item = bp_log_queue.shift()) {
+				bp_log_send(item[0], item[1], item[2]);
+			}
+		}
+
+		function bp_log_onload() {
+<?php
+		foreach ($this->console_javascript_onloads as $onload) {
+			echo "\t\t\t" . 'bp_log_send(\'' . $this->_esc_js_log( $onload['message'] ) . '\', ' . $this->_esc_js_log( $onload['level'] ) . ', \'' . $this->_esc_js_log( $onload['time'] ) . '\');' . "\n";
+		}
+?>
+			bp_log_process();
+		}
+
+		window.onload = bp_log_onload;
+	</script>
+
+<?php
+	}
+
+	function _esc_js_log( $message )
+	{
+		return str_replace(
+			array( '\'', "\n" ),
+			array( '\\\'', '\n' ),
+			$message
+		);
+	}
+	
+} // END class BP_Log
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-roles.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-roles.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-roles.php	(revision 5057)
@@ -0,0 +1,86 @@
+<?php
+
+class BP_Roles {
+	var $role_objects = array();
+	var $role_names = array();
+
+	function BP_Roles() {
+		$this->__construct();
+	}
+
+	function __construct() {
+		do_action_ref_array('init_roles', array(&$this) );
+	}
+
+	function add_role($role, $display_name, $capabilities = '') {
+		if ( isset($this->role_objects[$role]) )
+			return;
+
+		$this->role_objects[$role] = new BP_Role($role, $capabilities, $this);
+		$this->role_names[$role] = $display_name;
+		return $this->role_objects[$role];
+	}
+
+	function remove_role($role) {
+		if ( ! isset($this->role_objects[$role]) )
+			return;
+
+		unset($this->role_objects[$role]);
+		unset($this->role_names[$role]);
+	}
+
+	function add_cap($role, $cap, $grant = true) {
+		if ( isset($this->role_objects[$role]) )
+			$this->role_objects[$role]->add_cap($cap, $grant);
+	}
+
+	function remove_cap($role, $cap) {
+		if ( isset($this->role_objects[$role]) )
+			$this->role_objects[$role]->remove_cap($cap, $grant);
+	}
+
+	function &get_role($role) {
+		if ( isset($this->role_objects[$role]) )
+			return $this->role_objects[$role];
+		else
+			return null;
+	}
+
+	function get_names() {
+		return $this->role_names;
+	}
+
+	function is_role($role) {
+		return isset($this->role_names[$role]);
+	}
+
+	function map_meta_cap( $cap, $user_id ) {
+		$args = array_slice(func_get_args(), 2);
+		return apply_filters( 'map_meta_cap', array( $cap ), $cap, $user_id, $args );
+	}
+}
+
+class BP_Role {
+	var $name;
+	var $capabilities;
+
+	function BP_Role($role, $capabilities) {
+		$this->name = $role;
+		$this->capabilities = $capabilities;
+	}
+
+	function add_cap($cap, $grant = true) {
+		$this->capabilities[$cap] = $grant;
+	}
+
+	function remove_cap($cap) {
+		unset($this->capabilities[$cap]);
+	}
+
+	function has_cap($cap) {
+		$capabilities = apply_filters('role_has_cap', $this->capabilities, $cap, $this->name);
+		$grant = !empty( $capabilities[$cap] );
+		return apply_filters("{$this->name}_has_cap", $grant);
+	}
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-sql-schema-parser.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-sql-schema-parser.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-sql-schema-parser.php	(revision 5057)
@@ -0,0 +1,615 @@
+<?php
+/**
+ * Parses SQL schema statements for comparison to real table structures
+ *
+ * @package BackPress
+ **/
+class BP_SQL_Schema_Parser
+{
+	/**
+	 * Builds a column definition as used in CREATE TABLE statements from
+	 * an array such as those returned by DESCRIBE `foo` statements
+	 */
+	function get_column_definition( $column_data )
+	{
+		if ( !is_array( $column_data ) ) {
+			return $column_data;
+		}
+
+		$null = '';
+		if ( $column_data['Null'] != 'YES' ) {
+			$null = 'NOT NULL';
+		}
+
+		$default = '';
+
+		// Defaults aren't allowed at all on certain column types
+		if ( !in_array(
+			strtolower( $column_data['Type'] ),
+			array( 'tinytext', 'text', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob' )
+		) ) {
+			if ( $column_data['Null'] == 'YES' && $column_data['Default'] === null ) {
+				$default = 'default NULL';
+			} elseif ( preg_match( '@^\d+$@', $column_data['Default'] ) ) {
+				$default = 'default ' . $column_data['Default'];
+			} elseif ( is_string( $column_data['Default'] ) || is_float( $column_data['Default'] ) ) {
+				$default = 'default \'' . $column_data['Default'] . '\'';
+			}
+		}
+
+		$column_definition = '`' . $column_data['Field'] . '` ' . $column_data['Type'] . ' ' . $null . ' ' . $column_data['Extra'] . ' ' . $default;
+		return preg_replace( '@\s+@', ' ', trim( $column_definition ) );
+	}
+
+	/**
+	 * Builds an index definition as used in CREATE TABLE statements from
+	 * an array similar to those returned by SHOW INDEX FROM `foo` statements
+	 */
+	function get_index_definition( $index_data )
+	{
+		if ( !is_array( $index_data ) ) {
+			return $index_data;
+		}
+
+		if ( !count( $index_data ) ) {
+			return $index_data;
+		}
+
+		$_name = '`' . $index_data[0]['Key_name'] . '`';
+
+		if ( $index_data[0]['Index_type'] == 'BTREE' && $index_data[0]['Key_name'] == 'PRIMARY' ) {
+			$_type = 'PRIMARY KEY';
+			$_name = '';
+		} elseif ( $index_data[0]['Index_type'] == 'BTREE' && !$index_data[0]['Non_unique'] ) {
+			$_type = 'UNIQUE KEY';
+		} elseif ( $index_data[0]['Index_type'] == 'FULLTEXT' ) {
+			$_type = 'FULLTEXT KEY';
+		} else {
+			$_type = 'KEY';
+		}
+
+		$_columns = array();
+		foreach ( $index_data as $_index ) {
+			if ( $_index['Sub_part'] ) {
+				$_columns[] = '`' . $_index['Column_name'] . '`(' . $_index['Sub_part'] . ')';
+			} else {
+				$_columns[] = '`' . $_index['Column_name'] . '`';
+			}
+		}
+		$_columns = join( ', ', $_columns );
+
+		$index_definition = $_type . ' ' . $_name . ' (' . $_columns . ')';
+		return preg_replace( '@\s+@', ' ', $index_definition );
+	}
+
+	/**
+	 * Returns a table structure from a raw sql query of the form "CREATE TABLE foo" etc.
+	 * The resulting array contains the original query, the columns as would be returned by DESCRIBE `foo`
+	 * and the indices as would be returned by SHOW INDEX FROM `foo` on a real table
+	 */
+	function describe_table( $query )
+	{
+		// Retrieve the table structure from the query
+		if ( !preg_match( '@^CREATE\s+TABLE(\s+IF\s+NOT\s+EXISTS)?\s+`?([^\s|`]+)`?\s+\((.*)\)\s*([^\)|;]*)\s*;?@ims', $query, $_matches ) ) {
+			return $query;
+		}
+
+		$_if_not_exists = $_matches[1];
+
+		// Tidy up the table name
+		$_table_name = trim( $_matches[2] );
+
+		// Tidy up the table columns/indices
+		$_columns_indices = trim( $_matches[3], " \t\n\r\0\x0B," );
+		// Split by commas not followed by a closing parenthesis ")", using fancy lookaheads
+		$_columns_indices = preg_split( '@,(?!(?:[^\(]+\)))@ms', $_columns_indices );
+		$_columns_indices = array_map( 'trim', $_columns_indices );
+
+		// Tidy the table attributes
+		$_attributes = preg_replace( '@\s+@', ' ', trim( $_matches[4] ) );
+		unset( $_matches );
+
+		// Initialise some temporary arrays
+		$_columns = array();
+		$_indices = array();
+
+		// Loop over the columns/indices
+		foreach ( $_columns_indices as $_column_index ) {
+			if ( preg_match( '@^(PRIMARY\s+KEY|UNIQUE\s+(?:KEY|INDEX)|FULLTEXT\s+(?:KEY|INDEX)|KEY|INDEX)\s+(?:`?(\w+)`?\s+)*\((.+?)\)$@im', $_column_index, $_matches ) ) {
+				// It's an index
+
+				// Tidy the type
+				$_index_type = strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[1] ) ) );
+				$_index_type = str_replace( 'INDEX', 'KEY', $_index_type );
+				// Set the index name
+				$_index_name = ( 'PRIMARY KEY' == $_matches[1] ) ? 'PRIMARY' : $_matches[2];
+				// Split into columns
+				$_index_columns = array_map( 'trim', explode( ',', $_matches[3] ) );
+
+				foreach ( $_index_columns as $_index_columns_index => $_index_column ) {
+					preg_match( '@`?(\w+)`?(?:\s*\(\s*(\d+)\s*\))?@i', $_index_column, $_matches_column );
+
+					$_indices[$_index_name][] = array(
+						'Table'        => $_table_name,
+						'Non_unique'   => ( 'UNIQUE KEY' == $_index_type || 'PRIMARY' == $_index_name ) ? '0' : '1',
+						'Key_name'     => $_index_name,
+						'Seq_in_index' => (string) ( $_index_columns_index + 1 ),
+						'Column_name'  => $_matches_column[1],
+						'Sub_part'     => ( isset( $_matches_column[2] ) && $_matches_column[2] ) ? $_matches_column[2] : null,
+						'Index_type'   => ( 'FULLTEXT KEY' == $_index_type ) ? 'FULLTEXT' : 'BTREE'
+					);
+				}
+				unset( $_index_type, $_index_name, $_index_columns, $_index_columns_index, $_index_column, $_matches_column );
+
+			} elseif ( preg_match( "@^`?(\w+)`?\s+(?:(\w+)(?:\s*\(\s*(\d+)\s*\))?(?:\s+(unsigned)){0,1})(?:\s+(NOT\s+NULL))?(?:\s+(auto_increment))?(?:\s+(default)\s+(?:(NULL|'[^']*'|\d+)))?@im", $_column_index, $_matches ) ) {
+				// It's a column
+
+				// Tidy the NOT NULL
+				$_matches[5] = isset( $_matches[5] ) ? strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[5] ) ) ) : '';
+
+				$_columns[$_matches[1]] = array(
+					'Field'   => $_matches[1],
+					'Type'    => ( is_numeric( $_matches[3] ) ) ? $_matches[2] . '(' . $_matches[3] . ')' . ( ( isset( $_matches[4] ) && strtolower( $_matches[4] ) == 'unsigned' ) ? ' unsigned' : '' ) : $_matches[2],
+					'Null'    => ( 'NOT NULL' == strtoupper( $_matches[5] ) ) ? 'NO' : 'YES',
+					'Default' => ( isset( $_matches[7] ) && 'default' == strtolower( $_matches[7] ) && 'NULL' !== strtoupper( $_matches[8] ) ) ? trim( $_matches[8], "'" ) : null,
+					'Extra'   => ( isset( $_matches[6] ) && 'auto_increment' == strtolower( $_matches[6] ) ) ? 'auto_increment' : ''
+				);
+			}
+		}
+		unset( $_matches, $_columns_indices, $_column_index );
+
+		// Tidy up the original query
+		$_tidy_query = 'CREATE TABLE';
+		if ( $_if_not_exists ) {
+			$_tidy_query .= ' IF NOT EXISTS';
+		}
+		$_tidy_query .= ' `' . $_table_name . '` (' . "\n";
+		foreach ( $_columns as $_column ) {
+			$_tidy_query .= "\t" . BP_SQL_Schema_Parser::get_column_definition( $_column ) . ",\n";
+		}
+		unset( $_column );
+		foreach ( $_indices as $_index ) {
+			$_tidy_query .= "\t" . BP_SQL_Schema_Parser::get_index_definition( $_index ) . ",\n";
+		}
+		$_tidy_query = substr( $_tidy_query, 0, -2 ) . "\n" . ') ' . $_attributes . ';';
+
+		// Add to the query array using the table name as the index
+		$description = array(
+			'query_original' => $query,
+			'query_tidy' => $_tidy_query,
+			'columns' => $_columns,
+			'indices' => $_indices
+		);
+		unset( $_table_name, $_columns, $_indices, $_tidy_query );
+
+		return $description;
+	}
+
+	/**
+	 * Helper function to flatten arrays
+	 */
+	function _flatten_array( $array, $cut_branch = 0, $keep_child_array_keys = true )
+	{
+		if ( !is_array( $array ) ) {
+			return $array;
+		}
+
+		if ( empty( $array ) ) {
+			return null;
+		}
+
+		$temp = array();
+		foreach ( $array as $k => $v ) {
+			if ( $cut_branch && $k == $cut_branch )
+				continue;
+			if ( is_array( $v ) ) {
+				if ( $keep_child_array_keys ) {
+					$temp[$k] = true;
+				}
+				$temp += BP_SQL_Schema_Parser::_flatten_array( $v, $cut_branch, $keep_child_array_keys );
+			} else {
+				$temp[$k] = $v;
+			}
+		}
+		return $temp;
+	}
+
+	/**
+	 * Splits grouped SQL statements into queries within a highly structured array
+	 * Only supports CREATE TABLE, INSERT and UPDATE
+	 */
+	function parse( $sql )
+	{
+		// Only accept strings or arrays
+		if ( is_string( $sql ) ) {
+			// Just pop strings into an array to start with
+			$queries = array( $sql );
+		} elseif ( is_array( $sql ) ) {
+			// Flatten the array
+			$queries = BP_SQL_Schema_Parser::_flatten_array( $sql, 0, false );
+			// Remove empty nodes
+			$queries = array_filter( $queries );
+		} else {
+			return false;
+		}
+
+		// Clean up the queries
+		$_clean_queries = array();
+		foreach ( $queries as $_query ) {
+			// Trim space and semi-colons
+			$_query = trim( $_query, "; \t\n\r\0\x0B" );
+			// If it exists and isn't a number
+			if ( $_query && !is_numeric( $_query ) ) {
+				// Is it more than one query?
+				if ( strpos( ';', $_query ) !== false ) {
+					// Explode by semi-colon
+					foreach ( explode( ';', $_query ) as $_part ) {
+						$_part = trim( $_part );
+						if ( $_part && !is_numeric( $_part ) ) {
+							// Pull out any commented code
+							// Can't properly deal with /*!4321 FOO `bar` */ version specific inclusion, just includes it regardless of version
+							$_part = preg_replace( '@/\*![0-9]*([^\*]*)\*/@', '$1', $_part );
+							$_part = preg_replace( '@/\*[^\*]*\*/@', '', $_part );
+							$_part = preg_replace( '@[\-\-|#].*$@m', '', $_part );
+							$_clean_queries[] = trim( $_part ) . ';';
+						}
+					}
+					unset( $_part );
+				} else {
+					$_clean_queries[] = $_query . ';';
+				}
+			}
+		}
+		unset( $_query );
+		if ( !count( $_clean_queries ) ) {
+			return false;
+		}
+		$queries = $_clean_queries;
+		unset( $_clean_queries );
+
+		$_queries = array();
+		foreach ( $queries as $_query ) {
+			// Only process table creation, inserts and updates, capture the table/database name while we are at it
+			if ( !preg_match( '@^(CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?|INSERT\s+INTO|UPDATE)\s+`?([^\s|`]+)`?@im', $_query, $_matches ) ) {
+				continue;
+			}
+
+			// Tidy up the type so we can switch it
+			$_type = strtoupper( preg_replace( '@\s+@', ' ', trim( $_matches[1] ) ) );
+			$_table_name = trim( $_matches[2] );
+			unset( $_matches );
+
+			switch ( $_type ) {
+				case 'CREATE TABLE':
+				case 'CREATE TABLE IF NOT EXISTS':
+					$_description = BP_SQL_Schema_Parser::describe_table( $_query );
+					if ( is_array( $_description ) ) {
+						$_queries['tables'][$_table_name] = $_description;
+					}
+					break;
+
+				case 'INSERT INTO':
+					// Just add the query as is for now
+					$_queries['insert'][$_table_name][] = $_query;
+					break;
+
+				case 'UPDATE':
+					// Just add the query as is for now
+					$_queries['update'][$_table_name][] = $_query;
+					break;
+			}
+			unset( $_type, $_table_name );
+		}
+		unset( $_query );
+
+		if ( !count( $_queries ) ) {
+			return false;
+		}
+		return $_queries;
+	}
+
+	/**
+	 * Evaluates the difference between a given set of SQL queries and real database structure
+	 */
+	function delta( $db_object, $queries, $ignore = false, $execute = true )
+	{
+		if ( !$db_object || !is_object( $db_object ) || !( is_a( $db_object, 'BPDB' ) || is_a( $db_object, 'BPDB_Multi' ) || is_a( $db_object, 'BPDB_Hyper' ) ) ) {
+			return __( 'Passed variable is not a BackPress database object.' );
+		}
+
+		if ( !$_queries = BP_SQL_Schema_Parser::parse( $queries ) ) {
+			return __( 'No schema available.' );
+		}
+
+		// Set up default elements to ignore
+		$ignore_defaults = array(
+			'tables'  => array(), // Just a list of tablenames, including prefix. Does not affect INSERT and UPDATE queries.
+			'columns' => array(), // Arrays of column names, keyed with the table names, including prefix.
+			'indices' => array()  // Arrays of index names, keyed with the table names, including prefix.
+		);
+
+		// Add the elements to ignore that were passed to the function
+		if ( !$ignore || !is_array( $ignore ) ) {
+			$ignore = $ignore_defaults;
+		} else {
+			if ( isset( $ignore['tables'] ) && is_array( $ignore['tables'] ) ) {
+				$ignore['tables'] = array_merge( $ignore_defaults['tables'], $ignore['tables'] );
+			}
+			if ( isset( $ignore['columns'] ) && is_array( $ignore['columns'] ) ) {
+				$ignore['columns'] = array_merge( $ignore_defaults['columns'], $ignore['columns'] );
+			}
+			if ( isset( $ignore['indices'] ) && is_array( $ignore['indices'] ) ) {
+				$ignore['indices'] = array_merge( $ignore_defaults['indices'], $ignore['indices'] );
+			}
+		}
+
+		// Build an array of $db_object registered tables and their database identifiers
+		$_tables = $db_object->tables;
+		$db_object_tables = array();
+		foreach ( $_tables as $_table_id => $_table_name ) {
+			if ( is_array( $_table_name ) && isset( $db_object->db_servers['dbh_' . $_table_name[0]] ) ) {
+				$db_object_tables[$db_object->$_table_id] = 'dbh_' . $_table_name[0];
+			} else {
+				$db_object_tables[$db_object->$_table_id] = 'dbh_global';
+			}
+		}
+		unset( $_tables, $_table_id, $_table_name );
+
+		$alterations = array();
+
+		// Loop through table queries
+		if ( isset( $_queries['tables'] ) ) {
+			foreach ( $_queries['tables'] as $_new_table_name => $_new_table_data ) {
+				if ( in_array( $_new_table_name, $ignore['tables'] ) ) {
+					continue;
+				}
+
+				// See if the table is custom and registered in $db_object under a custom database
+				if (
+					isset( $db_object_tables[$_new_table_name] ) &&
+					$db_object_tables[$_new_table_name] != 'dbh_global' &&
+					isset( $db_object->db_servers[$db_object_tables[$_new_table_name]]['ds'] )
+				) {
+					// Force the database connection
+					$_dbhname = $db_object->db_servers[$db_object_tables[$_new_table_name]]['ds'];
+					$db_object->_force_dbhname = $_dbhname;
+				} else {
+					$_dbhname = 'dbh_global';
+				}
+
+				// Fetch the existing table column structure from the database
+				$db_object->suppress_errors();
+				if ( !$_existing_table_columns = $db_object->get_results( 'DESCRIBE `' . $_new_table_name . '`;', ARRAY_A ) ) {
+					$db_object->suppress_errors( false );
+					// The table doesn't exist, add it and then continue to the next table
+					$alterations[$_dbhname][$_new_table_name][] = array(
+						'action' => 'create_table',
+						'message' => __( 'Creating table' ),
+						'query' => $_new_table_data['query_tidy']
+					);
+					continue;
+				}
+				$db_object->suppress_errors( false );
+
+				// Add an index to the existing columns array
+				$__existing_table_columns = array();
+				foreach ( $_existing_table_columns as $_existing_table_column ) {
+					// Remove 'Key' from returned column structure
+					unset( $_existing_table_column['Key'] );
+					$__existing_table_columns[$_existing_table_column['Field']] = $_existing_table_column;
+				}
+				$_existing_table_columns = $__existing_table_columns;
+				unset( $__existing_table_columns );
+
+				// Loop over the columns in this table and look for differences
+				foreach ( $_new_table_data['columns'] as $_new_column_name => $_new_column_data ) {
+					if ( isset( $ignore['columns'][$_new_table_name] ) && in_array( $_new_column_name, $ignore['columns'][$_new_table_name] ) ) {
+						continue;
+					}
+
+					if ( !in_array( $_new_column_data, $_existing_table_columns ) ) {
+						// There is a difference
+						if ( !isset( $_existing_table_columns[$_new_column_name] ) ) {
+							// The column doesn't exist, so add it
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'add_column',
+								'message' => sprintf( __( 'Adding column: %s' ), $_new_column_name ),
+								'column' => $_new_column_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD COLUMN ' . BP_SQL_Schema_Parser::get_column_definition( $_new_column_data ) . ';'
+							);
+							continue;
+						}
+
+						// Adjust defaults on columns that allow defaults
+						if (
+							$_new_column_data['Default'] !== $_existing_table_columns[$_new_column_name]['Default'] &&
+							!in_array(
+								strtolower( $_new_column_data['Type'] ),
+								array( 'tinytext', 'text', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob' )
+							)
+						) {
+							// Change the default value for the column
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'set_default',
+								'message' => sprintf( __( 'Setting default on column: %s' ), $_new_column_name ),
+								'column' => $_new_column_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` ALTER COLUMN `' . $_new_column_name . '` SET DEFAULT \'' . $_new_column_data['Default'] . '\';'
+							);
+							// Don't continue, overwrite this if the next conditional is met
+						}
+
+						if (
+							$_new_column_data['Type'] !== $_existing_table_columns[$_new_column_name]['Type'] ||
+							( 'YES' === $_new_column_data['Null'] xor 'YES' === $_existing_table_columns[$_new_column_name]['Null'] ) ||
+							$_new_column_data['Extra'] !== $_existing_table_columns[$_new_column_name]['Extra']
+						) {
+							// Change the structure for the column
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'change_column',
+								'message' => sprintf( __( 'Changing column: %s' ), $_new_column_name ),
+								'column' => $_new_column_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` CHANGE COLUMN `' . $_new_column_name . '` ' . BP_SQL_Schema_Parser::get_column_definition( $_new_column_data ) . ';'
+							);
+						}
+					}
+				}
+				unset( $_existing_table_columns, $_new_column_name, $_new_column_data );
+
+				// Fetch the table index structure from the database
+				if ( !$_existing_table_indices = $db_object->get_results( 'SHOW INDEX FROM `' . $_new_table_name . '`;', ARRAY_A ) ) {
+					continue;
+				}
+
+				// Add an index to the existing columns array and organise by index name
+				$__existing_table_indices = array();
+				foreach ( $_existing_table_indices as $_existing_table_index ) {
+					// Remove unused parts from returned index structure
+					unset(
+						$_existing_table_index['Collation'],
+						$_existing_table_index['Cardinality'],
+						$_existing_table_index['Packed'],
+						$_existing_table_index['Null'],
+						$_existing_table_index['Comment']
+					);
+					$__existing_table_indices[$_existing_table_index['Key_name']][] = $_existing_table_index;
+				}
+				$_existing_table_indices = $__existing_table_indices;
+				unset( $__existing_table_indices );
+
+				// Loop over the indices in this table and look for differences
+				foreach ( $_new_table_data['indices'] as $_new_index_name => $_new_index_data ) {
+					if ( isset( $ignore['indices'][$_new_table_name] ) && in_array( $_new_index_name, $ignore['indices'][$_new_table_name] ) ) {
+						continue;
+					}
+
+					if ( !in_array( $_new_index_data, $_existing_table_indices ) ) {
+						// There is a difference
+						if ( !isset( $_existing_table_indices[$_new_index_name] ) ) {
+							// The index doesn't exist, so add it
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'add_index',
+								'message' => sprintf( __( 'Adding index: %s' ), $_new_index_name ),
+								'index' => $_new_index_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD ' . BP_SQL_Schema_Parser::get_index_definition( $_new_index_data ) . ';'
+							);
+							continue;
+						}
+
+						if ( $_new_index_data !== $_existing_table_indices[$_new_index_name] ) {
+							// The index is incorrect, so drop it and add the new one
+							if ( $_new_index_name == 'PRIMARY' ) {
+								$_drop_index_name = 'PRIMARY KEY';
+							} else {
+								$_drop_index_name = 'INDEX `' . $_new_index_name . '`';
+							}
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'drop_index',
+								'message' => sprintf( __( 'Dropping index: %s' ), $_new_index_name ),
+								'index' => $_new_index_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` DROP ' . $_drop_index_name . ';'
+							);
+							unset( $_drop_index_name );
+							$alterations[$_dbhname][$_new_table_name][] = array(
+								'action' => 'add_index',
+								'message' => sprintf( __( 'Adding index: %s' ), $_new_index_name ),
+								'index' => $_new_index_name,
+								'query' => 'ALTER TABLE `' . $_new_table_name . '` ADD ' . BP_SQL_Schema_Parser::get_index_definition( $_new_index_data ) . ';'
+							);
+						}
+					}
+				}
+				unset( $_new_index_name, $_new_index_data );
+
+				// Go back to the default database connection
+				$db_object->_force_dbhname = false;
+			}
+			unset( $_new_table_name, $_new_table_data, $_dbhname );
+		}
+
+		// Now deal with the sundry INSERT and UPDATE statements (if any)
+		if ( isset( $_queries['insert'] ) && is_array( $_queries['insert'] ) && count( $_queries['insert'] ) ) {
+			foreach ( $_queries['insert'] as $_table_name => $_inserts ) {
+				foreach ( $_inserts as $_insert ) {
+					$alterations['dbh_global'][$_table_name][] = array(
+						'action' => 'insert',
+						'message' => __( 'Inserting data' ),
+						'query' => $_insert
+					);
+				}
+				unset( $_insert );
+			}
+			unset( $_table_name, $_inserts );
+		}
+		if ( isset( $_queries['update'] ) && is_array( $_queries['update'] ) && count( $_queries['update'] ) ) {
+			foreach ( $_queries['update'] as $_table_name => $_updates ) {
+				foreach ( $_updates as $_update ) {
+					$alterations['dbh_global'][$_table_name][] = array(
+						'action' => 'update',
+						'message' => __( 'Updating data' ),
+						'query' => $_update
+					);
+				}
+				unset( $_update );
+			}
+			unset( $_table_name, $_updates );
+		}
+
+		// Initialise an array to hold the output messages
+		$messages = array();
+		$errors = array();
+
+		foreach ( $alterations as $_dbhname => $_tables ) {
+			// Force the database connection (this was already checked to be valid in the previous loop)
+			$db_object->_force_dbhname = $_dbhname;
+
+			// Note the database in the return messages
+			$messages[] = '>>> ' . sprintf( __( 'Modifying database: %s (%s)' ), $db_object->db_servers[$_dbhname]['name'], $db_object->db_servers[$_dbhname]['host'] );
+
+			foreach ( $_tables as $_table_name => $_alterations ) {
+				// Note the table in the return messages
+				$messages[] = '>>>>>> ' . sprintf( __( 'Table: %s' ), $_table_name );
+
+				foreach ( $_alterations as $_alteration ) {
+					// If there is no query, then skip
+					if ( !$_alteration['query'] ) {
+						continue;
+					}
+
+					// Note the action in the return messages
+					$messages[] = '>>>>>>>>> ' . $_alteration['message'];
+
+					if ( !$execute ) {
+						$messages[] = '>>>>>>>>>>>> ' . __( 'Skipped' );
+						continue;
+					}
+
+					// Run the query
+					$_result = $db_object->query( $_alteration['query'] );
+					$_result_error = $db_object->get_error();
+
+					if ( $_result_error ) {
+						// There was an error
+						$_result =& $_result_error;
+						unset( $_result_error );
+						$messages[] = '>>>>>>>>>>>> ' . __( 'SQL ERROR! See the error log for more detail' );
+						$errors[] = __( 'SQL ERROR!' );
+						$errors[] = '>>> ' . sprintf( __( 'Database: %s (%s)' ), $db_object->db_servers[$_dbhname]['name'], $db_object->db_servers[$_dbhname]['host'] );
+						$errors[] = '>>>>>> ' . $_result->error_data['db_query']['query'];
+						$errors[] = '>>>>>> ' . $_result->error_data['db_query']['error'];
+					} else {
+						$messages[] = '>>>>>>>>>>>> ' . __( 'Done' );
+					}
+					unset( $_result );
+				}
+				unset( $_alteration );
+			}
+			unset( $_table_name, $_alterations );
+		}
+		unset( $_dbhname, $_tables );
+
+		// Reset the database connection
+		$db_object->_force_dbhname = false;
+
+		return array( 'messages' => $messages, 'errors' => $errors );
+	}
+} // END class BP_SQL_Schema_Parser
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-user.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-user.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bp-user.php	(revision 5057)
@@ -0,0 +1,398 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * BackPress User class.
+ *
+ * @since 2.0.0
+ * @package BackPress
+ * @subpackage User
+ */
+class BP_User {
+	/**
+	 * User data container.
+	 *
+	 * This will be set as properties of the object.
+	 *
+	 * @since 2.0.0
+	 * @access private
+	 * @var array
+	 */
+	var $data;
+
+	/**
+	 * The user's ID.
+	 *
+	 * @since 2.1.0
+	 * @access public
+	 * @var int
+	 */
+	var $ID = 0;
+
+	/**
+	 * The deprecated user's ID.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 * @deprecated Use BP_User::$ID
+	 * @see BP_User::$ID
+	 * @var int
+	 */
+	var $id = 0;
+
+	/**
+	 * The individual capabilities the user has been given.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 * @var array
+	 */
+	var $caps = array();
+
+	/**
+	 * User metadata option name.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 * @var string
+	 */
+	var $cap_key;
+
+	/**
+	 * The roles the user is part of.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 * @var array
+	 */
+	var $roles = array();
+
+	/**
+	 * All capabilities the user has, including individual and role based.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 * @var array
+	 */
+	var $allcaps = array();
+
+	/**
+	 * First name of the user.
+	 *
+	 * Created to prevent notices.
+	 *
+	 * @since 2.7.0
+	 * @access public
+	 * @var string
+	 */
+	var $first_name = '';
+
+	/**
+	 * Last name of the user.
+	 *
+	 * Created to prevent notices.
+	 *
+	 * @since 2.7.0
+	 * @access public
+	 * @var string
+	 */
+	var $last_name = '';
+
+	/**
+	 * PHP4 Constructor - Sets up the object properties.
+	 *
+	 * Retrieves the userdata and then assigns all of the data keys to direct
+	 * properties of the object. Calls {@link BP_User::_init_caps()} after
+	 * setting up the object's user data properties.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param int|string $id User's ID or username
+	 * @param int $name Optional. User's username
+	 * @return BP_User
+	 */
+	function BP_User( $id, $name = '' ) {
+		global $wp_users_object;
+
+		if ( empty( $id ) && empty( $name ) )
+			return;
+
+		if ( ! is_numeric( $id ) ) {
+			$name = $id;
+			$id = 0;
+		}
+
+		if ( ! empty( $id ) )
+			$this->data = $wp_users_object->get_user( $id );
+		else
+			$this->data = $wp_users_object->get_user( $name, array( 'by' => 'login' ) );
+
+		if ( empty( $this->data->ID ) )
+			return;
+
+		foreach ( get_object_vars( $this->data ) as $key => $value ) {
+			$this->{$key} = $value;
+		}
+
+		$this->id = $this->ID;
+		$this->_init_caps();
+	}
+
+	/**
+	 * Setup capability object properties.
+	 *
+	 * Will set the value for the 'cap_key' property to current database table
+	 * prefix, followed by 'capabilities'. Will then check to see if the
+	 * property matching the 'cap_key' exists and is an array. If so, it will be
+	 * used.
+	 *
+	 * @since 2.1.0
+	 * @access protected
+	 */
+	function _init_caps() {
+		global $wp_users_object;
+		$this->cap_key = $wp_users_object->db->prefix . 'capabilities';
+		$this->caps = &$this->{$this->cap_key};
+		if ( ! is_array( $this->caps ) )
+			$this->caps = array();
+		$this->get_role_caps();
+	}
+
+	/**
+	 * Retrieve all of the role capabilities and merge with individual capabilities.
+	 *
+	 * All of the capabilities of the roles the user belongs to are merged with
+	 * the users individual roles. This also means that the user can be denied
+	 * specific roles that their role might have, but the specific user isn't
+	 * granted permission to.
+	 *
+	 * @since 2.0.0
+	 * @uses $wp_roles
+	 * @access public
+	 */
+	function get_role_caps() {
+		global $wp_roles, $wp_users_object;
+
+		if ( ! isset( $wp_roles ) )
+			$wp_roles = new BP_Roles( $wp_users_object->db );
+
+		//Filter out caps that are not role names and assign to $this->roles
+		if ( is_array( $this->caps ) )
+			$this->roles = array_filter( array_keys( $this->caps ), array( &$wp_roles, 'is_role' ) );
+
+		//Build $allcaps from role caps, overlay user's $caps
+		$this->allcaps = array();
+		foreach ( (array) $this->roles as $role ) {
+			$role = $wp_roles->get_role( $role );
+			$this->allcaps = array_merge( (array) $this->allcaps, (array) $role->capabilities );
+		}
+		$this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps );
+	}
+
+	/**
+	 * Add role to user.
+	 *
+	 * Updates the user's meta data option with capabilities and roles.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string $role Role name.
+	 */
+	function add_role( $role ) {
+		$this->caps[$role] = true;
+		$this->update_user();
+	}
+
+	/**
+	 * Remove role from user.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string $role Role name.
+	 */
+	function remove_role( $role ) {
+		if ( empty( $this->caps[$role] ) || ( count( $this->caps ) <= 1 ) )
+			return;
+		unset( $this->caps[$role] );
+		$this->update_user();
+	}
+
+	/**
+	 * Set the role of the user.
+	 *
+	 * This will remove the previous roles of the user and assign the user the
+	 * new one. You can set the role to an empty string and it will remove all
+	 * of the roles from the user.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string $role Role name.
+	 */
+	function set_role( $role ) {
+		foreach ( (array) $this->roles as $oldrole )
+			unset( $this->caps[$oldrole] );
+		if ( !empty( $role ) ) {
+			$this->caps[$role] = true;
+			$this->roles = array( $role => true );
+		} else {
+			$this->roles = false;
+		}
+		$this->update_user();
+	}
+
+	function update_user() {
+		global $wp_users_object;
+		$wp_users_object->update_meta( array( 'id' => $this->ID, 'meta_key' => $this->cap_key, 'meta_value' => $this->caps ) );
+		$this->get_role_caps();
+		//$this->update_user_level_from_caps(); // WP
+	}
+
+	/**
+	 * Choose the maximum level the user has.
+	 *
+	 * Will compare the level from the $item parameter against the $max
+	 * parameter. If the item is incorrect, then just the $max parameter value
+	 * will be returned.
+	 *
+	 * Used to get the max level based on the capabilities the user has. This
+	 * is also based on roles, so if the user is assigned the Administrator role
+	 * then the capability 'level_10' will exist and the user will get that
+	 * value.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param int $max Max level of user.
+	 * @param string $item Level capability name.
+	 * @return int Max Level.
+	 */
+/*
+	function level_reduction( $max, $item ) {
+		if ( preg_match( '/^level_(10|[0-9])$/i', $item, $matches ) ) {
+			$level = intval( $matches[1] );
+			return max( $max, $level );
+		} else {
+			return $max;
+		}
+	}
+*/
+
+	/**
+	 * Update the maximum user level for the user.
+	 *
+	 * Updates the 'user_level' user metadata (includes prefix that is the
+	 * database table prefix) with the maximum user level. Gets the value from
+	 * the all of the capabilities that the user has.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 */
+/*
+	function update_user_level_from_caps() {
+		global $wp_users_object;
+		$this->user_level = array_reduce( array_keys( $this->allcaps ), array( &$this, 'level_reduction' ), 0 );
+		update_usermeta( $this->ID, $wpdb->prefix.'user_level', $this->user_level );
+	}
+*/
+
+/*
+	function translate_level_to_cap($level) {
+		return 'level_' . $level;
+	}
+*/
+
+	/**
+	 * Add capability and grant or deny access to capability.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string $cap Capability name.
+	 * @param bool $grant Whether to grant capability to user.
+	 */
+	function add_cap( $cap, $grant = true ) {
+		$this->caps[$cap] = $grant;
+		$this->update_user();
+	}
+
+	/**
+	 * Remove capability from user.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string $cap Capability name.
+	 */
+	function remove_cap( $cap ) {
+		if ( empty( $this->caps[$cap] ) ) return;
+		unset( $this->caps[$cap] );
+		$this->update_user();
+	}
+
+	/**
+	 * Remove all of the capabilities of the user.
+	 *
+	 * @since 2.1.0
+	 * @access public
+	 */
+	function remove_all_caps() {
+		global $wp_users_object;
+		$this->caps = array();
+		$wp_users_object->delete_meta( $this->ID, $this->cap_key );
+		$this->get_role_caps();
+	}
+
+	/**
+	 * Whether user has capability or role name.
+	 *
+	 * This is useful for looking up whether the user has a specific role
+	 * assigned to the user. The second optional parameter can also be used to
+	 * check for capabilities against a specfic post.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param string|int $cap Capability or role name to search.
+	 * @param int $post_id Optional. Post ID to check capability against specific post.
+	 * @return bool True, if user has capability; false, if user does not have capability.
+	 */
+	function has_cap( $cap ) {
+		global $wp_roles;
+
+		if ( is_numeric( $cap ) )
+			$cap = $this->translate_level_to_cap( $cap );
+
+		$args = array_slice( func_get_args(), 1 );
+		$args = array_merge( array( $cap, $this->ID ), $args );
+		$caps = call_user_func_array( array(&$wp_roles, 'map_meta_cap'), $args );
+		// Must have ALL requested caps
+		$capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args );
+		foreach ( (array) $caps as $cap ) {
+			//echo "Checking cap $cap<br />";
+			if ( empty( $capabilities[$cap] ) || !$capabilities[$cap] )
+				return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Convert numeric level to level capability name.
+	 *
+	 * Prepends 'level_' to level number.
+	 *
+	 * @since 2.0.0
+	 * @access public
+	 *
+	 * @param int $level Level number, 1 to 10.
+	 * @return string
+	 */
+	function translate_level_to_cap( $level ) {
+		return 'level_' . $level;
+	}
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb-multi.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb-multi.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb-multi.php	(revision 5057)
@@ -0,0 +1,196 @@
+<?php
+//  backPress Multi DB Class
+
+//  ORIGINAL CODE FROM:
+//  Justin Vincent (justin@visunet.ie)
+//	http://php.justinvincent.com
+
+require( BACKPRESS_PATH . 'class.bpdb.php' );
+
+class BPDB_Multi extends BPDB {
+	/**
+	 * Associative array (dbhname => dbh) for established mysql connections
+	 * @var array
+	 */
+        var $dbhs = array();
+
+	var $_force_dbhname = false;
+	var $last_table = '';
+	var $db_tables = array();
+	var $db_servers = array();
+
+	// function BPDB_Multi() {} // Not used - rely on PHP4 constructor from BPDB to call BPDB_Multi::__construct
+
+	function __construct() {
+		$args = func_get_args();
+		$args = call_user_func_array( array(&$this, '_init'), $args );
+
+		if ( $args['host'] ) {
+			$this->db_servers['dbh_global'] = $args;
+			$this->db_connect( '/* */' );
+		}
+	}
+
+	/**
+	 * Figure out which database server should handle the query, and connect to it.
+	 * @param string query
+	 * @return resource mysql database connection
+	 */
+	function &db_connect( $query = '' ) {
+		$false = false;
+		if ( empty( $query ) )
+			return $false;
+		
+		$this->last_table = $table = $this->get_table_from_query( $query );
+		
+		// We can attempt to force the connection identifier in use
+		if ( $this->_force_dbhname && isset($this->db_servers[$this->_force_dbhname]) )
+			$dbhname = $this->_force_dbhname;
+		
+		if ( !isset($dbhname) ) {
+			if ( isset( $this->db_tables[$table] ) )
+				$dbhname = $this->db_tables[$table];
+			else
+				$dbhname = 'dbh_global';
+		}
+
+		if ( !isset($this->db_servers[$dbhname]) )
+			return $false;
+
+		if ( isset($this->dbhs[$dbhname]) && is_resource($this->dbhs[$dbhname]) ) // We're already connected!
+			return $this->dbhs[$dbhname];
+		
+		$success = $this->db_connect_host( $this->db_servers[$dbhname] );
+
+		if ( $success && is_resource($this->dbh) ) {
+			$this->dbhs[$dbhname] =& $this->dbh;
+		} else {
+			unset($this->dbhs[$dbhname]);
+			unset($this->dbh);
+			return $false;
+		}
+
+		return $this->dbhs[$dbhname];
+	}
+
+	/**
+	 * Sets the prefix of the database tables
+	 * @param string prefix
+	 * @param false|array tables (optional: false)
+	 *	table identifiers are array keys
+	 *	array values
+	 *		empty: set prefix: array( 'posts' => false, 'users' => false, ... )
+	 *		string: set to that array value: array( 'posts' => 'my_posts', 'users' => 'my_users' )
+	 *		array: array[0] is DB identifier, array[1] is table name: array( 'posts' => array( 'global', 'my_posts' ), 'users' => array( 'users', 'my_users' ) )
+	 *	OR array values (with numeric keys): array( 'posts', 'users', ... )
+	 *
+	 * @return string the previous prefix (mostly only meaningful if all $table parameter was false)
+	 */
+	function set_prefix( $prefix, $tables = false ) {
+		$old_prefix = parent::set_prefix( $prefix, $tables );
+		if ( !$old_prefix || is_wp_error($old_prefix) ) {
+			return $old_prefix;
+		}
+
+		if ( $tables && is_array($tables) ) {
+			$_tables = $tables;
+		} else {
+			$_tables = $this->tables;
+		}
+		
+		foreach ( $_tables as $key => $value ) {
+			// array( 'posts' => array( 'global', 'my_posts' ), 'users' => array( 'users', 'my_users' ) )
+			if ( is_array($value) && isset($this->db_servers['dbh_' . $value[0]]) ) {
+				$this->add_db_table( $value[0], $value[1] );
+				$this->$key = $value[1];
+			}
+		}
+
+		return $old_prefix;
+	}
+
+	/**
+	 * Find the first table name referenced in a query
+	 * @param string query
+	 * @return string table
+	 */
+	function get_table_from_query ( $q ) {
+		// Remove characters that can legally trail the table name
+		rtrim($q, ';/-#');
+
+		// Quickly match most common queries
+		if ( preg_match('/^\s*(?:'
+				. 'SELECT.*?\s+FROM'
+				. '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?'
+				. '|REPLACE(?:\s+INTO)?'
+				. '|UPDATE(?:\s+IGNORE)?'
+				. '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?'
+				. ')\s+`?(\w+)`?/is', $q, $maybe) )
+			return $maybe[1];
+
+		// Refer to the previous query
+		if ( preg_match('/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q) )
+			return $this->last_table;
+
+		// Big pattern for the rest of the table-related queries in MySQL 5.0
+		if ( preg_match('/^\s*(?:'
+				. '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
+				. '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
+				. '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
+				. '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
+				. '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
+				. '|DESCRIBE|DESC|EXPLAIN|HANDLER'
+				. '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
+				. '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE'
+				. '|TRUNCATE(?:\s+TABLE)?'
+				. '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
+				. '|ALTER(?:\s+IGNORE)?'
+				. '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
+				. '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
+				. '|DROP\s+INDEX.*\s+ON'
+				. '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
+				. '|(?:GRANT|REVOKE).*ON\s+TABLE'
+				. '|SHOW\s+(?:.*FROM|.*TABLE)'
+				. ')\s+`?(\w+)`?/is', $q, $maybe) )
+			return $maybe[1];
+
+		// All unmatched queries automatically fall to the global master
+		return '';
+	}
+
+	/**
+	 * Add a database server's information.  Does not automatically connect.
+	 * @param string $ds Dataset: the name of the dataset.
+	 * @param array $args
+	 *	name => string DB name (required)
+	 *	user => string DB user (optional: false)
+	 *	password => string DB user password (optional: false)
+	 *	host => string DB hostname (optional: 'localhost')
+	 *	charset => string DB default charset.  Used in a SET NAMES query. (optional)
+	 *	collate => string DB default collation.  If charset supplied, optionally added to the SET NAMES query (optional)
+	 */
+	function add_db_server( $ds, $args = null ) {
+		$defaults = array(
+			'user' => false,
+			'password' => false,
+			'name' => false,
+			'host' => 'localhost',
+			'charset' => false,
+			'collate' => false
+		);
+
+		$args = wp_parse_args( $args, $defaults );
+		$args['ds'] = 'dbh_' . $ds;
+
+		$this->db_servers['dbh_' . $ds] = $args;
+	}
+
+	/**
+	 * Maps a table to a dataset.
+	 * @param string $ds Dataset: the name of the dataset.
+	 * @param string $table
+	 */
+	function add_db_table( $ds, $table ) {
+		$this->db_tables[$table] = 'dbh_' . $ds;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.bpdb.php	(revision 5057)
@@ -0,0 +1,1164 @@
+<?php
+//  backPress DB Class
+
+//  ORIGINAL CODE FROM:
+//  Justin Vincent (justin@visunet.ie)
+//	http://php.justinvincent.com
+
+define( 'EZSQL_VERSION', 'BP1.25' );
+define( 'OBJECT', 'OBJECT', true );
+define( 'OBJECT_K', 'OBJECT_K', false );
+define( 'ARRAY_A', 'ARRAY_A', false );
+define( 'ARRAY_K', 'ARRAY_K', false );
+define( 'ARRAY_N', 'ARRAY_N', false );
+
+if ( !defined( 'SAVEQUERIES' ) ) {
+	define( 'SAVEQUERIES', false );
+}
+
+if ( !defined( 'BPDB__ERROR_STRING' ) ) {
+	define( 'BPDB__ERROR_STRING', 'DB Error: %s, %s: %s' );
+}
+if ( !defined( 'BPDB__ERROR_HTML' ) ) {
+	define( 'BPDB__ERROR_HTML', '<div class="error"><p><strong>DB Error in %3$s:</strong> %1$s</p><pre>%2$s</pre></div>' );
+}
+if ( !defined( 'BPDB__CONNECT_ERROR_MESSAGE' ) ) {
+	define( 'BPDB__CONNECT_ERROR_MESSAGE', 'DB Error: cannot connect' );
+}
+if ( !defined( 'BPDB__SELECT_ERROR_MESSAGE' ) ) {
+	define( 'BPDB__SELECT_ERROR_MESSAGE', 'DB Error: cannot select' );
+}
+if ( !defined( 'BPDB__DB_VERSION_ERROR' ) ) {
+	define( 'BPDB__DB_VERSION_ERROR', 'DB Requires MySQL version 4.0 or higher' );
+}
+if ( !defined( 'BPDB__PHP_EXTENSION_MISSING' ) ) {
+	define( 'BPDB__PHP_EXTENSION_MISSING', 'DB Requires The MySQL PHP extension' );
+}
+
+class BPDB
+{
+	/**
+	 * Whether to show SQL/DB errors
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var bool
+	 */
+	var $show_errors = false;
+
+	/**
+	 * Whether to suppress errors during the DB bootstrapping.
+	 *
+	 * @access private
+	 * @since 1.0
+	 * @var bool
+	 */
+	var $suppress_errors = false;
+
+	/**
+	 * The last error during query.
+	 *
+	 * @since 1.0
+	 * @var string
+	 */
+	var $last_error = '';
+
+	/**
+	 * Amount of queries made
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var int
+	 */
+	var $num_queries = 0;
+
+	/**
+	 * The last query made
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var string
+	 */
+	var $last_query = null;
+
+	/**
+	 * Saved info on the table column
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var array
+	 */
+	var $col_info = array();
+	
+	/**
+	 * Saved queries that were executed
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var array
+	 */
+	var $queries = array();
+
+	/**
+	 * Whether to use the query log
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var bool
+	 */
+	var $save_queries = false;
+
+	/**
+	 * Table prefix
+	 *
+	 * You can set this to have multiple installations
+	 * in a single database. The second reason is for possible
+	 * security precautions.
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var string
+	 */
+	var $prefix = '';
+	
+	/**
+	 * Whether the database queries are ready to start executing.
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var bool
+	 */
+	var $ready = false;
+
+	/**
+	 * The currently connected MySQL connection resource.
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var bool|resource
+	 */
+	var $dbh = false;
+
+	/**
+	 * List of tables
+	 *
+	 * @since 1.0
+	 * @access private
+	 * @var array
+	 */
+	var $tables = array();
+
+	/**
+	 * Whether to use mysql_real_escape_string
+	 *
+	 * @since 1.0
+	 * @access public
+	 * @var bool
+	 */
+	var $real_escape = false;
+
+	/**
+	 * PHP4 style constructor
+	 *
+	 * @since 1.0
+	 *
+	 * @return unknown Returns the result of bpdb::__construct()
+	 */
+	function BPDB()
+	{
+		$args = func_get_args();
+		register_shutdown_function( array( &$this, '__destruct' ) );
+		return call_user_func_array( array( &$this, '__construct' ), $args );
+	}
+
+	/**
+	 * PHP5 style constructor
+	 *
+	 * Grabs the arguments, calls bpdb::_init() and then connects to the database
+	 *
+	 * @since 1.0
+	 *
+	 * @return void
+	 */
+	function __construct()
+	{
+		$args = func_get_args();
+		$args = call_user_func_array( array( &$this, '_init' ), $args );
+
+		$this->db_connect_host( $args );
+	}
+
+	/**
+	 * Initialises the class variables based on provided arguments
+	 *
+	 * @since 1.0
+	 *
+	 * @param array $args The provided connection settings
+	 * @return array The current connection settings after processing by init
+	 */
+	function _init( $args )
+	{
+		if ( !extension_loaded( 'mysql' ) ) {
+			$this->show_errors();
+			$this->bail( BPDB__PHP_EXTENSION_MISSING );
+			return;
+		}
+
+		if ( 4 == func_num_args() ) {
+			$args = array(
+				'user' => $args,
+				'password' => func_get_arg( 1 ),
+				'name' => func_get_arg( 2 ),
+				'host' => func_get_arg( 3 )
+			);
+		}
+
+		$defaults = array(
+			'user' => false,
+			'password' => false,
+			'name' => false,
+			'host' => 'localhost',
+			'charset' => false,
+			'collate' => false,
+			'errors' => false
+		);
+
+		$args = wp_parse_args( $args, $defaults );
+
+		switch ( $args['errors'] ) {
+			case 'show' :
+				$this->show_errors( true );
+				break;
+			case 'suppress' :
+				$this->suppress_errors( true );
+				break;
+		}
+
+		return $args;
+	}
+
+	/**
+	 * PHP5 style destructor, registered as shutdown function in PHP4
+	 *
+	 * @since 1.0
+	 *
+	 * @return bool Always returns true
+	 */
+	function __destruct()
+	{
+		return true;
+	}
+
+	/**
+	 * Figure out which database server should handle the query, and connect to it.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string query
+	 * @return resource mysql database connection
+	 */
+	function &db_connect( $query = '' )
+	{
+		$false = false;
+		if ( empty( $query ) ) {
+			return $false;
+		}
+		return $this->dbh;
+	}
+
+	/**
+	 * Connects to the database server and selects a database
+	 *
+	 * @since 1.0
+	 *
+	 * @param array args
+	 *	name => string DB name (required)
+	 *	user => string DB user (optional: false)
+	 *	password => string DB user password (optional: false)
+	 *	host => string DB hostname (optional: 'localhost')
+	 *	charset => string DB default charset.  Used in a SET NAMES query. (optional)
+	 *	collate => string DB default collation.  If charset supplied, optionally added to the SET NAMES query (optional)
+	 * @return void|bool void if cannot connect, false if cannot select, true if success
+	 */
+	function db_connect_host( $args )
+	{
+		extract( $args, EXTR_SKIP );
+
+		unset( $this->dbh ); // De-reference before re-assigning
+		$this->dbh = @mysql_connect( $host, $user, $password, true );
+
+		if ( !$this->dbh ) {
+			if ( !$this->suppress_errors ) {
+				$this->show_errors();
+			}
+			$this->bail( BPDB__CONNECT_ERROR_MESSAGE );
+			return;
+		}
+
+		$this->ready = true;
+
+		if ( $this->has_cap( 'collation' ) ) {
+			if ( !empty( $charset ) ) {
+				if ( function_exists( 'mysql_set_charset' ) ) {
+					mysql_set_charset( $charset, $this->dbh );
+					$this->real_escape = true;
+				} else {
+					$collation_query = "SET NAMES '{$charset}'";
+					if ( !empty( $collate ) ) {
+						$collation_query .= " COLLATE '{$collate}'";
+					}
+					$this->query( $collation_query, true );
+				}
+			}
+		}
+
+		return $this->select( $name, $this->dbh );
+	}
+
+	/**
+	 * Sets the table prefix for the WordPress tables.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string prefix
+	 * @param false|array tables (optional: false)
+	 *	table identifiers are array keys
+	 *	array values
+	 *		empty: set prefix: array( 'posts' => false, 'users' => false, ... )
+	 *		string: set to that array value: array( 'posts' => 'my_posts', 'users' => 'my_users' )
+	 *	OR array values (with numeric keys): array( 'posts', 'users', ... )
+	 *
+	 * @return string the previous prefix (mostly only meaningful if all $table parameter was false)
+	 */
+	function set_prefix( $prefix, $tables = false )
+	{
+		if ( !$prefix ) {
+			return false;
+		}
+		if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) {
+			return new WP_Error( 'invalid_db_prefix', 'Invalid database prefix' ); // No gettext here
+		}
+
+		$old_prefix = $this->prefix;
+
+		if ( $tables && is_array( $tables ) ) {
+			$_tables =& $tables;
+		} else {
+			$_tables =& $this->tables;
+			$this->prefix = $prefix;
+		}
+
+		foreach ( $_tables as $key => $value ) {
+			if ( is_numeric( $key ) ) { // array( 'posts', 'users', ... )
+				$this->$value = $prefix . $value;
+			} elseif ( !$value ) {
+				$this->$key = $prefix . $key; // array( 'posts' => false, 'users' => false, ... )
+			} elseif ( is_string( $value ) ) { // array( 'posts' => 'my_posts', 'users' => 'my_users' )
+				$this->$key = $value;
+			}
+		}
+
+		return $old_prefix;
+	}
+
+	/**
+	 * Selects a database using the current database connection.
+	 *
+	 * The database name will be changed based on the current database
+	 * connection. On failure, the execution will bail and display an DB error.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $db MySQL database name
+	 * @return bool True on success, false on failure.
+	 */
+	function select( $db, &$dbh )
+	{
+		if ( !@mysql_select_db( $db, $dbh ) ) {
+			$this->ready = false;
+			$this->show_errors();
+			$this->bail( BPDB__SELECT_ERROR_MESSAGE );
+			return false;
+		}
+		return true;
+	}
+
+	function _weak_escape( $string )
+	{
+		return addslashes( $string );
+	}
+
+	function _real_escape( $string )
+	{
+		if ( $this->dbh && $this->real_escape ) {
+			return mysql_real_escape_string( $string, $this->dbh );
+		} else {
+			return addslashes( $string );
+		}
+	}
+
+	function _escape( $data )
+	{
+		if ( is_array( $data ) ) {
+			foreach ( (array) $data as $k => $v ) {
+				if ( is_array( $v ) ) {
+					$data[$k] = $this->_escape( $v );
+				} else {
+					$data[$k] = $this->_real_escape( $v );
+				}
+			}
+		} else {
+			$data = $this->_real_escape( $data );
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Escapes content for insertion into the database using addslashes(), for security
+	 *
+	 * @since 1.0
+	 *
+	 * @param string|array $data
+	 * @return string query safe string
+	 */
+	function escape( $data )
+	{
+		if ( is_array( $data ) ) {
+			foreach ( (array) $data as $k => $v ) {
+				if ( is_array( $v ) ) {
+					$data[$k] = $this->escape( $v );
+				} else {
+					$data[$k] = $this->_weak_escape( $v );
+				}
+			}
+		} else {
+			$data = $this->_weak_escape( $data );
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Escapes content by reference for insertion into the database, for security
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $s
+	 */
+	function escape_by_ref( &$string )
+	{
+		$string = $this->_real_escape( $string );
+	}
+
+	/**
+	 * Escapes array recursively for insertion into the database, for security
+	 * @param array $array
+	 */
+	function escape_deep( $array )
+	{
+		return $this->_escape( $array );
+	}
+
+	/**
+	 * Prepares a SQL query for safe execution.  Uses sprintf()-like syntax.
+	 *
+	 * This function only supports a small subset of the sprintf syntax; it only supports %d (decimal number), %s (string).
+	 * Does not support sign, padding, alignment, width or precision specifiers.
+	 * Does not support argument numbering/swapping.
+	 *
+	 * May be called like {@link http://php.net/sprintf sprintf()} or like {@link http://php.net/vsprintf vsprintf()}.
+	 *
+	 * Both %d and %s should be left unquoted in the query string.
+	 *
+	 * <code>
+	 * wpdb::prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", "foo", 1337 )
+	 * </code>
+	 *
+	 * @link http://php.net/sprintf Description of syntax.
+	 * @since 1.0
+	 *
+	 * @param string $query Query statement with sprintf()-like placeholders
+	 * @param array|mixed $args The array of variables to substitute into the query's placeholders if being called like {@link http://php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if being called like {@link http://php.net/sprintf sprintf()}.
+	 * @param mixed $args,... further variables to substitute into the query's placeholders if being called like {@link http://php.net/sprintf sprintf()}.
+	 * @return null|string Sanitized query string
+	 */
+	function prepare( $query = null ) // ( $query, *$args )
+	{
+		if ( is_null( $query ) ) {
+			return;
+		}
+		$args = func_get_args();
+		array_shift( $args );
+		// If args were passed as an array (as in vsprintf), move them up
+		if ( isset( $args[0] ) && is_array( $args[0] ) ) {
+			$args = $args[0];
+		}
+		$query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it
+		$query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting
+		$query = str_replace( '%s', "'%s'", $query ); // quote the strings
+		array_walk( $args, array( &$this, 'escape_by_ref' ) );
+		return @vsprintf( $query, $args );
+	}
+
+	/**
+	 * Get SQL/DB error
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $str Error string
+	 */
+	function get_error( $str = '' )
+	{
+		if ( empty( $str ) ) {
+			if ( $this->last_error ) {
+				$str = $this->last_error;
+			} else {
+				return false;
+			}
+		}
+
+		$caller = $this->get_caller();
+		$error_str = sprintf( BPDB__ERROR_STRING, $str, $this->last_query, $caller );
+
+		if ( class_exists( 'WP_Error' ) ) {
+			return new WP_Error( 'db_query', $error_str, array( 'query' => $this->last_query, 'error' => $str, 'caller' => $caller ) );
+		} else {
+			return array( 'query' => $this->last_query, 'error' => $str, 'caller' => $caller, 'error_str' => $error_str );
+		}
+	}
+
+	/**
+	 * Print SQL/DB error.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $str The error to display
+	 * @return bool False if the showing of errors is disabled.
+	 */
+	function print_error( $str = '' )
+	{
+		if ( $this->suppress_errors ) {
+			return false;
+		}
+
+		$error = $this->get_error( $str );
+		if ( is_object( $error ) && is_a( $error, 'WP_Error' ) ) {
+			$err = $error->get_error_data();
+			$err['error_str'] = $error->get_error_message();
+		} else {
+			$err =& $error;
+		}
+
+		$log_file = ini_get( 'error_log' );
+		if ( !empty( $log_file ) && ( 'syslog' != $log_file ) && is_writable( $log_file ) && function_exists( 'error_log' ) ) {
+			error_log($err['error_str'], 0);
+		}
+
+		// Is error output turned on or not
+		if ( !$this->show_errors ) {
+			return false;
+		}
+
+		$str = htmlspecialchars( $err['error'], ENT_QUOTES );
+		$query = htmlspecialchars( $err['query'], ENT_QUOTES );
+		$caller = htmlspecialchars( $err['caller'], ENT_QUOTES );
+
+		// If there is an error then take note of it
+
+		printf( BPDB__ERROR_HTML, $str, $query, $caller );
+	}
+
+	/**
+	 * Enables showing of database errors.
+	 *
+	 * This function should be used only to enable showing of errors.
+	 * bpdb::hide_errors() should be used instead for hiding of errors. However,
+	 * this function can be used to enable and disable showing of database
+	 * errors.
+	 *
+	 * @since 1.0
+	 *
+	 * @param bool $show Whether to show or hide errors
+	 * @return bool Old value for showing errors.
+	 */
+	function show_errors( $show = true )
+	{
+		$errors = $this->show_errors;
+		$this->show_errors = $show;
+		return $errors;
+	}
+
+	/**
+	 * Disables showing of database errors.
+	 *
+	 * @since 1.0
+	 *
+	 * @return bool Whether showing of errors was active or not
+	 */
+	function hide_errors()
+	{
+		return $this->show_errors( false );
+	}
+
+	/**
+	 * Whether to suppress database errors.
+	 *
+	 * @since 1.0
+	 *
+	 * @param bool $suppress
+	 * @return bool previous setting
+	 */
+	function suppress_errors( $suppress = true )
+	{
+		$errors = $this->suppress_errors;
+		$this->suppress_errors = $suppress;
+		return $errors;
+	}
+
+	/**
+	 * Kill cached query results.
+	 *
+	 * @since 1.0
+	 */
+	function flush()
+	{
+		$this->last_result = array();
+		$this->col_info = array();
+		$this->last_query = null;
+		$this->last_error = '';
+		$this->num_rows = 0;
+	}
+
+	/**
+	 * Perform a MySQL database query, using current database connection.
+	 *
+	 * More information can be found on the codex page.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $query
+	 * @return int|false Number of rows affected/selected or false on error
+	 */
+	function query( $query, $use_current = false )
+	{
+		if ( !$this->ready ) {
+			return false;
+		}
+
+		// filter the query, if filters are available
+		// NOTE: some queries are made before the plugins have been loaded, and thus cannot be filtered with this method
+		if ( function_exists( 'apply_filters' ) ) {
+			$query = apply_filters( 'query', $query );
+		}
+
+		// initialise return
+		$return_val = 0;
+		$this->flush();
+
+		// Log how the function was called
+		$this->func_call = "\$db->query(\"$query\")";
+
+		// Keep track of the last query for debug..
+		$this->last_query = $query;
+
+		// Perform the query via std mysql_query function..
+		if ( SAVEQUERIES ) {
+			$this->timer_start();
+		}
+
+		if ( $use_current ) {
+			$dbh =& $this->dbh;
+		} else {
+			$dbh = $this->db_connect( $query );
+		}
+
+		$this->result = @mysql_query( $query, $dbh );
+		++$this->num_queries;
+
+		if ( SAVEQUERIES ) {
+			$this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
+		}
+
+		// If there is an error then take note of it..
+		if ( $this->last_error = mysql_error( $dbh ) ) {
+			return $this->print_error( $this->last_error );
+		}
+
+		if ( preg_match( "/^\\s*(insert|delete|update|replace|alter) /i", $query ) ) {
+			$this->rows_affected = mysql_affected_rows( $dbh );
+			// Take note of the insert_id
+			if ( preg_match( "/^\\s*(insert|replace) /i", $query ) ) {
+				$this->insert_id = mysql_insert_id( $dbh );
+			}
+			// Return number of rows affected
+			$return_val = $this->rows_affected;
+		} else {
+			$i = 0;
+			while ( $i < @mysql_num_fields( $this->result ) ) {
+				$this->col_info[$i] = @mysql_fetch_field( $this->result );
+				$i++;
+			}
+			$num_rows = 0;
+			while ( $row = @mysql_fetch_object( $this->result ) ) {
+				$this->last_result[$num_rows] = $row;
+				$num_rows++;
+			}
+
+			@mysql_free_result( $this->result );
+
+			// Log number of rows the query returned
+			$this->num_rows = $num_rows;
+
+			// Return number of rows selected
+			$return_val = $this->num_rows;
+		}
+
+		return $return_val;
+	}
+
+	/**
+	 * Insert a row into a table.
+	 *
+	 * <code>
+	 * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
+	 * </code>
+	 *
+	 * @since 1.0
+	 * @see bpdb::prepare()
+	 *
+	 * @param string $table table name
+	 * @param array $data Data to insert (in column => value pairs).  Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+	 * @param array|string $format (optional) An array of formats to be mapped to each of the value in $data.  If string, that format will be used for all of the values in $data.  A format is one of '%d', '%s' (decimal number, string).  If omitted, all values in $data will be treated as strings.
+	 * @return int|false The number of rows inserted, or false on error.
+	 */
+	function insert( $table, $data, $format = null )
+	{
+		$formats = $format = (array) $format;
+		$fields = array_keys( $data );
+		$formatted_fields = array();
+		foreach ( $fields as $field ) {
+			if ( !empty( $format ) ) {
+				$form = ( $form = array_shift( $formats ) ) ? $form : $format[0];
+			} elseif ( isset( $this->field_types[$field] ) ) {
+				$form = $this->field_types[$field];
+			} elseif ( is_null( $data[$field] ) ) {
+				$form = 'NULL';
+				unset( $data[$field] );
+			} else {
+				$form = '%s';
+			}
+			$formatted_fields[] = $form;
+		}
+		$sql = "INSERT INTO `$table` (`" . implode( '`,`', $fields ) . "`) VALUES (" . implode( ",", $formatted_fields ) . ")";
+		return $this->query( $this->prepare( $sql, $data ) );
+	}
+
+	/**
+	 * Update a row in the table
+	 *
+	 * <code>
+	 * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) )
+	 * </code>
+	 *
+	 * @since 1.0
+	 * @see bpdb::prepare()
+	 *
+	 * @param string $table table name
+	 * @param array $data Data to update (in column => value pairs).  Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+	 * @param array $where A named array of WHERE clauses (in column => value pairs).  Multiple clauses will be joined with ANDs.  Both $where columns and $where values should be "raw".
+	 * @param array|string $format (optional) An array of formats to be mapped to each of the values in $data.  If string, that format will be used for all of the values in $data.  A format is one of '%d', '%s' (decimal number, string).  If omitted, all values in $data will be treated as strings.
+	 * @param array|string $format_where (optional) An array of formats to be mapped to each of the values in $where.  If string, that format will be used for all of  the items in $where.  A format is one of '%d', '%s' (decimal number, string).  If omitted, all values in $where will be treated as strings.
+	 * @return int|false The number of rows updated, or false on error.
+	 */
+	function update( $table, $data, $where, $format = null, $where_format = null )
+	{
+		if ( !is_array( $where ) ) {
+			return false;
+		}
+
+		$formats = $format = (array) $format;
+		$bits = $wheres = array();
+		foreach ( (array) array_keys( $data ) as $field ) {
+			if ( !empty( $format ) ) {
+				$form = ( $form = array_shift( $formats ) ) ? $form : $format[0];
+			} elseif ( isset( $this->field_types[$field] ) ) {
+				$form = $this->field_types[$field];
+			} elseif ( is_null( $data[$field] ) ) {
+				$form = 'NULL';
+				unset( $data[$field] );
+			} else {
+				$form = '%s';
+			}
+			$bits[] = "`$field` = {$form}";
+		}
+
+		$where_formats = $where_format = (array) $where_format;
+		foreach ( (array) array_keys( $where ) as $field ) {
+			if ( !empty( $where_format ) ) {
+				$form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0];
+			} elseif ( isset( $this->field_types[$field] ) ) {
+				$form = $this->field_types[$field];
+			} elseif ( is_null( $where[$field] ) ) {
+				unset( $where[$field] );
+				$wheres[] = "`$field` IS NULL";
+				continue;
+			} else {
+				$form = '%s';
+			}
+			$wheres[] = "`$field` = {$form}";
+		}
+
+		$sql = "UPDATE `$table` SET " . implode( ', ', $bits ) . ' WHERE ' . implode( ' AND ', $wheres );
+		return $this->query( $this->prepare( $sql, array_merge( array_values( $data ), array_values( $where ) ) ) );
+	}
+
+	/**
+	 * Retrieve one variable from the database.
+	 *
+	 * Executes a SQL query and returns the value from the SQL result.
+	 * If the SQL result contains more than one column and/or more than one row, this function returns the value in the column and row specified.
+	 * If $query is null, this function returns the value in the specified column and row from the previous SQL result.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string|null $query SQL query.  If null, use the result from the previous query.
+	 * @param int $x (optional) Column of value to return.  Indexed from 0.
+	 * @param int $y (optional) Row of value to return.  Indexed from 0.
+	 * @return string Database query result
+	 */
+	function get_var( $query=null, $x = 0, $y = 0 )
+	{
+		$this->func_call = "\$db->get_var(\"$query\",$x,$y)";
+		if ( $query ) {
+			$this->query( $query );
+		}
+
+		// Extract var out of cached results based x,y vals
+		if ( !empty( $this->last_result[$y] ) ) {
+			$values = array_values( get_object_vars( $this->last_result[$y] ) );
+		}
+
+		// If there is a value return it else return null
+		return ( isset($values[$x]) && $values[$x]!=='' ) ? $values[$x] : null;
+	}
+
+	/**
+	 * Retrieve one row from the database.
+	 *
+	 * Executes a SQL query and returns the row from the SQL result.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string|null $query SQL query.
+	 * @param string $output (optional) one of ARRAY_A | ARRAY_N | OBJECT constants.  Return an associative array (column => value, ...), a numerically indexed array (0 => value, ...) or an object ( ->column = value ), respectively.
+	 * @param int $y (optional) Row to return.  Indexed from 0.
+	 * @return mixed Database query result in format specifed by $output
+	 */
+	function get_row( $query = null, $output = OBJECT, $y = 0 )
+	{
+		$this->func_call = "\$db->get_row(\"$query\",$output,$y)";
+		if ( $query ) {
+			$this->query( $query );
+		} else {
+			return null;
+		}
+
+		if ( !isset( $this->last_result[$y] ) ) {
+			return null;
+		}
+
+		if ( $output == OBJECT ) {
+			return $this->last_result[$y] ? $this->last_result[$y] : null;
+		} elseif ( $output == ARRAY_A ) {
+			return $this->last_result[$y] ? get_object_vars( $this->last_result[$y] ) : null;
+		} elseif ( $output == ARRAY_N ) {
+			return $this->last_result[$y] ? array_values( get_object_vars( $this->last_result[$y] ) ) : null;
+		} else {
+			$this->print_error( " \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N" );
+		}
+	}
+
+	/**
+	 * Retrieve one column from the database.
+	 *
+	 * Executes a SQL query and returns the column from the SQL result.
+	 * If the SQL result contains more than one column, this function returns the column specified.
+	 * If $query is null, this function returns the specified column from the previous SQL result.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string|null $query SQL query.  If null, use the result from the previous query.
+	 * @param int $x Column to return.  Indexed from 0.
+	 * @return array Database query result.  Array indexed from 0 by SQL result row number.
+	 */
+	function get_col( $query = null , $x = 0 )
+	{
+		if ( $query ) {
+			$this->query( $query );
+		}
+
+		$new_array = array();
+		// Extract the column values
+		for ( $i=0; $i < count( $this->last_result ); $i++ ) {
+			$new_array[$i] = $this->get_var( null, $x, $i );
+		}
+		return $new_array;
+	}
+
+	/**
+	 * Retrieve an entire SQL result set from the database (i.e., many rows)
+	 *
+	 * Executes a SQL query and returns the entire SQL result.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $query SQL query.
+	 * @param string $output (optional) ane of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K | ARRAY_K constants.  With one of the first three, return an array of rows indexed from 0 by SQL result row number.  Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively.  With OBJECT_K and ARRAY_K, return an associative array of row objects keyed by the value of each row's first column's value.  Duplicate keys are discarded.
+	 * @return mixed Database query results
+	 */
+	function get_results( $query = null, $output = OBJECT ) 
+	{
+		$this->func_call = "\$db->get_results(\"$query\", $output)";
+
+		if ( $query ) {
+			$this->query($query);
+		} else {
+			return null;
+		}
+
+		if ( $output == OBJECT ) {
+			// Return an integer-keyed array of row objects
+			return $this->last_result;
+		} elseif ( $output == OBJECT_K || $output == ARRAY_K ) {
+			// Return an array of row objects with keys from column 1
+			// (Duplicates are discarded)
+			$key = $this->col_info[0]->name;
+			foreach ( $this->last_result as $row ) {
+				if ( !isset( $new_array[ $row->$key ] ) ) {
+					$new_array[ $row->$key ] = $row;
+				}
+			}
+			if ( $output == ARRAY_K ) {
+				return array_map( 'get_object_vars', $new_array );
+			}
+			return $new_array;
+		} elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
+			// Return an integer-keyed array of...
+			if ( $this->last_result ) {
+				$i = 0;
+				foreach( $this->last_result as $row ) {
+					if ( $output == ARRAY_N ) {
+						// ...integer-keyed row arrays
+						$new_array[$i] = array_values( get_object_vars( $row ) );
+					} else {
+						// ...column name-keyed row arrays
+						$new_array[$i] = get_object_vars( $row );
+					}
+					++$i;
+				}
+				return $new_array;
+			}
+		}
+	}
+
+	/**
+	 * Retrieve column metadata from the last query.
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $info_type one of name, table, def, max_length, not_null, primary_key, multiple_key, unique_key, numeric, blob, type, unsigned, zerofill
+	 * @param int $col_offset 0: col name. 1: which table the col's in. 2: col's max length. 3: if the col is numeric. 4: col's type
+	 * @return mixed Column Results
+	 */
+	function get_col_info( $info_type = 'name', $col_offset = -1 )
+	{
+		if ( $this->col_info ) {
+			if ( $col_offset == -1 ) {
+				$i = 0;
+				foreach( (array) $this->col_info as $col ) {
+					$new_array[$i] = $col->{$info_type};
+					$i++;
+				}
+				return $new_array;
+			} else {
+				return $this->col_info[$col_offset]->{$info_type};
+			}
+		}
+	}
+
+	/**
+	 * Starts the timer, for debugging purposes.
+	 *
+	 * @since 1.0
+	 *
+	 * @return true
+	 */
+	function timer_start()
+	{
+		$mtime = microtime();
+		$mtime = explode( ' ', $mtime );
+		$this->time_start = $mtime[1] + $mtime[0];
+		return true;
+	}
+
+	/**
+	 * Stops the debugging timer.
+	 *
+	 * @since 1.0
+	 *
+	 * @return int Total time spent on the query, in milliseconds
+	 */
+	function timer_stop()
+	{
+		$mtime = microtime();
+		$mtime = explode( ' ', $mtime );
+		$time_end = $mtime[1] + $mtime[0];
+		$time_total = $time_end - $this->time_start;
+		return $time_total;
+	}
+
+	/**
+	 * Wraps errors in a nice header and footer and dies.
+	 *
+	 * Will not die if bpdb::$show_errors is true
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $message
+	 * @return false|void
+	 */
+	function bail( $message )
+	{
+		if ( !$this->show_errors ) {
+			if ( class_exists( 'WP_Error' ) )
+				$this->error = new WP_Error( '500', $message );
+			else
+				$this->error = $message;
+			return false;
+		}
+		backpress_die( $message );
+	}
+
+	/**
+	 * Whether or not MySQL database is at least the required minimum version.
+	 *
+	 * @since 1.0
+	 *
+	 * @return WP_Error
+	 */
+	function check_database_version( $dbh_or_table = false )
+	{
+		// Make sure the server has MySQL 4.0
+		if ( version_compare( $this->db_version( $dbh_or_table ), '4.0.0', '<' ) ) {
+			return new WP_Error( 'database_version', BPDB__DB_VERSION_ERROR );
+		}
+	}
+
+	/**
+	 * Whether of not the database supports collation.
+	 *
+	 * Called when BackPress is generating the table scheme.
+	 *
+	 * @since 1.0
+	 *
+	 * @return bool True if collation is supported, false if version does not
+	 */
+	function supports_collation()
+	{
+		return $this->has_cap( 'collation' );
+	}
+
+	/**
+	 * Generic function to determine if a database supports a particular feature
+	 *
+	 * @since 1.0
+	 *
+	 * @param string $db_cap the feature
+	 * @param false|string|resource $dbh_or_table Which database to test.  False = the currently selected database, string = the database containing the specified table, resource = the database corresponding to the specified mysql resource.
+	 * @return bool
+	 */
+	function has_cap( $db_cap, $dbh_or_table = false )
+	{
+		$version = $this->db_version( $dbh_or_table );
+
+		switch ( strtolower( $db_cap ) ) {
+			case 'collation' :
+			case 'group_concat' :
+			case 'subqueries' :
+				return version_compare( $version, '4.1', '>=' );
+				break;
+
+			case 'index_hint_for_join' :
+				return version_compare( $version, '5.0', '>=' );
+				break;
+
+			case 'index_hint_lists' :
+			case 'index_hint_for_any' :
+				return version_compare( $version, '5.1', '>=' );
+				break;
+		}
+
+		return false;
+	}
+
+	/**
+	 * The database version number
+	 *
+	 * @since 1.0
+	 *
+	 * @param false|string|resource $dbh_or_table Which database to test. False = the currently selected database, string = the database containing the specified table, resource = the database corresponding to the specified mysql resource.
+	 * @return false|string false on failure, version number on success
+	 */
+	function db_version( $dbh_or_table = false )
+	{
+		if ( !$dbh_or_table ) {
+			$dbh =& $this->dbh;
+		} elseif ( is_resource( $dbh_or_table ) ) {
+			$dbh =& $dbh_or_table;
+		} else {
+			$dbh = $this->db_connect( "DESCRIBE $dbh_or_table" );
+		}
+
+		if ( $dbh ) {
+			return preg_replace( '|[^0-9\.]|', '', mysql_get_server_info( $dbh ) );
+		}
+		return false;
+	}
+
+	/**
+	 * Retrieve the name of the function that called bpdb.
+	 *
+	 * Requires PHP 4.3 and searches up the list of functions until it reaches
+	 * the one that would most logically had called this method.
+	 *
+	 * @since 1.0
+	 *
+	 * @return string The name of the calling function
+	 */
+	function get_caller()
+	{
+		// requires PHP 4.3+
+		if ( !is_callable( 'debug_backtrace' ) ) {
+			return '';
+		}
+
+		$bt = debug_backtrace();
+		$caller = array();
+
+		$bt = array_reverse( $bt );
+		foreach ( (array) $bt as $call ) {
+			if ( @$call['class'] == __CLASS__ ) {
+				continue;
+			}
+			$function = $call['function'];
+			if ( isset( $call['class'] ) ) {
+				$function = $call['class'] . "->$function";
+			}
+			$caller[] = $function;
+		}
+		$caller = join( ', ', $caller );
+
+		return $caller;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.ixr.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.ixr.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.ixr.php	(revision 5057)
@@ -0,0 +1,1065 @@
+<?php
+// Last sync [WP16063]
+/**
+ * IXR - The Incutio XML-RPC Library
+ *
+ * Copyright (c) 2010, Incutio Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  - Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  - Neither the name of Incutio Ltd. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @package IXR
+ * @since 1.5
+ *
+ * @copyright  Incutio Ltd 2010 (http://www.incutio.com)
+ * @version    1.7.4 7th September 2010
+ * @author     Simon Willison
+ * @link       http://scripts.incutio.com/xmlrpc/ Site/manual
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD
+ */
+
+/**
+ * IXR_Value
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Value {
+    var $data;
+    var $type;
+
+    function IXR_Value($data, $type = false)
+    {
+        $this->data = $data;
+        if (!$type) {
+            $type = $this->calculateType();
+        }
+        $this->type = $type;
+        if ($type == 'struct') {
+            // Turn all the values in the array in to new IXR_Value objects
+            foreach ($this->data as $key => $value) {
+                $this->data[$key] = new IXR_Value($value);
+            }
+        }
+        if ($type == 'array') {
+            for ($i = 0, $j = count($this->data); $i < $j; $i++) {
+                $this->data[$i] = new IXR_Value($this->data[$i]);
+            }
+        }
+    }
+
+    function calculateType()
+    {
+        if ($this->data === true || $this->data === false) {
+            return 'boolean';
+        }
+        if (is_integer($this->data)) {
+            return 'int';
+        }
+        if (is_double($this->data)) {
+            return 'double';
+        }
+
+        // Deal with IXR object types base64 and date
+        if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
+            return 'date';
+        }
+        if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
+            return 'base64';
+        }
+
+        // If it is a normal PHP object convert it in to a struct
+        if (is_object($this->data)) {
+            $this->data = get_object_vars($this->data);
+            return 'struct';
+        }
+        if (!is_array($this->data)) {
+            return 'string';
+        }
+
+        // We have an array - is it an array or a struct?
+        if ($this->isStruct($this->data)) {
+            return 'struct';
+        } else {
+            return 'array';
+        }
+    }
+
+    function getXml()
+    {
+        // Return XML for this value
+        switch ($this->type) {
+            case 'boolean':
+                return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
+                break;
+            case 'int':
+                return '<int>'.$this->data.'</int>';
+                break;
+            case 'double':
+                return '<double>'.$this->data.'</double>';
+                break;
+            case 'string':
+                return '<string>'.htmlspecialchars($this->data).'</string>';
+                break;
+            case 'array':
+                $return = '<array><data>'."\n";
+                foreach ($this->data as $item) {
+                    $return .= '  <value>'.$item->getXml()."</value>\n";
+                }
+                $return .= '</data></array>';
+                return $return;
+                break;
+            case 'struct':
+                $return = '<struct>'."\n";
+                foreach ($this->data as $name => $value) {
+					$name = htmlspecialchars($name);
+                    $return .= "  <member><name>$name</name><value>";
+                    $return .= $value->getXml()."</value></member>\n";
+                }
+                $return .= '</struct>';
+                return $return;
+                break;
+            case 'date':
+            case 'base64':
+                return $this->data->getXml();
+                break;
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether or not the supplied array is a struct or not
+     *
+     * @param unknown_type $array
+     * @return boolean
+     */
+    function isStruct($array)
+    {
+        $expected = 0;
+        foreach ($array as $key => $value) {
+            if ((string)$key != (string)$expected) {
+                return true;
+            }
+            $expected++;
+        }
+        return false;
+    }
+}
+
+/**
+ * IXR_MESSAGE
+ *
+ * @package IXR
+ * @since 1.5
+ *
+ */
+class IXR_Message
+{
+    var $message;
+    var $messageType;  // methodCall / methodResponse / fault
+    var $faultCode;
+    var $faultString;
+    var $methodName;
+    var $params;
+
+    // Current variable stacks
+    var $_arraystructs = array();   // The stack used to keep track of the current array/struct
+    var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
+    var $_currentStructName = array();  // A stack as well
+    var $_param;
+    var $_value;
+    var $_currentTag;
+    var $_currentTagContents;
+    // The XML parser
+    var $_parser;
+
+    function IXR_Message($message)
+    {
+        $this->message =& $message;
+    }
+
+    function parse()
+    {
+        // first remove the XML declaration
+        // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
+        $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1);
+        $this->message = substr_replace($this->message, $header, 0, 100);
+        if (trim($this->message) == '') {
+            return false;
+        }
+        $this->_parser = xml_parser_create();
+        // Set XML parser to take the case of tags in to account
+        xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
+        // Set XML parser callback functions
+        xml_set_object($this->_parser, $this);
+        xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
+        xml_set_character_data_handler($this->_parser, 'cdata');
+        $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages
+        do {
+            if (strlen($this->message) <= $chunk_size) {
+                $final = true;
+            }
+            $part = substr($this->message, 0, $chunk_size);
+            $this->message = substr($this->message, $chunk_size);
+            if (!xml_parse($this->_parser, $part, $final)) {
+                return false;
+            }
+            if ($final) {
+                break;
+            }
+        } while (true);
+        xml_parser_free($this->_parser);
+
+        // Grab the error messages, if any
+        if ($this->messageType == 'fault') {
+            $this->faultCode = $this->params[0]['faultCode'];
+            $this->faultString = $this->params[0]['faultString'];
+        }
+        return true;
+    }
+
+    function tag_open($parser, $tag, $attr)
+    {
+        $this->_currentTagContents = '';
+        $this->currentTag = $tag;
+        switch($tag) {
+            case 'methodCall':
+            case 'methodResponse':
+            case 'fault':
+                $this->messageType = $tag;
+                break;
+                /* Deal with stacks of arrays and structs */
+            case 'data':    // data is to all intents and puposes more interesting than array
+                $this->_arraystructstypes[] = 'array';
+                $this->_arraystructs[] = array();
+                break;
+            case 'struct':
+                $this->_arraystructstypes[] = 'struct';
+                $this->_arraystructs[] = array();
+                break;
+        }
+    }
+
+    function cdata($parser, $cdata)
+    {
+        $this->_currentTagContents .= $cdata;
+    }
+
+    function tag_close($parser, $tag)
+    {
+        $valueFlag = false;
+        switch($tag) {
+            case 'int':
+            case 'i4':
+                $value = (int)trim($this->_currentTagContents);
+                $valueFlag = true;
+                break;
+            case 'double':
+                $value = (double)trim($this->_currentTagContents);
+                $valueFlag = true;
+                break;
+            case 'string':
+                $value = (string)trim($this->_currentTagContents);
+                $valueFlag = true;
+                break;
+            case 'dateTime.iso8601':
+                $value = new IXR_Date(trim($this->_currentTagContents));
+                $valueFlag = true;
+                break;
+            case 'value':
+                // "If no type is indicated, the type is string."
+                if (trim($this->_currentTagContents) != '') {
+                    $value = (string)$this->_currentTagContents;
+                    $valueFlag = true;
+                }
+                break;
+            case 'boolean':
+                $value = (boolean)trim($this->_currentTagContents);
+                $valueFlag = true;
+                break;
+            case 'base64':
+                $value = base64_decode($this->_currentTagContents);
+                $valueFlag = true;
+                break;
+                /* Deal with stacks of arrays and structs */
+            case 'data':
+            case 'struct':
+                $value = array_pop($this->_arraystructs);
+                array_pop($this->_arraystructstypes);
+                $valueFlag = true;
+                break;
+            case 'member':
+                array_pop($this->_currentStructName);
+                break;
+            case 'name':
+                $this->_currentStructName[] = trim($this->_currentTagContents);
+                break;
+            case 'methodName':
+                $this->methodName = trim($this->_currentTagContents);
+                break;
+        }
+
+        if ($valueFlag) {
+            if (count($this->_arraystructs) > 0) {
+                // Add value to struct or array
+                if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
+                    // Add to struct
+                    $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
+                } else {
+                    // Add to array
+                    $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
+                }
+            } else {
+                // Just add as a paramater
+                $this->params[] = $value;
+            }
+        }
+        $this->_currentTagContents = '';
+    }
+}
+
+/**
+ * IXR_Server
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Server
+{
+    var $data;
+    var $callbacks = array();
+    var $message;
+    var $capabilities;
+
+    function IXR_Server($callbacks = false, $data = false, $wait = false)
+    {
+        $this->setCapabilities();
+        if ($callbacks) {
+            $this->callbacks = $callbacks;
+        }
+        $this->setCallbacks();
+        if (!$wait) {
+            $this->serve($data);
+        }
+    }
+
+    function serve($data = false)
+    {
+        if (!$data) {
+            if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
+            	header('Content-Type: text/plain'); // merged from WP #9093
+                die('XML-RPC server accepts POST requests only.');
+            }
+
+            global $HTTP_RAW_POST_DATA;
+            if (empty($HTTP_RAW_POST_DATA)) {
+                // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293
+                $data = file_get_contents('php://input');
+            } else {
+                $data =& $HTTP_RAW_POST_DATA;
+            }
+        }
+        $this->message = new IXR_Message($data);
+        if (!$this->message->parse()) {
+            $this->error(-32700, 'parse error. not well formed');
+        }
+        if ($this->message->messageType != 'methodCall') {
+            $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
+        }
+        $result = $this->call($this->message->methodName, $this->message->params);
+
+        // Is the result an error?
+        if (is_a($result, 'IXR_Error')) {
+            $this->error($result);
+        }
+
+        // Encode the result
+        $r = new IXR_Value($result);
+        $resultxml = $r->getXml();
+
+        // Create the XML
+        $xml = <<<EOD
+<methodResponse>
+  <params>
+    <param>
+      <value>
+      $resultxml
+      </value>
+    </param>
+  </params>
+</methodResponse>
+
+EOD;
+      // Send it
+      $this->output($xml);
+    }
+
+    function call($methodname, $args)
+    {
+        if (!$this->hasMethod($methodname)) {
+            return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
+        }
+        $method = $this->callbacks[$methodname];
+
+        // Perform the callback and send the response
+        if (count($args) == 1) {
+            // If only one paramater just send that instead of the whole array
+            $args = $args[0];
+        }
+
+        // Are we dealing with a function or a method?
+        if (is_string($method) && substr($method, 0, 5) == 'this:') {
+            // It's a class method - check it exists
+            $method = substr($method, 5);
+            if (!method_exists($this, $method)) {
+                return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.');
+            }
+
+            //Call the method
+            $result = $this->$method($args);
+        } else {
+            // It's a function - does it exist?
+            if (is_array($method)) {
+                if (!method_exists($method[0], $method[1])) {
+                    return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.');
+                }
+            } else if (!function_exists($method)) {
+                return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
+            }
+
+            // Call the function
+            $result = call_user_func($method, $args);
+        }
+        return $result;
+    }
+
+    function error($error, $message = false)
+    {
+        // Accepts either an error object or an error code and message
+        if ($message && !is_object($error)) {
+            $error = new IXR_Error($error, $message);
+        }
+        $this->output($error->getXml());
+    }
+
+    function output($xml)
+    {
+        $xml = '<?xml version="1.0"?>'."\n".$xml;
+        $length = strlen($xml);
+        header('Connection: close');
+        header('Content-Length: '.$length);
+        header('Content-Type: text/xml');
+        header('Date: '.date('r'));
+        echo $xml;
+        exit;
+    }
+
+    function hasMethod($method)
+    {
+        return in_array($method, array_keys($this->callbacks));
+    }
+
+    function setCapabilities()
+    {
+        // Initialises capabilities array
+        $this->capabilities = array(
+            'xmlrpc' => array(
+                'specUrl' => 'http://www.xmlrpc.com/spec',
+                'specVersion' => 1
+        ),
+            'faults_interop' => array(
+                'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
+                'specVersion' => 20010516
+        ),
+            'system.multicall' => array(
+                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
+                'specVersion' => 1
+        ),
+        );
+    }
+
+    function getCapabilities($args)
+    {
+        return $this->capabilities;
+    }
+
+    function setCallbacks()
+    {
+        $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
+        $this->callbacks['system.listMethods'] = 'this:listMethods';
+        $this->callbacks['system.multicall'] = 'this:multiCall';
+    }
+
+    function listMethods($args)
+    {
+        // Returns a list of methods - uses array_reverse to ensure user defined
+        // methods are listed before server defined methods
+        return array_reverse(array_keys($this->callbacks));
+    }
+
+    function multiCall($methodcalls)
+    {
+        // See http://www.xmlrpc.com/discuss/msgReader$1208
+        $return = array();
+        foreach ($methodcalls as $call) {
+            $method = $call['methodName'];
+            $params = $call['params'];
+            if ($method == 'system.multicall') {
+                $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
+            } else {
+                $result = $this->call($method, $params);
+            }
+            if (is_a($result, 'IXR_Error')) {
+                $return[] = array(
+                    'faultCode' => $result->code,
+                    'faultString' => $result->message
+                );
+            } else {
+                $return[] = array($result);
+            }
+        }
+        return $return;
+    }
+}
+
+/**
+ * IXR_Request
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Request
+{
+    var $method;
+    var $args;
+    var $xml;
+
+    function IXR_Request($method, $args)
+    {
+        $this->method = $method;
+        $this->args = $args;
+        $this->xml = <<<EOD
+<?xml version="1.0"?>
+<methodCall>
+<methodName>{$this->method}</methodName>
+<params>
+
+EOD;
+        foreach ($this->args as $arg) {
+            $this->xml .= '<param><value>';
+            $v = new IXR_Value($arg);
+            $this->xml .= $v->getXml();
+            $this->xml .= "</value></param>\n";
+        }
+        $this->xml .= '</params></methodCall>';
+    }
+
+    function getLength()
+    {
+        return strlen($this->xml);
+    }
+
+    function getXml()
+    {
+        return $this->xml;
+    }
+}
+
+/**
+ * IXR_Client
+ *
+ * @package IXR
+ * @since 1.5
+ *
+ */
+class IXR_Client
+{
+    var $server;
+    var $port;
+    var $path;
+    var $useragent;
+    var $response;
+    var $message = false;
+    var $debug = false;
+    var $timeout;
+    var $headers = array();
+
+    // Storage place for an error message
+    var $error = false;
+
+    function IXR_Client($server, $path = false, $port = 80, $timeout = 15)
+    {
+        if (!$path) {
+            // Assume we have been given a URL instead
+            $bits = parse_url($server);
+            $this->server = $bits['host'];
+            $this->port = isset($bits['port']) ? $bits['port'] : 80;
+            $this->path = isset($bits['path']) ? $bits['path'] : '/';
+
+            // Make absolutely sure we have a path
+            if (!$this->path) {
+                $this->path = '/';
+            }
+        } else {
+            $this->server = $server;
+            $this->path = $path;
+            $this->port = $port;
+        }
+        $this->useragent = 'The Incutio XML-RPC PHP Library';
+        $this->timeout = $timeout;
+    }
+
+    function query()
+    {
+        $args = func_get_args();
+        $method = array_shift($args);
+        $request = new IXR_Request($method, $args);
+        $length = $request->getLength();
+        $xml = $request->getXml();
+        $r = "\r\n";
+        $request  = "POST {$this->path} HTTP/1.0$r";
+
+        // Merged from WP #8145 - allow custom headers
+        $this->headers['Host']          = $this->server;
+        $this->headers['Content-Type']  = 'text/xml';
+        $this->headers['User-Agent']    = $this->useragent;
+        $this->headers['Content-Length']= $length;
+
+        foreach( $this->headers as $header => $value ) {
+            $request .= "{$header}: {$value}{$r}";
+        }
+        $request .= $r;
+
+        $request .= $xml;
+
+        // Now send the request
+        if ($this->debug) {
+            echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n";
+        }
+
+        if ($this->timeout) {
+            $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
+        } else {
+            $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
+        }
+        if (!$fp) {
+            $this->error = new IXR_Error(-32300, 'transport error - could not open socket');
+            return false;
+        }
+        fputs($fp, $request);
+        $contents = '';
+        $debugContents = '';
+        $gotFirstLine = false;
+        $gettingHeaders = true;
+        while (!feof($fp)) {
+            $line = fgets($fp, 4096);
+            if (!$gotFirstLine) {
+                // Check line for '200'
+                if (strstr($line, '200') === false) {
+                    $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
+                    return false;
+                }
+                $gotFirstLine = true;
+            }
+            if (trim($line) == '') {
+                $gettingHeaders = false;
+            }
+            if (!$gettingHeaders) {
+            	// merged from WP #12559 - remove trim
+                $contents .= $line;
+            }
+            if ($this->debug) {
+            	$debugContents .= $line;
+            }
+        }
+        if ($this->debug) {
+            echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n";
+        }
+
+        // Now parse what we've got back
+        $this->message = new IXR_Message($contents);
+        if (!$this->message->parse()) {
+            // XML error
+            $this->error = new IXR_Error(-32700, 'parse error. not well formed');
+            return false;
+        }
+
+        // Is the message a fault?
+        if ($this->message->messageType == 'fault') {
+            $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
+            return false;
+        }
+
+        // Message must be OK
+        return true;
+    }
+
+    function getResponse()
+    {
+        // methodResponses can only have one param - return that
+        return $this->message->params[0];
+    }
+
+    function isError()
+    {
+        return (is_object($this->error));
+    }
+
+    function getErrorCode()
+    {
+        return $this->error->code;
+    }
+
+    function getErrorMessage()
+    {
+        return $this->error->message;
+    }
+}
+
+
+/**
+ * IXR_Error
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Error
+{
+    var $code;
+    var $message;
+
+    function IXR_Error($code, $message)
+    {
+        $this->code = $code;
+        $this->message = htmlspecialchars($message);
+    }
+
+    function getXml()
+    {
+        $xml = <<<EOD
+<methodResponse>
+  <fault>
+    <value>
+      <struct>
+        <member>
+          <name>faultCode</name>
+          <value><int>{$this->code}</int></value>
+        </member>
+        <member>
+          <name>faultString</name>
+          <value><string>{$this->message}</string></value>
+        </member>
+      </struct>
+    </value>
+  </fault>
+</methodResponse>
+
+EOD;
+        return $xml;
+    }
+}
+
+/**
+ * IXR_Date
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Date {
+    var $year;
+    var $month;
+    var $day;
+    var $hour;
+    var $minute;
+    var $second;
+    var $timezone;
+
+    function IXR_Date($time)
+    {
+        // $time can be a PHP timestamp or an ISO one
+        if (is_numeric($time)) {
+            $this->parseTimestamp($time);
+        } else {
+            $this->parseIso($time);
+        }
+    }
+
+    function parseTimestamp($timestamp)
+    {
+        $this->year = date('Y', $timestamp);
+        $this->month = date('m', $timestamp);
+        $this->day = date('d', $timestamp);
+        $this->hour = date('H', $timestamp);
+        $this->minute = date('i', $timestamp);
+        $this->second = date('s', $timestamp);
+        $this->timezone = '';
+    }
+
+    function parseIso($iso)
+    {
+        $this->year = substr($iso, 0, 4);
+        $this->month = substr($iso, 4, 2);
+        $this->day = substr($iso, 6, 2);
+        $this->hour = substr($iso, 9, 2);
+        $this->minute = substr($iso, 12, 2);
+        $this->second = substr($iso, 15, 2);
+        $this->timezone = substr($iso, 17);
+    }
+
+    function getIso()
+    {
+        return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
+    }
+
+    function getXml()
+    {
+        return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
+    }
+
+    function getTimestamp()
+    {
+        return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
+    }
+}
+
+/**
+ * IXR_Base64
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_Base64
+{
+    var $data;
+
+    function IXR_Base64($data)
+    {
+        $this->data = $data;
+    }
+
+    function getXml()
+    {
+        return '<base64>'.base64_encode($this->data).'</base64>';
+    }
+}
+
+/**
+ * IXR_IntrospectionServer
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_IntrospectionServer extends IXR_Server
+{
+    var $signatures;
+    var $help;
+
+    function IXR_IntrospectionServer()
+    {
+        $this->setCallbacks();
+        $this->setCapabilities();
+        $this->capabilities['introspection'] = array(
+            'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
+            'specVersion' => 1
+        );
+        $this->addCallback(
+            'system.methodSignature',
+            'this:methodSignature',
+            array('array', 'string'),
+            'Returns an array describing the return type and required parameters of a method'
+        );
+        $this->addCallback(
+            'system.getCapabilities',
+            'this:getCapabilities',
+            array('struct'),
+            'Returns a struct describing the XML-RPC specifications supported by this server'
+        );
+        $this->addCallback(
+            'system.listMethods',
+            'this:listMethods',
+            array('array'),
+            'Returns an array of available methods on this server'
+        );
+        $this->addCallback(
+            'system.methodHelp',
+            'this:methodHelp',
+            array('string', 'string'),
+            'Returns a documentation string for the specified method'
+        );
+    }
+
+    function addCallback($method, $callback, $args, $help)
+    {
+        $this->callbacks[$method] = $callback;
+        $this->signatures[$method] = $args;
+        $this->help[$method] = $help;
+    }
+
+    function call($methodname, $args)
+    {
+        // Make sure it's in an array
+        if ($args && !is_array($args)) {
+            $args = array($args);
+        }
+
+        // Over-rides default call method, adds signature check
+        if (!$this->hasMethod($methodname)) {
+            return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
+        }
+        $method = $this->callbacks[$methodname];
+        $signature = $this->signatures[$methodname];
+        $returnType = array_shift($signature);
+
+        // Check the number of arguments
+        if (count($args) != count($signature)) {
+            return new IXR_Error(-32602, 'server error. wrong number of method parameters');
+        }
+
+        // Check the argument types
+        $ok = true;
+        $argsbackup = $args;
+        for ($i = 0, $j = count($args); $i < $j; $i++) {
+            $arg = array_shift($args);
+            $type = array_shift($signature);
+            switch ($type) {
+                case 'int':
+                case 'i4':
+                    if (is_array($arg) || !is_int($arg)) {
+                        $ok = false;
+                    }
+                    break;
+                case 'base64':
+                case 'string':
+                    if (!is_string($arg)) {
+                        $ok = false;
+                    }
+                    break;
+                case 'boolean':
+                    if ($arg !== false && $arg !== true) {
+                        $ok = false;
+                    }
+                    break;
+                case 'float':
+                case 'double':
+                    if (!is_float($arg)) {
+                        $ok = false;
+                    }
+                    break;
+                case 'date':
+                case 'dateTime.iso8601':
+                    if (!is_a($arg, 'IXR_Date')) {
+                        $ok = false;
+                    }
+                    break;
+            }
+            if (!$ok) {
+                return new IXR_Error(-32602, 'server error. invalid method parameters');
+            }
+        }
+        // It passed the test - run the "real" method call
+        return parent::call($methodname, $argsbackup);
+    }
+
+    function methodSignature($method)
+    {
+        if (!$this->hasMethod($method)) {
+            return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
+        }
+        // We should be returning an array of types
+        $types = $this->signatures[$method];
+        $return = array();
+        foreach ($types as $type) {
+            switch ($type) {
+                case 'string':
+                    $return[] = 'string';
+                    break;
+                case 'int':
+                case 'i4':
+                    $return[] = 42;
+                    break;
+                case 'double':
+                    $return[] = 3.1415;
+                    break;
+                case 'dateTime.iso8601':
+                    $return[] = new IXR_Date(time());
+                    break;
+                case 'boolean':
+                    $return[] = true;
+                    break;
+                case 'base64':
+                    $return[] = new IXR_Base64('base64');
+                    break;
+                case 'array':
+                    $return[] = array('array');
+                    break;
+                case 'struct':
+                    $return[] = array('struct' => 'struct');
+                    break;
+            }
+        }
+        return $return;
+    }
+
+    function methodHelp($method)
+    {
+        return $this->help[$method];
+    }
+}
+
+/**
+ * IXR_ClientMulticall
+ *
+ * @package IXR
+ * @since 1.5
+ */
+class IXR_ClientMulticall extends IXR_Client
+{
+    var $calls = array();
+
+    function IXR_ClientMulticall($server, $path = false, $port = 80)
+    {
+        parent::IXR_Client($server, $path, $port);
+        $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
+    }
+
+    function addCall()
+    {
+        $args = func_get_args();
+        $methodName = array_shift($args);
+        $struct = array(
+            'methodName' => $methodName,
+            'params' => $args
+        );
+        $this->calls[] = $struct;
+    }
+
+    function query()
+    {
+        // Prepare multicall, then call the parent::query() method
+        return parent::query('system.multicall', $this->calls);
+    }
+}
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer-smtp.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer-smtp.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer-smtp.php	(revision 5057)
@@ -0,0 +1,1061 @@
+<?php
+// Last sync [WP13425]
+
+/*~ class.smtp.php
+.---------------------------------------------------------------------------.
+|  Software: PHPMailer - PHP email class                                    |
+|   Version: 2.0.4                                                          |
+|   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
+|      Info: http://phpmailer.sourceforge.net                               |
+|   Support: http://sourceforge.net/projects/phpmailer/                     |
+| ------------------------------------------------------------------------- |
+|    Author: Andy Prevost (project admininistrator)                         |
+|    Author: Brent R. Matzelle (original founder)                           |
+| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
+| Copyright (c) 2001-2003, Brent R. Matzelle                                |
+| ------------------------------------------------------------------------- |
+|   License: Distributed under the Lesser General Public License (LGPL)     |
+|            http://www.gnu.org/copyleft/lesser.html                        |
+| This program is distributed in the hope that it will be useful - WITHOUT  |
+| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
+| FITNESS FOR A PARTICULAR PURPOSE.                                         |
+| ------------------------------------------------------------------------- |
+| We offer a number of paid services (www.codeworxtech.com):                |
+| - Web Hosting on highly optimized fast and secure servers                 |
+| - Technology Consulting                                                   |
+| - Oursourcing (highly qualified programmers and graphic designers)        |
+'---------------------------------------------------------------------------'
+ */
+/**
+ * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
+ * commands except TURN which will always return a not implemented
+ * error. SMTP also provides some utility methods for sending mail
+ * to an SMTP server.
+ * @package PHPMailer
+ * @author Chris Ryan
+ */
+
+class SMTP
+{
+  /**
+   *  SMTP server port
+   *  @var int
+   */
+  var $SMTP_PORT = 25;
+
+  /**
+   *  SMTP reply line ending
+   *  @var string
+   */
+  var $CRLF = "\r\n";
+
+  /**
+   *  Sets whether debugging is turned on
+   *  @var bool
+   */
+  var $do_debug;       # the level of debug to perform
+
+  /**
+   *  Sets VERP use on/off (default is off)
+   *  @var bool
+   */
+  var $do_verp = false;
+
+  /**#@+
+   * @access private
+   */
+  var $smtp_conn;      # the socket to the server
+  var $error;          # error if any on the last call
+  var $helo_rply;      # the reply the server sent to us for HELO
+  /**#@-*/
+
+  /**
+   * Initialize the class so that the data is in a known state.
+   * @access public
+   * @return void
+   */
+  function SMTP() {
+    $this->smtp_conn = 0;
+    $this->error = null;
+    $this->helo_rply = null;
+
+    $this->do_debug = 0;
+  }
+
+  /*************************************************************
+   *                    CONNECTION FUNCTIONS                  *
+   ***********************************************************/
+
+  /**
+   * Connect to the server specified on the port specified.
+   * If the port is not specified use the default SMTP_PORT.
+   * If tval is specified then a connection will try and be
+   * established with the server for that number of seconds.
+   * If tval is not specified the default is 30 seconds to
+   * try on the connection.
+   *
+   * SMTP CODE SUCCESS: 220
+   * SMTP CODE FAILURE: 421
+   * @access public
+   * @return bool
+   */
+  function Connect($host,$port=0,$tval=30) {
+    # set the error val to null so there is no confusion
+    $this->error = null;
+
+    # make sure we are __not__ connected
+    if($this->connected()) {
+      # ok we are connected! what should we do?
+      # for now we will just give an error saying we
+      # are already connected
+      $this->error = array("error" => "Already connected to a server");
+      return false;
+    }
+
+    if(empty($port)) {
+      $port = $this->SMTP_PORT;
+    }
+
+    #connect to the smtp server
+    $this->smtp_conn = fsockopen($host,    # the host of the server
+                                 $port,    # the port to use
+                                 $errno,   # error number if any
+                                 $errstr,  # error message if any
+                                 $tval);   # give up after ? secs
+    # verify we connected properly
+    if(empty($this->smtp_conn)) {
+      $this->error = array("error" => "Failed to connect to server",
+                           "errno" => $errno,
+                           "errstr" => $errstr);
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": $errstr ($errno)" . $this->CRLF;
+      }
+      return false;
+    }
+
+    # sometimes the SMTP server takes a little longer to respond
+    # so we will give it a longer timeout for the first read
+    // Windows still does not have support for this timeout function
+    if(substr(PHP_OS, 0, 3) != "WIN")
+     socket_set_timeout($this->smtp_conn, $tval, 0);
+
+    # get any announcement stuff
+    $announce = $this->get_lines();
+
+    # set the timeout  of any socket functions at 1/10 of a second
+    //if(function_exists("socket_set_timeout"))
+    //   socket_set_timeout($this->smtp_conn, 0, 100000);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
+    }
+
+    return true;
+  }
+
+  /**
+   * Performs SMTP authentication.  Must be run after running the
+   * Hello() method.  Returns true if successfully authenticated.
+   * @access public
+   * @return bool
+   */
+  function Authenticate($username, $password) {
+    // Start authentication
+    fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($code != 334) {
+      $this->error =
+        array("error" => "AUTH not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    // Send encoded username
+    fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($code != 334) {
+      $this->error =
+        array("error" => "Username not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    // Send encoded password
+    fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($code != 235) {
+      $this->error =
+        array("error" => "Password not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Returns true if connected to a server otherwise false
+   * @access private
+   * @return bool
+   */
+  function Connected() {
+    if(!empty($this->smtp_conn)) {
+      $sock_status = socket_get_status($this->smtp_conn);
+      if($sock_status["eof"]) {
+        # hmm this is an odd situation... the socket is
+        # valid but we are not connected anymore
+        if($this->do_debug >= 1) {
+            echo "SMTP -> NOTICE:" . $this->CRLF .
+                 "EOF caught while checking if connected";
+        }
+        $this->Close();
+        return false;
+      }
+      return true; # everything looks good
+    }
+    return false;
+  }
+
+  /**
+   * Closes the socket and cleans up the state of the class.
+   * It is not considered good to use this function without
+   * first trying to use QUIT.
+   * @access public
+   * @return void
+   */
+  function Close() {
+    $this->error = null; # so there is no confusion
+    $this->helo_rply = null;
+    if(!empty($this->smtp_conn)) {
+      # close the connection and cleanup
+      fclose($this->smtp_conn);
+      $this->smtp_conn = 0;
+    }
+  }
+
+  /***************************************************************
+   *                        SMTP COMMANDS                       *
+   *************************************************************/
+
+  /**
+   * Issues a data command and sends the msg_data to the server
+   * finializing the mail transaction. $msg_data is the message
+   * that is to be send with the headers. Each header needs to be
+   * on a single line followed by a <CRLF> with the message headers
+   * and the message body being separated by and additional <CRLF>.
+   *
+   * Implements rfc 821: DATA <CRLF>
+   *
+   * SMTP CODE INTERMEDIATE: 354
+   *     [data]
+   *     <CRLF>.<CRLF>
+   *     SMTP CODE SUCCESS: 250
+   *     SMTP CODE FAILURE: 552,554,451,452
+   * SMTP CODE FAILURE: 451,554
+   * SMTP CODE ERROR  : 500,501,503,421
+   * @access public
+   * @return bool
+   */
+  function Data($msg_data) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Data() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"DATA" . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 354) {
+      $this->error =
+        array("error" => "DATA command not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    # the server is ready to accept data!
+    # according to rfc 821 we should not send more than 1000
+    # including the CRLF
+    # characters on a single line so we will break the data up
+    # into lines by \r and/or \n then if needed we will break
+    # each of those into smaller lines to fit within the limit.
+    # in addition we will be looking for lines that start with
+    # a period '.' and append and additional period '.' to that
+    # line. NOTE: this does not count towards are limit.
+
+    # normalize the line breaks so we know the explode works
+    $msg_data = str_replace("\r\n","\n",$msg_data);
+    $msg_data = str_replace("\r","\n",$msg_data);
+    $lines = explode("\n",$msg_data);
+
+    # we need to find a good way to determine is headers are
+    # in the msg_data or if it is a straight msg body
+    # currently I am assuming rfc 822 definitions of msg headers
+    # and if the first field of the first line (':' sperated)
+    # does not contain a space then it _should_ be a header
+    # and we can process all lines before a blank "" line as
+    # headers.
+    $field = substr($lines[0],0,strpos($lines[0],":"));
+    $in_headers = false;
+    if(!empty($field) && !strstr($field," ")) {
+      $in_headers = true;
+    }
+
+    $max_line_length = 998; # used below; set here for ease in change
+
+    while(list(,$line) = @each($lines)) {
+      $lines_out = null;
+      if($line == "" && $in_headers) {
+        $in_headers = false;
+      }
+      # ok we need to break this line up into several
+      # smaller lines
+      while(strlen($line) > $max_line_length) {
+        $pos = strrpos(substr($line,0,$max_line_length)," ");
+
+        # Patch to fix DOS attack
+        if(!$pos) {
+          $pos = $max_line_length - 1;
+        }
+
+        $lines_out[] = substr($line,0,$pos);
+        $line = substr($line,$pos + 1);
+        # if we are processing headers we need to
+        # add a LWSP-char to the front of the new line
+        # rfc 822 on long msg headers
+        if($in_headers) {
+          $line = "\t" . $line;
+        }
+      }
+      $lines_out[] = $line;
+
+      # now send the lines to the server
+      while(list(,$line_out) = @each($lines_out)) {
+        if(strlen($line_out) > 0)
+        {
+          if(substr($line_out, 0, 1) == ".") {
+            $line_out = "." . $line_out;
+          }
+        }
+        fputs($this->smtp_conn,$line_out . $this->CRLF);
+      }
+    }
+
+    # ok all the message data has been sent so lets get this
+    # over with aleady
+    fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "DATA not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Expand takes the name and asks the server to list all the
+   * people who are members of the _list_. Expand will return
+   * back and array of the result or false if an error occurs.
+   * Each value in the array returned has the format of:
+   *     [ <full-name> <sp> ] <path>
+   * The definition of <path> is defined in rfc 821
+   *
+   * Implements rfc 821: EXPN <SP> <string> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE FAILURE: 550
+   * SMTP CODE ERROR  : 500,501,502,504,421
+   * @access public
+   * @return string array
+   */
+  function Expand($name) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+            "error" => "Called Expand() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "EXPN not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    # parse the reply and place in our array to return to user
+    $entries = explode($this->CRLF,$rply);
+    while(list(,$l) = @each($entries)) {
+      $list[] = substr($l,4);
+    }
+
+    return $list;
+  }
+
+  /**
+   * Sends the HELO command to the smtp server.
+   * This makes sure that we and the server are in
+   * the same known state.
+   *
+   * Implements from rfc 821: HELO <SP> <domain> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE ERROR  : 500, 501, 504, 421
+   * @access public
+   * @return bool
+   */
+  function Hello($host="") {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+            "error" => "Called Hello() without being connected");
+      return false;
+    }
+
+    # if a hostname for the HELO was not specified determine
+    # a suitable one to send
+    if(empty($host)) {
+      # we need to determine some sort of appopiate default
+      # to send to the server
+      $host = "localhost";
+    }
+
+    // Send extended hello first (RFC 2821)
+    if(!$this->SendHello("EHLO", $host))
+    {
+      if(!$this->SendHello("HELO", $host))
+          return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Sends a HELO/EHLO command.
+   * @access private
+   * @return bool
+   */
+  function SendHello($hello, $host) {
+    fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => $hello . " not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    $this->helo_rply = $rply;
+
+    return true;
+  }
+
+  /**
+   * Gets help information on the keyword specified. If the keyword
+   * is not specified then returns generic help, ussually contianing
+   * A list of keywords that help is available on. This function
+   * returns the results back to the user. It is up to the user to
+   * handle the returned data. If an error occurs then false is
+   * returned with $this->error set appropiately.
+   *
+   * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
+   *
+   * SMTP CODE SUCCESS: 211,214
+   * SMTP CODE ERROR  : 500,501,502,504,421
+   * @access public
+   * @return string
+   */
+  function Help($keyword="") {
+    $this->error = null; # to avoid confusion
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Help() without being connected");
+      return false;
+    }
+
+    $extra = "";
+    if(!empty($keyword)) {
+      $extra = " " . $keyword;
+    }
+
+    fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 211 && $code != 214) {
+      $this->error =
+        array("error" => "HELP not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    return $rply;
+  }
+
+  /**
+   * Starts a mail transaction from the email address specified in
+   * $from. Returns true if successful or false otherwise. If True
+   * the mail transaction is started and then one or more Recipient
+   * commands may be called followed by a Data command.
+   *
+   * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE SUCCESS: 552,451,452
+   * SMTP CODE SUCCESS: 500,501,421
+   * @access public
+   * @return bool
+   */
+  function Mail($from) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Mail() without being connected");
+      return false;
+    }
+
+    $useVerp = ($this->do_verp ? "XVERP" : "");
+    fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "MAIL not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Sends the command NOOP to the SMTP server.
+   *
+   * Implements from rfc 821: NOOP <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE ERROR  : 500, 421
+   * @access public
+   * @return bool
+   */
+  function Noop() {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Noop() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"NOOP" . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "NOOP not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Sends the quit command to the server and then closes the socket
+   * if there is no error or the $close_on_error argument is true.
+   *
+   * Implements from rfc 821: QUIT <CRLF>
+   *
+   * SMTP CODE SUCCESS: 221
+   * SMTP CODE ERROR  : 500
+   * @access public
+   * @return bool
+   */
+  function Quit($close_on_error=true) {
+    $this->error = null; # so there is no confusion
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Quit() without being connected");
+      return false;
+    }
+
+    # send the quit command to the server
+    fputs($this->smtp_conn,"quit" . $this->CRLF);
+
+    # get any good-bye messages
+    $byemsg = $this->get_lines();
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
+    }
+
+    $rval = true;
+    $e = null;
+
+    $code = substr($byemsg,0,3);
+    if($code != 221) {
+      # use e as a tmp var cause Close will overwrite $this->error
+      $e = array("error" => "SMTP server rejected quit command",
+                 "smtp_code" => $code,
+                 "smtp_rply" => substr($byemsg,4));
+      $rval = false;
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $e["error"] . ": " .
+                 $byemsg . $this->CRLF;
+      }
+    }
+
+    if(empty($e) || $close_on_error) {
+      $this->Close();
+    }
+
+    return $rval;
+  }
+
+  /**
+   * Sends the command RCPT to the SMTP server with the TO: argument of $to.
+   * Returns true if the recipient was accepted false if it was rejected.
+   *
+   * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250,251
+   * SMTP CODE FAILURE: 550,551,552,553,450,451,452
+   * SMTP CODE ERROR  : 500,501,503,421
+   * @access public
+   * @return bool
+   */
+  function Recipient($to) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Recipient() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250 && $code != 251) {
+      $this->error =
+        array("error" => "RCPT not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Sends the RSET command to abort and transaction that is
+   * currently in progress. Returns true if successful false
+   * otherwise.
+   *
+   * Implements rfc 821: RSET <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE ERROR  : 500,501,504,421
+   * @access public
+   * @return bool
+   */
+  function Reset() {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Reset() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"RSET" . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "RSET failed",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Starts a mail transaction from the email address specified in
+   * $from. Returns true if successful or false otherwise. If True
+   * the mail transaction is started and then one or more Recipient
+   * commands may be called followed by a Data command. This command
+   * will send the message to the users terminal if they are logged
+   * in.
+   *
+   * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE SUCCESS: 552,451,452
+   * SMTP CODE SUCCESS: 500,501,502,421
+   * @access public
+   * @return bool
+   */
+  function Send($from) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Send() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "SEND not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Starts a mail transaction from the email address specified in
+   * $from. Returns true if successful or false otherwise. If True
+   * the mail transaction is started and then one or more Recipient
+   * commands may be called followed by a Data command. This command
+   * will send the message to the users terminal if they are logged
+   * in and send them an email.
+   *
+   * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE SUCCESS: 552,451,452
+   * SMTP CODE SUCCESS: 500,501,502,421
+   * @access public
+   * @return bool
+   */
+  function SendAndMail($from) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+          "error" => "Called SendAndMail() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "SAML not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Starts a mail transaction from the email address specified in
+   * $from. Returns true if successful or false otherwise. If True
+   * the mail transaction is started and then one or more Recipient
+   * commands may be called followed by a Data command. This command
+   * will send the message to the users terminal if they are logged
+   * in or mail it to them if they are not.
+   *
+   * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE SUCCESS: 552,451,452
+   * SMTP CODE SUCCESS: 500,501,502,421
+   * @access public
+   * @return bool
+   */
+  function SendOrMail($from) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+          "error" => "Called SendOrMail() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250) {
+      $this->error =
+        array("error" => "SOML not accepted from server",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * This is an optional command for SMTP that this class does not
+   * support. This method is here to make the RFC821 Definition
+   * complete for this class and __may__ be implimented in the future
+   *
+   * Implements from rfc 821: TURN <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250
+   * SMTP CODE FAILURE: 502
+   * SMTP CODE ERROR  : 500, 503
+   * @access public
+   * @return bool
+   */
+  function Turn() {
+    $this->error = array("error" => "This method, TURN, of the SMTP ".
+                                    "is not implemented");
+    if($this->do_debug >= 1) {
+      echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
+    }
+    return false;
+  }
+
+  /**
+   * Verifies that the name is recognized by the server.
+   * Returns false if the name could not be verified otherwise
+   * the response from the server is returned.
+   *
+   * Implements rfc 821: VRFY <SP> <string> <CRLF>
+   *
+   * SMTP CODE SUCCESS: 250,251
+   * SMTP CODE FAILURE: 550,551,553
+   * SMTP CODE ERROR  : 500,501,502,421
+   * @access public
+   * @return int
+   */
+  function Verify($name) {
+    $this->error = null; # so no confusion is caused
+
+    if(!$this->connected()) {
+      $this->error = array(
+              "error" => "Called Verify() without being connected");
+      return false;
+    }
+
+    fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
+
+    $rply = $this->get_lines();
+    $code = substr($rply,0,3);
+
+    if($this->do_debug >= 2) {
+      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
+    }
+
+    if($code != 250 && $code != 251) {
+      $this->error =
+        array("error" => "VRFY failed on name '$name'",
+              "smtp_code" => $code,
+              "smtp_msg" => substr($rply,4));
+      if($this->do_debug >= 1) {
+        echo "SMTP -> ERROR: " . $this->error["error"] .
+                 ": " . $rply . $this->CRLF;
+      }
+      return false;
+    }
+    return $rply;
+  }
+
+  /*******************************************************************
+   *                       INTERNAL FUNCTIONS                       *
+   ******************************************************************/
+
+  /**
+   * Read in as many lines as possible
+   * either before eof or socket timeout occurs on the operation.
+   * With SMTP we can tell if we have more lines to read if the
+   * 4th character is '-' symbol. If it is a space then we don't
+   * need to read anything else.
+   * @access private
+   * @return string
+   */
+  function get_lines() {
+    $data = "";
+    while($str = @fgets($this->smtp_conn,515)) {
+      if($this->do_debug >= 4) {
+        echo "SMTP -> get_lines(): \$data was \"$data\"" .
+                 $this->CRLF;
+        echo "SMTP -> get_lines(): \$str is \"$str\"" .
+                 $this->CRLF;
+      }
+      $data .= $str;
+      if($this->do_debug >= 4) {
+        echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
+      }
+      # if the 4th character is a space then we are done reading
+      # so just break the loop
+      if(substr($str,3,1) == " ") { break; }
+    }
+    return $data;
+  }
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.mailer.php	(revision 5057)
@@ -0,0 +1,1921 @@
+<?php
+// Last sync [WP11537]
+
+/*~ class.phpmailer.php
+.---------------------------------------------------------------------------.
+|  Software: PHPMailer - PHP email class                                    |
+|   Version: 2.0.4                                                          |
+|   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
+|      Info: http://phpmailer.sourceforge.net                               |
+|   Support: http://sourceforge.net/projects/phpmailer/                     |
+| ------------------------------------------------------------------------- |
+|    Author: Andy Prevost (project admininistrator)                         |
+|    Author: Brent R. Matzelle (original founder)                           |
+| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
+| Copyright (c) 2001-2003, Brent R. Matzelle                                |
+| ------------------------------------------------------------------------- |
+|   License: Distributed under the Lesser General Public License (LGPL)     |
+|            http://www.gnu.org/copyleft/lesser.html                        |
+| This program is distributed in the hope that it will be useful - WITHOUT  |
+| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
+| FITNESS FOR A PARTICULAR PURPOSE.                                         |
+| ------------------------------------------------------------------------- |
+| We offer a number of paid services (www.codeworxtech.com):                |
+| - Web Hosting on highly optimized fast and secure servers                 |
+| - Technology Consulting                                                   |
+| - Oursourcing (highly qualified programmers and graphic designers)        |
+'---------------------------------------------------------------------------'
+ */
+/**
+ * PHPMailer - PHP email transport class
+ * @package PHPMailer
+ * @author Andy Prevost
+ * @copyright 2004 - 2009 Andy Prevost
+ */
+
+class PHPMailer {
+
+  /////////////////////////////////////////////////
+  // PROPERTIES, PUBLIC
+  /////////////////////////////////////////////////
+
+  /**
+   * Email priority (1 = High, 3 = Normal, 5 = low).
+   * @var int
+   */
+  var $Priority          = 3;
+
+  /**
+   * Sets the CharSet of the message.
+   * @var string
+   */
+  var $CharSet           = 'iso-8859-1';
+
+  /**
+   * Sets the Content-type of the message.
+   * @var string
+   */
+  var $ContentType        = 'text/plain';
+
+  /**
+   * Sets the Encoding of the message. Options for this are "8bit",
+   * "7bit", "binary", "base64", and "quoted-printable".
+   * @var string
+   */
+  var $Encoding          = '8bit';
+
+  /**
+   * Holds the most recent mailer error message.
+   * @var string
+   */
+  var $ErrorInfo         = '';
+
+  /**
+   * Sets the From email address for the message.
+   * @var string
+   */
+  var $From              = 'root@localhost';
+
+  /**
+   * Sets the From name of the message.
+   * @var string
+   */
+  var $FromName          = 'Root User';
+
+  /**
+   * Sets the Sender email (Return-Path) of the message.  If not empty,
+   * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+   * @var string
+   */
+  var $Sender            = '';
+
+  /**
+   * Sets the Subject of the message.
+   * @var string
+   */
+  var $Subject           = '';
+
+  /**
+   * Sets the Body of the message.  This can be either an HTML or text body.
+   * If HTML then run IsHTML(true).
+   * @var string
+   */
+  var $Body              = '';
+
+  /**
+   * Sets the text-only body of the message.  This automatically sets the
+   * email to multipart/alternative.  This body can be read by mail
+   * clients that do not have HTML email capability such as mutt. Clients
+   * that can read HTML will view the normal Body.
+   * @var string
+   */
+  var $AltBody           = '';
+
+  /**
+   * Sets word wrapping on the body of the message to a given number of
+   * characters.
+   * @var int
+   */
+  var $WordWrap          = 0;
+
+  /**
+   * Method to send mail: ("mail", "sendmail", or "smtp").
+   * @var string
+   */
+  var $Mailer            = 'mail';
+
+  /**
+   * Sets the path of the sendmail program.
+   * @var string
+   */
+  var $Sendmail          = '/usr/sbin/sendmail';
+
+  /**
+   * Path to PHPMailer plugins.  This is now only useful if the SMTP class
+   * is in a different directory than the PHP include path.
+   * @var string
+   */
+  var $PluginDir         = '';
+
+  /**
+   * Holds PHPMailer version.
+   * @var string
+   */
+  var $Version           = "2.0.4";
+
+  /**
+   * Sets the email address that a reading confirmation will be sent.
+   * @var string
+   */
+  var $ConfirmReadingTo  = '';
+
+  /**
+   * Sets the hostname to use in Message-Id and Received headers
+   * and as default HELO string. If empty, the value returned
+   * by SERVER_NAME is used or 'localhost.localdomain'.
+   * @var string
+   */
+  var $Hostname          = '';
+
+  /**
+   * Sets the message ID to be used in the Message-Id header.
+   * If empty, a unique id will be generated.
+   * @var string
+   */
+  var $MessageID         = '';
+
+  /////////////////////////////////////////////////
+  // PROPERTIES FOR SMTP
+  /////////////////////////////////////////////////
+
+  /**
+   * Sets the SMTP hosts.  All hosts must be separated by a
+   * semicolon.  You can also specify a different port
+   * for each host by using this format: [hostname:port]
+   * (e.g. "smtp1.example.com:25;smtp2.example.com").
+   * Hosts will be tried in order.
+   * @var string
+   */
+  var $Host        = 'localhost';
+
+  /**
+   * Sets the default SMTP server port.
+   * @var int
+   */
+  var $Port        = 25;
+
+  /**
+   * Sets the SMTP HELO of the message (Default is $Hostname).
+   * @var string
+   */
+  var $Helo        = '';
+
+  /**
+   * Sets connection prefix.
+   * Options are "", "ssl" or "tls"
+   * @var string
+   */
+  var $SMTPSecure = "";
+
+  /**
+   * Sets SMTP authentication. Utilizes the Username and Password variables.
+   * @var bool
+   */
+  var $SMTPAuth     = false;
+
+  /**
+   * Sets SMTP username.
+   * @var string
+   */
+  var $Username     = '';
+
+  /**
+   * Sets SMTP password.
+   * @var string
+   */
+  var $Password     = '';
+
+  /**
+   * Sets the SMTP server timeout in seconds. This function will not
+   * work with the win32 version.
+   * @var int
+   */
+  var $Timeout      = 10;
+
+  /**
+   * Sets SMTP class debugging on or off.
+   * @var bool
+   */
+  var $SMTPDebug    = false;
+
+  /**
+   * Prevents the SMTP connection from being closed after each mail
+   * sending.  If this is set to true then to close the connection
+   * requires an explicit call to SmtpClose().
+   * @var bool
+   */
+  var $SMTPKeepAlive = false;
+
+  /**
+   * Provides the ability to have the TO field process individual
+   * emails, instead of sending to entire TO addresses
+   * @var bool
+   */
+  var $SingleTo = false;
+
+  /////////////////////////////////////////////////
+  // PROPERTIES, PRIVATE
+  /////////////////////////////////////////////////
+
+  var $smtp            = NULL;
+  var $to              = array();
+  var $cc              = array();
+  var $bcc             = array();
+  var $ReplyTo         = array();
+  var $attachment      = array();
+  var $CustomHeader    = array();
+  var $message_type    = '';
+  var $boundary        = array();
+  var $language        = array();
+  var $error_count     = 0;
+  var $LE              = "\n";
+  var $sign_cert_file  = "";
+  var $sign_key_file   = "";
+  var $sign_key_pass   = "";
+
+  /////////////////////////////////////////////////
+  // METHODS, VARIABLES
+  /////////////////////////////////////////////////
+
+  /**
+   * Sets message type to HTML.
+   * @param bool $bool
+   * @return void
+   */
+  function IsHTML($bool) {
+    if($bool == true) {
+      $this->ContentType = 'text/html';
+    } else {
+      $this->ContentType = 'text/plain';
+    }
+  }
+
+  /**
+   * Sets Mailer to send message using SMTP.
+   * @return void
+   */
+  function IsSMTP() {
+    $this->Mailer = 'smtp';
+  }
+
+  /**
+   * Sets Mailer to send message using PHP mail() function.
+   * @return void
+   */
+  function IsMail() {
+    $this->Mailer = 'mail';
+  }
+
+  /**
+   * Sets Mailer to send message using the $Sendmail program.
+   * @return void
+   */
+  function IsSendmail() {
+    $this->Mailer = 'sendmail';
+  }
+
+  /**
+   * Sets Mailer to send message using the qmail MTA.
+   * @return void
+   */
+  function IsQmail() {
+    $this->Sendmail = '/var/qmail/bin/sendmail';
+    $this->Mailer = 'sendmail';
+  }
+
+  /////////////////////////////////////////////////
+  // METHODS, RECIPIENTS
+  /////////////////////////////////////////////////
+
+  /**
+   * Adds a "To" address.
+   * @param string $address
+   * @param string $name
+   * @return void
+   */
+  function AddAddress($address, $name = '') {
+    $cur = count($this->to);
+    $this->to[$cur][0] = trim($address);
+    $this->to[$cur][1] = $name;
+  }
+
+  /**
+   * Adds a "Cc" address. Note: this function works
+   * with the SMTP mailer on win32, not with the "mail"
+   * mailer.
+   * @param string $address
+   * @param string $name
+   * @return void
+   */
+  function AddCC($address, $name = '') {
+    $cur = count($this->cc);
+    $this->cc[$cur][0] = trim($address);
+    $this->cc[$cur][1] = $name;
+  }
+
+  /**
+   * Adds a "Bcc" address. Note: this function works
+   * with the SMTP mailer on win32, not with the "mail"
+   * mailer.
+   * @param string $address
+   * @param string $name
+   * @return void
+   */
+  function AddBCC($address, $name = '') {
+    $cur = count($this->bcc);
+    $this->bcc[$cur][0] = trim($address);
+    $this->bcc[$cur][1] = $name;
+  }
+
+  /**
+   * Adds a "Reply-To" address.
+   * @param string $address
+   * @param string $name
+   * @return void
+   */
+  function AddReplyTo($address, $name = '') {
+    $cur = count($this->ReplyTo);
+    $this->ReplyTo[$cur][0] = trim($address);
+    $this->ReplyTo[$cur][1] = $name;
+  }
+
+  /////////////////////////////////////////////////
+  // METHODS, MAIL SENDING
+  /////////////////////////////////////////////////
+
+  /**
+   * Creates message and assigns Mailer. If the message is
+   * not sent successfully then it returns false.  Use the ErrorInfo
+   * variable to view description of the error.
+   * @return bool
+   */
+  function Send() {
+    $header = '';
+    $body = '';
+    $result = true;
+
+    if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
+      $this->SetError($this->Lang('provide_address'));
+      return false;
+    }
+
+    /* Set whether the message is multipart/alternative */
+    if(!empty($this->AltBody)) {
+      $this->ContentType = 'multipart/alternative';
+    }
+
+    $this->error_count = 0; // reset errors
+    $this->SetMessageType();
+    $header .= $this->CreateHeader();
+    $body = $this->CreateBody();
+
+    if($body == '') {
+      return false;
+    }
+
+    /* Choose the mailer */
+    switch($this->Mailer) {
+      case 'sendmail':
+        $result = $this->SendmailSend($header, $body);
+        break;
+      case 'smtp':
+        $result = $this->SmtpSend($header, $body);
+        break;
+      case 'mail':
+        $result = $this->MailSend($header, $body);
+        break;
+      default:
+        $result = $this->MailSend($header, $body);
+        break;
+        //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported'));
+        //$result = false;
+        //break;
+    }
+
+    return $result;
+  }
+
+  /**
+   * Sends mail using the $Sendmail program.
+   * @access private
+   * @return bool
+   */
+  function SendmailSend($header, $body) {
+    if ($this->Sender != '') {
+      $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+    } else {
+      $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
+    }
+
+    if(!@$mail = popen($sendmail, 'w')) {
+      $this->SetError($this->Lang('execute') . $this->Sendmail);
+      return false;
+    }
+
+    fputs($mail, $header);
+    fputs($mail, $body);
+
+    $result = pclose($mail);
+    if (version_compare(phpversion(), '4.2.3') == -1) {
+      $result = $result >> 8 & 0xFF;
+    }
+    if($result != 0) {
+      $this->SetError($this->Lang('execute') . $this->Sendmail);
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Sends mail using the PHP mail() function.
+   * @access private
+   * @return bool
+   */
+  function MailSend($header, $body) {
+
+    $to = '';
+    for($i = 0; $i < count($this->to); $i++) {
+      if($i != 0) { $to .= ', '; }
+      $to .= $this->AddrFormat($this->to[$i]);
+    }
+
+    $toArr = split(',', $to);
+
+    $params = sprintf("-oi -f %s", $this->Sender);
+    if ($this->Sender != '' && strlen(ini_get('safe_mode')) < 1) {
+      $old_from = ini_get('sendmail_from');
+      ini_set('sendmail_from', $this->Sender);
+      if ($this->SingleTo === true && count($toArr) > 1) {
+        foreach ($toArr as $key => $val) {
+          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
+        }
+      } else {
+        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
+      }
+    } else {
+      if ($this->SingleTo === true && count($toArr) > 1) {
+        foreach ($toArr as $key => $val) {
+          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
+        }
+      } else {
+        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
+      }
+    }
+
+    if (isset($old_from)) {
+      ini_set('sendmail_from', $old_from);
+    }
+
+    if(!$rt) {
+      $this->SetError($this->Lang('instantiate'));
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Sends mail via SMTP using PhpSMTP (Author:
+   * Chris Ryan).  Returns bool.  Returns false if there is a
+   * bad MAIL FROM, RCPT, or DATA input.
+   * @access private
+   * @return bool
+   */
+  function SmtpSend($header, $body) {
+    include_once('class.mailer-smtp.php');
+    $error = '';
+    $bad_rcpt = array();
+
+    if(!$this->SmtpConnect()) {
+      return false;
+    }
+
+    $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
+    if(!$this->smtp->Mail($smtp_from)) {
+      $error = $this->Lang('from_failed') . $smtp_from;
+      $this->SetError($error);
+      $this->smtp->Reset();
+      return false;
+    }
+
+    /* Attempt to send attach all recipients */
+    for($i = 0; $i < count($this->to); $i++) {
+      if(!$this->smtp->Recipient($this->to[$i][0])) {
+        $bad_rcpt[] = $this->to[$i][0];
+      }
+    }
+    for($i = 0; $i < count($this->cc); $i++) {
+      if(!$this->smtp->Recipient($this->cc[$i][0])) {
+        $bad_rcpt[] = $this->cc[$i][0];
+      }
+    }
+    for($i = 0; $i < count($this->bcc); $i++) {
+      if(!$this->smtp->Recipient($this->bcc[$i][0])) {
+        $bad_rcpt[] = $this->bcc[$i][0];
+      }
+    }
+
+    if(count($bad_rcpt) > 0) { // Create error message
+      for($i = 0; $i < count($bad_rcpt); $i++) {
+        if($i != 0) {
+          $error .= ', ';
+        }
+        $error .= $bad_rcpt[$i];
+      }
+      $error = $this->Lang('recipients_failed') . $error;
+      $this->SetError($error);
+      $this->smtp->Reset();
+      return false;
+    }
+
+    if(!$this->smtp->Data($header . $body)) {
+      $this->SetError($this->Lang('data_not_accepted'));
+      $this->smtp->Reset();
+      return false;
+    }
+    if($this->SMTPKeepAlive == true) {
+      $this->smtp->Reset();
+    } else {
+      $this->SmtpClose();
+    }
+
+    return true;
+  }
+
+  /**
+   * Initiates a connection to an SMTP server.  Returns false if the
+   * operation failed.
+   * @access private
+   * @return bool
+   */
+  function SmtpConnect() {
+    if($this->smtp == NULL) {
+      $this->smtp = new SMTP();
+    }
+
+    $this->smtp->do_debug = $this->SMTPDebug;
+    $hosts = explode(';', $this->Host);
+    $index = 0;
+    $connection = ($this->smtp->Connected());
+
+    /* Retry while there is no connection */
+    while($index < count($hosts) && $connection == false) {
+      $hostinfo = array();
+      if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) {
+        $host = $hostinfo[1];
+        $port = $hostinfo[2];
+      } else {
+        $host = $hosts[$index];
+        $port = $this->Port;
+      }
+
+      if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) {
+        if ($this->Helo != '') {
+          $this->smtp->Hello($this->Helo);
+        } else {
+          $this->smtp->Hello($this->ServerHostname());
+        }
+
+        $connection = true;
+        if($this->SMTPAuth) {
+          if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
+            $this->SetError($this->Lang('authenticate'));
+            $this->smtp->Reset();
+            $connection = false;
+          }
+        }
+      }
+      $index++;
+    }
+    if(!$connection) {
+      $this->SetError($this->Lang('connect_host'));
+    }
+
+    return $connection;
+  }
+
+  /**
+   * Closes the active SMTP session if one exists.
+   * @return void
+   */
+  function SmtpClose() {
+    if($this->smtp != NULL) {
+      if($this->smtp->Connected()) {
+        $this->smtp->Quit();
+        $this->smtp->Close();
+      }
+    }
+  }
+
+  /**
+   * Sets the language for all class error messages.  Returns false
+   * if it cannot load the language file.  The default language type
+   * is English.
+   * @param string $lang_type Type of language (e.g. Portuguese: "br")
+   * @param string $lang_path Path to the language file directory
+   * @access public
+   * @return bool
+   */
+  function SetLanguage($lang_type, $lang_path = 'language/') {
+    if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) {
+      include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
+    } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) {
+      include($lang_path.'phpmailer.lang-en.php');
+    } else {
+      $PHPMAILER_LANG = array();
+      $PHPMAILER_LANG["provide_address"]      = 'You must provide at least one ' .
+      $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.';
+      $PHPMAILER_LANG["execute"]              = 'Could not execute: ';
+      $PHPMAILER_LANG["instantiate"]          = 'Could not instantiate mail function.';
+      $PHPMAILER_LANG["authenticate"]         = 'SMTP Error: Could not authenticate.';
+      $PHPMAILER_LANG["from_failed"]          = 'The following From address failed: ';
+      $PHPMAILER_LANG["recipients_failed"]    = 'SMTP Error: The following ' .
+      $PHPMAILER_LANG["data_not_accepted"]    = 'SMTP Error: Data not accepted.';
+      $PHPMAILER_LANG["connect_host"]         = 'SMTP Error: Could not connect to SMTP host.';
+      $PHPMAILER_LANG["file_access"]          = 'Could not access file: ';
+      $PHPMAILER_LANG["file_open"]            = 'File Error: Could not open file: ';
+      $PHPMAILER_LANG["encoding"]             = 'Unknown encoding: ';
+      $PHPMAILER_LANG["signing"]              = 'Signing Error: ';
+    }
+    $this->language = $PHPMAILER_LANG;
+
+    return true;
+  }
+
+  /////////////////////////////////////////////////
+  // METHODS, MESSAGE CREATION
+  /////////////////////////////////////////////////
+
+  /**
+   * Creates recipient headers.
+   * @access private
+   * @return string
+   */
+  function AddrAppend($type, $addr) {
+    $addr_str = $type . ': ';
+    $addr_str .= $this->AddrFormat($addr[0]);
+    if(count($addr) > 1) {
+      for($i = 1; $i < count($addr); $i++) {
+        $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
+      }
+    }
+    $addr_str .= $this->LE;
+
+    return $addr_str;
+  }
+
+  /**
+   * Formats an address correctly.
+   * @access private
+   * @return string
+   */
+  function AddrFormat($addr) {
+    if(empty($addr[1])) {
+      $formatted = $this->SecureHeader($addr[0]);
+    } else {
+      $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
+    }
+
+    return $formatted;
+  }
+
+  /**
+   * Wraps message for use with mailers that do not
+   * automatically perform wrapping and for quoted-printable.
+   * Original written by philippe.
+   * @access private
+   * @return string
+   */
+  function WrapText($message, $length, $qp_mode = false) {
+    $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
+    // If utf-8 encoding is used, we will need to make sure we don't
+    // split multibyte characters when we wrap
+    $is_utf8 = (strtolower($this->CharSet) == "utf-8");
+
+    $message = $this->FixEOL($message);
+    if (substr($message, -1) == $this->LE) {
+      $message = substr($message, 0, -1);
+    }
+
+    $line = explode($this->LE, $message);
+    $message = '';
+    for ($i=0 ;$i < count($line); $i++) {
+      $line_part = explode(' ', $line[$i]);
+      $buf = '';
+      for ($e = 0; $e<count($line_part); $e++) {
+        $word = $line_part[$e];
+        if ($qp_mode and (strlen($word) > $length)) {
+          $space_left = $length - strlen($buf) - 1;
+          if ($e != 0) {
+            if ($space_left > 20) {
+              $len = $space_left;
+              if ($is_utf8) {
+                $len = $this->UTF8CharBoundary($word, $len);
+              } elseif (substr($word, $len - 1, 1) == "=") {
+                $len--;
+              } elseif (substr($word, $len - 2, 1) == "=") {
+                $len -= 2;
+              }
+              $part = substr($word, 0, $len);
+              $word = substr($word, $len);
+              $buf .= ' ' . $part;
+              $message .= $buf . sprintf("=%s", $this->LE);
+            } else {
+              $message .= $buf . $soft_break;
+            }
+            $buf = '';
+          }
+          while (strlen($word) > 0) {
+            $len = $length;
+            if ($is_utf8) {
+              $len = $this->UTF8CharBoundary($word, $len);
+            } elseif (substr($word, $len - 1, 1) == "=") {
+              $len--;
+            } elseif (substr($word, $len - 2, 1) == "=") {
+              $len -= 2;
+            }
+            $part = substr($word, 0, $len);
+            $word = substr($word, $len);
+
+            if (strlen($word) > 0) {
+              $message .= $part . sprintf("=%s", $this->LE);
+            } else {
+              $buf = $part;
+            }
+          }
+        } else {
+          $buf_o = $buf;
+          $buf .= ($e == 0) ? $word : (' ' . $word);
+
+          if (strlen($buf) > $length and $buf_o != '') {
+            $message .= $buf_o . $soft_break;
+            $buf = $word;
+          }
+        }
+      }
+      $message .= $buf . $this->LE;
+    }
+
+    return $message;
+  }
+
+  /**
+   * Finds last character boundary prior to maxLength in a utf-8
+   * quoted (printable) encoded string.
+   * Original written by Colin Brown.
+   * @access private
+   * @param string $encodedText utf-8 QP text
+   * @param int    $maxLength   find last character boundary prior to this length
+   * @return int
+   */
+  function UTF8CharBoundary($encodedText, $maxLength) {
+    $foundSplitPos = false;
+    $lookBack = 3;
+    while (!$foundSplitPos) {
+      $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
+      $encodedCharPos = strpos($lastChunk, "=");
+      if ($encodedCharPos !== false) {
+        // Found start of encoded character byte within $lookBack block.
+        // Check the encoded byte value (the 2 chars after the '=')
+        $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
+        $dec = hexdec($hex);
+        if ($dec < 128) { // Single byte character.
+          // If the encoded char was found at pos 0, it will fit
+          // otherwise reduce maxLength to start of the encoded char
+          $maxLength = ($encodedCharPos == 0) ? $maxLength :
+          $maxLength - ($lookBack - $encodedCharPos);
+          $foundSplitPos = true;
+        } elseif ($dec >= 192) { // First byte of a multi byte character
+          // Reduce maxLength to split at start of character
+          $maxLength = $maxLength - ($lookBack - $encodedCharPos);
+          $foundSplitPos = true;
+        } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
+          $lookBack += 3;
+        }
+      } else {
+        // No encoded character found
+        $foundSplitPos = true;
+      }
+    }
+    return $maxLength;
+  }
+
+  /**
+   * Set the body wrapping.
+   * @access private
+   * @return void
+   */
+  function SetWordWrap() {
+    if($this->WordWrap < 1) {
+      return;
+    }
+
+    switch($this->message_type) {
+      case 'alt':
+        /* fall through */
+      case 'alt_attachments':
+        $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
+        break;
+      default:
+        $this->Body = $this->WrapText($this->Body, $this->WordWrap);
+        break;
+    }
+  }
+
+  /**
+   * Assembles message header.
+   * @access private
+   * @return string
+   */
+  function CreateHeader() {
+    $result = '';
+
+    /* Set the boundaries */
+    $uniq_id = md5(uniqid(time()));
+    $this->boundary[1] = 'b1_' . $uniq_id;
+    $this->boundary[2] = 'b2_' . $uniq_id;
+
+    $result .= $this->HeaderLine('Date', $this->RFCDate());
+    if($this->Sender == '') {
+      $result .= $this->HeaderLine('Return-Path', trim($this->From));
+    } else {
+      $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
+    }
+
+    /* To be created automatically by mail() */
+    if($this->Mailer != 'mail') {
+      if(count($this->to) > 0) {
+        $result .= $this->AddrAppend('To', $this->to);
+      } elseif (count($this->cc) == 0) {
+        $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
+      }
+    }
+
+    $from = array();
+    $from[0][0] = trim($this->From);
+    $from[0][1] = $this->FromName;
+    $result .= $this->AddrAppend('From', $from);
+
+    /* sendmail and mail() extract Cc from the header before sending */
+    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
+      $result .= $this->AddrAppend('Cc', $this->cc);
+    }
+
+    /* sendmail and mail() extract Bcc from the header before sending */
+    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
+      $result .= $this->AddrAppend('Bcc', $this->bcc);
+    }
+
+    if(count($this->ReplyTo) > 0) {
+      $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);
+    }
+
+    /* mail() sets the subject itself */
+    if($this->Mailer != 'mail') {
+      $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
+    }
+
+    if($this->MessageID != '') {
+      $result .= $this->HeaderLine('Message-ID',$this->MessageID);
+    } else {
+      $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
+    }
+    $result .= $this->HeaderLine('X-Priority', $this->Priority);
+    $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']');
+
+    if($this->ConfirmReadingTo != '') {
+      $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
+    }
+
+    // Add custom headers
+    for($index = 0; $index < count($this->CustomHeader); $index++) {
+      $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
+    }
+    if (!$this->sign_key_file) {
+      $result .= $this->HeaderLine('MIME-Version', '1.0');
+      $result .= $this->GetMailMIME();
+    }
+
+    return $result;
+  }
+
+  /**
+   * Returns the message MIME.
+   * @access private
+   * @return string
+   */
+  function GetMailMIME() {
+    $result = '';
+    switch($this->message_type) {
+      case 'plain':
+        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
+        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
+        break;
+      case 'attachments':
+        /* fall through */
+      case 'alt_attachments':
+        if($this->InlineImageExists()){
+          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
+        } else {
+          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
+          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
+        }
+        break;
+      case 'alt':
+        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
+        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
+        break;
+    }
+
+    if($this->Mailer != 'mail') {
+      $result .= $this->LE.$this->LE;
+    }
+
+    return $result;
+  }
+
+  /**
+   * Assembles the message body.  Returns an empty string on failure.
+   * @access private
+   * @return string
+   */
+  function CreateBody() {
+    $result = '';
+    if ($this->sign_key_file) {
+      $result .= $this->GetMailMIME();
+    }
+
+    $this->SetWordWrap();
+
+    switch($this->message_type) {
+      case 'alt':
+        $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
+        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
+        $result .= $this->LE.$this->LE;
+        $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
+        $result .= $this->EncodeString($this->Body, $this->Encoding);
+        $result .= $this->LE.$this->LE;
+        $result .= $this->EndBoundary($this->boundary[1]);
+        break;
+      case 'plain':
+        $result .= $this->EncodeString($this->Body, $this->Encoding);
+        break;
+      case 'attachments':
+        $result .= $this->GetBoundary($this->boundary[1], '', '', '');
+        $result .= $this->EncodeString($this->Body, $this->Encoding);
+        $result .= $this->LE;
+        $result .= $this->AttachAll();
+        break;
+      case 'alt_attachments':
+        $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
+        $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
+        $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
+        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
+        $result .= $this->LE.$this->LE;
+        $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
+        $result .= $this->EncodeString($this->Body, $this->Encoding);
+        $result .= $this->LE.$this->LE;
+        $result .= $this->EndBoundary($this->boundary[2]);
+        $result .= $this->AttachAll();
+        break;
+    }
+
+    if($this->IsError()) {
+      $result = '';
+    } else if ($this->sign_key_file) {
+      $file = tempnam("", "mail");
+      $fp = fopen($file, "w");
+      fwrite($fp, $result);
+      fclose($fp);
+      $signed = tempnam("", "signed");
+
+      if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), null)) {
+        $fp = fopen($signed, "r");
+        $result = fread($fp, filesize($this->sign_key_file));
+        $result = '';
+        while(!feof($fp)){
+          $result = $result . fread($fp, 1024);
+        }
+        fclose($fp);
+      } else {
+        $this->SetError($this->Lang("signing").openssl_error_string());
+        $result = '';
+      }
+
+      unlink($file);
+      unlink($signed);
+    }
+
+    return $result;
+  }
+
+  /**
+   * Returns the start of a message boundary.
+   * @access private
+   */
+  function GetBoundary($boundary, $charSet, $contentType, $encoding) {
+    $result = '';
+    if($charSet == '') {
+      $charSet = $this->CharSet;
+    }
+    if($contentType == '') {
+      $contentType = $this->ContentType;
+    }
+    if($encoding == '') {
+      $encoding = $this->Encoding;
+    }
+    $result .= $this->TextLine('--' . $boundary);
+    $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
+    $result .= $this->LE;
+    $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
+    $result .= $this->LE;
+
+    return $result;
+  }
+
+  /**
+   * Returns the end of a message boundary.
+   * @access private
+   */
+  function EndBoundary($boundary) {
+    return $this->LE . '--' . $boundary . '--' . $this->LE;
+  }
+
+  /**
+   * Sets the message type.
+   * @access private
+   * @return void
+   */
+  function SetMessageType() {
+    if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
+      $this->message_type = 'plain';
+    } else {
+      if(count($this->attachment) > 0) {
+        $this->message_type = 'attachments';
+      }
+      if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
+        $this->message_type = 'alt';
+      }
+      if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
+        $this->message_type = 'alt_attachments';
+      }
+    }
+  }
+
+  /* Returns a formatted header line.
+   * @access private
+   * @return string
+   */
+  function HeaderLine($name, $value) {
+    return $name . ': ' . $value . $this->LE;
+  }
+
+  /**
+   * Returns a formatted mail line.
+   * @access private
+   * @return string
+   */
+  function TextLine($value) {
+    return $value . $this->LE;
+  }
+
+  /////////////////////////////////////////////////
+  // CLASS METHODS, ATTACHMENTS
+  /////////////////////////////////////////////////
+
+  /**
+   * Adds an attachment from a path on the filesystem.
+   * Returns false if the file could not be found
+   * or accessed.
+   * @param string $path Path to the attachment.
+   * @param string $name Overrides the attachment name.
+   * @param string $encoding File encoding (see $Encoding).
+   * @param string $type File extension (MIME) type.
+   * @return bool
+   */
+  function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
+    if(!@is_file($path)) {
+      $this->SetError($this->Lang('file_access') . $path);
+      return false;
+    }
+
+    $filename = basename($path);
+    if($name == '') {
+      $name = $filename;
+    }
+
+    $cur = count($this->attachment);
+    $this->attachment[$cur][0] = $path;
+    $this->attachment[$cur][1] = $filename;
+    $this->attachment[$cur][2] = $name;
+    $this->attachment[$cur][3] = $encoding;
+    $this->attachment[$cur][4] = $type;
+    $this->attachment[$cur][5] = false; // isStringAttachment
+    $this->attachment[$cur][6] = 'attachment';
+    $this->attachment[$cur][7] = 0;
+
+    return true;
+  }
+
+  /**
+   * Attaches all fs, string, and binary attachments to the message.
+   * Returns an empty string on failure.
+   * @access private
+   * @return string
+   */
+  function AttachAll() {
+    /* Return text of body */
+    $mime = array();
+
+    /* Add all attachments */
+    for($i = 0; $i < count($this->attachment); $i++) {
+      /* Check for string attachment */
+      $bString = $this->attachment[$i][5];
+      if ($bString) {
+        $string = $this->attachment[$i][0];
+      } else {
+        $path = $this->attachment[$i][0];
+      }
+
+      $filename    = $this->attachment[$i][1];
+      $name        = $this->attachment[$i][2];
+      $encoding    = $this->attachment[$i][3];
+      $type        = $this->attachment[$i][4];
+      $disposition = $this->attachment[$i][6];
+      $cid         = $this->attachment[$i][7];
+
+      $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
+      $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
+      $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
+
+      if($disposition == 'inline') {
+        $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
+      }
+
+      $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
+
+      /* Encode as string attachment */
+      if($bString) {
+        $mime[] = $this->EncodeString($string, $encoding);
+        if($this->IsError()) {
+          return '';
+        }
+        $mime[] = $this->LE.$this->LE;
+      } else {
+        $mime[] = $this->EncodeFile($path, $encoding);
+        if($this->IsError()) {
+          return '';
+        }
+        $mime[] = $this->LE.$this->LE;
+      }
+    }
+
+    $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
+
+    return join('', $mime);
+  }
+
+  /**
+   * Encodes attachment in requested format.  Returns an
+   * empty string on failure.
+   * @access private
+   * @return string
+   */
+  function EncodeFile ($path, $encoding = 'base64') {
+    if(!@$fd = fopen($path, 'rb')) {
+      $this->SetError($this->Lang('file_open') . $path);
+      return '';
+    }
+    $magic_quotes = get_magic_quotes_runtime();
+    set_magic_quotes_runtime(0);
+    $file_buffer = fread($fd, filesize($path));
+    $file_buffer = $this->EncodeString($file_buffer, $encoding);
+    fclose($fd);
+    set_magic_quotes_runtime($magic_quotes);
+
+    return $file_buffer;
+  }
+
+  /**
+   * Encodes string to requested format. Returns an
+   * empty string on failure.
+   * @access private
+   * @return string
+   */
+  function EncodeString ($str, $encoding = 'base64') {
+    $encoded = '';
+    switch(strtolower($encoding)) {
+      case 'base64':
+        /* chunk_split is found in PHP >= 3.0.6 */
+        $encoded = chunk_split(base64_encode($str), 76, $this->LE);
+        break;
+      case '7bit':
+      case '8bit':
+        $encoded = $this->FixEOL($str);
+        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
+          $encoded .= $this->LE;
+        break;
+      case 'binary':
+        $encoded = $str;
+        break;
+      case 'quoted-printable':
+        $encoded = $this->EncodeQP($str);
+        break;
+      default:
+        $this->SetError($this->Lang('encoding') . $encoding);
+        break;
+    }
+    return $encoded;
+  }
+
+  /**
+   * Encode a header string to best of Q, B, quoted or none.
+   * @access private
+   * @return string
+   */
+  function EncodeHeader ($str, $position = 'text') {
+    $x = 0;
+
+    switch (strtolower($position)) {
+      case 'phrase':
+        if (!preg_match('/[\200-\377]/', $str)) {
+          /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
+          $encoded = addcslashes($str, "\0..\37\177\\\"");
+          if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
+            return ($encoded);
+          } else {
+            return ("\"$encoded\"");
+          }
+        }
+        $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
+        break;
+      case 'comment':
+        $x = preg_match_all('/[()"]/', $str, $matches);
+        /* Fall-through */
+      case 'text':
+      default:
+        $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+        break;
+    }
+
+    if ($x == 0) {
+      return ($str);
+    }
+
+    $maxlen = 75 - 7 - strlen($this->CharSet);
+    /* Try to select the encoding which should produce the shortest output */
+    if (strlen($str)/3 < $x) {
+      $encoding = 'B';
+      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
+     // Use a custom function which correctly encodes and wraps long
+     // multibyte strings without breaking lines within a character
+        $encoded = $this->Base64EncodeWrapMB($str);
+      } else {
+        $encoded = base64_encode($str);
+        $maxlen -= $maxlen % 4;
+        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
+      }
+    } else {
+      $encoding = 'Q';
+      $encoded = $this->EncodeQ($str, $position);
+      $encoded = $this->WrapText($encoded, $maxlen, true);
+      $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
+    }
+
+    $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
+    $encoded = trim(str_replace("\n", $this->LE, $encoded));
+
+    return $encoded;
+  }
+
+  /**
+   * Checks if a string contains multibyte characters.
+   * @access private
+   * @param string $str multi-byte text to wrap encode
+   * @return bool
+   */
+  function HasMultiBytes($str) {
+    if (function_exists('mb_strlen')) {
+      return (strlen($str) > mb_strlen($str, $this->CharSet));
+    } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
+      return False;
+    }
+  }
+
+  /**
+   * Correctly encodes and wraps long multibyte strings for mail headers
+   * without breaking lines within a character.
+   * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
+   * @access private
+   * @param string $str multi-byte text to wrap encode
+   * @return string
+   */
+  function Base64EncodeWrapMB($str) {
+    $start = "=?".$this->CharSet."?B?";
+    $end = "?=";
+    $encoded = "";
+
+    $mb_length = mb_strlen($str, $this->CharSet);
+    // Each line must have length <= 75, including $start and $end
+    $length = 75 - strlen($start) - strlen($end);
+    // Average multi-byte ratio
+    $ratio = $mb_length / strlen($str);
+    // Base64 has a 4:3 ratio
+    $offset = $avgLength = floor($length * $ratio * .75);
+
+    for ($i = 0; $i < $mb_length; $i += $offset) {
+      $lookBack = 0;
+
+      do {
+        $offset = $avgLength - $lookBack;
+        $chunk = mb_substr($str, $i, $offset, $this->CharSet);
+        $chunk = base64_encode($chunk);
+        $lookBack++;
+      }
+      while (strlen($chunk) > $length);
+
+      $encoded .= $chunk . $this->LE;
+    }
+
+    // Chomp the last linefeed
+    $encoded = substr($encoded, 0, -strlen($this->LE));
+    return $encoded;
+  }
+
+  /**
+   * Encode string to quoted-printable.
+   * @access private
+   * @return string
+   */
+  function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) {
+    $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+    $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
+    $eol = "\r\n";
+    $escape = '=';
+    $output = '';
+    while( list(, $line) = each($lines) ) {
+      $linlen = strlen($line);
+      $newline = '';
+      for($i = 0; $i < $linlen; $i++) {
+        $c = substr( $line, $i, 1 );
+        $dec = ord( $c );
+        if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
+          $c = '=2E';
+        }
+        if ( $dec == 32 ) {
+          if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
+            $c = '=20';
+          } else if ( $space_conv ) {
+            $c = '=20';
+          }
+        } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
+          $h2 = floor($dec/16);
+          $h1 = floor($dec%16);
+          $c = $escape.$hex[$h2].$hex[$h1];
+        }
+        if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
+          $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
+          $newline = '';
+          // check if newline first character will be point or not
+          if ( $dec == 46 ) {
+            $c = '=2E';
+          }
+        }
+        $newline .= $c;
+      } // end of for
+      $output .= $newline.$eol;
+    } // end of while
+    return $output;
+  }
+
+  /**
+   * Callback for converting to "=XX".
+   * @access private
+   * @return string
+   */
+  function EncodeQ_callback ($matches) {
+    return sprintf('=%02X', ord($matches[1]));
+  }
+
+  /**
+   * Encode string to q encoding.
+   * @access private
+   * @return string
+   */
+  function EncodeQ ($str, $position = 'text') {
+    /* There should not be any EOL in the string */
+    $encoded = preg_replace("/[\r\n]/", '', $str);
+
+    switch (strtolower($position)) {
+      case 'phrase':
+        $encoded = preg_replace_callback("/([^A-Za-z0-9!*+\/ -])/",
+                                         array('PHPMailer', 'EncodeQ_callback'), $encoded);
+        break;
+      case 'comment':
+        $encoded = preg_replace_callback("/([\(\)\"])/",
+                                         array('PHPMailer', 'EncodeQ_callback'), $encoded);
+        break;
+      case 'text':
+      default:
+        /* Replace every high ascii, control =, ? and _ characters */
+        $encoded = preg_replace_callback('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/',
+                                         array('PHPMailer', 'EncodeQ_callback'), $encoded);
+        break;
+    }
+
+    /* Replace every spaces to _ (more readable than =20) */
+    $encoded = str_replace(' ', '_', $encoded);
+
+    return $encoded;
+  }
+
+  /**
+   * Adds a string or binary attachment (non-filesystem) to the list.
+   * This method can be used to attach ascii or binary data,
+   * such as a BLOB record from a database.
+   * @param string $string String attachment data.
+   * @param string $filename Name of the attachment.
+   * @param string $encoding File encoding (see $Encoding).
+   * @param string $type File extension (MIME) type.
+   * @return void
+   */
+  function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
+    /* Append to $attachment array */
+    $cur = count($this->attachment);
+    $this->attachment[$cur][0] = $string;
+    $this->attachment[$cur][1] = $filename;
+    $this->attachment[$cur][2] = $filename;
+    $this->attachment[$cur][3] = $encoding;
+    $this->attachment[$cur][4] = $type;
+    $this->attachment[$cur][5] = true; // isString
+    $this->attachment[$cur][6] = 'attachment';
+    $this->attachment[$cur][7] = 0;
+  }
+
+  /**
+   * Adds an embedded attachment.  This can include images, sounds, and
+   * just about any other document.  Make sure to set the $type to an
+   * image type.  For JPEG images use "image/jpeg" and for GIF images
+   * use "image/gif".
+   * @param string $path Path to the attachment.
+   * @param string $cid Content ID of the attachment.  Use this to identify
+   *        the Id for accessing the image in an HTML form.
+   * @param string $name Overrides the attachment name.
+   * @param string $encoding File encoding (see $Encoding).
+   * @param string $type File extension (MIME) type.
+   * @return bool
+   */
+  function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
+
+    if(!@is_file($path)) {
+      $this->SetError($this->Lang('file_access') . $path);
+      return false;
+    }
+
+    $filename = basename($path);
+    if($name == '') {
+      $name = $filename;
+    }
+
+    /* Append to $attachment array */
+    $cur = count($this->attachment);
+    $this->attachment[$cur][0] = $path;
+    $this->attachment[$cur][1] = $filename;
+    $this->attachment[$cur][2] = $name;
+    $this->attachment[$cur][3] = $encoding;
+    $this->attachment[$cur][4] = $type;
+    $this->attachment[$cur][5] = false;
+    $this->attachment[$cur][6] = 'inline';
+    $this->attachment[$cur][7] = $cid;
+
+    return true;
+  }
+
+  /**
+   * Returns true if an inline attachment is present.
+   * @access private
+   * @return bool
+   */
+  function InlineImageExists() {
+    $result = false;
+    for($i = 0; $i < count($this->attachment); $i++) {
+      if($this->attachment[$i][6] == 'inline') {
+        $result = true;
+        break;
+      }
+    }
+
+    return $result;
+  }
+
+  /////////////////////////////////////////////////
+  // CLASS METHODS, MESSAGE RESET
+  /////////////////////////////////////////////////
+
+  /**
+   * Clears all recipients assigned in the TO array.  Returns void.
+   * @return void
+   */
+  function ClearAddresses() {
+    $this->to = array();
+  }
+
+  /**
+   * Clears all recipients assigned in the CC array.  Returns void.
+   * @return void
+   */
+  function ClearCCs() {
+    $this->cc = array();
+  }
+
+  /**
+   * Clears all recipients assigned in the BCC array.  Returns void.
+   * @return void
+   */
+  function ClearBCCs() {
+    $this->bcc = array();
+  }
+
+  /**
+   * Clears all recipients assigned in the ReplyTo array.  Returns void.
+   * @return void
+   */
+  function ClearReplyTos() {
+    $this->ReplyTo = array();
+  }
+
+  /**
+   * Clears all recipients assigned in the TO, CC and BCC
+   * array.  Returns void.
+   * @return void
+   */
+  function ClearAllRecipients() {
+    $this->to = array();
+    $this->cc = array();
+    $this->bcc = array();
+  }
+
+  /**
+   * Clears all previously set filesystem, string, and binary
+   * attachments.  Returns void.
+   * @return void
+   */
+  function ClearAttachments() {
+    $this->attachment = array();
+  }
+
+  /**
+   * Clears all custom headers.  Returns void.
+   * @return void
+   */
+  function ClearCustomHeaders() {
+    $this->CustomHeader = array();
+  }
+
+  /////////////////////////////////////////////////
+  // CLASS METHODS, MISCELLANEOUS
+  /////////////////////////////////////////////////
+
+  /**
+   * Adds the error message to the error container.
+   * Returns void.
+   * @access private
+   * @return void
+   */
+  function SetError($msg) {
+    $this->error_count++;
+    $this->ErrorInfo = $msg;
+  }
+
+  /**
+   * Returns the proper RFC 822 formatted date.
+   * @access private
+   * @return string
+   */
+  function RFCDate() {
+    $tz = date('Z');
+    $tzs = ($tz < 0) ? '-' : '+';
+    $tz = abs($tz);
+    $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
+    $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
+
+    return $result;
+  }
+
+  /**
+   * Returns the appropriate server variable.  Should work with both
+   * PHP 4.1.0+ as well as older versions.  Returns an empty string
+   * if nothing is found.
+   * @access private
+   * @return mixed
+   */
+  function ServerVar($varName) {
+    global $HTTP_SERVER_VARS;
+    global $HTTP_ENV_VARS;
+
+    if(!isset($_SERVER)) {
+      $_SERVER = $HTTP_SERVER_VARS;
+      if(!isset($_SERVER['REMOTE_ADDR'])) {
+        $_SERVER = $HTTP_ENV_VARS; // must be Apache
+      }
+    }
+
+    if(isset($_SERVER[$varName])) {
+      return $_SERVER[$varName];
+    } else {
+      return '';
+    }
+  }
+
+  /**
+   * Returns the server hostname or 'localhost.localdomain' if unknown.
+   * @access private
+   * @return string
+   */
+  function ServerHostname() {
+    if ($this->Hostname != '') {
+      $result = $this->Hostname;
+    } elseif ($this->ServerVar('SERVER_NAME') != '') {
+      $result = $this->ServerVar('SERVER_NAME');
+    } else {
+      $result = 'localhost.localdomain';
+    }
+
+    return $result;
+  }
+
+  /**
+   * Returns a message in the appropriate language.
+   * @access private
+   * @return string
+   */
+  function Lang($key) {
+    if(count($this->language) < 1) {
+      $this->SetLanguage('en'); // set the default language
+    }
+
+    if(isset($this->language[$key])) {
+      return $this->language[$key];
+    } else {
+      return 'Language string failed to load: ' . $key;
+    }
+  }
+
+  /**
+   * Returns true if an error occurred.
+   * @return bool
+   */
+  function IsError() {
+    return ($this->error_count > 0);
+  }
+
+  /**
+   * Changes every end of line from CR or LF to CRLF.
+   * @access private
+   * @return string
+   */
+  function FixEOL($str) {
+    $str = str_replace("\r\n", "\n", $str);
+    $str = str_replace("\r", "\n", $str);
+    $str = str_replace("\n", $this->LE, $str);
+    return $str;
+  }
+
+  /**
+   * Adds a custom header.
+   * @return void
+   */
+  function AddCustomHeader($custom_header) {
+    $this->CustomHeader[] = explode(':', $custom_header, 2);
+  }
+
+  /**
+   * Evaluates the message and returns modifications for inline images and backgrounds
+   * @access public
+   * @return $message
+   */
+  function MsgHTML($message,$basedir='') {
+    preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
+    if(isset($images[2])) {
+      foreach($images[2] as $i => $url) {
+        // do not change urls for absolute images (thanks to corvuscorax)
+        if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) {
+          $filename = basename($url);
+          $directory = dirname($url);
+          ($directory == '.')?$directory='':'';
+          $cid = 'cid:' . md5($filename);
+          $fileParts = split("\.", $filename);
+          $ext = $fileParts[1];
+          $mimeType = $this->_mime_types($ext);
+          if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
+          if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
+          if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
+            $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
+          }
+        }
+      }
+    }
+    $this->IsHTML(true);
+    $this->Body = $message;
+    $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
+    if ( !empty($textMsg) && empty($this->AltBody) ) {
+      $this->AltBody = html_entity_decode($textMsg);
+    }
+    if ( empty($this->AltBody) ) {
+      $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
+    }
+  }
+
+  /**
+   * Gets the mime type of the embedded or inline image
+   * @access private
+   * @return mime type of ext
+   */
+  function _mime_types($ext = '') {
+    $mimes = array(
+      'ai'    =>  'application/postscript',
+      'aif'   =>  'audio/x-aiff',
+      'aifc'  =>  'audio/x-aiff',
+      'aiff'  =>  'audio/x-aiff',
+      'avi'   =>  'video/x-msvideo',
+      'bin'   =>  'application/macbinary',
+      'bmp'   =>  'image/bmp',
+      'class' =>  'application/octet-stream',
+      'cpt'   =>  'application/mac-compactpro',
+      'css'   =>  'text/css',
+      'dcr'   =>  'application/x-director',
+      'dir'   =>  'application/x-director',
+      'dll'   =>  'application/octet-stream',
+      'dms'   =>  'application/octet-stream',
+      'doc'   =>  'application/msword',
+      'dvi'   =>  'application/x-dvi',
+      'dxr'   =>  'application/x-director',
+      'eml'   =>  'message/rfc822',
+      'eps'   =>  'application/postscript',
+      'exe'   =>  'application/octet-stream',
+      'gif'   =>  'image/gif',
+      'gtar'  =>  'application/x-gtar',
+      'htm'   =>  'text/html',
+      'html'  =>  'text/html',
+      'jpe'   =>  'image/jpeg',
+      'jpeg'  =>  'image/jpeg',
+      'jpg'   =>  'image/jpeg',
+      'hqx'   =>  'application/mac-binhex40',
+      'js'    =>  'application/x-javascript',
+      'lha'   =>  'application/octet-stream',
+      'log'   =>  'text/plain',
+      'lzh'   =>  'application/octet-stream',
+      'mid'   =>  'audio/midi',
+      'midi'  =>  'audio/midi',
+      'mif'   =>  'application/vnd.mif',
+      'mov'   =>  'video/quicktime',
+      'movie' =>  'video/x-sgi-movie',
+      'mp2'   =>  'audio/mpeg',
+      'mp3'   =>  'audio/mpeg',
+      'mpe'   =>  'video/mpeg',
+      'mpeg'  =>  'video/mpeg',
+      'mpg'   =>  'video/mpeg',
+      'mpga'  =>  'audio/mpeg',
+      'oda'   =>  'application/oda',
+      'pdf'   =>  'application/pdf',
+      'php'   =>  'application/x-httpd-php',
+      'php3'  =>  'application/x-httpd-php',
+      'php4'  =>  'application/x-httpd-php',
+      'phps'  =>  'application/x-httpd-php-source',
+      'phtml' =>  'application/x-httpd-php',
+      'png'   =>  'image/png',
+      'ppt'   =>  'application/vnd.ms-powerpoint',
+      'ps'    =>  'application/postscript',
+      'psd'   =>  'application/octet-stream',
+      'qt'    =>  'video/quicktime',
+      'ra'    =>  'audio/x-realaudio',
+      'ram'   =>  'audio/x-pn-realaudio',
+      'rm'    =>  'audio/x-pn-realaudio',
+      'rpm'   =>  'audio/x-pn-realaudio-plugin',
+      'rtf'   =>  'text/rtf',
+      'rtx'   =>  'text/richtext',
+      'rv'    =>  'video/vnd.rn-realvideo',
+      'sea'   =>  'application/octet-stream',
+      'shtml' =>  'text/html',
+      'sit'   =>  'application/x-stuffit',
+      'so'    =>  'application/octet-stream',
+      'smi'   =>  'application/smil',
+      'smil'  =>  'application/smil',
+      'swf'   =>  'application/x-shockwave-flash',
+      'tar'   =>  'application/x-tar',
+      'text'  =>  'text/plain',
+      'txt'   =>  'text/plain',
+      'tgz'   =>  'application/x-tar',
+      'tif'   =>  'image/tiff',
+      'tiff'  =>  'image/tiff',
+      'wav'   =>  'audio/x-wav',
+      'wbxml' =>  'application/vnd.wap.wbxml',
+      'wmlc'  =>  'application/vnd.wap.wmlc',
+      'word'  =>  'application/msword',
+      'xht'   =>  'application/xhtml+xml',
+      'xhtml' =>  'application/xhtml+xml',
+      'xl'    =>  'application/excel',
+      'xls'   =>  'application/vnd.ms-excel',
+      'xml'   =>  'text/xml',
+      'xsl'   =>  'text/xml',
+      'zip'   =>  'application/zip'
+    );
+    return ( ! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
+  }
+
+  /**
+   * Set (or reset) Class Objects (variables)
+   *
+   * Usage Example:
+   * $page->set('X-Priority', '3');
+   *
+   * @access public
+   * @param string $name Parameter Name
+   * @param mixed $value Parameter Value
+   * NOTE: will not work with arrays, there are no arrays to set/reset
+   */
+  function set ( $name, $value = '' ) {
+    if ( isset($this->$name) ) {
+      $this->$name = $value;
+    } else {
+      $this->SetError('Cannot set or reset variable ' . $name);
+      return false;
+    }
+  }
+
+  /**
+   * Read a file from a supplied filename and return it.
+   *
+   * @access public
+   * @param string $filename Parameter File Name
+   */
+  function getFile($filename) {
+    $return = '';
+    if ($fp = fopen($filename, 'rb')) {
+      while (!feof($fp)) {
+        $return .= fread($fp, 1024);
+      }
+      fclose($fp);
+      return $return;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Strips newlines to prevent header injection.
+   * @access private
+   * @param string $str String
+   * @return string
+   */
+  function SecureHeader($str) {
+    $str = trim($str);
+    $str = str_replace("\r", "", $str);
+    $str = str_replace("\n", "", $str);
+    return $str;
+  }
+
+  /**
+   * Set the private key file and password to sign the message.
+   *
+   * @access public
+   * @param string $key_filename Parameter File Name
+   * @param string $key_pass Password for private key
+   */
+  function Sign($cert_filename, $key_filename, $key_pass) {
+    $this->sign_cert_file = $cert_filename;
+    $this->sign_key_file = $key_filename;
+    $this->sign_key_pass = $key_pass;
+  }
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.passwordhash.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.passwordhash.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.passwordhash.php	(revision 5057)
@@ -0,0 +1,258 @@
+<?php
+// Last sync [WP13429]
+
+/**
+ * Portable PHP password hashing framework.
+ * @package phpass
+ * @since 2.5
+ * @version 0.2 / genuine.
+ * @link http://www.openwall.com/phpass/
+ */
+
+#
+# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
+# the public domain.
+#
+# There's absolutely no warranty.
+#
+# Please be sure to update the Version line if you edit this file in any way.
+# It is suggested that you leave the main version number intact, but indicate
+# your project name (after the slash) and add your own revision information.
+#
+# Please do not change the "private" password hashing method implemented in
+# here, thereby making your hashes incompatible.  However, if you must, please
+# change the hash type identifier (the "$P$") to something different.
+#
+# Obviously, since this code is in the public domain, the above are not
+# requirements (there can be none), but merely suggestions.
+#
+
+/**
+ * Portable PHP password hashing framework.
+ *
+ * @package phpass
+ * @version 0.2 / genuine.
+ * @link http://www.openwall.com/phpass/
+ * @since 2.5
+ */
+class PasswordHash {
+	var $itoa64;
+	var $iteration_count_log2;
+	var $portable_hashes;
+	var $random_state;
+
+	function PasswordHash($iteration_count_log2, $portable_hashes)
+	{
+		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
+			$iteration_count_log2 = 8;
+		$this->iteration_count_log2 = $iteration_count_log2;
+
+		$this->portable_hashes = $portable_hashes;
+
+		$this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compability reasons
+	}
+
+	function get_random_bytes($count)
+	{
+		$output = '';
+		if ( @is_readable('/dev/urandom') &&
+		    ($fh = @fopen('/dev/urandom', 'rb'))) {
+			$output = fread($fh, $count);
+			fclose($fh);
+		}
+
+		if (strlen($output) < $count) {
+			$output = '';
+			for ($i = 0; $i < $count; $i += 16) {
+				$this->random_state =
+				    md5(microtime() . $this->random_state);
+				$output .=
+				    pack('H*', md5($this->random_state));
+			}
+			$output = substr($output, 0, $count);
+		}
+
+		return $output;
+	}
+
+	function encode64($input, $count)
+	{
+		$output = '';
+		$i = 0;
+		do {
+			$value = ord($input[$i++]);
+			$output .= $this->itoa64[$value & 0x3f];
+			if ($i < $count)
+				$value |= ord($input[$i]) << 8;
+			$output .= $this->itoa64[($value >> 6) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			if ($i < $count)
+				$value |= ord($input[$i]) << 16;
+			$output .= $this->itoa64[($value >> 12) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			$output .= $this->itoa64[($value >> 18) & 0x3f];
+		} while ($i < $count);
+
+		return $output;
+	}
+
+	function gensalt_private($input)
+	{
+		$output = '$P$';
+		$output .= $this->itoa64[min($this->iteration_count_log2 +
+			((PHP_VERSION >= '5') ? 5 : 3), 30)];
+		$output .= $this->encode64($input, 6);
+
+		return $output;
+	}
+
+	function crypt_private($password, $setting)
+	{
+		$output = '*0';
+		if (substr($setting, 0, 2) == $output)
+			$output = '*1';
+
+		if (substr($setting, 0, 3) != '$P$')
+			return $output;
+
+		$count_log2 = strpos($this->itoa64, $setting[3]);
+		if ($count_log2 < 7 || $count_log2 > 30)
+			return $output;
+
+		$count = 1 << $count_log2;
+
+		$salt = substr($setting, 4, 8);
+		if (strlen($salt) != 8)
+			return $output;
+
+		# We're kind of forced to use MD5 here since it's the only
+		# cryptographic primitive available in all versions of PHP
+		# currently in use.  To implement our own low-level crypto
+		# in PHP would result in much worse performance and
+		# consequently in lower iteration counts and hashes that are
+		# quicker to crack (by non-PHP code).
+		if (PHP_VERSION >= '5') {
+			$hash = md5($salt . $password, TRUE);
+			do {
+				$hash = md5($hash . $password, TRUE);
+			} while (--$count);
+		} else {
+			$hash = pack('H*', md5($salt . $password));
+			do {
+				$hash = pack('H*', md5($hash . $password));
+			} while (--$count);
+		}
+
+		$output = substr($setting, 0, 12);
+		$output .= $this->encode64($hash, 16);
+
+		return $output;
+	}
+
+	function gensalt_extended($input)
+	{
+		$count_log2 = min($this->iteration_count_log2 + 8, 24);
+		# This should be odd to not reveal weak DES keys, and the
+		# maximum valid value is (2**24 - 1) which is odd anyway.
+		$count = (1 << $count_log2) - 1;
+
+		$output = '_';
+		$output .= $this->itoa64[$count & 0x3f];
+		$output .= $this->itoa64[($count >> 6) & 0x3f];
+		$output .= $this->itoa64[($count >> 12) & 0x3f];
+		$output .= $this->itoa64[($count >> 18) & 0x3f];
+
+		$output .= $this->encode64($input, 3);
+
+		return $output;
+	}
+
+	function gensalt_blowfish($input)
+	{
+		# This one needs to use a different order of characters and a
+		# different encoding scheme from the one in encode64() above.
+		# We care because the last character in our encoded string will
+		# only represent 2 bits.  While two known implementations of
+		# bcrypt will happily accept and correct a salt string which
+		# has the 4 unused bits set to non-zero, we do not want to take
+		# chances and we also do not want to waste an additional byte
+		# of entropy.
+		$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+		$output = '$2a$';
+		$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
+		$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
+		$output .= '$';
+
+		$i = 0;
+		do {
+			$c1 = ord($input[$i++]);
+			$output .= $itoa64[$c1 >> 2];
+			$c1 = ($c1 & 0x03) << 4;
+			if ($i >= 16) {
+				$output .= $itoa64[$c1];
+				break;
+			}
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 4;
+			$output .= $itoa64[$c1];
+			$c1 = ($c2 & 0x0f) << 2;
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 6;
+			$output .= $itoa64[$c1];
+			$output .= $itoa64[$c2 & 0x3f];
+		} while (1);
+
+		return $output;
+	}
+
+	function HashPassword($password)
+	{
+		$random = '';
+
+		if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
+			$random = $this->get_random_bytes(16);
+			$hash =
+			    crypt($password, $this->gensalt_blowfish($random));
+			if (strlen($hash) == 60)
+				return $hash;
+		}
+
+		if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
+			if (strlen($random) < 3)
+				$random = $this->get_random_bytes(3);
+			$hash =
+			    crypt($password, $this->gensalt_extended($random));
+			if (strlen($hash) == 20)
+				return $hash;
+		}
+
+		if (strlen($random) < 6)
+			$random = $this->get_random_bytes(6);
+		$hash =
+		    $this->crypt_private($password,
+		    $this->gensalt_private($random));
+		if (strlen($hash) == 34)
+			return $hash;
+
+		# Returning '*' on error is safe here, but would _not_ be safe
+		# in a crypt(3)-like function used _both_ for generating new
+		# hashes and for validating passwords against existing hashes.
+		return '*';
+	}
+
+	function CheckPassword($password, $stored_hash)
+	{
+		$hash = $this->crypt_private($password, $stored_hash);
+		if ($hash[0] == '*')
+			$hash = crypt($password, $stored_hash);
+
+		return $hash == $stored_hash;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-ajax-response.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-ajax-response.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-ajax-response.php	(revision 5057)
@@ -0,0 +1,138 @@
+<?php
+// Last sync [WP11537] - split out from wp-includes/classes.php
+
+/**
+ * Send XML response back to AJAX request.
+ *
+ * @package WordPress
+ * @since 2.1.0
+ */
+class WP_Ajax_Response {
+	/**
+	 * Store XML responses to send.
+	 *
+	 * @since 2.1.0
+	 * @var array
+	 * @access private
+	 */
+	var $responses = array();
+
+	/**
+	 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.
+	 *
+	 * @since 2.1.0
+	 * @see WP_Ajax_Response::add()
+	 *
+	 * @param string|array $args Optional. Will be passed to add() method.
+	 * @return WP_Ajax_Response
+	 */
+	function WP_Ajax_Response( $args = '' ) {
+		if ( !empty($args) )
+			$this->add($args);
+	}
+
+	/**
+	 * Append to XML response based on given arguments.
+	 *
+	 * The arguments that can be passed in the $args parameter are below. It is
+	 * also possible to pass a WP_Error object in either the 'id' or 'data'
+	 * argument. The parameter isn't actually optional, content should be given
+	 * in order to send the correct response.
+	 *
+	 * 'what' argument is a string that is the XMLRPC response type.
+	 * 'action' argument is a boolean or string that acts like a nonce.
+	 * 'id' argument can be WP_Error or an integer.
+	 * 'old_id' argument is false by default or an integer of the previous ID.
+	 * 'position' argument is an integer or a string with -1 = top, 1 = bottom,
+	 * html ID = after, -html ID = before.
+	 * 'data' argument is a string with the content or message.
+	 * 'supplemental' argument is an array of strings that will be children of
+	 * the supplemental element.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param string|array $args Override defaults.
+	 * @return string XML response.
+	 */
+	function add( $args = '' ) {
+		$defaults = array(
+			'what' => 'object', 'action' => false,
+			'id' => '0', 'old_id' => false,
+			'position' => 1,
+			'data' => '', 'supplemental' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+		$position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );
+
+		if ( is_wp_error($id) ) {
+			$data = $id;
+			$id = 0;
+		}
+
+		$response = '';
+		if ( is_wp_error($data) ) {
+			foreach ( (array) $data->get_error_codes() as $code ) {
+				$response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";
+				if ( !$error_data = $data->get_error_data($code) )
+					continue;
+				$class = '';
+				if ( is_object($error_data) ) {
+					$class = ' class="' . get_class($error_data) . '"';
+					$error_data = get_object_vars($error_data);
+				}
+
+				$response .= "<wp_error_data code='$code'$class>";
+
+				if ( is_scalar($error_data) ) {
+					$response .= "<![CDATA[$error_data]]>";
+				} elseif ( is_array($error_data) ) {
+					foreach ( $error_data as $k => $v )
+						$response .= "<$k><![CDATA[$v]]></$k>";
+				}
+
+				$response .= "</wp_error_data>";
+			}
+		} else {
+			$response = "<response_data><![CDATA[$data]]></response_data>";
+		}
+
+		$s = '';
+		if ( is_array($supplemental) ) {
+			foreach ( $supplemental as $k => $v )
+				$s .= "<$k><![CDATA[$v]]></$k>";
+			$s = "<supplemental>$s</supplemental>";
+		}
+
+		if ( false === $action )
+			$action = $_POST['action'];
+
+		$x = '';
+		$x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action
+		$x .=	"<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";
+		$x .=		$response;
+		$x .=		$s;
+		$x .=	"</$what>";
+		$x .= "</response>";
+
+		$this->responses[] = $x;
+		return $x;
+	}
+
+	/**
+	 * Display XML formatted responses.
+	 *
+	 * Sets the content type header to text/xml.
+	 *
+	 * @since 2.1.0
+	 */
+	function send() {
+		header('Content-Type: text/xml');
+		echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";
+		foreach ( (array) $this->responses as $response )
+			echo $response;
+		echo '</wp_ajax>';
+		die();
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-auth.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-auth.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-auth.php	(revision 5057)
@@ -0,0 +1,305 @@
+<?php
+class WP_Auth
+{
+	var $db; // BBPDB object
+	var $users; // WP_Users object
+
+	var $cookies;
+
+	var $current = 0;
+
+	function WP_Auth( &$db, &$users, $cookies )
+	{
+		$this->__construct( $db, $users, $cookies );
+	}
+
+	/**
+	 * @param array $cookies Array indexed by internal name of cookie.  Values are arrays of array defining cookie parameters.
+	 * $cookies = array(
+	 *	'auth' => array(
+	 *		0 => array(
+	 *			'domain' => (string) cookie domain
+	 *			'path' => (string) cookie path
+	 *			'name' => (string) cookie name
+	 *			'secure' => (bool) restrict cookie to HTTPS only
+	 *		)
+	 *	)
+	 * );
+	 *
+	 * At least one cookie is required.  Give it an internal name of 'auth'.
+	 *
+	 * Suggested cookie structure:
+	 * 	logged_in: whether or not a user is logged in.  Send everywhere.
+	 *	auth: used to authorize user's actions.  Send only to domains/paths that need it (e.g. wp-admin/)
+	 *	secure_auth: used to authorize user's actions.  Send only to domains/paths that need it and only over HTTPS
+	 *
+	 * You should be very careful when setting cookie domain to ensure that it always follows the rules set out in
+	 * the {@link http://curl.haxx.se/rfc/cookie_spec.html Set Cookie spec}.  In most cases it is best to leave cookie
+	 * set to false and allow for user configuration to define a cookie domain in a configuration file when
+	 * cross subdomain cookies are required.
+	 * 
+	 * @link 
+	 */
+	function __construct( &$db, &$users, $cookies )
+	{
+		$this->db =& $db;
+		$this->users =& $users;
+		
+		$cookies = wp_parse_args( $cookies, array( 'auth' => null ) );
+		$_cookies = array();
+		foreach ( $cookies as $_scheme => $_scheme_cookies ) {
+			foreach ( $_scheme_cookies as $_scheme_cookie ) {
+				$_cookies[$_scheme][] = wp_parse_args( $_scheme_cookie, array( 'domain' => null, 'path' => null, 'name' => '' ) );
+			}
+			unset( $_scheme_cookie );
+		}
+		unset( $_scheme, $_scheme_cookies );
+		$this->cookies = $_cookies;
+		unset( $_cookies );
+	}
+
+	/**
+	 * Changes the current user by ID or name
+	 *
+	 * Set $id to null and specify a name if you do not know a user's ID
+	 *
+	 * Some WordPress functionality is based on the current user and
+	 * not based on the signed in user. Therefore, it opens the ability
+	 * to edit and perform actions on users who aren't signed in.
+	 *
+	 * @since 2.0.4
+	 * @uses do_action() Calls 'set_current_user' hook after setting the current user.
+	 *
+	 * @param int $id User ID
+	 * @param string $name User's username
+	 * @return BP_User Current user User object
+	 */
+	function set_current_user( $user_id )
+	{
+		$user = $this->users->get_user( $user_id );
+		if ( !$user || is_wp_error( $user ) ) {
+			$this->current = 0;
+			return $this->current;
+		}
+
+		$user_id = $user->ID;
+
+		if ( isset( $this->current->ID ) && $user_id == $this->current->ID ) {
+			return $this->current;
+		}
+
+		if ( class_exists( 'BP_User' ) ) {
+			$this->current = new BP_User( $user_id );
+		} else {
+			$this->current =& $user;
+		}
+
+		// WP add_action( 'set_current_user', 'setup_userdata', 1 );
+
+		do_action( 'set_current_user', $user_id );
+
+		return $this->current;
+	}
+
+	/**
+	 * Populate variables with information about the currently logged in user
+	 *
+	 * Will set the current user, if the current user is not set. The current
+	 * user will be set to the logged in person. If no user is logged in, then
+	 * it will set the current user to 0, which is invalid and won't have any
+	 * permissions.
+ 	 *
+	 * @since 0.71
+ 	 * @uses $current_user Checks if the current user is set
+ 	 * @uses wp_validate_auth_cookie() Retrieves current logged in user.
+ 	 *
+ 	 * @return bool|null False on XMLRPC Request and invalid auth cookie. Null when current user set
+ 	 */
+	function get_current_user()
+	{
+		if ( !empty( $this->current ) ) {
+			return $this->current;
+		}
+
+		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
+			$this->set_current_user( 0 );
+		} elseif ( $user_id = $this->validate_auth_cookie( null, 'logged_in' ) ) {
+			$this->set_current_user( $user_id );
+		} else {
+			$this->set_current_user( 0 );
+		}
+
+		return $this->current;
+	}
+
+	/**
+	 * Validates authentication cookie
+	 *
+	 * The checks include making sure that the authentication cookie
+	 * is set and pulling in the contents (if $cookie is not used).
+	 *
+	 * Makes sure the cookie is not expired. Verifies the hash in
+	 * cookie is what is should be and compares the two.
+	 *
+	 * @since 2.5
+	 *
+	 * @param string $cookie Optional. If used, will validate contents instead of cookie's
+	 * @return bool|int False if invalid cookie, User ID if valid.
+	 */
+	function validate_auth_cookie( $cookie = null, $scheme = 'auth' )
+	{
+		if ( empty( $cookie ) ) {
+			foreach ( $this->cookies[$scheme] as $_scheme_cookie ) {
+				// Take the first cookie of type scheme that exists
+				if ( !empty( $_COOKIE[$_scheme_cookie['name']] ) ) {
+					$cookie = $_COOKIE[$_scheme_cookie['name']];
+					break;
+				}
+			}
+		}
+		
+		if ( !$cookie ) {
+			return false;
+		}
+
+		$cookie_elements = explode( '|', $cookie );
+		if ( count( $cookie_elements ) != 3 ) {
+			do_action( 'auth_cookie_malformed', $cookie, $scheme );
+			return false;
+		}
+
+		list( $username, $expiration, $hmac ) = $cookie_elements;
+
+		$expired = $expiration;
+
+		// Allow a grace period for POST and AJAX requests
+		if ( defined( 'DOING_AJAX' ) || 'POST' == $_SERVER['REQUEST_METHOD'] ) {
+			$expired += 3600;
+		}
+
+		if ( $expired < time() ) {
+			do_action( 'auth_cookie_expired', $cookie_elements );
+			return false;
+		}
+
+		$user = $this->users->get_user( $username, array( 'by' => 'login' ) );
+		if ( !$user || is_wp_error( $user ) ) {
+			do_action( 'auth_cookie_bad_username', $cookie_elements );
+			return $user;
+		}
+
+		$pass_frag = '';
+		if ( 1 < WP_AUTH_COOKIE_VERSION ) {
+			$pass_frag = substr( $user->user_pass, 8, 4 );
+		}
+
+		$key  = call_user_func( backpress_get_option( 'hash_function_name' ), $username . $pass_frag . '|' . $expiration, $scheme );
+		$hash = hash_hmac( 'md5', $username . '|' . $expiration, $key );
+	
+		if ( $hmac != $hash ) {
+			do_action( 'auth_cookie_bad_hash', $cookie_elements );
+			return false;
+		}
+
+		do_action( 'auth_cookie_valid', $cookie_elements, $user );
+
+		return $user->ID;
+	}
+
+	/**
+	 * Generate authentication cookie contents
+	 *
+	 * @since 2.5
+	 * @uses apply_filters() Calls 'auth_cookie' hook on $cookie contents, User ID
+	 *		and expiration of cookie.
+	 *
+	 * @param int $user_id User ID
+	 * @param int $expiration Cookie expiration in seconds
+	 * @return string Authentication cookie contents
+	 */
+	function generate_auth_cookie( $user_id, $expiration, $scheme = 'auth' )
+	{
+		$user = $this->users->get_user( $user_id );
+		if ( !$user || is_wp_error( $user ) ) {
+			return $user;
+		}
+
+		$pass_frag = '';
+		if ( 1 < WP_AUTH_COOKIE_VERSION ) {
+			$pass_frag = substr( $user->user_pass, 8, 4 );
+		}
+
+		$key  = call_user_func( backpress_get_option( 'hash_function_name' ), $user->user_login . $pass_frag . '|' . $expiration, $scheme );
+		$hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
+
+		$cookie = $user->user_login . '|' . $expiration . '|' . $hash;
+
+		return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme );
+	}
+
+	/**
+	 * Sets the authentication cookies based User ID
+	 *
+	 * The $remember parameter increases the time that the cookie will
+	 * be kept. The default the cookie is kept without remembering is
+	 * two days. When $remember is set, the cookies will be kept for
+	 * 14 days or two weeks.
+	 *
+	 * @since 2.5
+	 *
+	 * @param int $user_id User ID
+	 * @param int $expiration the UNIX time after which the cookie's authentication token is no longer valid
+	 * @param int $expire the UNIX time at which the cookie expires
+	 * @param int $scheme name of the 
+	 */
+	function set_auth_cookie( $user_id, $expiration = 0, $expire = 0, $scheme = 'auth' )
+	{
+		if ( !isset( $this->cookies[$scheme] ) ) {
+			return;
+		}
+
+		if ( !$expiration = absint( $expiration ) ) {
+			$expiration = time() + 172800; // 2 days
+		}
+
+		$expire = absint( $expire );
+
+		foreach ( $this->cookies[$scheme] as $_cookie ) {
+			$cookie = $this->generate_auth_cookie( $user_id, $expiration, $scheme );
+			if ( is_wp_error( $cookie ) ) {
+				return $cookie;
+			}
+
+			do_action( 'set_' . $scheme . '_cookie', $cookie, $expire, $expiration, $user_id, $scheme );
+
+			$domain = $_cookie['domain'];
+			$secure = isset($_cookie['secure']) ? (bool) $_cookie['secure'] : false;
+
+			// Set httponly if the php version is >= 5.2.0
+			if ( version_compare( phpversion(), '5.2.0', 'ge' ) ) {
+				backpress_set_cookie( $_cookie['name'], $cookie, $expire, $_cookie['path'], $domain, $secure, true );
+			} else {
+				$domain = ( empty( $domain ) ) ? $domain : $domain . '; HttpOnly';
+				backpress_set_cookie( $_cookie['name'], $cookie, $expire, $_cookie['path'], $domain, $secure );
+			}
+		}
+		unset( $_cookie );
+	}
+
+	/**
+	 * Deletes all of the cookies associated with authentication
+	 *
+	 * @since 2.5
+	 */
+	function clear_auth_cookie()
+	{
+		do_action( 'clear_auth_cookie' );
+		foreach ( $this->cookies as $_scheme => $_scheme_cookies ) {
+			foreach ( $_scheme_cookies as $_cookie ) {
+				backpress_set_cookie( $_cookie['name'], ' ', time() - 31536000, $_cookie['path'], $_cookie['domain'] );
+			}
+			unset( $_cookie );
+		}
+		unset( $_scheme, $_scheme_cookies );
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-dependencies.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-dependencies.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-dependencies.php	(revision 5057)
@@ -0,0 +1,243 @@
+<?php
+/**
+ * BackPress Scripts enqueue.
+ *
+ * These classes were refactored from the WordPress WP_Scripts and WordPress
+ * script enqueue API.
+ *
+ * @package BackPress
+ * @since r74
+ */
+
+/**
+ * BackPress enqueued dependiences class.
+ *
+ * @package BackPress
+ * @uses _WP_Dependency
+ * @since r74
+ */
+class WP_Dependencies {
+	var $registered = array();
+	var $queue = array();
+	var $to_do = array();
+	var $done = array();
+	var $args = array();
+	var $groups = array();
+	var $group = 0;
+
+	function WP_Dependencies() {
+		$args = func_get_args();
+		call_user_func_array( array(&$this, '__construct'), $args );
+	}
+
+	function __construct() {}
+
+	/**
+	 * Do the dependencies
+	 *
+	 * Process the items passed to it or the queue.  Processes all dependencies.
+	 *
+	 * @param mixed handles (optional) items to be processed.  (void) processes queue, (string) process that item, (array of strings) process those items
+	 * @return array Items that have been processed
+	 */
+	function do_items( $handles = false, $group = false ) {
+		// Print the queue if nothing is passed.  If a string is passed, print that script.  If an array is passed, print those scripts.
+		$handles = false === $handles ? $this->queue : (array) $handles;
+		$this->all_deps( $handles );
+
+		foreach( $this->to_do as $key => $handle ) {
+			if ( !in_array($handle, $this->done) && isset($this->registered[$handle]) ) {
+
+				if ( ! $this->registered[$handle]->src ) { // Defines a group.
+					$this->done[] = $handle;
+					continue;
+				}
+
+				if ( $this->do_item( $handle, $group ) )
+					$this->done[] = $handle;
+
+				unset( $this->to_do[$key] );
+			}
+		}
+
+		return $this->done;
+	}
+
+	function do_item( $handle ) {
+		return isset($this->registered[$handle]);
+	}
+
+	/**
+	 * Determines dependencies
+	 *
+	 * Recursively builds array of items to process taking dependencies into account.  Does NOT catch infinite loops.
+	 *
+
+	 * @param mixed handles Accepts (string) dep name or (array of strings) dep names
+	 * @param bool recursion Used internally when function calls itself
+	 */
+	function all_deps( $handles, $recursion = false, $group = false ) {
+		if ( !$handles = (array) $handles )
+			return false;
+
+		foreach ( $handles as $handle ) {
+			$handle_parts = explode('?', $handle);
+			$handle = $handle_parts[0];
+			$queued = in_array($handle, $this->to_do, true);
+
+			if ( in_array($handle, $this->done, true) ) // Already done
+				continue;
+
+			$moved = $this->set_group( $handle, $recursion, $group );
+
+			if ( $queued && !$moved ) // already queued and in the right group
+				continue;
+
+			$keep_going = true;
+			if ( !isset($this->registered[$handle]) )
+				$keep_going = false; // Script doesn't exist
+			elseif ( $this->registered[$handle]->deps && array_diff($this->registered[$handle]->deps, array_keys($this->registered)) )
+				$keep_going = false; // Script requires deps which don't exist (not a necessary check.  efficiency?)
+			elseif ( $this->registered[$handle]->deps && !$this->all_deps( $this->registered[$handle]->deps, true, $group ) )
+				$keep_going = false; // Script requires deps which don't exist
+
+			if ( !$keep_going ) { // Either script or its deps don't exist.
+				if ( $recursion )
+					return false; // Abort this branch.
+				else
+					continue; // We're at the top level.  Move on to the next one.
+			}
+
+			if ( $queued ) // Already grobbed it and its deps
+				continue;
+
+			if ( isset($handle_parts[1]) )
+				$this->args[$handle] = $handle_parts[1];
+
+			$this->to_do[] = $handle;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Adds item
+	 *
+	 * Adds the item only if no item of that name already exists
+	 *
+	 * @param string handle Script name
+	 * @param string src Script url
+	 * @param array deps (optional) Array of script names on which this script depends
+	 * @param string ver (optional) Script version (used for cache busting)
+	 * @return array Hierarchical array of dependencies
+	 */
+	function add( $handle, $src, $deps = array(), $ver = false, $args = null ) {
+		if ( isset($this->registered[$handle]) )
+			return false;
+		$this->registered[$handle] = new _WP_Dependency( $handle, $src, $deps, $ver, $args );
+		return true;
+	}
+
+	/**
+	 * Adds extra data
+	 *
+	 * Adds data only if script has already been added
+	 *
+	 * @param string handle Script name
+	 * @param string data_name Name of object in which to store extra data
+	 * @param array data Array of extra data
+	 * @return bool success
+	 */
+	function add_data( $handle, $data_name, $data ) {
+		if ( !isset($this->registered[$handle]) )
+			return false;
+		return $this->registered[$handle]->add_data( $data_name, $data );
+	}
+
+	function remove( $handles ) {
+		foreach ( (array) $handles as $handle )
+			unset($this->registered[$handle]);
+	}
+
+	function enqueue( $handles ) {
+		foreach ( (array) $handles as $handle ) {
+			$handle = explode('?', $handle);
+			if ( !in_array($handle[0], $this->queue) && isset($this->registered[$handle[0]]) ) {
+				$this->queue[] = $handle[0];
+				if ( isset($handle[1]) )
+					$this->args[$handle[0]] = $handle[1];
+			}
+		}
+	}
+
+	function dequeue( $handles ) {
+		foreach ( (array) $handles as $handle ) {
+			$handle = explode('?', $handle);
+			$key = array_search($handle[0], $this->queue);
+			if ( false !== $key ) {
+				unset($this->queue[$key]);
+				unset($this->args[$handle[0]]);
+			}
+		}
+	}
+
+	function query( $handle, $list = 'registered' ) { // registered, queue, done, to_do
+		switch ( $list ) :
+		case 'registered':
+		case 'scripts': // back compat
+			if ( isset($this->registered[$handle]) )
+				return $this->registered[$handle];
+			break;
+		case 'to_print': // back compat
+		case 'printed': // back compat
+			if ( 'to_print' == $list )
+				$list = 'to_do';
+			else
+				$list = 'printed';
+		default:
+			if ( in_array($handle, $this->$list) )
+				return true;
+			break;
+		endswitch;
+		return false;
+	}
+
+	function set_group( $handle, $recursion, $group ) {
+		$group = (int) $group;
+
+		if ( $recursion )
+			$group = min($this->group, $group);
+		else
+			$this->group = $group;
+
+		if ( isset($this->groups[$handle]) && $this->groups[$handle] <= $group )
+			return false;
+
+		$this->groups[$handle] = $group;
+		return true;
+	}
+
+}
+
+class _WP_Dependency {
+	var $handle;
+	var $src;
+	var $deps = array();
+	var $ver = false;
+	var $args = null;
+
+	var $extra = array();
+
+	function _WP_Dependency() {
+		@list($this->handle, $this->src, $this->deps, $this->ver, $this->args) = func_get_args();
+		if ( !is_array($this->deps) )
+			$this->deps = array();
+	}
+
+	function add_data( $name, $data ) {
+		if ( !is_scalar($name) )
+			return false;
+		$this->extra[$name] = $data;
+		return true;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-error.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-error.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-error.php	(revision 5057)
@@ -0,0 +1,204 @@
+<?php
+// Last sync [WP11537] - split out from wp-includes/classes.php
+
+/**
+ * WordPress Error class.
+ *
+ * Container for checking for WordPress errors and error messages. Return
+ * WP_Error and use {@link is_wp_error()} to check if this class is returned.
+ * Many core WordPress functions pass this class in the event of an error and
+ * if not handled properly will result in code errors.
+ *
+ * @package WordPress
+ * @since 2.1.0
+ */
+class WP_Error {
+	/**
+	 * Stores the list of errors.
+	 *
+	 * @since 2.1.0
+	 * @var array
+	 * @access private
+	 */
+	var $errors = array();
+
+	/**
+	 * Stores the list of data for error codes.
+	 *
+	 * @since 2.1.0
+	 * @var array
+	 * @access private
+	 */
+	var $error_data = array();
+
+	/**
+	 * PHP4 Constructor - Sets up error message.
+	 *
+	 * If code parameter is empty then nothing will be done. It is possible to
+	 * add multiple messages to the same code, but with other methods in the
+	 * class.
+	 *
+	 * All parameters are optional, but if the code parameter is set, then the
+	 * data parameter is optional.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param string|int $code Error code
+	 * @param string $message Error message
+	 * @param mixed $data Optional. Error data.
+	 * @return WP_Error
+	 */
+	function WP_Error($code = '', $message = '', $data = '') {
+		if ( empty($code) )
+			return;
+
+		$this->errors[$code][] = $message;
+
+		if ( ! empty($data) )
+			$this->error_data[$code] = $data;
+	}
+
+	/**
+	 * Retrieve all error codes.
+	 *
+	 * @since 2.1.0
+	 * @access public
+	 *
+	 * @return array List of error codes, if avaiable.
+	 */
+	function get_error_codes() {
+		if ( empty($this->errors) )
+			return array();
+
+		return array_keys($this->errors);
+	}
+
+	/**
+	 * Retrieve first error code available.
+	 *
+	 * @since 2.1.0
+	 * @access public
+	 *
+	 * @return string|int Empty string, if no error codes.
+	 */
+	function get_error_code() {
+		$codes = $this->get_error_codes();
+
+		if ( empty($codes) )
+			return '';
+
+		return $codes[0];
+	}
+
+	/**
+	 * Retrieve all error messages or error messages matching code.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param string|int $code Optional. Retrieve messages matching code, if exists.
+	 * @return array Error strings on success, or empty array on failure (if using codee parameter).
+	 */
+	function get_error_messages($code = '') {
+		// Return all messages if no code specified.
+		if ( empty($code) ) {
+			$all_messages = array();
+			foreach ( (array) $this->errors as $code => $messages )
+				$all_messages = array_merge($all_messages, $messages);
+
+			return $all_messages;
+		}
+
+		if ( isset($this->errors[$code]) )
+			return $this->errors[$code];
+		else
+			return array();
+	}
+
+	/**
+	 * Get single error message.
+	 *
+	 * This will get the first message available for the code. If no code is
+	 * given then the first code available will be used.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param string|int $code Optional. Error code to retrieve message.
+	 * @return string
+	 */
+	function get_error_message($code = '') {
+		if ( empty($code) )
+			$code = $this->get_error_code();
+		$messages = $this->get_error_messages($code);
+		if ( empty($messages) )
+			return '';
+		return $messages[0];
+	}
+
+	/**
+	 * Retrieve error data for error code.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param string|int $code Optional. Error code.
+	 * @return mixed Null, if no errors.
+	 */
+	function get_error_data($code = '') {
+		if ( empty($code) )
+			$code = $this->get_error_code();
+
+		if ( isset($this->error_data[$code]) )
+			return $this->error_data[$code];
+		return null;
+	}
+
+	/**
+	 * Append more error messages to list of error messages.
+	 *
+	 * @since 2.1.0
+	 * @access public
+	 *
+	 * @param string|int $code Error code.
+	 * @param string $message Error message.
+	 * @param mixed $data Optional. Error data.
+	 */
+	function add($code, $message, $data = '') {
+		$this->errors[$code][] = $message;
+		if ( ! empty($data) )
+			$this->error_data[$code] = $data;
+	}
+
+	/**
+	 * Add data for error code.
+	 *
+	 * The error code can only contain one error data.
+	 *
+	 * @since 2.1.0
+	 *
+	 * @param mixed $data Error data.
+	 * @param string|int $code Error code.
+	 */
+	function add_data($data, $code = '') {
+		if ( empty($code) )
+			$code = $this->get_error_code();
+
+		$this->error_data[$code] = $data;
+	}
+}
+
+/**
+ * Check whether variable is a WordPress Error.
+ *
+ * Looks at the object and if a WP_Error class. Does not check to see if the
+ * parent is also WP_Error, so can't inherit WP_Error and still use this
+ * function.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $thing Check if unknown variable is WordPress Error object.
+ * @return bool True, if WP_Error. False, if not WP_Error.
+ */
+function is_wp_error($thing) {
+	if ( is_object($thing) && is_a($thing, 'WP_Error') )
+		return true;
+	return false;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http-ixr-client.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http-ixr-client.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http-ixr-client.php	(revision 5057)
@@ -0,0 +1,97 @@
+<?php
+/**
+ * WP_HTTP_IXR_Client
+ *
+ * @package WordPress
+ * @since 3.1.0
+ *
+ */
+class WP_HTTP_IXR_Client extends IXR_Client
+{
+    function WP_HTTP_IXR_Client($server, $path = false, $port = 80, $timeout = 15)
+    {
+        if (!$path) {
+            // Assume we have been given a URL instead
+            $bits = parse_url($server);
+            $this->scheme = $bits['scheme'];
+            $this->server = $bits['host'];
+            $this->port = isset($bits['port']) ? $bits['port'] : 80;
+            $this->path = isset($bits['path']) ? $bits['path'] : '/';
+
+            // Make absolutely sure we have a path
+            if (!$this->path) {
+                $this->path = '/';
+            }
+        } else {
+        	$this->scheme = 'http'; 
+            $this->server = $server;
+            $this->path = $path;
+            $this->port = $port;
+        }
+        $this->useragent = 'The Incutio XML-RPC PHP Library';
+        $this->timeout = $timeout;
+    }
+
+    function query()
+    {
+        $args = func_get_args();
+        $method = array_shift($args);
+        $request = new IXR_Request($method, $args);
+        $xml = $request->getXml();
+
+        $url = $this->scheme . '://' . $this->server . ':' . $this->port . $this->path;
+        $args = array(
+            'headers'    => array('Content-Type' => 'text/xml'),
+            'user-agent' => $this->useragent,
+            'body'       => $xml,
+        );
+        
+        // Merge Custom headers ala #8145
+		foreach ( $this->headers as $header => $value )
+			$args['headers'][$header] = $value;
+        
+		if ( $this->timeout !== false )
+			$args['timeout'] = $this->timeout;
+        	
+        // Now send the request
+        if ($this->debug) {
+            echo '<pre class="ixr_request">'.htmlspecialchars($xml)."\n</pre>\n\n";
+        }
+
+		$response = wp_remote_post($url, $args); 
+
+		if ( is_wp_error($response) ) { 
+			$errno    = $response->get_error_code(); 
+			$errorstr = $response->get_error_message(); 
+			$this->error = new IXR_Error(-32300, "transport error: $errno $errstr"); 
+			return false;
+		}
+ 	
+		$code = $response['response']['code']; 
+		if ( $code != 200 ) { 
+			$this->error = new IXR_Error(-32301, "transport error - HTTP status code was not 200 ($code)");             return false;
+        }
+
+        if ($this->debug) {
+            echo '<pre class="ixr_response">'.htmlspecialchars($response['body'])."\n</pre>\n\n";
+        }
+
+        // Now parse what we've got back
+        $this->message = new IXR_Message( $response['body'] );
+        if (!$this->message->parse()) {
+            // XML error
+            $this->error = new IXR_Error(-32700, 'parse error. not well formed');
+            return false;
+        }
+
+        // Is the message a fault?
+        if ($this->message->messageType == 'fault') {
+            $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
+            return false;
+        }
+
+        // Message must be OK
+        return true;
+    }
+}
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-http.php	(revision 5057)
@@ -0,0 +1,2052 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * Simple and uniform HTTP request API.
+ *
+ * Will eventually replace and standardize the WordPress HTTP requests made.
+ *
+ * @link http://trac.wordpress.org/ticket/4779 HTTP API Proposal
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ * @author Jacob Santos <wordpress@santosj.name>
+ */
+
+/**
+ * WordPress HTTP Class for managing HTTP Transports and making HTTP requests.
+ *
+ * This class is called for the functionality of making HTTP requests and should replace Snoopy
+ * functionality, eventually. There is no available functionality to add HTTP transport
+ * implementations, since most of the HTTP transports are added and available for use.
+ *
+ * The exception is that cURL is not available as a transport and lacking an implementation. It will
+ * be added later and should be a patch on the WordPress Trac.
+ *
+ * There are no properties, because none are needed and for performance reasons. Some of the
+ * functions are static and while they do have some overhead over functions in PHP4, the purpose is
+ * maintainability. When PHP5 is finally the requirement, it will be easy to add the static keyword
+ * to the code. It is not as easy to convert a function to a method after enough code uses the old
+ * way.
+ *
+ * Debugging includes several actions, which pass different variables for debugging the HTTP API.
+ *
+ * <strong>http_transport_get_debug</strong> - gives working, nonblocking, and blocking transports.
+ *
+ * <strong>http_transport_post_debug</strong> - gives working, nonblocking, and blocking transports.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ */
+class WP_Http {
+
+	/**
+	 * PHP4 style Constructor - Calls PHP5 Style Constructor
+	 *
+	 * @since 2.7.0
+	 * @return WP_Http
+	 */
+	function WP_Http() {
+		$this->__construct();
+	}
+
+	/**
+	 * PHP5 style Constructor - Setup available transport if not available.
+	 *
+	 * PHP4 does not have the 'self' keyword and since WordPress supports PHP4,
+	 * the class needs to be used for the static call.
+	 *
+	 * The transport are setup to save time. This should only be called once, so
+	 * the overhead should be fine.
+	 *
+	 * @since 2.7.0
+	 * @return WP_Http
+	 */
+	function __construct() {
+		WP_Http::_getTransport();
+		WP_Http::_postTransport();
+	}
+
+	/**
+	 * Tests the WordPress HTTP objects for an object to use and returns it.
+	 *
+	 * Tests all of the objects and returns the object that passes. Also caches
+	 * that object to be used later.
+	 *
+	 * The order for the GET/HEAD requests are HTTP Extension, FSockopen Streams, 
+	 * Fopen, and finally cURL. Whilst Fsockopen has the highest overhead, Its
+	 * used 2nd due to high compatibility with most hosts, The HTTP Extension is
+	 * tested first due to hosts which have it enabled, are likely to work
+	 * correctly with it.
+	 *
+	 * There are currently issues with "localhost" not resolving correctly with
+	 * DNS. This may cause an error "failed to open stream: A connection attempt
+	 * failed because the connected party did not properly respond after a
+	 * period of time, or established connection failed because connected host
+	 * has failed to respond."
+	 *
+	 * @since 2.7.0
+	 * @access private
+	 *
+	 * @param array $args Request args, default us an empty array
+	 * @return object|null Null if no transports are available, HTTP transport object.
+	 */
+	function &_getTransport( $args = array() ) {
+		static $working_transport, $blocking_transport, $nonblocking_transport;
+
+		if ( is_null($working_transport) ) {
+			if ( true === WP_Http_ExtHttp::test($args) ) {
+				$working_transport['exthttp'] = new WP_Http_ExtHttp();
+				$blocking_transport[] = &$working_transport['exthttp'];
+			} else if ( true === WP_Http_Fsockopen::test($args) ) {
+				$working_transport['fsockopen'] = new WP_Http_Fsockopen();
+				$blocking_transport[] = &$working_transport['fsockopen'];
+			} else if ( true === WP_Http_Streams::test($args) ) {
+				$working_transport['streams'] = new WP_Http_Streams();
+				$blocking_transport[] = &$working_transport['streams'];
+			} else if ( true === WP_Http_Fopen::test($args) ) {
+				$working_transport['fopen'] = new WP_Http_Fopen();
+				$blocking_transport[] = &$working_transport['fopen'];
+			} else if ( true === WP_Http_Curl::test($args) ) {
+				$working_transport['curl'] = new WP_Http_Curl();
+				$blocking_transport[] = &$working_transport['curl'];
+			}
+
+			foreach ( array('curl', 'streams', 'fopen', 'fsockopen', 'exthttp') as $transport ) {
+				if ( isset($working_transport[$transport]) )
+					$nonblocking_transport[] = &$working_transport[$transport];
+			}
+		}
+
+		if ( has_filter('http_transport_get_debug') )
+			do_action('http_transport_get_debug', $working_transport, $blocking_transport, $nonblocking_transport);
+
+		if ( isset($args['blocking']) && !$args['blocking'] )
+			return $nonblocking_transport;
+		else
+			return $blocking_transport;
+	}
+
+	/**
+	 * Tests the WordPress HTTP objects for an object to use and returns it.
+	 *
+	 * Tests all of the objects and returns the object that passes. Also caches
+	 * that object to be used later. This is for posting content to a URL and
+	 * is used when there is a body. The plain Fopen Transport can not be used
+	 * to send content, but the streams transport can. This is a limitation that
+	 * is addressed here, by just not including that transport.
+	 *
+	 * @since 2.7.0
+	 * @access private
+	 *
+	 * @param array $args Request args, default us an empty array
+	 * @return object|null Null if no transports are available, HTTP transport object.
+	 */
+	function &_postTransport( $args = array() ) {
+		static $working_transport, $blocking_transport, $nonblocking_transport;
+
+		if ( is_null($working_transport) ) {
+			if ( true === WP_Http_ExtHttp::test($args) ) {
+				$working_transport['exthttp'] = new WP_Http_ExtHttp();
+				$blocking_transport[] = &$working_transport['exthttp'];
+			} else if ( true === WP_Http_Fsockopen::test($args) ) {
+				$working_transport['fsockopen'] = new WP_Http_Fsockopen();
+				$blocking_transport[] = &$working_transport['fsockopen'];
+			} else if ( true === WP_Http_Streams::test($args) ) {
+				$working_transport['streams'] = new WP_Http_Streams();
+				$blocking_transport[] = &$working_transport['streams'];
+			} else if ( true === WP_Http_Curl::test($args) ) {
+				$working_transport['curl'] = new WP_Http_Curl();
+				$blocking_transport[] = &$working_transport['curl'];
+			}
+
+			foreach ( array('curl', 'streams', 'fsockopen', 'exthttp') as $transport ) {
+				if ( isset($working_transport[$transport]) )
+					$nonblocking_transport[] = &$working_transport[$transport];
+			}
+		}
+
+		if ( has_filter('http_transport_post_debug') )
+			do_action('http_transport_post_debug', $working_transport, $blocking_transport, $nonblocking_transport);
+
+		if ( isset($args['blocking']) && !$args['blocking'] )
+			return $nonblocking_transport;
+		else
+			return $blocking_transport;
+	}
+
+	/**
+	 * Send a HTTP request to a URI.
+	 *
+	 * The body and headers are part of the arguments. The 'body' argument is for the body and will
+	 * accept either a string or an array. The 'headers' argument should be an array, but a string
+	 * is acceptable. If the 'body' argument is an array, then it will automatically be escaped
+	 * using http_build_query().
+	 *
+	 * The only URI that are supported in the HTTP Transport implementation are the HTTP and HTTPS
+	 * protocols. HTTP and HTTPS are assumed so the server might not know how to handle the send
+	 * headers. Other protocols are unsupported and most likely will fail.
+	 *
+	 * The defaults are 'method', 'timeout', 'redirection', 'httpversion', 'blocking' and
+	 * 'user-agent'.
+	 *
+	 * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports technically allow
+	 * others, but should not be assumed. The 'timeout' is used to sent how long the connection
+	 * should stay open before failing when no response. 'redirection' is used to track how many
+	 * redirects were taken and used to sent the amount for other transports, but not all transports
+	 * accept setting that value.
+	 *
+	 * The 'httpversion' option is used to sent the HTTP version and accepted values are '1.0', and
+	 * '1.1' and should be a string. Version 1.1 is not supported, because of chunk response. The
+	 * 'user-agent' option is the user-agent and is used to replace the default user-agent, which is
+	 * 'WordPress/WP_Version', where WP_Version is the value from $wp_version.
+	 *
+	 * 'blocking' is the default, which is used to tell the transport, whether it should halt PHP
+	 * while it performs the request or continue regardless. Actually, that isn't entirely correct.
+	 * Blocking mode really just means whether the fread should just pull what it can whenever it
+	 * gets bytes or if it should wait until it has enough in the buffer to read or finishes reading
+	 * the entire content. It doesn't actually always mean that PHP will continue going after making
+	 * the request.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array containing 'headers', 'body', 'response', 'cookies'
+	 */
+	function request( $url, $args = array() ) {
+		$defaults = array(
+			'method' => 'GET',
+			'timeout' => apply_filters( 'http_request_timeout', 5),
+			'redirection' => apply_filters( 'http_request_redirection_count', 5),
+			'httpversion' => apply_filters( 'http_request_version', '1.0'),
+			'user-agent' => apply_filters( 'http_headers_useragent', backpress_get_option( 'wp_http_version' ) ),
+			'blocking' => true,
+			'headers' => array(),
+			'cookies' => array(),
+			'body' => null,
+			'compress' => false,
+			'decompress' => true,
+			'sslverify' => true
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		$r = apply_filters( 'http_request_args', $r, $url );
+
+		$arrURL = parse_url($url);
+
+		if ( $this->block_request( $url ) )
+			return new WP_Error('http_request_failed', 'User has blocked requests through HTTP.');
+
+		// Determine if this is a https call and pass that on to the transport functions
+		// so that we can blacklist the transports that do not support ssl verification
+		$r['ssl'] = $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl';
+
+		// Determine if this request is to OUR install of WordPress
+		$homeURL = parse_url( backpress_get_option( 'application_uri' ) );
+		$r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host'];
+		unset($homeURL);
+
+		if ( is_null( $r['headers'] ) )
+			$r['headers'] = array();
+
+		if ( ! is_array($r['headers']) ) {
+			$processedHeaders = WP_Http::processHeaders($r['headers']);
+			$r['headers'] = $processedHeaders['headers'];
+		}
+
+		if ( isset($r['headers']['User-Agent']) ) {
+			$r['user-agent'] = $r['headers']['User-Agent'];
+			unset($r['headers']['User-Agent']);
+		}
+
+		if ( isset($r['headers']['user-agent']) ) {
+			$r['user-agent'] = $r['headers']['user-agent'];
+			unset($r['headers']['user-agent']);
+		}
+
+		// Construct Cookie: header if any cookies are set
+		WP_Http::buildCookieHeader( $r );
+
+		if ( WP_Http_Encoding::is_available() )
+			$r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding();
+
+		if ( is_null($r['body']) ) {
+			// Some servers fail when sending content without the content-length
+			// header being set.
+			$r['headers']['Content-Length'] = 0;
+			$transports = WP_Http::_getTransport($r);
+		} else {
+			if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) {
+				if ( ! version_compare(phpversion(), '5.1.2', '>=') )
+					$r['body'] = _http_build_query($r['body'], null, '&');
+				else
+					$r['body'] = http_build_query($r['body'], null, '&');
+				$r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . backpress_get_option( 'charset' );
+				$r['headers']['Content-Length'] = strlen($r['body']);
+			}
+
+			if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) )
+				$r['headers']['Content-Length'] = strlen($r['body']);
+
+			$transports = WP_Http::_postTransport($r);
+		}
+
+		if ( has_action('http_api_debug') )
+			do_action('http_api_debug', $transports, 'transports_list');
+
+		$response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+		foreach ( (array) $transports as $transport ) {
+			$response = $transport->request($url, $r);
+
+			if ( has_action('http_api_debug') )
+				do_action( 'http_api_debug', $response, 'response', get_class($transport) );
+
+			if ( ! is_wp_error($response) )
+				return $response;
+		}
+
+		return $response;
+	}
+
+	/**
+	 * Uses the POST HTTP method.
+	 *
+	 * Used for sending data that is expected to be in the body.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return boolean
+	 */
+	function post($url, $args = array()) {
+		$defaults = array('method' => 'POST');
+		$r = wp_parse_args( $args, $defaults );
+		return $this->request($url, $r);
+	}
+
+	/**
+	 * Uses the GET HTTP method.
+	 *
+	 * Used for sending data that is expected to be in the body.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return boolean
+	 */
+	function get($url, $args = array()) {
+		$defaults = array('method' => 'GET');
+		$r = wp_parse_args( $args, $defaults );
+		return $this->request($url, $r);
+	}
+
+	/**
+	 * Uses the HEAD HTTP method.
+	 *
+	 * Used for sending data that is expected to be in the body.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return boolean
+	 */
+	function head($url, $args = array()) {
+		$defaults = array('method' => 'HEAD');
+		$r = wp_parse_args( $args, $defaults );
+		return $this->request($url, $r);
+	}
+
+	/**
+	 * Parses the responses and splits the parts into headers and body.
+	 *
+	 * @access public
+	 * @static
+	 * @since 2.7.0
+	 *
+	 * @param string $strResponse The full response string
+	 * @return array Array with 'headers' and 'body' keys.
+	 */
+	function processResponse($strResponse) {
+		list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
+		return array('headers' => $theHeaders, 'body' => $theBody);
+	}
+
+	/**
+	 * Transform header string into an array.
+	 *
+	 * If an array is given then it is assumed to be raw header data with numeric keys with the
+	 * headers as the values. No headers must be passed that were already processed.
+	 *
+	 * @access public
+	 * @static
+	 * @since 2.7.0
+	 *
+	 * @param string|array $headers
+	 * @return array Processed string headers. If duplicate headers are encountered,
+	 * 					Then a numbered array is returned as the value of that header-key.
+	 */
+	function processHeaders($headers) {
+		// split headers, one per array element
+		if ( is_string($headers) ) {
+			// tolerate line terminator: CRLF = LF (RFC 2616 19.3)
+			$headers = str_replace("\r\n", "\n", $headers);
+			// unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>, <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2)
+			$headers = preg_replace('/\n[ \t]/', ' ', $headers);
+			// create the headers array
+			$headers = explode("\n", $headers);
+		}
+
+		$response = array('code' => 0, 'message' => '');
+
+		$cookies = array();
+		$newheaders = array();
+		foreach ( $headers as $tempheader ) {
+			if ( empty($tempheader) )
+				continue;
+
+			if ( false === strpos($tempheader, ':') ) {
+				list( , $iResponseCode, $strResponseMsg) = explode(' ', $tempheader, 3);
+				$response['code'] = $iResponseCode;
+				$response['message'] = $strResponseMsg;
+				continue;
+			}
+
+			list($key, $value) = explode(':', $tempheader, 2);
+
+			if ( !empty( $value ) ) {
+				$key = strtolower( $key );
+				if ( isset( $newheaders[$key] ) ) {
+					$newheaders[$key] = array( $newheaders[$key], trim( $value ) );
+				} else {
+					$newheaders[$key] = trim( $value );
+				}
+				if ( 'set-cookie' == strtolower( $key ) )
+					$cookies[] = new WP_Http_Cookie( $value );
+			}
+		}
+
+		return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
+	}
+
+	/**
+	 * Takes the arguments for a ::request() and checks for the cookie array.
+	 *
+	 * If it's found, then it's assumed to contain WP_Http_Cookie objects, which are each parsed
+	 * into strings and added to the Cookie: header (within the arguments array). Edits the array by
+	 * reference.
+	 *
+	 * @access public
+	 * @version 2.8.0
+	 * @static
+	 *
+	 * @param array $r Full array of args passed into ::request()
+	 */
+	function buildCookieHeader( &$r ) {
+		if ( ! empty($r['cookies']) ) {
+			$cookies_header = '';
+			foreach ( (array) $r['cookies'] as $cookie ) {
+				$cookies_header .= $cookie->getHeaderValue() . '; ';
+			}
+			$cookies_header = substr( $cookies_header, 0, -2 );
+			$r['headers']['cookie'] = $cookies_header;
+		}
+	}
+
+	/**
+	 * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
+	 *
+	 * Based off the HTTP http_encoding_dechunk function. Does not support UTF-8. Does not support
+	 * returning footer headers. Shouldn't be too difficult to support it though.
+	 *
+	 * @todo Add support for footer chunked headers.
+	 * @access public
+	 * @since 2.7.0
+	 * @static
+	 *
+	 * @param string $body Body content
+	 * @return string Chunked decoded body on success or raw body on failure.
+	 */
+	function chunkTransferDecode($body) {
+		$body = str_replace(array("\r\n", "\r"), "\n", $body);
+		// The body is not chunked encoding or is malformed.
+		if ( ! preg_match( '/^[0-9a-f]+(\s|\n)+/mi', trim($body) ) )
+			return $body;
+
+		$parsedBody = '';
+		//$parsedHeaders = array(); Unsupported
+
+		while ( true ) {
+			$hasChunk = (bool) preg_match( '/^([0-9a-f]+)(\s|\n)+/mi', $body, $match );
+
+			if ( $hasChunk ) {
+				if ( empty( $match[1] ) )
+					return $body;
+
+				$length = hexdec( $match[1] );
+				$chunkLength = strlen( $match[0] );
+
+				$strBody = substr($body, $chunkLength, $length);
+				$parsedBody .= $strBody;
+
+				$body = ltrim(str_replace(array($match[0], $strBody), '', $body), "\n");
+
+				if ( "0" == trim($body) )
+					return $parsedBody; // Ignore footer headers.
+			} else {
+				return $body;
+			}
+		}
+	}
+
+	/**
+	 * Block requests through the proxy.
+	 *
+	 * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
+	 * prevent plugins from working and core functionality, if you don't include api.wordpress.org.
+	 *
+	 * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL in your wp-config.php file
+	 * and this will only allow localhost and your blog to make requests. The constant
+	 * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the
+	 * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow.
+	 *
+	 * @since 2.8.0
+	 * @link http://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
+	 *
+	 * @param string $uri URI of url.
+	 * @return bool True to block, false to allow.
+	 */
+	function block_request($uri) {
+		// We don't need to block requests, because nothing is blocked.
+		if ( ! defined('WP_HTTP_BLOCK_EXTERNAL') || ( defined('WP_HTTP_BLOCK_EXTERNAL') && WP_HTTP_BLOCK_EXTERNAL == false ) )
+			return false;
+
+		// parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
+		// This will be displayed on blogs, which is not reasonable.
+		$check = @parse_url($uri);
+
+		/* Malformed URL, can not process, but this could mean ssl, so let through anyway.
+		 *
+		 * This isn't very security sound. There are instances where a hacker might attempt
+		 * to bypass the proxy and this check. However, the reason for this behavior is that
+		 * WordPress does not do any checking currently for non-proxy requests, so it is keeps with
+		 * the default unsecure nature of the HTTP request.
+		 */
+		if ( $check === false )
+			return false;
+
+		$home = parse_url( backpress_get_option( 'application_uri' ) );
+
+		// Don't block requests back to ourselves by default
+		if ( $check['host'] == 'localhost' || $check['host'] == $home['host'] )
+			return apply_filters('block_local_requests', false);
+
+		if ( !defined('WP_ACCESSIBLE_HOSTS') )
+			return true;
+
+		static $accessible_hosts;
+		if ( null == $accessible_hosts )
+			$accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS);
+
+		return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If its in the array, then we can't access it.
+	}
+}
+
+/**
+ * HTTP request method uses fsockopen function to retrieve the url.
+ *
+ * This would be the preferred method, but the fsockopen implementation has the most overhead of all
+ * the HTTP transport implementations.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ */
+class WP_Http_Fsockopen {
+	/**
+	 * Send a HTTP request to a URI using fsockopen().
+	 *
+	 * Does not support non-blocking mode.
+	 *
+	 * @see WP_Http::request For default options descriptions.
+	 *
+	 * @since 2.7
+	 * @access public
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array 'headers', 'body', 'cookies' and 'response' keys.
+	 */
+	function request($url, $args = array()) {
+		$defaults = array(
+			'method' => 'GET', 'timeout' => 5,
+			'redirection' => 5, 'httpversion' => '1.0',
+			'blocking' => true,
+			'headers' => array(), 'body' => null, 'cookies' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+
+		if ( isset($r['headers']['User-Agent']) ) {
+			$r['user-agent'] = $r['headers']['User-Agent'];
+			unset($r['headers']['User-Agent']);
+		} else if( isset($r['headers']['user-agent']) ) {
+			$r['user-agent'] = $r['headers']['user-agent'];
+			unset($r['headers']['user-agent']);
+		}
+
+		// Construct Cookie: header if any cookies are set
+		WP_Http::buildCookieHeader( $r );
+
+		$iError = null; // Store error number
+		$strError = null; // Store error string
+
+		$arrURL = parse_url($url);
+
+		$fsockopen_host = $arrURL['host'];
+
+		$secure_transport = false;
+
+		if ( ! isset( $arrURL['port'] ) ) {
+			if ( ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) && extension_loaded('openssl') ) {
+				$fsockopen_host = "ssl://$fsockopen_host";
+				$arrURL['port'] = 443;
+				$secure_transport = true;
+			} else {
+				$arrURL['port'] = 80;
+			}
+		}
+
+		// There are issues with the HTTPS and SSL protocols that cause errors that can be safely
+		// ignored and should be ignored.
+		if ( true === $secure_transport )
+			$error_reporting = error_reporting(0);
+
+		$startDelay = time();
+
+		$proxy = new WP_HTTP_Proxy();
+
+		if ( !defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) ) {
+			if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
+				$handle = @fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
+			else
+				$handle = @fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
+		} else {
+			if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
+				$handle = fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
+			else
+				$handle = fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
+		}
+
+		$endDelay = time();
+
+		// If the delay is greater than the timeout then fsockopen should't be used, because it will
+		// cause a long delay.
+		$elapseDelay = ($endDelay-$startDelay) > $r['timeout'];
+		if ( true === $elapseDelay )
+			backpress_add_option( 'disable_fsockopen', $endDelay, null, true );
+
+		if ( false === $handle )
+			return new WP_Error('http_request_failed', $iError . ': ' . $strError);
+
+		stream_set_timeout($handle, $r['timeout'] );
+
+		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field.
+			$requestPath = $url;
+		else
+			$requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' );
+
+		if ( empty($requestPath) )
+			$requestPath .= '/';
+
+		$strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n";
+
+		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
+			$strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n";
+		else
+			$strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n";
+
+		if ( isset($r['user-agent']) )
+			$strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n";
+
+		if ( is_array($r['headers']) ) {
+			foreach ( (array) $r['headers'] as $header => $headerValue )
+				$strHeaders .= $header . ': ' . $headerValue . "\r\n";
+		} else {
+			$strHeaders .= $r['headers'];
+		}
+
+		if ( $proxy->use_authentication() )
+			$strHeaders .= $proxy->authentication_header() . "\r\n";
+
+		$strHeaders .= "\r\n";
+
+		if ( ! is_null($r['body']) )
+			$strHeaders .= $r['body'];
+
+		fwrite($handle, $strHeaders);
+
+		if ( ! $r['blocking'] ) {
+			fclose($handle);
+			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+		}
+
+		$strResponse = '';
+		while ( ! feof($handle) )
+			$strResponse .= fread($handle, 4096);
+
+		fclose($handle);
+
+		if ( true === $secure_transport )
+			error_reporting($error_reporting);
+
+		$process = WP_Http::processResponse($strResponse);
+		$arrHeaders = WP_Http::processHeaders($process['headers']);
+
+		// Is the response code within the 400 range?
+		if ( (int) $arrHeaders['response']['code'] >= 400 && (int) $arrHeaders['response']['code'] < 500 )
+			return new WP_Error('http_request_failed', $arrHeaders['response']['code'] . ': ' . $arrHeaders['response']['message']);
+
+		// If location is found, then assume redirect and redirect to location.
+		if ( isset($arrHeaders['headers']['location']) ) {
+			if ( $r['redirection']-- > 0 ) {
+				return $this->request($arrHeaders['headers']['location'], $r);
+			} else {
+				return new WP_Error('http_request_failed', __('Too many redirects.'));
+			}
+		}
+
+		// If the body was chunk encoded, then decode it.
+		if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] )
+			$process['body'] = WP_Http::chunkTransferDecode($process['body']);
+
+		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) )
+			$process['body'] = WP_Http_Encoding::decompress( $process['body'] );
+
+		return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
+	}
+
+	/**
+	 * Whether this class can be used for retrieving an URL.
+	 *
+	 * @since 2.7.0
+	 * @static
+	 * @return boolean False means this class can not be used, true means it can.
+	 */
+	function test( $args = array() ) {
+		if ( false !== ($option = backpress_get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours
+			return false;
+
+		$is_ssl = isset($args['ssl']) && $args['ssl'];
+
+		if ( ! $is_ssl && function_exists( 'fsockopen' ) )
+			$use = true;
+		elseif ( $is_ssl && extension_loaded('openssl') && function_exists( 'fsockopen' ) )
+			$use = true;
+		else
+			$use = false;
+
+		return apply_filters('use_fsockopen_transport', $use, $args);
+	}
+}
+
+/**
+ * HTTP request method uses fopen function to retrieve the url.
+ *
+ * Requires PHP version greater than 4.3.0 for stream support. Does not allow for $context support,
+ * but should still be okay, to write the headers, before getting the response. Also requires that
+ * 'allow_url_fopen' to be enabled.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ */
+class WP_Http_Fopen {
+	/**
+	 * Send a HTTP request to a URI using fopen().
+	 *
+	 * This transport does not support sending of headers and body, therefore should not be used in
+	 * the instances, where there is a body and headers.
+	 *
+	 * Notes: Does not support non-blocking mode. Ignores 'redirection' option.
+	 *
+	 * @see WP_Http::retrieve For default options descriptions.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url URI resource.
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array 'headers', 'body', 'cookies' and 'response' keys.
+	 */
+	function request($url, $args = array()) {
+		$defaults = array(
+			'method' => 'GET', 'timeout' => 5,
+			'redirection' => 5, 'httpversion' => '1.0',
+			'blocking' => true,
+			'headers' => array(), 'body' => null, 'cookies' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+
+		$arrURL = parse_url($url);
+
+		if ( false === $arrURL )
+			return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
+
+		if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] )
+			$url = str_replace($arrURL['scheme'], 'http', $url);
+
+		if ( !defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) )
+			$handle = @fopen($url, 'r');
+		else
+			$handle = fopen($url, 'r');
+
+		if (! $handle)
+			return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
+
+		stream_set_timeout($handle, $r['timeout'] );
+
+		if ( ! $r['blocking'] ) {
+			fclose($handle);
+			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+		}
+
+		$strResponse = '';
+		while ( ! feof($handle) )
+			$strResponse .= fread($handle, 4096);
+
+		if ( function_exists('stream_get_meta_data') ) {
+			$meta = stream_get_meta_data($handle);
+			$theHeaders = $meta['wrapper_data'];
+			if ( isset( $meta['wrapper_data']['headers'] ) )
+				$theHeaders = $meta['wrapper_data']['headers'];
+		} else {
+			//$http_response_header is a PHP reserved variable which is set in the current-scope when using the HTTP Wrapper
+			//see http://php.oregonstate.edu/manual/en/reserved.variables.httpresponseheader.php
+			$theHeaders = $http_response_header;
+		}
+
+		fclose($handle);
+
+		$processedHeaders = WP_Http::processHeaders($theHeaders);
+
+		if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] )
+			$strResponse = WP_Http::chunkTransferDecode($strResponse);
+
+		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
+			$strResponse = WP_Http_Encoding::decompress( $strResponse );
+
+		return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
+	}
+
+	/**
+	 * Whether this class can be used for retrieving an URL.
+	 *
+	 * @since 2.7.0
+	 * @static
+	 * @return boolean False means this class can not be used, true means it can.
+	 */
+	function test($args = array()) {
+		if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
+			return false;
+
+		$use = true;
+
+		//PHP does not verify SSL certs, We can only make a request via this transports if SSL Verification is turned off.
+		$is_ssl = isset($args['ssl']) && $args['ssl'];
+		if ( $is_ssl ) {
+			$is_local = isset($args['local']) && $args['local'];
+			$ssl_verify = isset($args['sslverify']) && $args['sslverify'];
+			if ( $is_local && true != apply_filters('https_local_ssl_verify', true) )
+				$use = true;
+			elseif ( !$is_local && true != apply_filters('https_ssl_verify', true) )
+				$use = true;
+			elseif ( !$ssl_verify )
+				$use = true;
+			else
+				$use = false;
+		}
+
+		return apply_filters('use_fopen_transport', $use, $args);
+	}
+}
+
+/**
+ * HTTP request method uses Streams to retrieve the url.
+ *
+ * Requires PHP 5.0+ and uses fopen with stream context. Requires that 'allow_url_fopen' PHP setting
+ * to be enabled.
+ *
+ * Second preferred method for getting the URL, for PHP 5.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ */
+class WP_Http_Streams {
+	/**
+	 * Send a HTTP request to a URI using streams with fopen().
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array 'headers', 'body', 'cookies' and 'response' keys.
+	 */
+	function request($url, $args = array()) {
+		$defaults = array(
+			'method' => 'GET', 'timeout' => 5,
+			'redirection' => 5, 'httpversion' => '1.0',
+			'blocking' => true,
+			'headers' => array(), 'body' => null, 'cookies' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+
+		if ( isset($r['headers']['User-Agent']) ) {
+			$r['user-agent'] = $r['headers']['User-Agent'];
+			unset($r['headers']['User-Agent']);
+		} else if( isset($r['headers']['user-agent']) ) {
+			$r['user-agent'] = $r['headers']['user-agent'];
+			unset($r['headers']['user-agent']);
+		}
+
+		// Construct Cookie: header if any cookies are set
+		WP_Http::buildCookieHeader( $r );
+
+		$arrURL = parse_url($url);
+
+		if ( false === $arrURL )
+			return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
+
+		if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] )
+			$url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url);
+
+		// Convert Header array to string.
+		$strHeaders = '';
+		if ( is_array( $r['headers'] ) )
+			foreach ( $r['headers'] as $name => $value )
+				$strHeaders .= "{$name}: $value\r\n";
+		else if ( is_string( $r['headers'] ) )
+			$strHeaders = $r['headers'];
+
+		$is_local = isset($args['local']) && $args['local'];
+		$ssl_verify = isset($args['sslverify']) && $args['sslverify'];
+		if ( $is_local )
+			$ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
+		elseif ( ! $is_local )
+			$ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
+
+		$arrContext = array('http' =>
+			array(
+				'method' => strtoupper($r['method']),
+				'user_agent' => $r['user-agent'],
+				'max_redirects' => $r['redirection'],
+				'protocol_version' => (float) $r['httpversion'],
+				'header' => $strHeaders,
+				'timeout' => $r['timeout'],
+				'ssl' => array(
+						'verify_peer' => $ssl_verify,
+						'verify_host' => $ssl_verify
+				)
+			)
+		);
+
+		$proxy = new WP_HTTP_Proxy();
+
+		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
+			$arrContext['http']['proxy'] = 'tcp://' . $proxy->host() . ':' . $proxy->port();
+			$arrContext['http']['request_fulluri'] = true;
+
+			// We only support Basic authentication so this will only work if that is what your proxy supports.
+			if ( $proxy->use_authentication() )
+				$arrContext['http']['header'] .= $proxy->authentication_header() . "\r\n";
+		}
+
+		if ( ! is_null($r['body']) && ! empty($r['body'] ) )
+			$arrContext['http']['content'] = $r['body'];
+
+		$context = stream_context_create($arrContext);
+
+		if ( ! defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) )
+			$handle = @fopen($url, 'r', false, $context);
+		else
+			$handle = fopen($url, 'r', false, $context);
+
+		if ( ! $handle)
+			return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
+
+		// WordPress supports PHP 4.3, which has this function. Removed sanity checking for
+		// performance reasons.
+		stream_set_timeout($handle, $r['timeout'] );
+
+		if ( ! $r['blocking'] ) {
+			stream_set_blocking($handle, 0);
+			fclose($handle);
+			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+		}
+
+		$strResponse = stream_get_contents($handle);
+		$meta = stream_get_meta_data($handle);
+
+		fclose($handle);
+
+		$processedHeaders = array();
+		if ( isset( $meta['wrapper_data']['headers'] ) )
+			$processedHeaders = WP_Http::processHeaders($meta['wrapper_data']['headers']);
+		else
+			$processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);
+
+		if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] )
+			$strResponse = WP_Http::chunkTransferDecode($strResponse);
+
+		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
+			$strResponse = WP_Http_Encoding::decompress( $strResponse );
+
+		return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
+	}
+
+	/**
+	 * Whether this class can be used for retrieving an URL.
+	 *
+	 * @static
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @return boolean False means this class can not be used, true means it can.
+	 */
+	function test($args = array()) {
+		if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
+			return false;
+
+		if ( version_compare(PHP_VERSION, '5.0', '<') )
+			return false;
+
+		//HTTPS via Proxy was added in 5.1.0
+		$is_ssl = isset($args['ssl']) && $args['ssl'];
+		if ( $is_ssl && version_compare(PHP_VERSION, '5.1.0', '<') ) {
+			$proxy = new WP_HTTP_Proxy();
+			/**
+			 * No URL check, as its not currently passed to the ::test() function
+			 * In the case where a Proxy is in use, Just bypass this transport for HTTPS.
+			 */
+			if ( $proxy->is_enabled() )
+				return false;
+		}
+
+		return apply_filters('use_streams_transport', true, $args);
+	}
+}
+
+/**
+ * HTTP request method uses HTTP extension to retrieve the url.
+ *
+ * Requires the HTTP extension to be installed. This would be the preferred transport since it can
+ * handle a lot of the problems that forces the others to use the HTTP version 1.0. Even if PHP 5.2+
+ * is being used, it doesn't mean that the HTTP extension will be enabled.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7.0
+ */
+class WP_Http_ExtHTTP {
+	/**
+	 * Send a HTTP request to a URI using HTTP extension.
+	 *
+	 * Does not support non-blocking.
+	 *
+	 * @access public
+	 * @since 2.7
+	 *
+	 * @param string $url
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array 'headers', 'body', 'cookies' and 'response' keys.
+	 */
+	function request($url, $args = array()) {
+		$defaults = array(
+			'method' => 'GET', 'timeout' => 5,
+			'redirection' => 5, 'httpversion' => '1.0',
+			'blocking' => true,
+			'headers' => array(), 'body' => null, 'cookies' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+
+		if ( isset($r['headers']['User-Agent']) ) {
+			$r['user-agent'] = $r['headers']['User-Agent'];
+			unset($r['headers']['User-Agent']);
+		} else if( isset($r['headers']['user-agent']) ) {
+			$r['user-agent'] = $r['headers']['user-agent'];
+			unset($r['headers']['user-agent']);
+		}
+
+		// Construct Cookie: header if any cookies are set
+		WP_Http::buildCookieHeader( $r );
+
+		switch ( $r['method'] ) {
+			case 'POST':
+				$r['method'] = HTTP_METH_POST;
+				break;
+			case 'HEAD':
+				$r['method'] = HTTP_METH_HEAD;
+				break;
+			case 'GET':
+			default:
+				$r['method'] = HTTP_METH_GET;
+		}
+
+		$arrURL = parse_url($url);
+
+		if ( 'http' != $arrURL['scheme'] || 'https' != $arrURL['scheme'] )
+			$url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url);
+
+		$is_local = isset($args['local']) && $args['local'];
+		$ssl_verify = isset($args['sslverify']) && $args['sslverify'];
+		if ( $is_local )
+			$ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
+		elseif ( ! $is_local )
+			$ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
+
+		$options = array(
+			'timeout' => $r['timeout'],
+			'connecttimeout' => $r['timeout'],
+			'redirect' => $r['redirection'],
+			'useragent' => $r['user-agent'],
+			'headers' => $r['headers'],
+			'ssl' => array(
+				'verifypeer' => $ssl_verify,
+				'verifyhost' => $ssl_verify
+			)
+		);
+
+		// The HTTP extensions offers really easy proxy support.
+		$proxy = new WP_HTTP_Proxy();
+
+		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
+			$options['proxyhost'] = $proxy->host();
+			$options['proxyport'] = $proxy->port();
+			$options['proxytype'] = HTTP_PROXY_HTTP;
+
+			if ( $proxy->use_authentication() ) {
+				$options['proxyauth'] = $proxy->authentication();
+				$options['proxyauthtype'] = HTTP_AUTH_BASIC;
+			}
+		}
+
+		if ( !defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) ) //Emits warning level notices for max redirects and timeouts
+			$strResponse = @http_request($r['method'], $url, $r['body'], $options, $info);
+		else
+			$strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts
+
+		// Error may still be set, Response may return headers or partial document, and error
+		// contains a reason the request was aborted, eg, timeout expired or max-redirects reached.
+		if ( false === $strResponse || ! empty($info['error']) )
+			return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
+
+		if ( ! $r['blocking'] )
+			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+
+		list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
+		$theHeaders = WP_Http::processHeaders($theHeaders);
+
+		if ( ! empty( $theBody ) && isset( $theHeaders['headers']['transfer-encoding'] ) && 'chunked' == $theHeaders['headers']['transfer-encoding'] ) {
+			if ( !defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) )
+				$theBody = @http_chunked_decode($theBody);
+			else
+				$theBody = http_chunked_decode($theBody);
+		}
+
+		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
+			$theBody = http_inflate( $theBody );
+
+		$theResponse = array();
+		$theResponse['code'] = $info['response_code'];
+		$theResponse['message'] = get_status_header_desc($info['response_code']);
+
+		return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
+	}
+
+	/**
+	 * Whether this class can be used for retrieving an URL.
+	 *
+	 * @static
+	 * @since 2.7.0
+	 *
+	 * @return boolean False means this class can not be used, true means it can.
+	 */
+	function test($args = array()) {
+		return apply_filters('use_http_extension_transport', function_exists('http_request'), $args );
+	}
+}
+
+/**
+ * HTTP request method uses Curl extension to retrieve the url.
+ *
+ * Requires the Curl extension to be installed.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.7
+ */
+class WP_Http_Curl {
+
+	/**
+	 * Send a HTTP request to a URI using cURL extension.
+	 *
+	 * @access public
+	 * @since 2.7.0
+	 *
+	 * @param string $url
+	 * @param str|array $args Optional. Override the defaults.
+	 * @return array 'headers', 'body', 'cookies' and 'response' keys.
+	 */
+	function request($url, $args = array()) {
+		$defaults = array(
+			'method' => 'GET', 'timeout' => 5,
+			'redirection' => 5, 'httpversion' => '1.0',
+			'blocking' => true,
+			'headers' => array(), 'body' => null, 'cookies' => array()
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+
+		if ( isset($r['headers']['User-Agent']) ) {
+			$r['user-agent'] = $r['headers']['User-Agent'];
+			unset($r['headers']['User-Agent']);
+		} else if( isset($r['headers']['user-agent']) ) {
+			$r['user-agent'] = $r['headers']['user-agent'];
+			unset($r['headers']['user-agent']);
+		}
+
+		// Construct Cookie: header if any cookies are set.
+		WP_Http::buildCookieHeader( $r );
+
+		// cURL extension will sometimes fail when the timeout is less than 1 as it may round down
+		// to 0, which gives it unlimited timeout.
+		if ( $r['timeout'] > 0 && $r['timeout'] < 1 )
+			$r['timeout'] = 1;
+
+		$handle = curl_init();
+
+		// cURL offers really easy proxy support.
+		$proxy = new WP_HTTP_Proxy();
+
+		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
+
+			$isPHP5 = version_compare(PHP_VERSION, '5.0.0', '>=');
+
+			if ( $isPHP5 ) {
+				curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
+				curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
+				curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
+			} else {
+				curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() .':'. $proxy->port() );
+			}
+
+			if ( $proxy->use_authentication() ) {
+				if ( $isPHP5 )
+					curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC );
+
+				curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
+			}
+		}
+
+		$is_local = isset($args['local']) && $args['local'];
+		$ssl_verify = isset($args['sslverify']) && $args['sslverify'];
+		if ( $is_local )
+			$ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
+		elseif ( ! $is_local )
+			$ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
+
+		curl_setopt( $handle, CURLOPT_URL, $url);
+		curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
+		curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, $ssl_verify );
+		curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
+		curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
+		curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $r['timeout'] );
+		curl_setopt( $handle, CURLOPT_TIMEOUT, $r['timeout'] );
+		curl_setopt( $handle, CURLOPT_MAXREDIRS, $r['redirection'] );
+
+		switch ( $r['method'] ) {
+			case 'HEAD':
+				curl_setopt( $handle, CURLOPT_NOBODY, true );
+				break;
+			case 'POST':
+				curl_setopt( $handle, CURLOPT_POST, true );
+				curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
+				break;
+		}
+
+		if ( true === $r['blocking'] )
+			curl_setopt( $handle, CURLOPT_HEADER, true );
+		else
+			curl_setopt( $handle, CURLOPT_HEADER, false );
+
+		// The option doesn't work with safe mode or when open_basedir is set.
+		if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
+			curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
+
+		if ( !empty( $r['headers'] ) ) {
+			// cURL expects full header strings in each element
+			$headers = array();
+			foreach ( $r['headers'] as $name => $value ) {
+				$headers[] = "{$name}: $value";
+			}
+			curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
+		}
+
+		if ( $r['httpversion'] == '1.0' )
+			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
+		else
+			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
+
+		// Cookies are not handled by the HTTP API currently. Allow for plugin authors to handle it
+		// themselves... Although, it is somewhat pointless without some reference.
+		do_action_ref_array( 'http_api_curl', array(&$handle) );
+
+		// We don't need to return the body, so don't. Just execute request and return.
+		if ( ! $r['blocking'] ) {
+			curl_exec( $handle );
+			curl_close( $handle );
+			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
+		}
+
+		$theResponse = curl_exec( $handle );
+
+		if ( !empty($theResponse) ) {
+			$parts = explode("\r\n\r\n", $theResponse);
+
+			$headerLength = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
+			$theHeaders = trim( substr($theResponse, 0, $headerLength) );
+			$theBody = substr( $theResponse, $headerLength );
+			if ( false !== strrpos($theHeaders, "\r\n\r\n") ) {
+				$headerParts = explode("\r\n\r\n", $theHeaders);
+				$theHeaders = $headerParts[ count($headerParts) -1 ];
+			}
+			$theHeaders = WP_Http::processHeaders($theHeaders);
+		} else {
+			if ( $curl_error = curl_error($handle) )
+				return new WP_Error('http_request_failed', $curl_error);
+			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
+				return new WP_Error('http_request_failed', __('Too many redirects.'));
+
+			$theHeaders = array( 'headers' => array(), 'cookies' => array() );
+			$theBody = '';
+		}
+
+		$response = array();
+		$response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE );
+		$response['message'] = get_status_header_desc($response['code']);
+
+		curl_close( $handle );
+
+		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
+			$theBody = WP_Http_Encoding::decompress( $theBody );
+
+		return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
+	}
+
+	/**
+	 * Whether this class can be used for retrieving an URL.
+	 *
+	 * @static
+	 * @since 2.7.0
+	 *
+	 * @return boolean False means this class can not be used, true means it can.
+	 */
+	function test($args = array()) {
+		if ( function_exists('curl_init') && function_exists('curl_exec') )
+			return apply_filters('use_curl_transport', true, $args);
+
+		return false;
+	}
+}
+
+/**
+ * Adds Proxy support to the WordPress HTTP API.
+ *
+ * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to
+ * enable proxy support. There are also a few filters that plugins can hook into for some of the
+ * constants.
+ *
+ * The constants are as follows:
+ * <ol>
+ * <li>WP_PROXY_HOST - Enable proxy support and host for connecting.</li>
+ * <li>WP_PROXY_PORT - Proxy port for connection. No default, must be defined.</li>
+ * <li>WP_PROXY_USERNAME - Proxy username, if it requires authentication.</li>
+ * <li>WP_PROXY_PASSWORD - Proxy password, if it requires authentication.</li>
+ * <li>WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy.
+ * You do not need to have localhost and the blog host in this list, because they will not be passed
+ * through the proxy. The list should be presented in a comma separated list</li>
+ * </ol>
+ *
+ * An example can be as seen below.
+ * <code>
+ * define('WP_PROXY_HOST', '192.168.84.101');
+ * define('WP_PROXY_PORT', '8080');
+ * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com');
+ * </code>
+ *
+ * @link http://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress.
+ * @since 2.8
+ */
+class WP_HTTP_Proxy {
+
+	/**
+	 * Whether proxy connection should be used.
+	 *
+	 * @since 2.8
+	 * @use WP_PROXY_HOST
+	 * @use WP_PROXY_PORT
+	 *
+	 * @return bool
+	 */
+	function is_enabled() {
+		return defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT');
+	}
+
+	/**
+	 * Whether authentication should be used.
+	 *
+	 * @since 2.8
+	 * @use WP_PROXY_USERNAME
+	 * @use WP_PROXY_PASSWORD
+	 *
+	 * @return bool
+	 */
+	function use_authentication() {
+		return defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD');
+	}
+
+	/**
+	 * Retrieve the host for the proxy server.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function host() {
+		if ( defined('WP_PROXY_HOST') )
+			return WP_PROXY_HOST;
+
+		return '';
+	}
+
+	/**
+	 * Retrieve the port for the proxy server.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function port() {
+		if ( defined('WP_PROXY_PORT') )
+			return WP_PROXY_PORT;
+
+		return '';
+	}
+
+	/**
+	 * Retrieve the username for proxy authentication.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function username() {
+		if ( defined('WP_PROXY_USERNAME') )
+			return WP_PROXY_USERNAME;
+
+		return '';
+	}
+
+	/**
+	 * Retrieve the password for proxy authentication.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function password() {
+		if ( defined('WP_PROXY_PASSWORD') )
+			return WP_PROXY_PASSWORD;
+
+		return '';
+	}
+
+	/**
+	 * Retrieve authentication string for proxy authentication.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function authentication() {
+		return $this->username() . ':' . $this->password();
+	}
+
+	/**
+	 * Retrieve header string for proxy authentication.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string
+	 */
+	function authentication_header() {
+		return 'Proxy-Authentication: Basic ' . base64_encode( $this->authentication() );
+	}
+
+	/**
+	 * Whether URL should be sent through the proxy server.
+	 *
+	 * We want to keep localhost and the blog URL from being sent through the proxy server, because
+	 * some proxies can not handle this. We also have the constant available for defining other
+	 * hosts that won't be sent through the proxy.
+	 *
+	 * @uses WP_PROXY_BYPASS_HOSTS
+	 * @since unknown
+	 *
+	 * @param string $uri URI to check.
+	 * @return bool True, to send through the proxy and false if, the proxy should not be used.
+	 */
+	function send_through_proxy( $uri ) {
+		// parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
+		// This will be displayed on blogs, which is not reasonable.
+		$check = @parse_url($uri);
+
+		// Malformed URL, can not process, but this could mean ssl, so let through anyway.
+		if ( $check === false )
+			return true;
+
+		$home = parse_url( backpress_get_option( 'application_uri' ) );
+
+		if ( $check['host'] == 'localhost' || $check['host'] == $home['host'] )
+			return false;
+
+		if ( !defined('WP_PROXY_BYPASS_HOSTS') )
+			return true;
+
+		static $bypass_hosts;
+		if ( null == $bypass_hosts )
+			$bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS);
+
+		return !in_array( $check['host'], $bypass_hosts );
+	}
+}
+/**
+ * Internal representation of a single cookie.
+ *
+ * Returned cookies are represented using this class, and when cookies are set, if they are not
+ * already a WP_Http_Cookie() object, then they are turned into one.
+ *
+ * @todo The WordPress convention is to use underscores instead of camelCase for function and method
+ * names. Need to switch to use underscores instead for the methods.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ * @since 2.8.0
+ * @author Beau Lebens
+ */
+class WP_Http_Cookie {
+
+	/**
+	 * Cookie name.
+	 *
+	 * @since 2.8.0
+	 * @var string
+	 */
+	var $name;
+
+	/**
+	 * Cookie value.
+	 *
+	 * @since 2.8.0
+	 * @var string
+	 */
+	var $value;
+
+	/**
+	 * When the cookie expires.
+	 *
+	 * @since 2.8.0
+	 * @var string
+	 */
+	var $expires;
+
+	/**
+	 * Cookie URL path.
+	 *
+	 * @since 2.8.0
+	 * @var string
+	 */
+	var $path;
+
+	/**
+	 * Cookie Domain.
+	 *
+	 * @since 2.8.0
+	 * @var string
+	 */
+	var $domain;
+
+	/**
+	 * PHP4 style Constructor - Calls PHP5 Style Constructor.
+	 *
+	 * @access public
+	 * @since 2.8.0
+	 * @param string|array $data Raw cookie data.
+	 */
+	function WP_Http_Cookie( $data ) {
+		$this->__construct( $data );
+	}
+
+	/**
+	 * Sets up this cookie object.
+	 *
+	 * The parameter $data should be either an associative array containing the indices names below
+	 * or a header string detailing it.
+	 *
+	 * If it's an array, it should include the following elements:
+	 * <ol>
+	 * <li>Name</li>
+	 * <li>Value - should NOT be urlencoded already.</li>
+	 * <li>Expires - (optional) String or int (UNIX timestamp).</li>
+	 * <li>Path (optional)</li>
+	 * <li>Domain (optional)</li>
+	 * </ol>
+	 *
+	 * @access public
+	 * @since 2.8.0
+	 *
+	 * @param string|array $data Raw cookie data.
+	 */
+	function __construct( $data ) {
+		if ( is_string( $data ) ) {
+			// Assume it's a header string direct from a previous request
+			$pairs = explode( ';', $data );
+
+			// Special handling for first pair; name=value. Also be careful of "=" in value
+			$name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
+			$value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
+			$this->name  = $name;
+			$this->value = urldecode( $value );
+			array_shift( $pairs ); //Removes name=value from items.
+
+			// Set everything else as a property
+			foreach ( $pairs as $pair ) {
+				if ( empty($pair) ) //Handles the cookie ending in ; which results in a empty final pair
+					continue;
+
+				list( $key, $val ) = explode( '=', $pair );
+				$key = strtolower( trim( $key ) );
+				if ( 'expires' == $key )
+					$val = strtotime( $val );
+				$this->$key = $val;
+			}
+		} else {
+			if ( !isset( $data['name'] ) )
+				return false;
+
+			// Set properties based directly on parameters
+			$this->name   = $data['name'];
+			$this->value  = isset( $data['value'] ) ? $data['value'] : '';
+			$this->path   = isset( $data['path'] ) ? $data['path'] : '';
+			$this->domain = isset( $data['domain'] ) ? $data['domain'] : '';
+
+			if ( isset( $data['expires'] ) )
+				$this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
+			else
+				$this->expires = null;
+		}
+	}
+
+	/**
+	 * Confirms that it's OK to send this cookie to the URL checked against.
+	 *
+	 * Decision is based on RFC 2109/2965, so look there for details on validity.
+	 *
+	 * @access public
+	 * @since 2.8.0
+	 *
+	 * @param string $url URL you intend to send this cookie to
+	 * @return boolean TRUE if allowed, FALSE otherwise.
+	 */
+	function test( $url ) {
+		// Expires - if expired then nothing else matters
+		if ( time() > $this->expires )
+			return false;
+
+		// Get details on the URL we're thinking about sending to
+		$url = parse_url( $url );
+		$url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
+		$url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
+
+		// Values to use for comparison against the URL
+		$path   = isset( $this->path )   ? $this->path   : '/';
+		$port   = isset( $this->port )   ? $this->port   : 80;
+		$domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
+		if ( false === stripos( $domain, '.' ) )
+			$domain .= '.local';
+
+		// Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
+		$domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
+		if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
+			return false;
+
+		// Port - supports "port-lists" in the format: "80,8000,8080"
+		if ( !in_array( $url['port'], explode( ',', $port) ) )
+			return false;
+
+		// Path - request path must start with path restriction
+		if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
+			return false;
+
+		return true;
+	}
+
+	/**
+	 * Convert cookie name and value back to header string.
+	 *
+	 * @access public
+	 * @since 2.8.0
+	 *
+	 * @return string Header encoded cookie name and value.
+	 */
+	function getHeaderValue() {
+		if ( empty( $this->name ) || empty( $this->value ) )
+			return '';
+
+		return $this->name . '=' . urlencode( $this->value );
+	}
+
+	/**
+	 * Retrieve cookie header for usage in the rest of the WordPress HTTP API.
+	 *
+	 * @access public
+	 * @since 2.8.0
+	 *
+	 * @return string
+	 */
+	function getFullHeader() {
+		return 'Cookie: ' . $this->getHeaderValue();
+	}
+}
+
+/**
+ * Implementation for deflate and gzip transfer encodings.
+ *
+ * Includes RFC 1950, RFC 1951, and RFC 1952.
+ *
+ * @since 2.8
+ * @package WordPress
+ * @subpackage HTTP
+ */
+class WP_Http_Encoding {
+
+	/**
+	 * Compress raw string using the deflate format.
+	 *
+	 * Supports the RFC 1951 standard.
+	 *
+	 * @since 2.8
+	 *
+	 * @param string $raw String to compress.
+	 * @param int $level Optional, default is 9. Compression level, 9 is highest.
+	 * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
+	 * @return string|bool False on failure.
+	 */
+	function compress( $raw, $level = 9, $supports = null ) {
+		return gzdeflate( $raw, $level );
+	}
+
+	/**
+	 * Decompression of deflated string.
+	 *
+	 * Will attempt to decompress using the RFC 1950 standard, and if that fails
+	 * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
+	 * 1952 standard gzip decode will be attempted. If all fail, then the
+	 * original compressed string will be returned.
+	 *
+	 * @since 2.8
+	 *
+	 * @param string $compressed String to decompress.
+	 * @param int $length The optional length of the compressed data.
+	 * @return string|bool False on failure.
+	 */
+	function decompress( $compressed, $length = null ) {
+		$decompressed = gzinflate( $compressed );
+
+		if ( false !== $decompressed )
+			return $decompressed;
+
+		$decompressed = gzuncompress( $compressed );
+
+		if ( false !== $decompressed )
+			return $decompressed;
+
+		if ( function_exists('gzdecode') ) {
+			$decompressed = gzdecode( $compressed );
+
+			if ( false !== $decompressed )
+				return $decompressed;
+		}
+
+		return $compressed;
+	}
+
+	/**
+	 * What encoding types to accept and their priority values.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string Types of encoding to accept.
+	 */
+	function accept_encoding() {
+		$type = array();
+		if ( function_exists( 'gzinflate' ) )
+			$type[] = 'deflate;q=1.0';
+
+		if ( function_exists( 'gzuncompress' ) )
+			$type[] = 'compress;q=0.5';
+
+		if ( function_exists( 'gzdecode' ) )
+			$type[] = 'gzip;q=0.5';
+
+		return implode(', ', $type);
+	}
+
+	/**
+	 * What enconding the content used when it was compressed to send in the headers.
+	 *
+	 * @since 2.8
+	 *
+	 * @return string Content-Encoding string to send in the header.
+	 */
+	function content_encoding() {
+		return 'deflate';
+	}
+
+	/**
+	 * Whether the content be decoded based on the headers.
+	 *
+	 * @since 2.8
+	 *
+	 * @param array|string $headers All of the available headers.
+	 * @return bool
+	 */
+	function should_decode($headers) {
+		if ( is_array( $headers ) ) {
+			if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
+				return true;
+		} else if( is_string( $headers ) ) {
+			return ( stripos($headers, 'content-encoding:') !== false );
+		}
+
+		return false;
+	}
+
+	/**
+	 * Whether decompression and compression are supported by the PHP version.
+	 *
+	 * Each function is tested instead of checking for the zlib extension, to
+	 * ensure that the functions all exist in the PHP version and aren't
+	 * disabled.
+	 *
+	 * @since 2.8
+	 *
+	 * @return bool
+	 */
+	function is_available() {
+		return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') );
+	}
+}
+
+/**
+ * Returns the initialized WP_Http Object
+ *
+ * @since 2.7.0
+ * @access private
+ *
+ * @return WP_Http HTTP Transport object.
+ */
+function &_wp_http_get_object() {
+	static $http;
+
+	if ( is_null($http) )
+		$http = new WP_Http();
+
+	return $http;
+}
+
+/**
+ * Retrieve the raw response from the HTTP request.
+ *
+ * The array structure is a little complex.
+ *
+ * <code>
+ * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
+ * </code>
+ *
+ * All of the headers in $res['headers'] are with the name as the key and the
+ * value as the value. So to get the User-Agent, you would do the following.
+ *
+ * <code>
+ * $user_agent = $res['headers']['user-agent'];
+ * </code>
+ *
+ * The body is the raw response content and can be retrieved from $res['body'].
+ *
+ * This function is called first to make the request and there are other API
+ * functions to abstract out the above convoluted setup.
+ *
+ * @since 2.7.0
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Override the defaults.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_request($url, $args = array()) {
+	$objFetchSite = _wp_http_get_object();
+	return $objFetchSite->request($url, $args);
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the GET method.
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ *
+ * @since 2.7.0
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Override the defaults.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_get($url, $args = array()) {
+	$objFetchSite = _wp_http_get_object();
+	return $objFetchSite->get($url, $args);
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the POST method.
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ *
+ * @since 2.7.0
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Override the defaults.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_post($url, $args = array()) {
+	$objFetchSite = _wp_http_get_object();
+	return $objFetchSite->post($url, $args);
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the HEAD method.
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ *
+ * @since 2.7.0
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Override the defaults.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_head($url, $args = array()) {
+	$objFetchSite = _wp_http_get_object();
+	return $objFetchSite->head($url, $args);
+}
+
+/**
+ * Retrieve only the headers from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return array The headers of the response. Empty array if incorrect parameter given.
+ */
+function wp_remote_retrieve_headers(&$response) {
+	if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
+		return array();
+
+	return $response['headers'];
+}
+
+/**
+ * Retrieve a single header by name from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response
+ * @param string $header Header name to retrieve value from.
+ * @return string The header value. Empty string on if incorrect parameter given, or if the header doesnt exist.
+ */
+function wp_remote_retrieve_header(&$response, $header) {
+	if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
+		return '';
+
+	if ( array_key_exists($header, $response['headers']) )
+		return $response['headers'][$header];
+
+	return '';
+}
+
+/**
+ * Retrieve only the response code from the raw response.
+ *
+ * Will return an empty array if incorrect parameter value is given.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return string the response code. Empty string on incorrect parameter given.
+ */
+function wp_remote_retrieve_response_code(&$response) {
+	if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
+		return '';
+
+	return $response['response']['code'];
+}
+
+/**
+ * Retrieve only the response message from the raw response.
+ *
+ * Will return an empty array if incorrect parameter value is given.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return string The response message. Empty string on incorrect parameter given.
+ */
+function wp_remote_retrieve_response_message(&$response) {
+	if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
+		return '';
+
+	return $response['response']['message'];
+}
+
+/**
+ * Retrieve only the body from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return string The body of the response. Empty string if no body or incorrect parameter given.
+ */
+function wp_remote_retrieve_body(&$response) {
+	if ( is_wp_error($response) || ! isset($response['body']) )
+		return '';
+
+	return $response['body'];
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache-memcached.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache-memcached.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache-memcached.php	(revision 5057)
@@ -0,0 +1,467 @@
+<?php
+
+class WP_Object_Cache
+{
+	// WordPress would need to be initialised with this:
+	// wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss' ) );
+	var $global_groups = array (
+		'_cache_keys'
+	);
+
+	// WordPress would need to be initialised with this:
+	// wp_cache_add_non_persistent_groups( array( 'comment', 'counts' ) );
+	var $no_mc_groups = array();
+
+	var $cache = array();
+	var $mc = array();
+	var $stats = array();
+	var $group_ops = array();
+
+	var $default_expiration = 0;
+
+	function WP_Object_Cache()
+	{
+		global $memcached_servers;
+
+		if ( isset( $memcached_servers ) ) {
+			$buckets = $memcached_servers;
+		} else {
+			$buckets = array('default' => array('127.0.0.1:11211'));
+		}
+
+		foreach ( $buckets as $bucket => $servers ) {
+			$this->mc[$bucket] = new Memcache();
+			foreach ( $servers as $server ) {
+				list ( $node, $port ) = explode( ':', $server );
+				$this->mc[$bucket]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) );
+				$this->mc[$bucket]->setCompressThreshold( 20000, 0.2 );
+			}
+		}
+	}
+
+	function &get_mc( $group )
+	{
+		if ( isset( $this->mc[$group] ) ) {
+			return $this->mc[$group];
+		}
+		return $this->mc['default'];
+	}
+
+	function failure_callback( $host, $port )
+	{
+		//error_log( "Connection failure for $host:$port\n", 3, '/tmp/memcached.txt' );
+	}
+
+	function close()
+	{
+		foreach ( $this->mc as $bucket => $mc ) {
+			$mc->close();
+		}
+	}
+
+	function add_global_groups( $groups )
+	{
+		if ( !is_array( $groups ) ) {
+			$groups = (array) $groups;
+		}
+
+		$this->global_groups = array_merge( $this->global_groups, $groups );
+		$this->global_groups = array_unique( $this->global_groups );
+	}
+
+	function add_non_persistent_groups( $groups )
+	{
+		if ( !is_array( $groups ) ) {
+			$groups = (array) $groups;
+		}
+
+		$this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
+		$this->no_mc_groups = array_unique( $this->no_mc_groups );
+	}
+
+	function key( $key, $group )
+	{
+		if ( empty( $group ) ) {
+			$group = 'default';
+		}
+
+		if ( false !== array_search( $group, $this->global_groups ) ) {
+			$prefix = '';
+		} else {
+			$prefix = backpress_get_option( 'application_id' ) . ':';
+		}
+
+		return preg_replace( '/\s+/', '', $prefix . $group . ':' . $key );
+	}
+
+	function get( $id, $group = 'default' )
+	{
+		$key = $this->key( $id, $group );
+		$mc =& $this->get_mc( $group );
+
+		if ( isset( $this->cache[$key] ) ) {
+			$value = $this->cache[$key];
+		} elseif ( in_array( $group, $this->no_mc_groups ) ) {
+			$value = false;
+		} else {
+			$value = $mc->get($key);
+		}
+
+		@ ++$this->stats['get'];
+		$this->group_ops[$group][] = "get $id";
+
+		if ( NULL === $value ) {
+			$value = false;
+		}
+
+		$this->cache[$key] = $value;
+
+		if ( 'checkthedatabaseplease' === $value ) {
+			$value = false;
+		}
+
+		return $value;
+	}
+
+	/*
+	format: $get['group-name'] = array( 'key1', 'key2' );
+	*/
+	function get_multi( $groups )
+	{
+		$return = array();
+		foreach ( $groups as $group => $ids ) {
+			$mc =& $this->get_mc( $group );
+			foreach ( $ids as $id ) {
+				$key = $this->key( $id, $group );
+				if ( isset( $this->cache[$key] ) ) {
+					$return[$key] = $this->cache[$key];
+					continue;
+				} elseif ( in_array( $group, $this->no_mc_groups ) ) {
+					$return[$key] = false;
+					continue;
+				} else {
+					$return[$key] = $mc->get( $key );
+				}
+			}
+			if ( $to_get ) {
+				$vals = $mc->get_multi( $to_get );
+				$return = array_merge( $return, $vals );
+			}
+		}
+
+		@ ++$this->stats['get_multi'];
+		$this->group_ops[$group][] = "get_multi $id";
+
+		$this->cache = array_merge( $this->cache, $return );
+
+		return $return;
+	}
+
+	function add( $id, $data, $group = 'default', $expire = 0 )
+	{
+		$key = $this->key( $id, $group );
+
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			$this->cache[$key] = $data;
+			return true;
+		}
+
+		$mc =& $this->get_mc( $group );
+		$expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
+		$result = $mc->add( $key, $data, false, $expire );
+
+		@ ++$this->stats['add'];
+		$this->group_ops[$group][] = "add $id";
+
+		if ( false !== $result ) {
+			$this->cache[$key] = $data;
+			$this->add_key_to_group_keys_cache( $key, $group );
+		}
+
+		return $result;
+	}
+
+	function set( $id, $data, $group = 'default', $expire = 0 )
+	{
+		$key = $this->key($id, $group);
+
+		if ( isset( $this->cache[$key] ) && 'checkthedatabaseplease' === $this->cache[$key] ) {
+			return false;
+		}
+		$this->cache[$key] = $data;
+
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			return true;
+		}
+
+		$expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
+		$mc =& $this->get_mc( $group );
+		$result = $mc->set( $key, $data, false, $expire );
+
+		if ( false !== $result ) {
+			$this->add_key_to_group_keys_cache($key, $group);
+		}
+
+		return $result;
+	}
+
+	function replace($id, $data, $group = 'default', $expire = 0) {
+		$key = $this->key($id, $group);
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			$this->cache[$key] = $data;
+			return true;
+		}
+		$expire = ($expire == 0) ? $this->default_expiration : $expire;
+		$mc =& $this->get_mc($group);
+		$result = $mc->replace($key, $data, false, $expire);
+		if ( false !== $result ) {
+			$this->cache[$key] = $data;
+			$this->add_key_to_group_keys_cache( $key, $group );
+		}
+		return $result;
+	}
+
+	function delete( $id, $group = 'default' )
+	{
+		$key = $this->key( $id, $group );
+
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			unset( $this->cache[$key] );
+			return true;
+		}
+
+		$mc =& $this->get_mc( $group );
+
+		$result = $mc->delete( $key );
+
+		@ ++$this->stats['delete'];
+		$this->group_ops[$group][] = "delete $id";
+
+		if ( false !== $result ) {
+			unset( $this->cache[$key] );
+			$this->remove_key_from_group_keys_cache( $key, $group );
+		}
+
+		return $result; 
+	}
+
+	function flush( $group = null )
+	{
+		// Get all the group keys
+		if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
+			return true;
+		}
+
+		if ( !is_array( $_groups ) || !count( $_groups ) ) {
+			return $this->delete( 1, '_group_keys' );
+		}
+
+		if ( is_null( $group ) ) {
+			$results = array();
+			foreach ( $_groups as $_group => $_keys ) {
+				$results[] = $this->delete_all_keys_in_group_key_cache( $_group );
+			}
+			if ( in_array( false, $results ) ) {
+				return false;
+			}
+			return true;
+		}
+
+		return $this->delete_all_keys_in_group_key_cache( $group );
+	}
+
+	// Update the cache of group keys or add a new cache if it isn't there
+	function add_key_to_group_keys_cache( $key, $group )
+	{
+		if ( '_group_keys' === $group ) {
+			return;
+		}
+
+		//error_log( 'Adding key ' . $key . ' to group ' . $group );
+
+		// Get all the group keys
+		if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
+			$_groups = array( $group => array( $key ) );
+			return $this->add( 1, $_groups, '_group_keys' );
+		}
+
+		// Don't blow up if it isn't an array
+		if ( !is_array( $_groups ) ) {
+			$_groups = array();
+		}
+
+		// If this group isn't in there, then insert it
+		if ( !isset( $_groups[$group] ) || !is_array( $_groups[$group] ) ) {
+			$_groups[$group] = array();
+		}
+
+		// If it's already there then do nothing
+		if ( in_array( $key, $_groups[$group] ) ) {
+			return true;
+		}
+
+		$_groups[$group][] = $key;
+
+		// Remove duplicates
+		$_groups[$group] = array_unique( $_groups[$group] );
+
+		return $this->replace( 1, $_groups, '_group_keys' );
+	}
+
+	// Remove the key from the cache of group keys, delete the cache if it is emptied
+	function remove_key_from_group_keys_cache( $key, $group )
+	{
+		if ( '_group_keys' === $group ) {
+			return;
+		}
+
+		//error_log( 'Removing key ' . $key . ' from group ' . $group );
+
+		// Get all the group keys
+		if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
+			return true;
+		}
+
+		// If group keys are somehow borked delete it all
+		if ( !is_array( $_groups ) ) {
+			return $this->delete( 1, '_group_keys' );
+		}
+
+		// If it's not there, we're good
+		if (
+			!isset( $_groups[$group] ) ||
+			!is_array( $_groups[$group] ) ||
+			!in_array( $key, $_groups[$group] )
+		) {
+			return true;
+		}
+
+		// Remove duplicates
+		$_groups[$group] = array_unique( $_groups[$group] );
+
+		// If there is only one key or no keys in the group then delete the group
+		if ( 2 > count( $_groups[$group] ) ) {
+			unset( $_groups[$group] );
+			return $this->replace( 1, $_groups, '_group_keys' );
+		}
+
+		// array_unique() made sure there is only one
+		if ( $_key = array_search( $key, $_groups[$group] ) ) {
+			unset( $_groups[$group][$_key] );
+		}
+
+		return $this->replace( 1, $_groups, '_group_keys' );
+	}
+
+	function delete_all_keys_in_group_key_cache( $group )
+	{
+		if ( '_group_keys' === $group ) {
+			return;
+		}
+
+		//error_log( 'Deleting all keys in group ' . $group );
+
+		// Get all the group keys
+		if ( !$_groups = $this->get( 1, '_group_keys' ) ) {
+			//error_log( '--> !!!! No groups' );
+			return true;
+		}
+
+		// Check that what we want to loop over is there
+		if ( !is_array( $_groups ) ) {
+			//error_log( '--> !!!! Groups is not an array, delete whole key' );
+			return $this->delete( 1, '_group_keys' );
+		}
+
+		// Check that what we want to loop over is there
+		if (
+			!isset( $_groups[$group] ) ||
+			!is_array( $_groups[$group] )
+		) {
+			//error_log( '--> !!!! No specific group' );
+			return true;
+		}
+
+		$_groups[$group] = array_unique( $_groups[$group] );
+
+		$_remaining_keys = array();
+		$mc =& $this->get_mc($group);
+		foreach ( $_groups[$group] as $_key ) {
+			//error_log( '--> Deleting key ' . $_key );
+			if ( false !== $mc->delete( $_key ) ) {
+				//error_log( '--> Deleted key ' . $_key );
+				unset( $this->cache[$_key] );
+			}
+		}
+
+		unset( $_groups[$group] );
+		if ( count( $_groups ) ) {
+			//error_log( '--> Remove single group' );
+			return $this->replace( 1, $_groups, '_group_keys' );
+		}
+
+		//error_log( '--> No groups left, delete whole key' );
+		return $this->delete( 1, '_group_keys' );
+	}
+
+	function incr( $id, $n, $group )
+	{
+		$key = $this->key( $id, $group );
+		$mc =& $this->get_mc( $group );
+
+		return $mc->increment( $key, $n );
+	}
+
+	function decr( $id, $n, $group )
+	{
+		$key = $this->key( $id, $group );
+		$mc =& $this->get_mc( $group );
+
+		return $mc->decrement( $key, $n );
+	}
+
+	function colorize_debug_line( $line )
+	{
+		$colors = array(
+			'get' => 'green',
+			'set' => 'purple',
+			'add' => 'blue',
+			'delete' => 'red'
+		);
+
+		$cmd = substr( $line, 0, strpos( $line, ' ' ) );
+
+		$cmd2 = "<span style='color:{$colors[$cmd]}'>$cmd</span>";
+
+		return $cmd2 . substr( $line, strlen( $cmd ) ) . "\n";
+	}
+
+	function stats()
+	{
+		echo "<p>\n";
+		foreach ( $this->stats as $stat => $n ) {
+			echo "<strong>$stat</strong> $n";
+			echo "<br/>\n";
+		}
+		echo "</p>\n";
+		echo "<h3>Memcached:</h3>";
+		foreach ( $this->group_ops as $group => $ops ) {
+			if ( !isset( $_GET['debug_queries'] ) && 500 < count( $ops ) ) { 
+				$ops = array_slice( $ops, 0, 500 ); 
+				echo "<big>Too many to show! <a href='" . add_query_arg( 'debug_queries', 'true' ) . "'>Show them anyway</a>.</big>\n";
+			} 
+			echo "<h4>$group commands</h4>";
+			echo "<pre>\n";
+			$lines = array();
+			foreach ( $ops as $op ) {
+				$lines[] = $this->colorize_debug_line( $op ); 
+			}
+			print_r( $lines );
+			echo "</pre>\n";
+		}
+
+		if ( $this->debug ) {
+			var_dump( $this->memcache_debug );
+		}
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-object-cache.php	(revision 5057)
@@ -0,0 +1,305 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * WordPress Object Cache
+ *
+ * The WordPress Object Cache is used to save on trips to the database. The
+ * Object Cache stores all of the cache data to memory and makes the cache
+ * contents available by using a key, which is used to name and later retrieve
+ * the cache contents.
+ *
+ * The Object Cache can be replaced by other caching mechanisms by placing files
+ * in the wp-content folder which is looked at in wp-settings. If that file
+ * exists, then this file will not be included.
+ *
+ * @package WordPress
+ * @subpackage Cache
+ * @since 2.0
+ */
+class WP_Object_Cache {
+
+	/**
+	 * Holds the cached objects
+	 *
+	 * @var array
+	 * @access private
+	 * @since 2.0.0
+	 */
+	var $cache = array ();
+
+	/**
+	 * Cache objects that do not exist in the cache
+	 *
+	 * @var array
+	 * @access private
+	 * @since 2.0.0
+	 */
+	var $non_existant_objects = array ();
+
+	/**
+	 * The amount of times the cache data was already stored in the cache.
+	 *
+	 * @since 2.5.0
+	 * @access private
+	 * @var int
+	 */
+	var $cache_hits = 0;
+
+	/**
+	 * Amount of times the cache did not have the request in cache
+	 *
+	 * @var int
+	 * @access public
+	 * @since 2.0.0
+	 */
+	var $cache_misses = 0;
+
+	/**
+	 * Adds data to the cache if it doesn't already exist.
+	 *
+	 * @uses WP_Object_Cache::get Checks to see if the cache already has data.
+	 * @uses WP_Object_Cache::set Sets the data after the checking the cache
+	 *		contents existance.
+	 *
+	 * @since 2.0.0
+	 *
+	 * @param int|string $id What to call the contents in the cache
+	 * @param mixed $data The contents to store in the cache
+	 * @param string $group Where to group the cache contents
+	 * @param int $expire When to expire the cache contents
+	 * @return bool False if cache ID and group already exists, true on success
+	 */
+	function add($id, $data, $group = 'default', $expire = '') {
+		if (empty ($group))
+			$group = 'default';
+
+		if (false !== $this->get($id, $group, false))
+			return false;
+
+		return $this->set($id, $data, $group, $expire);
+	}
+
+	/**
+	 * Remove the contents of the cache ID in the group
+	 *
+	 * If the cache ID does not exist in the group and $force parameter is set
+	 * to false, then nothing will happen. The $force parameter is set to false
+	 * by default.
+	 *
+	 * On success the group and the id will be added to the
+	 * $non_existant_objects property in the class.
+	 *
+	 * @since 2.0.0
+	 *
+	 * @param int|string $id What the contents in the cache are called
+	 * @param string $group Where the cache contents are grouped
+	 * @param bool $force Optional. Whether to force the unsetting of the cache
+	 *		ID in the group
+	 * @return bool False if the contents weren't deleted and true on success
+	 */
+	function delete($id, $group = 'default', $force = false) {
+		if (empty ($group))
+			$group = 'default';
+
+		if (!$force && false === $this->get($id, $group, false))
+			return false;
+
+		unset ($this->cache[$group][$id]);
+		$this->non_existant_objects[$group][$id] = true;
+		return true;
+	}
+
+	/**
+	 * Clears the object cache of all data
+	 *
+	 * @since 2.0.0
+	 *
+	 * @return bool Always returns true
+	 */
+	function flush( $group = null ) {
+		// WP does not support group flushing
+		if ( is_null($group) ) {
+			$this->cache = array();
+			$this->non_existant_objects = array();
+			return true;
+		}
+
+		if ( isset($this->cache[$group]) )
+			$this->cache[$group] = array();
+		if ( isset($this->non_existant_objects[$group]) )
+			$this->non_existant_objects[$group] = array();
+	}
+
+	/**
+	 * Retrieves the cache contents, if it exists
+	 *
+	 * The contents will be first attempted to be retrieved by searching by the
+	 * ID in the cache group. If the cache is hit (success) then the contents
+	 * are returned.
+	 *
+	 * On failure, the $non_existant_objects property is checked and if the
+	 * cache group and ID exist in there the cache misses will not be
+	 * incremented. If not in the nonexistant objects property, then the cache
+	 * misses will be incremented and the cache group and ID will be added to
+	 * the nonexistant objects.
+	 *
+	 * @since 2.0.0
+	 *
+	 * @param int|string $id What the contents in the cache are called
+	 * @param string $group Where the cache contents are grouped
+	 * @return bool|mixed False on failure to retrieve contents or the cache
+	 *		contents on success
+	 */
+	function get($id, $group = 'default') {
+		if (empty ($group))
+			$group = 'default';
+
+		if (isset ($this->cache[$group][$id])) {
+			$this->cache_hits += 1;
+			if ( is_object($this->cache[$group][$id]) )
+				return wp_clone($this->cache[$group][$id]);
+			else
+				return $this->cache[$group][$id];
+		}
+
+		if ( isset ($this->non_existant_objects[$group][$id]) )
+			return false;
+
+		$this->non_existant_objects[$group][$id] = true;
+		$this->cache_misses += 1;
+		return false;
+	}
+
+	/**
+	 * Replace the contents in the cache, if contents already exist
+	 *
+	 * @since 2.0.0
+	 * @see WP_Object_Cache::set()
+	 *
+	 * @param int|string $id What to call the contents in the cache
+	 * @param mixed $data The contents to store in the cache
+	 * @param string $group Where to group the cache contents
+	 * @param int $expire When to expire the cache contents
+	 * @return bool False if not exists, true if contents were replaced
+	 */
+	function replace($id, $data, $group = 'default', $expire = '') {
+		if (empty ($group))
+			$group = 'default';
+
+		if (false === $this->get($id, $group, false))
+			return false;
+
+		return $this->set($id, $data, $group, $expire);
+	}
+
+	/**
+	 * Sets the data contents into the cache
+	 *
+	 * The cache contents is grouped by the $group parameter followed by the
+	 * $id. This allows for duplicate ids in unique groups. Therefore, naming of
+	 * the group should be used with care and should follow normal function
+	 * naming guidelines outside of core WordPress usage.
+	 *
+	 * The $expire parameter is not used, because the cache will automatically
+	 * expire for each time a page is accessed and PHP finishes. The method is
+	 * more for cache plugins which use files.
+	 *
+	 * @since 2.0.0
+	 *
+	 * @param int|string $id What to call the contents in the cache
+	 * @param mixed $data The contents to store in the cache
+	 * @param string $group Where to group the cache contents
+	 * @param int $expire Not Used
+	 * @return bool Always returns true
+	 */
+	function set($id, $data, $group = 'default', $expire = '') {
+		if (empty ($group))
+			$group = 'default';
+
+		if (NULL === $data)
+			$data = '';
+
+		if ( is_object($data) )
+			$data = wp_clone($data);
+
+		$this->cache[$group][$id] = $data;
+
+		if(isset($this->non_existant_objects[$group][$id]))
+			unset ($this->non_existant_objects[$group][$id]);
+
+		return true;
+	}
+
+	/**
+	 * Echos the stats of the caching.
+	 *
+	 * Gives the cache hits, and cache misses. Also prints every cached group,
+	 * key and the data.
+	 *
+	 * @since 2.0.0
+	 */
+	function stats() {
+		echo "<p>";
+		echo "<strong>Cache Hits:</strong> {$this->cache_hits}<br />";
+		echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br />";
+		echo "</p>";
+
+		foreach ($this->cache as $group => $cache) {
+			echo "<p>";
+			echo "<strong>Group:</strong> $group<br />";
+			echo "<strong>Cache:</strong>";
+			echo "<pre>";
+			print_r($cache);
+			echo "</pre>";
+		}
+	}
+
+	function add_global_groups( $groups )
+	{
+		return true;
+	}
+
+	function add_non_persistent_groups( $groups )
+	{
+		return true;
+	}
+
+	/**
+	 * PHP4 constructor; Calls PHP 5 style constructor
+	 *
+	 * @since 2.0.0
+	 *
+	 * @return WP_Object_Cache
+	 */
+	function WP_Object_Cache() {
+		return $this->__construct();
+	}
+
+	/**
+	 * Sets up object properties; PHP 5 style constructor
+	 *
+	 * @since 2.0.8
+	 * @return null|WP_Object_Cache If cache is disabled, returns null.
+	 */
+	function __construct() {
+		/**
+		 * @todo This should be moved to the PHP4 style constructor, PHP5
+		 * already calls __destruct()
+		 */
+		register_shutdown_function(array(&$this, "__destruct"));
+	}
+
+	/**
+	 * Will save the object cache before object is completely destroyed.
+	 *
+	 * Called upon object destruction, which should be when PHP ends.
+	 *
+	 * @since  2.0.8
+	 *
+	 * @return bool True value. Won't be used by PHP
+	 */
+	function __destruct() {
+		return true;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-pass.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-pass.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-pass.php	(revision 5057)
@@ -0,0 +1,141 @@
+<?php
+// Last sync [WP10712] - Refactored into a class from wp-incldues/pluggable.php
+
+class WP_Pass {
+	/**
+	 * Create a hash (encrypt) of a plain text password.
+	 *
+	 * For integration with other applications, this function can be overwritten to
+	 * instead use the other package password checking algorithm.
+	 *
+	 * @since WP 2.5
+	 * @global object $wp_hasher PHPass object
+	 * @uses PasswordHash::HashPassword
+	 *
+	 * @param string $password Plain text user password to hash
+	 * @return string The hash string of the password
+	 */
+	function hash_password($password) {
+		global $wp_hasher;
+
+		if ( empty($wp_hasher) ) {
+			require_once( BACKPRESS_PATH . 'class.passwordhash.php');
+			// By default, use the portable hash from phpass
+			$wp_hasher = new PasswordHash(8, TRUE);
+		}
+
+		return $wp_hasher->HashPassword($password);
+	}
+
+	/**
+	 * Checks the plaintext password against the encrypted Password.
+	 *
+	 * Maintains compatibility between old version and the new cookie authentication
+	 * protocol using PHPass library. The $hash parameter is the encrypted password
+	 * and the function compares the plain text password when encypted similarly
+	 * against the already encrypted password to see if they match.
+	 *
+	 * For integration with other applications, this function can be overwritten to
+	 * instead use the other package password checking algorithm.
+	 *
+	 * @since WP 2.5
+	 * @global object $wp_hasher PHPass object used for checking the password
+	 *	against the $hash + $password
+	 * @uses PasswordHash::CheckPassword
+	 *
+	 * @param string $password Plaintext user's password
+	 * @param string $hash Hash of the user's password to check against.
+	 * @return bool False, if the $password does not match the hashed password
+	 */
+	function check_password($password, $hash, $user_id = '') {
+		global $wp_hasher, $wp_users_object;
+
+		list($hash, $broken) = array_pad( explode( '---', $hash ), 2, '' );
+
+		// If the hash is still md5...
+		if ( strlen($hash) <= 32 ) {
+			$check = ( $hash == md5($password) );
+			if ( $check && $user_id && !$broken ) {
+				// Rehash using new hash.
+				$wp_users_object->set_password($password, $user_id);
+				$hash = WP_Pass::hash_password($password);
+			}
+
+			return apply_filters('check_password', $check, $password, $hash, $user_id);
+		}
+
+		// If the stored hash is longer than an MD5, presume the
+		// new style phpass portable hash.
+		if ( empty($wp_hasher) ) {
+			require_once( BACKPRESS_PATH . 'class.passwordhash.php');
+			// By default, use the portable hash from phpass
+			$wp_hasher = new PasswordHash(8, TRUE);
+		}
+
+		$check = $wp_hasher->CheckPassword($password, $hash);
+
+		return apply_filters('check_password', $check, $password, $hash, $user_id);
+	}
+
+	/**
+	 * Generates a random password drawn from the defined set of characters
+	 *
+	 * @since WP 2.5
+	 *
+	 * @param int $length The length of password to generate
+	 * @param bool $special_chars Whether to include standard special characters 
+	 * @return string The random password
+	 */
+	function generate_password($length = 12, $special_chars = true) {
+		$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		if ( $special_chars )
+			$chars .= '!@#$%^&*()';
+
+		$password = '';
+		for ( $i = 0; $i < $length; $i++ )
+			$password .= substr($chars, WP_Pass::rand(0, strlen($chars) - 1), 1);
+		return $password;
+	}
+
+	/**
+	 * Generates a random number
+	 *
+	 * Not verbatim WordPress, keeps seed value in backpress options.
+	 *
+	 * @since WP 2.6.2
+	 *
+	 * @param int $min Lower limit for the generated number (optional, default is 0)
+	 * @param int $max Upper limit for the generated number (optional, default is 4294967295)
+	 * @return int A random number between min and max
+	 */
+	function rand( $min = 0, $max = 0 ) {
+		global $rnd_value;
+
+		$seed = backpress_get_transient('random_seed');
+
+		// Reset $rnd_value after 14 uses
+		// 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value
+		if ( strlen($rnd_value) < 8 ) {
+			$rnd_value = md5( uniqid(microtime() . mt_rand(), true ) . $seed );
+			$rnd_value .= sha1($rnd_value);
+			$rnd_value .= sha1($rnd_value . $seed);
+			$seed = md5($seed . $rnd_value);
+			backpress_set_transient('random_seed', $seed);
+		}
+
+		// Take the first 8 digits for our value
+		$value = substr($rnd_value, 0, 8);
+
+		// Strip the first eight, leaving the remainder for the next call to wp_rand().
+		$rnd_value = substr($rnd_value, 8);
+
+		$value = abs(hexdec($value));
+
+		// Reduce the value to be within the min - max range
+		// 4294967295 = 0xffffffff = max random number
+		if ( $max != 0 )
+			$value = $min + (($max - $min + 1) * ($value / (4294967295 + 1)));
+
+		return abs(intval($value));
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-scripts.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-scripts.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-scripts.php	(revision 5057)
@@ -0,0 +1,202 @@
+<?php
+/**
+ * BackPress Scripts enqueue.
+ *
+ * These classes were refactored from the WordPress WP_Scripts and WordPress
+ * script enqueue API.
+ *
+ * @package BackPress
+ * @since r16
+ */
+
+/**
+ * BackPress Scripts enqueue class.
+ *
+ * @package BackPress
+ * @uses WP_Dependencies
+ * @since r16
+ */
+class WP_Scripts extends WP_Dependencies {
+	var $base_url; // Full URL with trailing slash
+	var $content_url;
+	var $default_version;
+	var $in_footer = array();
+	var $concat = '';
+	var $concat_version = '';
+	var $do_concat = false;
+	var $print_html = '';
+	var $print_code = '';
+	var $ext_handles = '';
+	var $ext_version = '';
+	var $default_dirs;
+
+	function __construct() {
+		do_action_ref_array( 'wp_default_scripts', array(&$this) );
+	}
+
+	/**
+	 * Prints scripts
+	 *
+	 * Prints the scripts passed to it or the print queue.  Also prints all necessary dependencies.
+	 *
+	 * @param mixed handles (optional) Scripts to be printed.  (void) prints queue, (string) prints that script, (array of strings) prints those scripts.
+	 * @param int group (optional) If scripts were queued in groups prints this group number.
+	 * @return array Scripts that have been printed
+	 */
+	function print_scripts( $handles = false, $group = false ) {
+		return $this->do_items( $handles, $group );
+	}
+
+	function print_scripts_l10n( $handle, $echo = true ) {
+		if ( empty($this->registered[$handle]->extra['l10n']) || empty($this->registered[$handle]->extra['l10n'][0]) || !is_array($this->registered[$handle]->extra['l10n'][1]) )
+			return false;
+
+		$object_name = $this->registered[$handle]->extra['l10n'][0];
+
+		$data = "var $object_name = {\n";
+		$eol = '';
+		foreach ( $this->registered[$handle]->extra['l10n'][1] as $var => $val ) {
+			if ( 'l10n_print_after' == $var ) {
+				$after = $val;
+				continue;
+			}
+			$data .= "$eol\t$var: \"" . esc_js( $val ) . '"';
+			$eol = ",\n";
+		}
+		$data .= "\n};\n";
+		$data .= isset($after) ? "$after\n" : '';
+
+		if ( $echo ) {
+			echo "<script type='text/javascript'>\n";
+			echo "/* <![CDATA[ */\n";
+			echo $data;
+			echo "/* ]]> */\n";
+			echo "</script>\n";
+			return true;
+		} else {
+			return $data;
+		}
+	}
+
+	function do_item( $handle, $group = false ) {
+		if ( !parent::do_item($handle) )
+			return false;
+
+		if ( 0 === $group && $this->groups[$handle] > 0 ) {
+			$this->in_footer[] = $handle;
+			return false;
+		}
+
+		if ( false === $group && in_array($handle, $this->in_footer, true) )
+			$this->in_footer = array_diff( $this->in_footer, (array) $handle );
+
+		if ( null === $this->registered[$handle]->ver )
+			$ver = '';
+		else
+			$ver = $this->registered[$handle]->ver ? $this->registered[$handle]->ver : $this->default_version;
+
+		if ( isset($this->args[$handle]) )
+			$ver = $ver ? $ver . '&amp;' . $this->args[$handle] : $this->args[$handle];
+
+		$src = $this->registered[$handle]->src;
+
+		if ( $this->do_concat ) {
+			$srce = apply_filters( 'script_loader_src', $src, $handle );
+			if ( $this->in_default_dir($srce) ) {
+				$this->print_code .= $this->print_scripts_l10n( $handle, false );
+				$this->concat .= "$handle,";
+				$this->concat_version .= "$handle$ver";
+				return true;
+			} else {
+				$this->ext_handles .= "$handle,";
+				$this->ext_version .= "$handle$ver";
+			}
+		}
+
+		$this->print_scripts_l10n( $handle );
+		if ( !preg_match('|^https?://|', $src) && ! ( $this->content_url && 0 === strpos($src, $this->content_url) ) ) {
+			$src = $this->base_url . $src;
+		}
+
+		if ( !empty($ver) )
+			$src = add_query_arg('ver', $ver, $src);
+		$src = esc_url(apply_filters( 'script_loader_src', $src, $handle ));
+
+		if ( $this->do_concat )
+			$this->print_html .= "<script type='text/javascript' src='$src'></script>\n";
+		else
+			echo "<script type='text/javascript' src='$src'></script>\n";
+
+		return true;
+	}
+
+	/**
+	 * Localizes a script
+	 *
+	 * Localizes only if script has already been added
+	 *
+	 * @param string handle Script name
+	 * @param string object_name Name of JS object to hold l10n info
+	 * @param array l10n Array of JS var name => localized string
+	 * @return bool Successful localization
+	 */
+	function localize( $handle, $object_name, $l10n ) {
+		if ( !$object_name || !$l10n )
+			return false;
+		return $this->add_data( $handle, 'l10n', array( $object_name, $l10n ) );
+	}
+
+	function set_group( $handle, $recursion, $group = false ) {
+		$grp = isset($this->registered[$handle]->extra['group']) ? (int) $this->registered[$handle]->extra['group'] : 0;
+		if ( false !== $group && $grp > $group )
+			$grp = $group;
+
+		return parent::set_group( $handle, $recursion, $grp );
+	}
+
+	function all_deps( $handles, $recursion = false, $group = false ) {
+		$r = parent::all_deps( $handles, $recursion );
+		if ( !$recursion )
+			$this->to_do = apply_filters( 'print_scripts_array', $this->to_do );
+		return $r;
+	}
+
+	function do_head_items() {
+		$this->do_items(false, 0);
+		return $this->done;
+	}
+
+	function do_footer_items() {
+		if ( !empty($this->in_footer) ) {
+			foreach( $this->in_footer as $key => $handle ) {
+				if ( !in_array($handle, $this->done, true) && isset($this->registered[$handle]) ) {
+					$this->do_item($handle);
+					$this->done[] = $handle;
+					unset( $this->in_footer[$key] );
+				}
+			}
+		}
+		return $this->done;
+	}
+
+	function in_default_dir($src) {
+		if ( ! $this->default_dirs )
+			return true;
+
+		foreach ( (array) $this->default_dirs as $test ) {
+			if ( 0 === strpos($src, $test) )
+				return true;
+		}
+		return false;
+	}
+
+	function reset() {
+		$this->do_concat = false;
+		$this->print_code = '';
+		$this->concat = '';
+		$this->concat_version = '';
+		$this->print_html = '';
+		$this->ext_version = '';
+		$this->ext_handles = '';
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-styles.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-styles.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-styles.php	(revision 5057)
@@ -0,0 +1,124 @@
+<?php
+/**
+ * BackPress Styles enqueue.
+ *
+ * These classes were refactored from the WordPress WP_Scripts and WordPress
+ * script enqueue API.
+ *
+ * @package BackPress
+ * @since r74
+ */
+
+/**
+ * BackPress Styles enqueue class.
+ *
+ * @package BackPress
+ * @uses WP_Dependencies
+ * @since r74
+ */
+class WP_Styles extends WP_Dependencies {
+	var $base_url;
+	var $content_url;
+	var $default_version;
+	var $text_direction = 'ltr';
+	var $concat = '';
+	var $concat_version = '';
+	var $do_concat = false;
+	var $print_html = '';
+	var $default_dirs;
+
+	function __construct() {
+		do_action_ref_array( 'wp_default_styles', array(&$this) );
+	}
+
+	function do_item( $handle ) {
+		if ( !parent::do_item($handle) )
+			return false;
+
+		if ( null === $this->registered[$handle]->ver )
+			$ver = '';
+		else
+			$ver = $this->registered[$handle]->ver ? $this->registered[$handle]->ver : $this->default_version;
+
+		if ( isset($this->args[$handle]) )
+			$ver = $ver ? $ver . '&amp;' . $this->args[$handle] : $this->args[$handle];
+
+		if ( $this->do_concat ) {
+			if ( $this->in_default_dir($this->registered[$handle]->src) && !isset($this->registered[$handle]->extra['conditional']) && !isset($this->registered[$handle]->extra['alt']) ) {
+				$this->concat .= "$handle,";
+				$this->concat_version .= "$handle$ver";
+				return true;
+			}
+		}
+
+		if ( isset($this->registered[$handle]->args) )
+			$media = esc_attr( $this->registered[$handle]->args );
+		else
+			$media = 'all';
+
+		$href = $this->_css_href( $this->registered[$handle]->src, $ver, $handle );
+		$rel = isset($this->registered[$handle]->extra['alt']) && $this->registered[$handle]->extra['alt'] ? 'alternate stylesheet' : 'stylesheet';
+		$title = isset($this->registered[$handle]->extra['title']) ? "title='" . esc_attr( $this->registered[$handle]->extra['title'] ) . "'" : '';
+
+		$end_cond = $tag = '';
+		if ( isset($this->registered[$handle]->extra['conditional']) && $this->registered[$handle]->extra['conditional'] ) {
+			$tag .= "<!--[if {$this->registered[$handle]->extra['conditional']}]>\n";
+			$end_cond = "<![endif]-->\n";
+		}
+
+		$tag .= apply_filters( 'style_loader_tag', "<link rel='$rel' id='$handle-css' $title href='$href' type='text/css' media='$media' />\n", $handle );
+		if ( 'rtl' === $this->text_direction && isset($this->registered[$handle]->extra['rtl']) && $this->registered[$handle]->extra['rtl'] ) {
+			if ( is_bool( $this->registered[$handle]->extra['rtl'] ) )
+				$rtl_href = str_replace( '.css', '-rtl.css', $this->_css_href( $this->registered[$handle]->src , $ver, "$handle-rtl" ));
+			else
+				$rtl_href = $this->_css_href( $this->registered[$handle]->extra['rtl'], $ver, "$handle-rtl" );
+
+			$tag .= apply_filters( 'style_loader_tag', "<link rel='$rel' id='$handle-rtl-css' $title href='$rtl_href' type='text/css' media='$media' />\n", $handle );
+		}
+
+		$tag .= $end_cond;
+
+		if ( $this->do_concat )
+			$this->print_html .= $tag;
+		else
+			echo $tag;
+
+		// Could do something with $this->registered[$handle]->extra here to print out extra CSS rules
+//		echo "<style type='text/css'>\n";
+//		echo "/* <![CDATA[ */\n";
+//		echo "/* ]]> */\n";
+//		echo "</style>\n";
+
+		return true;
+	}
+
+	function all_deps( $handles, $recursion = false, $group = false ) {
+		$r = parent::all_deps( $handles, $recursion );
+		if ( !$recursion )
+			$this->to_do = apply_filters( 'print_styles_array', $this->to_do );
+		return $r;
+	}
+
+	function _css_href( $src, $ver, $handle ) {
+		if ( !preg_match('|^https?://|', $src) && ! ( $this->content_url && 0 === strpos($src, $this->content_url) ) ) {
+			$src = $this->base_url . $src;
+		}
+
+		if ( !empty($ver) )
+			$src = add_query_arg('ver', $ver, $src);
+		$src = apply_filters( 'style_loader_src', $src, $handle );
+		return esc_url( $src );
+	}
+
+	function in_default_dir($src) {
+		if ( ! $this->default_dirs )
+			return true;
+
+		foreach ( (array) $this->default_dirs as $test ) {
+			if ( 0 === strpos($src, $test) )
+				return true;
+		}
+		return false;
+	}
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-taxonomy.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-taxonomy.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-taxonomy.php	(revision 5057)
@@ -0,0 +1,2082 @@
+<?php
+// Last sync [WP11537] - Refactored into a class based on wp-includes/taxonomy.php
+
+/**
+ * Taxonomy API
+ *
+ * @package WordPress
+ * @subpackage Taxonomy
+ * @since 2.3.0
+ */
+
+/**
+ * WordPress Taxonomy based off of WordPress revision 8782.
+ *
+ * @since 2.3.0
+ */
+class WP_Taxonomy {
+	/**
+	 * Stores the database.
+	 *
+	 * @var unknown_type
+	 */
+	var $db;
+	var $taxonomies = array();
+
+	function WP_Taxonomy( &$db ) {
+		$this->__construct( $db );
+		register_shutdown_function( array(&$this, '__destruct') );
+	}
+
+	/**
+	 * PHP5 constructor - Assigns the database to an attribute of the class.
+	 *
+	 * @param unknown_type $db
+	 */
+	function __construct( &$db ) {
+		$this->db =& $db;
+	}
+
+	/**
+	 * Does nothing.
+	 *
+	 * @package BackPress
+	 * @subpackage Taxonomy
+	 */
+	function __destruct() {
+	}
+
+	/**
+	 * Return all of the taxonomy names that are of $object_type.
+	 *
+	 * It appears that this function can be used to find all of the names inside of
+	 * $this->taxonomies variable.
+	 *
+	 * <code><?php $taxonomies = $this->get_object_taxonomies('post'); ?></code> Should
+	 * result in <code>Array('category', 'post_tag')</code>
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->taxonomies
+	 *
+	 * @param array|string|object $object_type Name of the type of taxonomy object, or an object (row from posts)
+	 * @return array The names of all taxonomy of $object_type.
+	 */
+	function get_object_taxonomies($object_type) {
+		$object_type = (array) $object_type;
+
+		// WP DIFF
+		$taxonomies = array();
+		foreach ( (array) $this->taxonomies as $taxonomy ) {
+			if ( array_intersect($object_type, (array) $taxonomy->object_type) )
+				$taxonomies[] = $taxonomy->name;
+		}
+
+		return $taxonomies;
+	}
+
+	/**
+	 * Retrieves the taxonomy object of $taxonomy.
+	 *
+	 * The get_taxonomy function will first check that the parameter string given
+	 * is a taxonomy object and if it is, it will return it.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->taxonomies
+	 * @uses $this->is_taxonomy() Checks whether taxonomy exists
+	 *
+	 * @param string $taxonomy Name of taxonomy object to return
+	 * @return object|bool The Taxonomy Object or false if $taxonomy doesn't exist
+	 */
+	function get_taxonomy( $taxonomy ) {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return false;
+
+		return $this->taxonomies[$taxonomy];
+	}
+
+	/**
+	 * Checks that the taxonomy name exists.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 * 
+	 * @uses $this->taxonomies
+	 *
+	 * @param string $taxonomy Name of taxonomy object
+	 * @return bool Whether the taxonomy exists or not.
+	 */
+	function is_taxonomy( $taxonomy ) {
+		return isset($this->taxonomies[$taxonomy]);
+	}
+
+	/**
+	 * Whether the taxonomy object is hierarchical.
+	 *
+	 * Checks to make sure that the taxonomy is an object first. Then Gets the
+	 * object, and finally returns the hierarchical value in the object.
+	 *
+	 * A false return value might also mean that the taxonomy does not exist.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->is_taxonomy() Checks whether taxonomy exists
+	 * @uses $this->get_taxonomy() Used to get the taxonomy object
+	 *
+	 * @param string $taxonomy Name of taxonomy object
+	 * @return bool Whether the taxonomy is hierarchical
+	 */
+	function is_taxonomy_hierarchical($taxonomy) {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return false;
+
+		$taxonomy = $this->get_taxonomy($taxonomy);
+		return $taxonomy->hierarchical;
+	}
+
+	/**
+	 * Create or modify a taxonomy object. Do not use before init.
+	 *
+	 * A simple function for creating or modifying a taxonomy object based on the
+	 * parameters given. The function will accept an array (third optional
+	 * parameter), along with strings for the taxonomy name and another string for
+	 * the object type.
+	 *
+	 * The function keeps a default set, allowing for the $args to be optional but
+	 * allow the other functions to still work. It is possible to overwrite the
+	 * default set, which contains two keys: hierarchical and update_count_callback.
+	 *
+	 * Nothing is returned, so expect error maybe or use is_taxonomy() to check
+	 * whether taxonomy exists.
+	 *
+	 * Optional $args contents:
+	 *
+	 * hierarachical - has some defined purpose at other parts of the API and is a
+	 * boolean value.
+	 *
+	 * update_count_callback - works much like a hook, in that it will be called
+	 * when the count is updated.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 * @uses $this->taxonomies Inserts new taxonomy object into the list
+	 * 
+	 * @param string $taxonomy Name of taxonomy object
+	 * @param string $object_type Name of the object type for the taxonomy object.
+	 * @param array|string $args See above description for the two keys values.
+	 */
+	function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
+		$defaults = array('hierarchical' => false, 'update_count_callback' => '');
+		$args = wp_parse_args($args, $defaults);
+
+		$args['name'] = $taxonomy;
+		$args['object_type'] = $object_type;
+		$this->taxonomies[$taxonomy] = (object) $args;
+	}
+
+	//
+	// Term API
+	//
+
+	/**
+	 * Retrieve object_ids of valid taxonomy and term.
+	 *
+	 * The strings of $taxonomies must exist before this function will continue. On
+	 * failure of finding a valid taxonomy, it will return an WP_Error class, kind
+	 * of like Exceptions in PHP 5, except you can't catch them. Even so, you can
+	 * still test for the WP_Error class and get the error message.
+	 *
+	 * The $terms aren't checked the same as $taxonomies, but still need to exist
+	 * for $object_ids to be returned.
+	 *
+	 * It is possible to change the order that object_ids is returned by either
+	 * using PHP sort family functions or using the database by using $args with
+	 * either ASC or DESC array. The value should be in the key named 'order'.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses wp_parse_args() Creates an array from string $args.
+	 *
+	 * @param string|array $terms String of term or array of string values of terms that will be used
+	 * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names
+	 * @param array|string $args Change the order of the object_ids, either ASC or DESC
+	 * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success
+	 *	the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
+	 */
+	function get_objects_in_term( $terms, $taxonomies, $args = null ) {
+		if ( !is_array($terms) )
+			$terms = array($terms);
+
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			if ( !$this->is_taxonomy($taxonomy) )
+				return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+		}
+
+		$defaults = array('order' => 'ASC', 'field' => 'term_id');
+		$args = wp_parse_args( $args, $defaults );
+		extract($args, EXTR_SKIP);
+
+		if ( 'tt_id' == $field )
+			$field = 'tt.term_taxonomy_id';
+		else
+			$field = 'tt.term_id';
+
+		$order = ( 'desc' == strtolower($order) ) ? 'DESC' : 'ASC';
+
+		$terms = array_map('intval', $terms);
+
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
+		$terms = "'" . implode("', '", $terms) . "'";
+
+		$object_ids = $this->db->get_col("SELECT tr.object_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND $field IN ($terms) ORDER BY tr.object_id $order");
+
+		if ( ! $object_ids )
+			return array();
+
+		return $object_ids;
+	}
+
+	/**
+	 * Get all Term data from database by Term ID.
+	 *
+	 * The usage of the get_term function is to apply filters to a term object. It
+	 * is possible to get a term object from the database before applying the
+	 * filters.
+	 *
+	 * $term ID must be part of $taxonomy, to get from the database. Failure, might
+	 * be able to be captured by the hooks. Failure would be the same value as $this->db
+	 * returns for the get_row method.
+	 *
+	 * There are two hooks, one is specifically for each term, named 'get_term', and
+	 * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the
+	 * term object, and the taxonomy name as parameters. Both hooks are expected to
+	 * return a Term object.
+	 *
+	 * 'get_term' hook - Takes two parameters the term Object and the taxonomy name.
+	 * Must return term object. Used in get_term() as a catch-all filter for every
+	 * $term.
+	 *
+	 * 'get_$taxonomy' hook - Takes two parameters the term Object and the taxonomy
+	 * name. Must return term object. $taxonomy will be the taxonomy name, so for
+	 * example, if 'category', it would be 'get_category' as the filter name. Useful
+	 * for custom taxonomies or plugging into default taxonomies.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning.
+	 * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
+	 *
+	 * @param int|object $term If integer, will get from database. If object will apply filters and return $term.
+	 * @param string $taxonomy Taxonomy name that $term is part of.
+	 * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
+	 * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
+	 * @return mixed|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not
+	 * exist then WP_Error will be returned.
+	 */
+	function &get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
+		if ( empty($term) ) {
+			$error = new WP_Error('invalid_term', __('Empty Term'));
+			return $error;
+		}
+
+		if ( !$this->is_taxonomy($taxonomy) ) {
+			$error = new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+			return $error;
+		}
+
+		if ( is_object($term) ) {
+			wp_cache_add($term->term_id, $term, $taxonomy);
+			wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" );
+			$_term = $term;
+		} else {
+			$term = (int) $term;
+			if ( ! $_term = wp_cache_get($term, $taxonomy) ) {
+				$_term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %s LIMIT 1", $taxonomy, $term) );
+				wp_cache_add($term, $_term, $taxonomy);
+				wp_cache_add($_term->term_taxonomy_id, $_term->term_id, "$taxonomy:tt_id" );
+			}
+		}
+
+		$_term = apply_filters('get_term', $_term, $taxonomy);
+		$_term = apply_filters("get_$taxonomy", $_term, $taxonomy);
+		$_term = $this->sanitize_term($_term, $taxonomy, $filter);
+
+		backpress_convert_object( $_term, $output );
+
+		return $_term;
+	}
+
+	/**
+	 * Get all Term data from database by Term field and data.
+	 *
+	 * Warning: $value is not escaped for 'name' $field. You must do it yourself, if
+	 * required.
+	 *
+	 * The default $field is 'id', therefore it is possible to also use null for
+	 * field, but not recommended that you do so.
+	 *
+	 * If $value does not exist, the return value will be false. If $taxonomy exists
+	 * and $field and $value combinations exist, the Term will be returned.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning.
+	 * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
+	 *
+	 * @param string $field Either 'slug', 'name', 'id', or 'tt_id'
+	 * @param string|int $value Search for this term value
+	 * @param string $taxonomy Taxonomy Name
+	 * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
+	 * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
+	 * @return mixed Term Row from database. Will return false if $taxonomy does not exist or $term was not found.
+	 */
+	function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return false;
+
+		if ( 'slug' == $field ) {
+			$field = 't.slug';
+			$value = $this->sanitize_term_slug($value, $taxonomy);
+			if ( empty($value) )
+				return false;
+		} else if ( 'name' == $field ) {
+			// Assume already escaped
+			$field = 't.name';
+		} else if ( 'tt_id' == $field ) {
+			$field = 'tt.term_taxonomy_id';
+			$value = (int) $value;
+			if ( $_term_id = wp_cache_get( $value, "$taxonomy:tt_id" ) )
+				return $this->get_term( $_term_id, $taxonomy, $output, $filter );
+		} else {
+			$field = 't.term_id';
+			$value = (int) $value;
+		}
+
+		$term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) );
+		if ( !$term )
+			return false;
+
+		wp_cache_add($term->term_id, $term, $taxonomy);
+		wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" );
+
+		$term = $this->sanitize_term($term, $taxonomy, $filter);
+
+		backpress_convert_object( $term, $output );
+
+		return $term;
+	}
+
+	/**
+	 * Merge all term children into a single array of their IDs.
+	 *
+	 * This recursive function will merge all of the children of $term into the same
+	 * array of term IDs. Only useful for taxonomies which are hierarchical.
+	 *
+	 * Will return an empty array if $term does not exist in $taxonomy.
+	 * 
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->_get_term_hierarchy()
+	 * @uses $this->get_term_children() Used to get the children of both $taxonomy and the parent $term
+	 *
+	 * @param string $term ID of Term to get children
+	 * @param string $taxonomy Taxonomy Name
+	 * @return array|WP_Error List of Term Objects. WP_Error returned if $taxonomy does not exist
+	 */
+	function get_term_children( $term_id, $taxonomy ) {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+
+		$term_id = intval( $term_id );
+
+		$terms = $this->_get_term_hierarchy($taxonomy);
+
+		if ( ! isset($terms[$term_id]) )
+			return array();
+
+		$children = $terms[$term_id];
+
+		foreach ( (array) $terms[$term_id] as $child ) {
+			if ( isset($terms[$child]) )
+				$children = array_merge($children, $this->get_term_children($child, $taxonomy));
+		}
+
+		return $children;
+	}
+
+	/**
+	 * Get sanitized Term field.
+	 *
+	 * Does checks for $term, based on the $taxonomy. The function is for contextual
+	 * reasons and for simplicity of usage. See sanitize_term_field() for more
+	 * information.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->sanitize_term_field() Passes the return value in sanitize_term_field on success.
+	 *
+	 * @param string $field Term field to fetch
+	 * @param int $term Term ID
+	 * @param string $taxonomy Taxonomy Name
+	 * @param string $context Optional, default is display. Look at sanitize_term_field() for available options.
+	 * @return mixed Will return an empty string if $term is not an object or if $field is not set in $term.
+	 */
+	function get_term_field( $field, $term, $taxonomy, $context = 'display' ) {
+		$term = (int) $term;
+		$term = $this->get_term( $term, $taxonomy );
+		if ( is_wp_error($term) )
+			return $term;
+
+		if ( !is_object($term) )
+			return '';
+
+		if ( !isset($term->$field) )
+			return '';
+
+		return $this->sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context);
+	}
+
+	/**
+	 * Sanitizes Term for editing.
+	 *
+	 * Return value is sanitize_term() and usage is for sanitizing the term for
+	 * editing. Function is for contextual and simplicity.
+	 * 
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->sanitize_term() Passes the return value on success
+	 *
+	 * @param int|object $id Term ID or Object
+	 * @param string $taxonomy Taxonomy Name
+	 * @return mixed|null|WP_Error Will return empty string if $term is not an object.
+	 */
+	function get_term_to_edit( $id, $taxonomy ) {
+		$term = $this->get_term( $id, $taxonomy );
+
+		if ( is_wp_error($term) )
+			return $term;
+
+		if ( !is_object($term) )
+			return '';
+
+		return $this->sanitize_term($term, $taxonomy, 'edit');
+	}
+
+	/**
+	 * Retrieve the terms in a given taxonomy or list of taxonomies.
+	 *
+	 * You can fully inject any customizations to the query before it is sent, as
+	 * well as control the output with a filter.
+	 *
+	 * The 'get_terms' filter will be called when the cache has the term and will
+	 * pass the found term along with the array of $taxonomies and array of $args.
+	 * This filter is also called before the array of terms is passed and will pass
+	 * the array of terms, along with the $taxonomies and $args.
+	 *
+	 * The 'list_terms_exclusions' filter passes the compiled exclusions along with
+	 * the $args.
+	 *
+	 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
+	 * along with the $args array.
+	 *
+	 * The 'get_terms_fields' filter passes the fields for the SELECT query
+	 * along with the $args array.
+	 *
+	 * The list of arguments that $args can contain, which will overwrite the defaults:
+	 *
+	 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
+	 * (will use term_id), Passing a custom value other than these will cause it to
+	 * order based on the custom value.
+	 *
+	 * order - Default is ASC. Can use DESC.
+	 *
+	 * hide_empty - Default is true. Will not return empty terms, which means
+	 * terms whose count is 0 according to the given taxonomy.
+	 *
+	 * exclude - Default is an empty string.  A comma- or space-delimited string
+	 * of term ids to exclude from the return array.  If 'include' is non-empty,
+	 * 'exclude' is ignored.
+	 *
+	 * include - Default is an empty string.  A comma- or space-delimited string
+	 * of term ids to include in the return array.
+	 *
+	 * number - The maximum number of terms to return.  Default is empty.
+	 *
+	 * offset - The number by which to offset the terms query.
+	 *
+	 * fields - Default is 'all', which returns an array of term objects.
+	 * If 'fields' is 'ids' or 'names', returns an array of
+	 * integers or strings, respectively.
+	 *
+	 * slug - Returns terms whose "slug" matches this value. Default is empty string.
+	 *
+	 * hierarchical - Whether to include terms that have non-empty descendants
+	 * (even if 'hide_empty' is set to true).
+	 *
+	 * search - Returned terms' names will contain the value of 'search',
+	 * case-insensitive.  Default is an empty string.
+	 *
+	 * name__like - Returned terms' names will begin with the value of 'name__like',
+	 * case-insensitive. Default is empty string.
+	 *
+	 * The argument 'pad_counts', if set to true will include the quantity of a term's
+	 * children in the quantity of each term's "count" object variable.
+	 *
+	 * The 'get' argument, if set to 'all' instead of its default empty string,
+	 * returns terms regardless of ancestry or whether the terms are empty.
+	 *
+	 * The 'child_of' argument, when used, should be set to the integer of a term ID.  Its default
+	 * is 0.  If set to a non-zero value, all returned terms will be descendants
+	 * of that term according to the given taxonomy.  Hence 'child_of' is set to 0
+	 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
+	 * make term ancestry ambiguous.
+	 *
+	 * The 'parent' argument, when used, should be set to the integer of a term ID.  Its default is
+	 * the empty string '', which has a different meaning from the integer 0.
+	 * If set to an integer value, all returned terms will have as an immediate
+	 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
+	 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
+	 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
+	 *
+	 * @param string|array Taxonomy name or list of Taxonomy names
+	 * @param string|array $args The values of what to search for when returning terms
+	 * @return array|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies do not exist.
+	 */
+	function &get_terms($taxonomies, $args = '') {
+		$empty_array = array();
+
+		$single_taxonomy = false;
+		if ( !is_array($taxonomies) ) {
+			$single_taxonomy = true;
+			$taxonomies = array($taxonomies);
+		}
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			if ( ! $this->is_taxonomy($taxonomy) ) {
+				$error = & new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+				return $error;
+			}
+		}
+
+		$in_taxonomies = "'" . implode("', '", $taxonomies) . "'";
+
+		$defaults = array('orderby' => 'name', 'order' => 'ASC',
+			'hide_empty' => true, 'exclude' => '', 'exclude_tree' => '', 'include' => '',
+			'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',
+			'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',
+			'pad_counts' => false, 'offset' => '', 'search' => '');
+		$args = wp_parse_args( $args, $defaults );
+		$args['number'] = absint( $args['number'] );
+		$args['offset'] = absint( $args['offset'] );
+		if ( !$single_taxonomy || !$this->is_taxonomy_hierarchical($taxonomies[0]) ||
+			'' !== $args['parent'] ) {
+			$args['child_of'] = 0;
+			$args['hierarchical'] = false;
+			$args['pad_counts'] = false;
+		}
+
+		if ( 'all' == $args['get'] ) {
+			$args['child_of'] = 0;
+			$args['hide_empty'] = 0;
+			$args['hierarchical'] = false;
+			$args['pad_counts'] = false;
+		}
+		extract($args, EXTR_SKIP);
+
+		if ( $child_of ) {
+			$hierarchy = $this->_get_term_hierarchy($taxonomies[0]);
+			if ( !isset($hierarchy[$child_of]) )
+				return $empty_array;
+		}
+
+		if ( $parent ) {
+			$hierarchy = $this->_get_term_hierarchy($taxonomies[0]);
+			if ( !isset($hierarchy[$parent]) )
+				return $empty_array;
+		}
+
+		// $args can be whatever, only use the args defined in defaults to compute the key
+		$filter_key = ( has_filter('list_terms_exclusions') ) ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
+		$key = md5( serialize( compact(array_keys($defaults)) ) . serialize( $taxonomies ) . $filter_key );
+		$last_changed = wp_cache_get('last_changed', 'terms');
+		if ( !$last_changed ) {
+			$last_changed = time();
+			wp_cache_set('last_changed', $last_changed, 'terms');
+		}
+		$cache_key = "get_terms:$key:$last_changed";
+		$cache = wp_cache_get( $cache_key, 'terms' );
+		if ( false !== $cache ) {
+			$cache = apply_filters('get_terms', $cache, $taxonomies, $args);
+			return $cache;
+		}
+
+		$_orderby = strtolower($orderby);
+		if ( 'count' == $_orderby )
+			$orderby = 'tt.count';
+		else if ( 'name' == $_orderby )
+			$orderby = 't.name';
+		else if ( 'slug' == $_orderby )
+			$orderby = 't.slug';
+		else if ( 'term_group' == $_orderby )
+			$orderby = 't.term_group';
+		elseif ( empty($_orderby) || 'id' == $_orderby )
+			$orderby = 't.term_id';
+
+		$orderby = apply_filters( 'get_terms_orderby', $orderby, $args );
+
+		$where = '';
+		$inclusions = '';
+		if ( !empty($include) ) {
+			$exclude = '';
+			$exclude_tree = '';
+			$interms = preg_split('/[\s,]+/',$include);
+			if ( count($interms) ) {
+				foreach ( (array) $interms as $interm ) {
+					if (empty($inclusions))
+						$inclusions = ' AND ( t.term_id = ' . intval($interm) . ' ';
+					else
+						$inclusions .= ' OR t.term_id = ' . intval($interm) . ' ';
+				}
+			}
+		}
+
+		if ( !empty($inclusions) )
+			$inclusions .= ')';
+		$where .= $inclusions;
+
+		$exclusions = '';
+		if ( ! empty( $exclude_tree ) ) {
+			$excluded_trunks = preg_split('/[\s,]+/',$exclude_tree);
+			foreach( (array) $excluded_trunks as $extrunk ) {
+				$excluded_children = (array) $this->get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids'));
+				$excluded_children[] = $extrunk;
+				foreach( (array) $excluded_children as $exterm ) {
+					if ( empty($exclusions) )
+						$exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' ';
+					else
+						$exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' ';
+
+				}
+			}
+		}
+		if ( !empty($exclude) ) {
+			$exterms = preg_split('/[\s,]+/',$exclude);
+			if ( count($exterms) ) {
+				foreach ( (array) $exterms as $exterm ) {
+					if ( empty($exclusions) )
+						$exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' ';
+					else
+						$exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' ';
+				}
+			}
+		}
+
+		if ( !empty($exclusions) )
+			$exclusions .= ')';
+		$exclusions = apply_filters('list_terms_exclusions', $exclusions, $args );
+		$where .= $exclusions;
+
+		if ( !empty($slug) ) {
+			$slug =  $this->sanitize_term_slug($slug);
+			$where .= " AND t.slug = '$slug'";
+		}
+
+		if ( !empty($name__like) )
+			$where .= " AND t.name LIKE '{$name__like}%'";
+
+		if ( '' !== $parent ) {
+			$parent = (int) $parent;
+			$where .= " AND tt.parent = '$parent'";
+		}
+
+		if ( $hide_empty && !$hierarchical )
+			$where .= ' AND tt.count > 0';
+
+		// don't limit the query results when we have to descend the family tree
+		if ( ! empty($number) && ! $hierarchical && empty( $child_of ) && '' === $parent ) {
+			if( $offset )
+				$limit = 'LIMIT ' . $offset . ',' . $number;
+			else
+				$limit = 'LIMIT ' . $number;
+
+		} else
+			$limit = '';
+
+		if ( !empty($search) ) {
+			$search = like_escape($search);
+			$where .= " AND (t.name LIKE '%$search%')";
+		}
+
+		if ( !in_array( $fields, array( 'all', 'ids', 'names', 'tt_ids' ) ) )
+			$fields = 'all';
+
+		$selects = array();
+		if ( 'all' == $fields )
+			$selects = array('t.*', 'tt.*');
+		else if ( 'ids' == $fields )
+			$selects = array('t.term_id', 'tt.parent', 'tt.count');
+		else if ( 'names' == $fields )
+			$selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
+	        $select_this = implode(', ', apply_filters( 'get_terms_fields', $selects, $args ));
+
+		$query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($in_taxonomies) $where ORDER BY $orderby $order $limit";
+
+		$terms = $this->db->get_results($query);
+		if ( 'all' == $fields ) {
+			$this->update_term_cache($terms);
+		}
+
+		if ( empty($terms) ) {
+			wp_cache_add( $cache_key, array(), 'terms' );
+			$terms = apply_filters('get_terms', array(), $taxonomies, $args);
+			return $terms;
+		}
+
+		if ( $child_of || $hierarchical ) {
+			$children = $this->_get_term_hierarchy($taxonomies[0]);
+			if ( ! empty($children) )
+				$terms = & $this->_get_term_children($child_of, $terms, $taxonomies[0]);
+		}
+
+		// Update term counts to include children.
+		if ( $pad_counts )
+			$this->_pad_term_counts($terms, $taxonomies[0]);
+
+		// Make sure we show empty categories that have children.
+		if ( $hierarchical && $hide_empty && is_array($terms) ) {
+			foreach ( $terms as $k => $term ) {
+				if ( ! $term->count ) {
+					$children = $this->_get_term_children($term->term_id, $terms, $taxonomies[0]);
+					if( is_array($children) )
+						foreach ( $children as $child )
+							if ( $child->count )
+								continue 2;
+
+					// It really is empty
+					unset($terms[$k]);
+				}
+			}
+		}
+		reset ( $terms );
+
+		$_terms = array();
+		if ( 'ids' == $fields ) {
+			while ( $term = array_shift($terms) )
+				$_terms[] = $term->term_id;
+			$terms = $_terms;
+		} elseif ( 'names' == $fields ) {
+			while ( $term = array_shift($terms) )
+				$_terms[] = $term->name;
+			$terms = $_terms;
+		}
+
+		if ( 0 < $number && intval(@count($terms)) > $number ) {
+			$terms = array_slice($terms, $offset, $number);
+		}
+
+		wp_cache_add( $cache_key, $terms, 'terms' );
+
+		$terms = apply_filters('get_terms', $terms, $taxonomies, $args);
+		return $terms;
+	}
+
+	/**
+	 * Check if Term exists.
+	 *
+	 * Returns the index of a defined term, or 0 (false) if the term doesn't exist.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param int|string $term The term to check
+	 * @param string $taxonomy The taxonomy name to use
+	 * @param int $parent ID of parent term under which to confine the exists search.
+	 * @return mixed Get the term id or Term Object, if exists.
+	 */
+	function is_term($term, $taxonomy = '', $parent = 0) {
+		$select = "SELECT term_id FROM {$this->db->terms} as t WHERE ";
+		$tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE ";
+		
+		if ( is_int($term) ) {
+			if ( 0 == $term )
+				return 0;
+			$where = 't.term_id = %d';
+			if ( !empty($taxonomy) )
+				return $this->db->get_row( $this->db->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A );
+			else
+				return $this->db->get_var( $this->db->prepare( $select . $where, $term ) );
+		}
+
+		$term = trim( stripslashes( $term ) );
+
+		if ( '' === $slug = $this->sanitize_term_slug($term) )
+			return 0;
+
+		$where = 't.slug = %s';
+		$else_where = 't.name = %s';
+		$where_fields = array($slug);
+		$else_where_fields = array($term);
+		if ( !empty($taxonomy) ) {
+			$parent = (int) $parent;
+			if ( $parent > 0 ) {
+				$where_fields[] = $parent;
+				$else_where_fields[] = $parent;
+				$where .= ' AND tt.parent = %d';
+				$else_where .= ' AND tt.parent = %d';
+			}
+
+			$where_fields[] = $taxonomy;
+			$else_where_fields[] = $taxonomy;
+
+			if ( $result = $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s", $where_fields), ARRAY_A) )
+				return $result;
+
+			return $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s", $else_where_fields), ARRAY_A);
+		}
+
+		if ( $result = $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $where", $where_fields) ) )
+			return $result;
+
+		return $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $else_where", $else_where_fields) );
+	}
+
+	function sanitize_term_slug( $title, $taxonomy = '', $term_id = 0 ) {
+		return apply_filters( 'pre_term_slug', $title, $taxonomy, $term_id );
+	}
+
+	function format_to_edit( $text ) {
+		return format_to_edit( $text );
+	}
+
+	/**
+	 * Sanitize Term all fields
+	 *
+	 * Relies on sanitize_term_field() to sanitize the term. The difference
+	 * is that this function will sanitize <strong>all</strong> fields. The
+	 * context is based on sanitize_term_field().
+	 *
+	 * The $term is expected to be either an array or an object.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses $this->sanitize_term_field Used to sanitize all fields in a term
+	 *
+	 * @param array|object $term The term to check
+	 * @param string $taxonomy The taxonomy name to use
+	 * @param string $context Default is 'display'.
+	 * @return array|object Term with all fields sanitized
+	 */
+	function sanitize_term($term, $taxonomy, $context = 'display') {
+
+		if ( 'raw' == $context )
+			return $term;
+
+		$fields = array('term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group');
+
+		$do_object = false;
+		if ( is_object($term) )
+			$do_object = true;
+
+		$term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0);
+
+		foreach ( (array) $fields as $field ) {
+			if ( $do_object ) {
+				if ( isset($term->$field) )
+					$term->$field = $this->sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context);
+			} else {
+				if ( isset($term[$field]) )
+					$term[$field] = $this->sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context);
+			}
+		}
+
+		if ( $do_object )
+			$term->filter = $context;
+		else
+			$term['filter'] = $context;
+
+		return $term;
+	}
+
+	/**
+	 * Cleanse the field value in the term based on the context.
+	 *
+	 * Passing a term field value through the function should be assumed to have
+	 * cleansed the value for whatever context the term field is going to be used.
+	 *
+	 * If no context or an unsupported context is given, then default filters will
+	 * be applied.
+	 *
+	 * There are enough filters for each context to support a custom filtering
+	 * without creating your own filter function. Simply create a function that
+	 * hooks into the filter you need.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param string $field Term field to sanitize
+	 * @param string $value Search for this term value
+	 * @param int $term_id Term ID
+	 * @param string $taxonomy Taxonomy Name
+	 * @param string $context Either edit, db, display, attribute, or js.
+	 * @return mixed sanitized field
+	 */
+	function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) {
+		if ( 'parent' == $field  || 'term_id' == $field || 'count' == $field || 'term_group' == $field ) {
+			$value = (int) $value;
+			if ( $value < 0 )
+				$value = 0;
+		}
+
+		if ( 'raw' == $context )
+			return $value;
+
+		if ( 'edit' == $context ) {
+			$value = apply_filters("edit_term_$field", $value, $term_id, $taxonomy);
+			$value = apply_filters("edit_${taxonomy}_$field", $value, $term_id);
+			if ( 'description' == $field )
+				$value = $this->format_to_edit($value);
+			else
+				$value = esc_attr($value);
+		} else if ( 'db' == $context ) {
+			$value = apply_filters("pre_term_$field", $value, $taxonomy);
+			$value = apply_filters("pre_${taxonomy}_$field", $value);
+			// WP DIFF
+		} else if ( 'rss' == $context ) {
+			$value = apply_filters("term_${field}_rss", $value, $taxonomy);
+			$value = apply_filters("${taxonomy}_${field}_rss", $value);
+		} else {
+			// Use display filters by default.
+			$value = apply_filters("term_$field", $value, $term_id, $taxonomy, $context);
+			$value = apply_filters("${taxonomy}_$field", $value, $term_id, $context);
+		}
+
+		if ( 'attribute' == $context )
+			$value = esc_attr($value);
+		else if ( 'js' == $context )
+			$value = esc_js($value);
+
+		return $value;
+	}
+
+	/**
+	 * Count how many terms are in Taxonomy.
+	 *
+	 * Default $args is 'ignore_empty' which can be <code>'ignore_empty=true'</code>
+	 * or <code>array('ignore_empty' => true);</code>.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses wp_parse_args() Turns strings into arrays and merges defaults into an array.
+	 *
+	 * @param string $taxonomy Taxonomy name
+	 * @param array|string $args Overwrite defaults
+	 * @return int How many terms are in $taxonomy
+	 */
+	function count_terms( $taxonomy, $args = array() ) {
+		$defaults = array('ignore_empty' => false);
+		$args = wp_parse_args($args, $defaults);
+		extract($args, EXTR_SKIP);
+
+		$where = '';
+		if ( $ignore_empty )
+			$where = 'AND count > 0';
+
+		return $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE taxonomy = %s $where", $taxonomy ) );
+	}
+
+	/**
+	 * Will unlink the term from the taxonomy.
+	 *
+	 * Will remove the term's relationship to the taxonomy, not the term or taxonomy
+	 * itself. The term and taxonomy will still exist. Will require the term's
+	 * object ID to perform the operation.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param int $object_id The term Object Id that refers to the term
+	 * @param string|array $taxonomy List of Taxonomy Names or single Taxonomy name.
+	 */
+	function delete_object_term_relationships( $object_id, $taxonomies ) {
+		$object_id = (int) $object_id;
+
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			$terms = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids'));
+			$in_terms = "'" . implode("', '", $terms) . "'";
+			$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_terms)", $object_id ) );
+			$this->update_term_count($terms, $taxonomy);
+		}
+	}
+
+	/**
+	 * Removes a term from the database.
+	 *
+	 * If the term is a parent of other terms, then the children will be updated to
+	 * that term's parent.
+	 *
+	 * The $args 'default' will only override the terms found, if there is only one
+	 * term found. Any other and the found terms are used.
+	 *
+	 * The $args 'force_default' will force the term supplied as default to be
+	 * assigned even if the object was not going to be termless
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses do_action() Calls both 'delete_term' and 'delete_$taxonomy' action
+	 *  hooks, passing term object, term id. 'delete_term' gets an additional
+	 *  parameter with the $taxonomy parameter.
+	 *
+	 * @param int $term Term ID
+	 * @param string $taxonomy Taxonomy Name
+	 * @param array|string $args Optional. Change 'default' term id and override found term ids.
+	 * @return bool|WP_Error Returns false if not term; true if completes delete action.
+	 */
+	function delete_term( $term, $taxonomy, $args = array() ) {
+		$term = (int) $term;
+
+		if ( ! $ids = $this->is_term($term, $taxonomy) )
+			return false;
+		if ( is_wp_error( $ids ) )
+			return $ids;
+
+		$tt_id = $ids['term_taxonomy_id'];
+
+		$defaults = array();
+		$args = wp_parse_args($args, $defaults);
+		extract($args, EXTR_SKIP);
+
+		if ( isset($default) ) {
+			$default = (int) $default;
+			if ( !$this->is_term($default, $taxonomy) )
+				unset($default);
+		}
+
+		// Update children to point to new parent
+		if ( $this->is_taxonomy_hierarchical($taxonomy) ) {
+			$term_obj = $this->get_term($term, $taxonomy);
+			if ( is_wp_error( $term_obj ) )
+				return $term_obj;
+			$parent = $term_obj->parent;
+
+			$this->db->update( $this->db->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id ) + compact( 'taxonomy' ) );
+		}
+
+		$objects = $this->db->get_col( $this->db->prepare( "SELECT object_id FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $tt_id ) );
+
+		foreach ( (array) $objects as $object ) {
+			$terms = $this->get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
+			if ( 1 == count($terms) && isset($default) ) {
+				$terms = array($default);
+			} else {
+				$terms = array_diff($terms, array($term));
+				if (isset($default) && isset($force_default) && $force_default)
+					$terms = array_merge($terms, array($default));
+			}
+			$terms = array_map('intval', $terms);
+			$this->set_object_terms($object, $terms, $taxonomy);
+		}
+
+		$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id = %d", $tt_id ) );
+
+		// Delete the term if no taxonomies use it.
+		if ( !$this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE term_id = %d", $term) ) )
+			$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->terms} WHERE term_id = %d", $term) );
+
+		$this->clean_term_cache($term, $taxonomy);
+
+		do_action('delete_term', $term, $tt_id, $taxonomy);
+		do_action("delete_$taxonomy", $term, $tt_id);
+
+		return true;
+	}
+
+	/**
+	 * Retrieves the terms associated with the given object(s), in the supplied taxonomies.
+	 *
+	 * The following information has to do the $args parameter and for what can be
+	 * contained in the string or array of that parameter, if it exists.
+	 *
+	 * The first argument is called, 'orderby' and has the default value of 'name'.
+	 * The other value that is supported is 'count'.
+	 *
+	 * The second argument is called, 'order' and has the default value of 'ASC'.
+	 * The only other value that will be acceptable is 'DESC'.
+	 *
+	 * The final argument supported is called, 'fields' and has the default value of
+	 * 'all'. There are multiple other options that can be used instead. Supported
+	 * values are as follows: 'all', 'ids', 'names', and finally
+	 * 'all_with_object_id'.
+	 *
+	 * The fields argument also decides what will be returned. If 'all' or
+	 * 'all_with_object_id' is choosen or the default kept intact, then all matching
+	 * terms objects will be returned. If either 'ids' or 'names' is used, then an
+	 * array of all matching term ids or term names will be returned respectively.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param int|array $object_id The id of the object(s) to retrieve.
+	 * @param string|array $taxonomies The taxonomies to retrieve terms from.
+	 * @param array|string $args Change what is returned
+	 * @return array|WP_Error The requested term data or empty array if no terms found. WP_Error if $taxonomy does not exist.
+	 */
+	function get_object_terms($object_ids, $taxonomies, $args = array()) {
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			if ( !$this->is_taxonomy($taxonomy) )
+				return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+		}
+
+		if ( !is_array($object_ids) )
+			$object_ids = array($object_ids);
+		$object_ids = array_map('intval', $object_ids);
+
+		$defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all');
+		$args = wp_parse_args( $args, $defaults );
+
+		$terms = array();
+		if ( count($taxonomies) > 1 ) {
+			foreach ( $taxonomies as $index => $taxonomy ) {
+				$t = $this->get_taxonomy($taxonomy);
+				if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) {
+					unset($taxonomies[$index]);
+					$terms = array_merge($terms, $this->get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
+				}
+			}
+		} else {
+			$t = $this->get_taxonomy($taxonomies[0]);
+			if ( isset($t->args) && is_array($t->args) )
+				$args = array_merge($args, $t->args);
+		}
+
+		extract($args, EXTR_SKIP);
+
+		if ( 'count' == $orderby )
+			$orderby = 'tt.count';
+		else if ( 'name' == $orderby )
+			$orderby = 't.name';
+		else if ( 'slug' == $orderby )
+			$orderby = 't.slug';
+		else if ( 'term_group' == $orderby )
+			$orderby = 't.term_group';
+		else if ( 'term_order' == $orderby )
+			$orderby = 'tr.term_order';
+		else if ( 'none' == $orderby ) {
+			$orderby = '';
+			$order = '';
+		} else {
+			$orderby = 't.term_id';
+		}
+
+		// tt_ids queries can only be none or tr.term_taxonomy_id
+		if ( ('tt_ids' == $fields) && !empty($orderby) )
+			$orderby = 'tr.term_taxonomy_id';
+
+		if ( !empty($orderby) )
+			$orderby = "ORDER BY $orderby";
+
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
+		$object_ids = implode(', ', $object_ids);
+
+		$select_this = '';
+		if ( 'all' == $fields )
+			$select_this = 't.*, tt.*';
+		else if ( 'ids' == $fields )
+			$select_this = 't.term_id';
+		else if ( 'names' == $fields )
+			$select_this = 't.name';
+		else if ( 'all_with_object_id' == $fields )
+			$select_this = 't.*, tt.*, tr.object_id';
+
+		$query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON tt.term_id = t.term_id INNER JOIN {$this->db->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids) $orderby $order";
+
+		if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
+			$terms = array_merge($terms, $this->db->get_results($query));
+			$this->update_term_cache($terms);
+		} else if ( 'ids' == $fields || 'names' == $fields ) {
+			$terms = array_merge($terms, $this->db->get_col($query));
+		} else if ( 'tt_ids' == $fields ) {
+			$terms = $this->db->get_col("SELECT tr.term_taxonomy_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) $orderby $order");
+		}
+
+		if ( ! $terms )
+			$terms = array();
+
+		return apply_filters('wp_get_object_terms', $terms, $object_ids, $taxonomies, $args);
+	}
+
+	/**
+	 * Adds a new term to the database. Optionally marks it as an alias of an existing term.
+	 *
+	 * Error handling is assigned for the nonexistance of the $taxonomy and $term
+	 * parameters before inserting. If both the term id and taxonomy exist
+	 * previously, then an array will be returned that contains the term id and the
+	 * contents of what is returned. The keys of the array are 'term_id' and
+	 * 'term_taxonomy_id' containing numeric values.
+	 *
+	 * It is assumed that the term does not yet exist or the above will apply. The
+	 * term will be first added to the term table and then related to the taxonomy
+	 * if everything is well. If everything is correct, then several actions will be
+	 * run prior to a filter and then several actions will be run after the filter
+	 * is run.
+	 *
+	 * The arguments decide how the term is handled based on the $args parameter.
+	 * The following is a list of the available overrides and the defaults.
+	 *
+	 * 'alias_of'. There is no default, but if added, expected is the slug that the
+	 * term will be an alias of. Expected to be a string.
+	 *
+	 * 'description'. There is no default. If exists, will be added to the database
+	 * along with the term. Expected to be a string.
+	 *
+	 * 'parent'. Expected to be numeric and default is 0 (zero). Will assign value
+	 * of 'parent' to the term.
+	 *
+	 * 'slug'. Expected to be a string. There is no default.
+	 *
+	 * If 'slug' argument exists then the slug will be checked to see if it is not
+	 * a valid term. If that check succeeds (it is not a valid term), then it is
+	 * added and the term id is given. If it fails, then a check is made to whether
+	 * the taxonomy is hierarchical and the parent argument is not empty. If the
+	 * second check succeeds, the term will be inserted and the term id will be
+	 * given.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses do_action() Calls 'create_term' hook with the term id and taxonomy id as parameters.
+	 * @uses do_action() Calls 'create_$taxonomy' hook with term id and taxonomy id as parameters.
+	 * @uses apply_filters() Calls 'term_id_filter' hook with term id and taxonomy id as parameters.
+	 * @uses do_action() Calls 'created_term' hook with the term id and taxonomy id as parameters.
+	 * @uses do_action() Calls 'created_$taxonomy' hook with term id and taxonomy id as parameters.
+	 *
+	 * @param int|string $term The term to add or update.
+	 * @param string $taxonomy The taxonomy to which to add the term
+	 * @param array|string $args Change the values of the inserted term
+	 * @return array|WP_Error The Term ID and Term Taxonomy ID
+	 */
+	function insert_term( $term, $taxonomy, $args = array() ) {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+
+		if ( is_int($term) && 0 == $term )
+			return new WP_Error('invalid_term_id', __('Invalid term ID'));
+
+		if ( '' == trim($term) )
+			return new WP_Error('empty_term_name', __('A name is required for this term'));
+
+		$defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
+		$args = wp_parse_args($args, $defaults);
+		$args['name'] = $term;
+		$args['taxonomy'] = $taxonomy;
+		$args = $this->sanitize_term($args, $taxonomy, 'db');
+		extract($args, EXTR_SKIP);
+
+		// expected_slashed ($name)
+		$name = stripslashes($name);
+		$description = stripslashes($description);
+
+		if ( empty($slug) )
+			$slug = $this->sanitize_term_slug($name, $taxonomy);
+
+		$term_group = 0;
+		if ( $alias_of ) {
+			$alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) );
+			if ( $alias->term_group ) {
+				// The alias we want is already in a group, so let's use that one.
+				$term_group = $alias->term_group;
+			} else {
+				// The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
+				$term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1;
+				$this->db->query( $this->db->prepare( "UPDATE {$this->db->terms} SET term_group = %d WHERE term_id = %d", $term_group, $alias->term_id ) );
+			}
+		}
+
+		if ( ! $term_id = $this->is_term($slug) ) {
+			if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) )
+				return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error);
+			$term_id = (int) $this->db->insert_id;
+		} else if ( $this->is_taxonomy_hierarchical($taxonomy) && !empty($parent) ) {
+			// If the taxonomy supports hierarchy and the term has a parent, make the slug unique
+			// by incorporating parent slugs.
+			$slug = $this->unique_term_slug($slug, (object) $args);
+			if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) )
+				return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error);
+			$term_id = (int) $this->db->insert_id;
+		}
+
+		if ( empty($slug) ) {
+			$slug = $this->sanitize_term_slug($slug, $taxonomy, $term_id);
+			$this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) );
+		}
+
+		$tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) );
+
+		if ( !empty($tt_id) )
+			return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+
+		$this->db->insert( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
+		$tt_id = (int) $this->db->insert_id;
+
+		do_action("create_term", $term_id, $tt_id);
+		do_action("create_$taxonomy", $term_id, $tt_id);
+
+		$term_id = apply_filters('term_id_filter', $term_id, $tt_id);
+
+		$this->clean_term_cache($term_id, $taxonomy);
+
+		do_action("created_term", $term_id, $tt_id);
+		do_action("created_$taxonomy", $term_id, $tt_id);
+
+		return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+	}
+
+	/**
+	 * Create Term and Taxonomy Relationships.
+	 *
+	 * Relates an object (post, link etc) to a term and taxonomy type. Creates the
+	 * term and taxonomy relationship if it doesn't already exist. Creates a term if
+	 * it doesn't exist (using the slug).
+	 *
+	 * A relationship means that the term is grouped in or belongs to the taxonomy.
+	 * A term has no meaning until it is given context by defining which taxonomy it
+	 * exists under.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param int $object_id The object to relate to.
+	 * @param array|int|string $term The slug or id of the term, will replace all existing
+	 * related terms in this taxonomy.
+	 * @param array|string $taxonomy The context in which to relate the term to the object.
+	 * @param bool $append If false will delete difference of terms.
+	 * @return array|WP_Error Affected Term IDs
+	 */
+	function set_object_terms($object_id, $terms, $taxonomy, $append = false) {
+		$object_id = (int) $object_id;
+
+		if ( !$this->is_taxonomy($taxonomy) )
+			return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+
+		if ( !is_array($terms) )
+			$terms = array($terms);
+
+		if ( ! $append )
+			$old_tt_ids = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none'));
+
+		$tt_ids = array();
+		$term_ids = array();
+
+		foreach ( (array) $terms as $term ) {
+			if ( !strlen(trim($term)) )
+				continue;
+
+			if ( !$id = $this->is_term($term, $taxonomy) )
+				$id = $this->insert_term($term, $taxonomy);
+			if ( is_wp_error($id) )
+				return $id;
+			$term_ids[] = $id['term_id'];
+			$id = $id['term_taxonomy_id'];
+			$tt_ids[] = $id;
+
+			if ( $this->db->get_var( $this->db->prepare( "SELECT term_taxonomy_id FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $id ) ) )
+				continue;
+			$this->db->insert( $this->db->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $id ) );
+		}
+
+		$this->update_term_count($tt_ids, $taxonomy);
+
+		if ( ! $append ) {
+			$delete_terms = array_diff($old_tt_ids, $tt_ids);
+			if ( $delete_terms ) {
+				$in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
+				$this->db->query( $this->db->prepare("DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) );
+				$this->update_term_count($delete_terms, $taxonomy);
+			}
+		}
+
+		$t = $this->get_taxonomy($taxonomy);
+		if ( ! $append && isset($t->sort) && $t->sort ) {
+			$values = array();
+			$term_order = 0;
+			$final_tt_ids = $this->get_object_terms($object_id, $taxonomy, 'fields=tt_ids');
+			foreach ( $tt_ids as $tt_id )
+				if ( in_array($tt_id, $final_tt_ids) )
+					$values[] = $this->db->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order);
+			if ( $values )
+				$this->db->query("INSERT INTO {$this->db->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . join(',', $values) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)");
+		}
+
+		do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append);
+		return $tt_ids;
+	}
+
+	/**
+	 * Will make slug unique, if it isn't already.
+	 *
+	 * The $slug has to be unique global to every taxonomy, meaning that one
+	 * taxonomy term can't have a matching slug with another taxonomy term. Each
+	 * slug has to be globally unique for every taxonomy.
+	 *
+	 * The way this works is that if the taxonomy that the term belongs to is
+	 * heirarchical and has a parent, it will append that parent to the $slug.
+	 *
+	 * If that still doesn't return an unique slug, then it try to append a number
+	 * until it finds a number that is truely unique.
+	 *
+	 * The only purpose for $term is for appending a parent, if one exists.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param string $slug The string that will be tried for a unique slug
+	 * @param object $term The term object that the $slug will belong too
+	 * @return string Will return a true unique slug.
+	 */
+	function unique_term_slug($slug, $term) {
+		// If the taxonomy supports hierarchy and the term has a parent, make the slug unique
+		// by incorporating parent slugs.
+		if ( $this->is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) {
+			$the_parent = $term->parent;
+			while ( ! empty($the_parent) ) {
+				$parent_term = $this->get_term($the_parent, $term->taxonomy);
+				if ( is_wp_error($parent_term) || empty($parent_term) )
+					break;
+					$slug .= '-' . $parent_term->slug;
+				if ( empty($parent_term->parent) )
+					break;
+				$the_parent = $parent_term->parent;
+			}
+		}
+
+		// If we didn't get a unique slug, try appending a number to make it unique.
+		if ( !empty($args['term_id']) )
+			$query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s AND term_id != %d", $slug, $args['term_id'] );
+		else
+			$query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $slug );
+
+		if ( $this->db->get_var( $query ) ) {
+			$num = 2;
+			do {
+				$alt_slug = $slug . "-$num";
+				$num++;
+				$slug_check = $this->db->get_var( $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $alt_slug ) );
+			} while ( $slug_check );
+			$slug = $alt_slug;
+		}
+
+		return $slug;
+	}
+
+	/**
+	 * Update term based on arguments provided.
+	 *
+	 * The $args will indiscriminately override all values with the same field name.
+	 * Care must be taken to not override important information need to update or
+	 * update will fail (or perhaps create a new term, neither would be acceptable).
+	 *
+	 * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not
+	 * defined in $args already.
+	 *
+	 * 'alias_of' will create a term group, if it doesn't already exist, and update
+	 * it for the $term.
+	 *
+	 * If the 'slug' argument in $args is missing, then the 'name' in $args will be
+	 * used. It should also be noted that if you set 'slug' and it isn't unique then
+	 * a WP_Error will be passed back. If you don't pass any slug, then a unique one
+	 * will be created for you.
+	 *
+	 * For what can be overrode in $args, check the term scheme can contain and stay
+	 * away from the term keys.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses do_action() Will call both 'edit_term' and 'edit_$taxonomy' twice.
+	 * @uses apply_filters() Will call the 'term_id_filter' filter and pass the term
+	 *  id and taxonomy id.
+	 *
+	 * @param int $term_id The ID of the term
+	 * @param string $taxonomy The context in which to relate the term to the object.
+	 * @param array|string $args Overwrite term field values
+	 * @return array|WP_Error Returns Term ID and Taxonomy Term ID
+	 */
+	function update_term( $term_id, $taxonomy, $args = array() ) {
+		if ( !$this->is_taxonomy($taxonomy) )
+			return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+
+		$term_id = (int) $term_id;
+
+		// First, get all of the original args
+		$term = $this->get_term($term_id, $taxonomy, ARRAY_A);
+
+		if ( is_wp_error( $term ) )
+			return $term;
+
+		// Merge old and new args with new args overwriting old ones.
+		$args = array_merge($term, $args);
+
+		$defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
+		$args = wp_parse_args($args, $defaults);
+		$args = $this->sanitize_term($args, $taxonomy, 'db');
+		extract($args, EXTR_SKIP);
+
+		// expected_slashed ($name)
+		$name = stripslashes($name);
+		$description = stripslashes($description);
+
+		if ( '' == trim($name) )
+			return new WP_Error('empty_term_name', __('A name is required for this term'));
+
+		$empty_slug = false;
+		if ( empty($slug) ) {
+			$empty_slug = true;
+			$slug = $this->sanitize_term_slug($name, $taxonomy, $term_id);
+		}
+
+		if ( $alias_of ) {
+			$alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) );
+			if ( $alias->term_group ) {
+				// The alias we want is already in a group, so let's use that one.
+				$term_group = $alias->term_group;
+			} else {
+				// The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
+				$term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1;
+				$this->db->update( $this->db->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) );
+			}
+		}
+
+		// Check for duplicate slug
+		$id = $this->db->get_var( $this->db->prepare( "SELECT term_id FROM {$this->db->terms} WHERE slug = %s", $slug ) );
+		if ( $id && ($id != $term_id) ) {
+			// If an empty slug was passed or the parent changed, reset the slug to something unique.
+			// Otherwise, bail.
+			if ( $empty_slug || ( $parent != $term->parent) )
+				$slug = $this->unique_term_slug($slug, (object) $args);
+			else
+				return new WP_Error('duplicate_term_slug', sprintf(__('The slug &#8220;%s&#8221; is already in use by another term'), $slug));
+		}
+
+		$this->db->update($this->db->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) );
+
+		if ( empty($slug) ) {
+			$slug = $this->sanitize_term_slug($name, $taxonomy, $term_id);
+			$this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) );
+		}
+
+		$tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
+
+		$this->db->update( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) );
+
+		do_action("edit_term", $term_id, $tt_id);
+		do_action("edit_$taxonomy", $term_id, $tt_id);
+
+		$term_id = apply_filters('term_id_filter', $term_id, $tt_id);
+
+		$this->clean_term_cache($term_id, $taxonomy);
+
+		do_action("edited_term", $term_id, $tt_id);
+		do_action("edited_$taxonomy", $term_id, $tt_id);
+
+		return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+	}
+
+	/**
+	 * Enable or disable term counting.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param bool $defer Optional. Enable if true, disable if false.
+	 * @return bool Whether term counting is enabled or disabled.
+	 */
+	function defer_term_counting($defer=NULL) {
+		static $_defer = false;
+
+		if ( is_bool($defer) ) {
+			$_defer = $defer;
+			// flush any deferred counts
+			if ( !$defer )
+				$this->update_term_count( NULL, NULL, true );
+		}
+
+		return $_defer;
+	}
+
+	/**
+	 * Updates the amount of terms in taxonomy.
+	 *
+	 * If there is a taxonomy callback applied, then it will be called for updating
+	 * the count.
+	 *
+	 * The default action is to count what the amount of terms have the relationship
+	 * of term ID. Once that is done, then update the database.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 * @uses $this->db
+	 *
+	 * @param int|array $terms The ID of the terms
+	 * @param string $taxonomy The context of the term.
+	 * @return bool If no terms will return false, and if successful will return true.
+	 */
+	function update_term_count( $terms, $taxonomy, $do_deferred=false ) {
+		static $_deferred = array();
+
+		if ( $do_deferred ) {
+			foreach ( (array) array_keys($_deferred) as $tax ) {
+				$this->update_term_count_now( $_deferred[$tax], $tax );
+				unset( $_deferred[$tax] );
+			}
+		}
+
+		if ( empty($terms) )
+			return false;
+
+		if ( !is_array($terms) )
+			$terms = array($terms);
+
+		if ( $this->defer_term_counting() ) {
+			if ( !isset($_deferred[$taxonomy]) )
+				$_deferred[$taxonomy] = array();
+			$_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) );
+			return true;
+		}
+
+		return $this->update_term_count_now( $terms, $taxonomy );
+	}
+
+	/**
+	 * Perform term count update immediately.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param array $terms The term_taxonomy_id of terms to update.
+	 * @param string $taxonomy The context of the term.
+	 * @return bool Always true when complete.
+	 */
+	function update_term_count_now( $terms, $taxonomy ) {
+		$terms = array_map('intval', $terms);
+
+		$taxonomy = $this->get_taxonomy($taxonomy);
+		if ( !empty($taxonomy->update_count_callback) ) {
+			call_user_func($taxonomy->update_count_callback, $terms);
+		} else {
+			// Default count updater
+			foreach ( (array) $terms as $term ) {
+				$count = $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $term) );
+				$this->db->update( $this->db->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
+			}
+
+		}
+
+		$this->clean_term_cache($terms);
+
+		return true;
+	}
+
+	//
+	// Cache
+	//
+
+	/**
+	 * Removes the taxonomy relationship to terms from the cache.
+	 *
+	 * Will remove the entire taxonomy relationship containing term $object_id. The
+	 * term IDs have to exist within the taxonomy $object_type for the deletion to
+	 * take place.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3
+	 *
+	 * @see $this->get_object_taxonomies() for more on $object_type
+	 * @uses do_action() Will call action hook named, 'clean_object_term_cache' after completion.
+	 *	Passes, function params in same order.
+	 *
+	 * @param int|array $object_ids Single or list of term object ID(s)
+	 * @param string $object_type The taxonomy object type
+	 */
+	function clean_object_term_cache($object_ids, $object_type) {
+		if ( !is_array($object_ids) )
+			$object_ids = array($object_ids);
+
+		foreach ( $object_ids as $id )
+			foreach ( $this->get_object_taxonomies($object_type) as $taxonomy )
+				wp_cache_delete($id, "{$taxonomy}_relationships");
+
+		do_action('clean_object_term_cache', $object_ids, $object_type);
+	}
+
+	/**
+	 * Will remove all of the term ids from the cache.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param int|array $ids Single or list of Term IDs
+	 * @param string $taxonomy Can be empty and will assume tt_ids, else will use for context.
+	 */
+	function clean_term_cache($ids, $taxonomy = '') {
+		static $cleaned = array();
+
+		if ( !is_array($ids) )
+			$ids = array($ids);
+
+		$taxonomies = array();
+		// If no taxonomy, assume tt_ids.
+		if ( empty($taxonomy) ) {
+			$tt_ids = implode( ',', array_map( 'intval', $ids ) );
+			$terms = $this->db->get_results("SELECT term_id, term_taxonomy_id, taxonomy FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id IN ($tt_ids)");
+			foreach ( (array) $terms as $term ) {
+				$taxonomies[] = $term->taxonomy;
+				wp_cache_delete($term->term_id, $term->taxonomy);
+				wp_cache_delete($term->term_taxonomy_id, "{$term->taxonomy}:tt_id");
+			}
+			$taxonomies = array_unique($taxonomies);
+		} else {
+			$tt_ids = implode( ',', array_map( 'intval', $ids ) );
+			$terms = $this->db->get_results("SELECT term_id, term_taxonomy_id FROM {$this->db->term_taxonomy} WHERE term_id IN ($tt_ids)");
+			foreach ( (array) $terms as $term ) {
+				wp_cache_delete($term->term_id, $taxonomy);
+				wp_cache_delete($term->term_taxonomy_id, "$taxonomy:tt_id");
+			}
+			$taxonomies = array($taxonomy);
+		}
+
+		foreach ( $taxonomies as $taxonomy ) {
+			if ( isset($cleaned[$taxonomy]) )
+				continue;
+			$cleaned[$taxonomy] = true;
+			wp_cache_delete('all_ids', $taxonomy);
+			wp_cache_delete('get', $taxonomy);
+			$this->delete_children_cache($taxonomy);
+		}
+
+		wp_cache_delete('get_terms', 'terms');
+
+		do_action('clean_term_cache', $ids, $taxonomy);
+	}
+
+	/**
+	 * Retrieves the taxonomy relationship to the term object id.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @uses wp_cache_get() Retrieves taxonomy relationship from cache
+	 *
+	 * @param int|array $id Term object ID
+	 * @param string $taxonomy Taxonomy Name
+	 * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id.
+	 */
+	function &get_object_term_cache($id, $taxonomy) {
+		$cache = wp_cache_get($id, "{$taxonomy}_relationships");
+		return $cache;
+	}
+
+	/**
+	 * Updates the cache for Term ID(s).
+	 *
+	 * Will only update the cache for terms not already cached.
+	 *
+	 * The $object_ids expects that the ids be separated by commas, if it is a
+	 * string.
+	 *
+	 * It should be noted that update_object_term_cache() is very time extensive. It
+	 * is advised that the function is not called very often or at least not for a
+	 * lot of terms that exist in a lot of taxonomies. The amount of time increases
+	 * for each term and it also increases for each taxonomy the term belongs to.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 * @uses $this->get_object_terms() Used to get terms from the database to update
+	 *
+	 * @param string|array $object_ids Single or list of term object ID(s)
+	 * @param string $object_type The taxonomy object type
+	 * @return null|bool Null value is given with empty $object_ids. False if 
+	 */
+	function update_object_term_cache($object_ids, $object_type) {
+		if ( empty($object_ids) )
+			return;
+
+		if ( !is_array($object_ids) )
+			$object_ids = explode(',', $object_ids);
+
+		$object_ids = array_map('intval', $object_ids);
+
+		$taxonomies = $this->get_object_taxonomies($object_type);
+
+		$ids = array();
+		foreach ( (array) $object_ids as $id ) {
+			foreach ( $taxonomies as $taxonomy ) {
+				if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) {
+					$ids[] = $id;
+					break;
+				}
+			}
+		}
+
+		if ( empty( $ids ) )
+			return false;
+
+		$terms = $this->get_object_terms($ids, $taxonomies, 'fields=all_with_object_id');
+
+		$object_terms = array();
+		foreach ( (array) $terms as $term )
+			$object_terms[$term->object_id][$term->taxonomy][$term->term_id] = $term;
+
+		foreach ( $ids as $id ) {
+			foreach ( $taxonomies  as $taxonomy ) {
+				if ( ! isset($object_terms[$id][$taxonomy]) ) {
+					if ( !isset($object_terms[$id]) )
+						$object_terms[$id] = array();
+					$object_terms[$id][$taxonomy] = array();
+				}
+			}
+		}
+
+		foreach ( $object_terms as $id => $value ) {
+			foreach ( $value as $taxonomy => $terms ) {
+				wp_cache_set($id, $terms, "{$taxonomy}_relationships");
+			}
+		}
+	}
+
+	/**
+	 * Updates Terms to Taxonomy in cache.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @since 2.3.0
+	 *
+	 * @param array $terms List of Term objects to change
+	 * @param string $taxonomy Optional. Update Term to this taxonomy in cache
+	 */
+	function update_term_cache($terms, $taxonomy = '') {
+		foreach ( (array) $terms as $term ) {
+			$term_taxonomy = $taxonomy;
+			if ( empty($term_taxonomy) )
+				$term_taxonomy = $term->taxonomy;
+
+			wp_cache_add($term->term_id, $term, $term_taxonomy);
+			wp_cache_add($term->term_taxonomy_id, $term->term_id, "$term_taxonomy:tt_id");
+		}
+	}
+
+	//
+	// Private
+	//
+
+	/**
+	 * Retrieves children of taxonomy as Term IDs.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @access private
+	 * @since 2.3.0
+	 *
+	 * @uses backpress_update_option() Stores all of the children in "$taxonomy_children"
+	 *  option. That is the name of the taxonomy, immediately followed by '_children'.
+	 *
+	 * @param string $taxonomy Taxonomy Name
+	 * @return array Empty if $taxonomy isn't hierarachical or returns children as Term IDs.
+	 */
+	function _get_term_hierarchy($taxonomy) {
+		if ( !$this->is_taxonomy_hierarchical($taxonomy) )
+			return array();
+		$children = $this->get_children_cache($taxonomy);
+		if ( is_array($children) )
+			return $children;
+
+		$children = array();
+		$terms = $this->get_terms($taxonomy, 'get=all');
+		foreach ( $terms as $term ) {
+			if ( $term->parent > 0 )
+				$children[$term->parent][] = $term->term_id;
+		}
+		$this->set_children_cache($taxonomy, $children);
+
+		return $children;
+	}
+
+	/**
+	 * Get the subset of $terms that are descendants of $term_id.
+	 *
+	 * If $terms is an array of objects, then _get_term_children returns an array of objects.
+	 * If $terms is an array of IDs, then _get_term_children returns an array of IDs.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @access private
+	 * @since 2.3.0
+	 *
+	 * @param int $term_id The ancestor term: all returned terms should be descendants of $term_id.
+	 * @param array $terms The set of terms---either an array of term objects or term IDs---from which those that are descendants of $term_id will be chosen.
+	 * @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
+	 * @return array The subset of $terms that are descendants of $term_id.
+	 */
+	function &_get_term_children($term_id, $terms, $taxonomy) {
+		$empty_array = array();
+		if ( empty($terms) )
+			return $empty_array;
+
+		$term_list = array();
+		$has_children = $this->_get_term_hierarchy($taxonomy);
+
+		if  ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
+			return $empty_array;
+
+		foreach ( (array) $terms as $term ) {
+			$use_id = false;
+			if ( !is_object($term) ) {
+				$term = $this->get_term($term, $taxonomy);
+				if ( is_wp_error( $term ) )
+					return $term;
+				$use_id = true;
+			}
+
+			if ( $term->term_id == $term_id )
+				continue;
+
+			if ( $term->parent == $term_id ) {
+				if ( $use_id )
+					$term_list[] = $term->term_id;
+				else
+					$term_list[] = $term;
+
+				if ( !isset($has_children[$term->term_id]) )
+					continue;
+
+				if ( $children = $this->_get_term_children($term->term_id, $terms, $taxonomy) )
+					$term_list = array_merge($term_list, $children);
+			}
+		}
+
+		return $term_list;
+	}
+
+	/**
+	 * Add count of children to parent count.
+	 *
+	 * Recalculates term counts by including items from child terms. Assumes all
+	 * relevant children are already in the $terms argument.
+	 *
+	 * @package WordPress
+	 * @subpackage Taxonomy
+	 * @access private
+	 * @since 2.3
+	 *
+	 * @param array $terms List of Term IDs
+	 * @param string $taxonomy Term Context
+	 * @return null Will break from function if conditions are not met.
+	 */
+	function _pad_term_counts(&$terms, $taxonomy) {
+		return;
+	}
+
+	/**
+	 * Determine if the given object is associated with any of the given terms.
+	 *
+	 * The given terms are checked against the object's terms' term_ids, names and slugs.
+	 * Terms given as integers will only be checked against the object's terms' term_ids.
+	 * If no terms are given, determines if object is associated with any terms in the given taxonomy.
+	 *
+	 * @since 2.7.0
+	 * @uses WP_Taxonomy::get_object_term_cache()
+	 * @uses WP_Taxonomy::get_object_terms()
+	 *
+	 * @param int $object_id.  ID of the object (post ID, link ID, ...)
+	 * @param string $taxonomy.  Single taxonomy name
+	 * @param int|string|array $terms Optional.  Term term_id, name, slug or array of said
+	 * @return bool|WP_Error. WP_Error on input error.
+	 */
+	function is_object_in_term( $object_id, $taxonomy, $terms = null ) {
+		if ( !$object_id = (int) $object_id )
+			return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) );
+
+		$object_terms = $this->get_object_term_cache( $object_id, $taxonomy );
+		if ( empty( $object_terms ) )
+			 $object_terms = $this->get_object_terms( $object_id, $taxonomy );
+
+		if ( is_wp_error( $object_terms ) )
+			return $object_terms;
+		if ( empty( $object_terms ) )
+			return false;
+		if ( empty( $terms ) )
+			return ( !empty( $object_terms ) );
+
+		$terms = (array) $terms;
+
+		if ( $ints = array_filter( $terms, 'is_int' ) )
+			$strs = array_diff( $terms, $ints );
+		else
+			$strs =& $terms;
+
+		foreach ( $object_terms as $object_term ) {
+			if ( $ints && in_array( $object_term->term_id, $ints ) ) return true; // If int, check against term_id
+			if ( $strs ) {
+				if ( in_array( $object_term->term_id, $strs ) ) return true;
+				if ( in_array( $object_term->name, $strs ) )    return true;
+				if ( in_array( $object_term->slug, $strs ) )    return true;
+			}
+		}
+
+		return false;
+	}
+
+	function get_children_cache( $taxonomy ) { return false; }
+	function set_children_cache( $taxonomy, $children ) {}
+	function delete_children_cache( $taxonomy ) {}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-users.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-users.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/class.wp-users.php	(revision 5057)
@@ -0,0 +1,579 @@
+<?php
+
+class WP_Users {
+	var $db;
+
+	function WP_Users( &$db ) {
+		$this->__construct( $db );
+//		register_shutdown_function( array(&$this, '__destruct') );
+	}
+
+	function __construct( &$db ) {
+		$this->db =& $db;
+	}
+
+//	function __destruct() {
+//	}
+
+	function _put_user( $args = null ) {
+		$defaults = array(
+			'ID' => false,
+			'user_login' => '',
+			'user_nicename' => '',
+			'user_email' => '',
+			'user_url' => '',
+			'user_pass' => false,
+			'user_registered' => time(),
+			'display_name' => '',
+			'user_status' => 0,
+			'strict_user_login' => true
+		);
+
+		$fields = array_keys( wp_parse_args( $args ) );
+		$args = wp_parse_args( $args, $defaults );
+		unset($defaults['strict_user_login']);
+
+		if ( isset($args['ID']) && $args['ID'] ) {
+			unset($defaults['ID']);
+			$fields = array_intersect( $fields, array_keys( $defaults ) );
+		} else {
+			$fields = array_keys( $defaults );
+		}
+
+		extract( $args, EXTR_SKIP );
+
+		$ID = (int) $ID;
+
+		if ( !$ID || in_array( 'user_login', $fields ) ) {
+			$user_login = $this->sanitize_user( $user_login, $strict_user_login );
+
+			if ( !$user_login )
+				return new WP_Error( 'user_login', __('Invalid login name') );
+			if ( !$ID && $this->get_user( $user_login, array( 'by' => 'login' ) ) )
+				return new WP_Error( 'user_login', __('Name already exists') );
+		}
+
+		if ( !$ID || in_array( 'user_nicename', $fields ) ) {
+			if ( !$user_nicename = $this->sanitize_nicename( $user_nicename ? $user_nicename : $user_login ) )
+				return new WP_Error( 'user_nicename', __('Invalid nicename') );
+			if ( !$ID && $this->get_user( $user_nicename, array( 'by' => 'nicename' ) ) )
+				return new WP_Error( 'user_nicename', __('Nicename already exists') );
+		}
+
+		if ( !$ID || in_array( 'user_email', $fields ) ) {
+			if ( !$this->is_email( $user_email ) )
+				return new WP_Error( 'user_email', __('Invalid email address') );
+
+			if ( $already_email = $this->get_user( $user_email, array( 'by' => 'email' ) ) ) {
+				// if new user, or if multiple users with that email, or if only one user with that email, but it's not the user being updated
+				if ( !$ID || is_wp_error( $already_email ) || $already_email->ID != $ID )
+					return new WP_Error( 'user_email', __('Email already exists') );
+			}
+		}
+
+		if ( !$ID || in_array( 'user_url', $fields ) ) {
+			$user_url = esc_url( $user_url );
+		}
+
+		if ( !$ID || in_array( 'user_pass', $fields ) ) {
+			if ( !$user_pass )
+				$user_pass = WP_Pass::generate_password();
+			$plain_pass = $user_pass;
+			$user_pass  = WP_Pass::hash_password( $user_pass );
+		}
+
+		if ( !$ID || in_array( 'user_registered', $fields ) ) {
+			if ( !is_numeric($user_registered) )
+				$user_registered = backpress_gmt_strtotime( $user_registered );
+
+			if ( !$user_registered || $user_registered < 0 )
+				return new WP_Error( 'user_registered', __('Invalid registration time') );
+
+			if ( !$user_registered = @gmdate('Y-m-d H:i:s', $user_registered) )
+				return new WP_Error( 'user_registered', __('Invalid registration timestamp') );
+		}
+
+		if ( !$ID || in_array( 'user_display', $fields ) ) {
+			if ( !$display_name )
+				$display_name = $user_login;
+		}
+
+		$db_return = NULL;
+		if ( $ID ) {
+			$db_return = $this->db->update( $this->db->users, compact( $fields ), compact('ID') );
+		} else {
+			$db_return = $this->db->insert( $this->db->users, compact( $fields ) );
+			$ID = $this->db->insert_id;
+		}
+
+		if ( !$db_return )
+			return new WP_Error( 'WP_Users::_put_user', __('Query failed') );
+
+		// Cache the result
+		if ( $ID ) {
+			wp_cache_delete( $ID, 'users' );
+			$this->get_user( $ID, array( 'from_cache' => false ) );
+		}
+
+		$args = compact( array_keys($args) );
+		$args['plain_pass'] = $plain_pass;
+
+		do_action( __CLASS__ . '::' . __FUNCTION__, $args );
+
+		return $args;
+	}
+
+	function new_user( $args = null ) {
+		$args = wp_parse_args( $args );
+		$args['ID'] = false;
+
+		$r = $this->_put_user( $args );
+
+		if ( is_wp_error($r) )
+			return $r;
+
+		do_action( __CLASS__ . '::' . __FUNCTION__, $r, $args );
+
+		return $r;
+	}
+
+	function update_user( $ID, $args = null ) {
+		$args = wp_parse_args( $args );
+
+		$args['output'] = OBJECT;
+		$user = $this->get_user( $ID, $args );
+		if ( !$user || is_wp_error( $user ) )
+			return $user;
+
+		$args['ID'] = $user->ID;
+
+		$r = $this->_put_user( $args );
+
+		if ( is_wp_error($r) )
+			return $r;
+
+		do_action( __CLASS__ . '::' . __FUNCTION__, $r, $args );
+
+		return $r;
+	}
+
+	/**
+	 * set_password() - Updates the user's password with a new encrypted one
+	 *
+	 * For integration with other applications, this function can be
+	 * overwritten to instead use the other package password checking
+	 * algorithm.
+	 *
+	 * @since 2.5
+	 * @uses WP_Pass::hash_password() Used to encrypt the user's password before passing to the database
+	 *
+	 * @param string $password The plaintext new user password
+	 * @param int $user_id User ID
+	 */
+	function set_password( $password, $user_id ) {
+		$user = $this->get_user( $user_id );
+		if ( !$user || is_wp_error( $user ) )
+			return $user;
+
+		$user_id = $user->ID;
+		$hash = WP_Pass::hash_password($password);
+		$this->update_user( $user->ID, array( 'user_pass' => $password ) );
+	}
+
+	// $user_id can be user ID#, user_login, user_email (by specifying by = email)
+	function get_user( $user_id = 0, $args = null ) {
+		$defaults = array( 'output' => OBJECT, 'by' => false, 'from_cache' => true, 'append_meta' => true );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		if ( !$user_id ) {
+			return false;
+		}
+
+		// Let's just deal with arrays
+		$user_ids = (array) $user_id;
+
+		if ( !count( $user_ids ) ) {
+			return false;
+		}
+
+		// Validate passed ids
+		$safe_user_ids = array();
+		foreach ( $user_ids as $_user_id ) {
+			switch ( $by ) {
+				case 'login':
+					$safe_user_ids[] = $this->sanitize_user( $_user_id, true );
+					break;
+				case 'email':
+					if ( $this->is_email( $_user_id ) ) {
+						$safe_user_ids[] = $_user_id;
+					}
+					break;
+				case 'nicename':
+					$safe_user_ids[] = $this->sanitize_nicename( $_user_id );
+					break;
+				default:
+					if ( is_numeric( $_user_id ) ) {
+						$safe_user_ids[] = (int) $_user_id;
+					} else { // If one $_user_id is non-numerical, treat all $user_ids as user_logins
+						$safe_user_ids[] = $this->sanitize_user( $_user_id, true );
+						$by = 'login';
+					}
+					break;
+			}
+		}
+
+		// No soup for you!
+		if ( !count( $safe_user_ids ) ) {
+			return false;
+		}
+
+		// Name the cache storing non-existant ids and the SQL field to query by
+		switch ( $by ) {
+			case 'login':
+				$non_existant_cache = 'userlogins';
+				$sql_field = 'user_login';
+				break;
+			case 'email':
+				$non_existant_cache = 'useremail';
+				$sql_field = 'user_email';
+				break;
+			case 'nicename':
+				$non_existant_cache = 'usernicename';
+				$sql_field = 'user_nicename';
+				break;
+			default:
+				$non_existant_cache = 'users';
+				$sql_field = 'ID';
+				break;
+		}
+		
+		// Check if the numeric user IDs exist from caches
+		$cached_users = array();
+		if ( $from_cache ) {
+			$existant_user_ids = array();
+			$maybe_existant_user_ids = array();
+			
+			switch ( $by ) {
+				case 'login':
+				case 'email':
+				case 'nicename':
+					foreach ( $safe_user_ids as $_safe_user_id ) {
+						$ID = wp_cache_get( $_safe_user_id, $non_existant_cache );
+						if ( false === $ID ) {
+							$maybe_existant_user_ids[] = $_safe_user_id;
+						} elseif ( 0 !== $ID ) {
+							$existant_user_ids[] = $ID;
+						}
+					}
+					if ( count( $existant_user_ids ) ) {
+						// We need to run again using numeric ids
+						$args['by'] = false;
+						$cached_users = $this->get_user( $existant_user_ids, $args );
+					}
+					break;
+				default:
+					foreach ( $safe_user_ids as $_safe_user_id ) {
+						$user = wp_cache_get( $_safe_user_id, 'users' );
+						if ( false === $user ) {
+							$maybe_existant_user_ids[] = $_safe_user_id;
+						} elseif ( 0 !== $user ) {
+							$cached_users[] = $user;
+						}
+					}
+					break;
+			}
+
+			// No maybes? Then it's definite.
+			if ( !count( $maybe_existant_user_ids ) ) {
+				if ( !count( $cached_users ) ) {
+					// Nothing there sorry
+					return false;
+				}
+
+				// Deal with the case where one record was requested but multiple records are returned
+				if ( !is_array( $user_id ) && $user_id ) {
+					if ( 1 < count( $cached_users ) ) {
+						if ( 'user_email' == $sql_field ) {
+							$err = __( 'Multiple email matches.  Log in with your username.' );
+						} else {
+							$err = sprintf( __( 'Multiple %s matches' ), $sql_field );
+						}
+						return new WP_Error( $sql_field, $err, $args + array( 'user_id' => $user_id, 'unique' => false ) );
+					}
+
+					// If one item was requested, it expects a single user object back
+					$cached_users = array_shift( $cached_users );
+				}
+
+				backpress_convert_object( $cached_users, $output );
+				return $cached_users;
+			}
+
+			// If we get this far, there are some maybes so try and grab them
+		} else {
+			$maybe_existant_user_ids = $safe_user_ids;
+		}
+
+		// Escape the ids for the SQL query
+		$maybe_existant_user_ids = $this->db->escape_deep( $maybe_existant_user_ids );
+
+		// Sort the ids so the MySQL will more consistently cache the query
+		sort( $maybe_existant_user_ids );
+
+		// Get the users from the database
+		$sql = "SELECT * FROM `{$this->db->users}` WHERE `$sql_field` in ('" . join( "','", $maybe_existant_user_ids ) . "');";
+		$db_users = $this->db->get_results( $sql );
+
+		// Merge in the cached users if available
+		if ( count( $cached_users ) ) {
+			// Create a convenient array of database fetched user ids
+			$db_user_ids = array();
+			foreach ( $db_users as $_db_user ) {
+				$db_user_ids[] = $_db_user->ID;
+			}
+			$users = array_merge( $cached_users, $db_users );
+		} else {
+			$users = $db_users;
+		}
+
+		// Deal with the case where one record was requested but multiple records are returned
+		if ( !is_array( $user_id ) && $user_id ) {
+			if ( 1 < count( $users ) ) {
+				if ( 'user_email' == $sql_field ) {
+					$err = __( 'Multiple email matches.  Log in with your username.' );
+				} else {
+					$err = sprintf( __( 'Multiple %s matches' ), $sql_field );
+				}
+				return new WP_Error( $sql_field, $err, $args + array( 'user_id' => $user_id, 'unique' => false ) );
+			}
+		}
+
+		// Create a convenient array of final user ids
+		$final_user_ids = array();
+		foreach ( $users as $_user ) {
+			$final_user_ids[] = $_user->$sql_field;
+		}
+
+		foreach ( $safe_user_ids as $_safe_user_id ) {
+			if ( !in_array( $_safe_user_id, $final_user_ids ) ) {
+				wp_cache_add( $_safe_user_id, 0, $non_existant_cache );
+			}
+		}
+
+		if ( !count( $users ) ) {
+			return false;
+		}
+
+		// Add display names
+		$final_users = array();
+		foreach ( $users as $_user ) {
+			// Make sure there is a display_name set
+			if ( !$_user->display_name ) {
+				$_user->display_name = $_user->user_login;
+			}
+
+			$final_users[] = $_user;
+		}
+
+		// append_meta() does the user object, useremail, userlogins caching
+		if ( $append_meta ) {
+			if ( count( $cached_users ) ) {
+				$db_final_users =array();
+				$cached_final_users = array();
+				foreach ( $final_users as $final_user ) {
+					if ( in_array( $final_user->ID, $db_user_ids ) ) {
+						$db_final_users[] = $final_user;
+					} else {
+						$cached_final_users[] = $final_user;
+					}
+				}
+				$db_final_users = $this->append_meta( $db_final_users );
+				$final_users = array_merge( $cached_final_users, $db_final_users );
+			} else {
+				$final_users = $this->append_meta( $final_users );
+			}
+		}
+
+		// If one item was requested, it expects a single user object back
+		if ( !is_array( $user_id ) && $user_id ) {
+			$final_users = array_shift( $final_users );
+		}
+
+		backpress_convert_object( $final_users, $output );
+		return $final_users;
+	}
+
+	function delete_user( $user_id ) {
+		$user = $this->get_user( $user_id );
+
+		if ( !$user || is_wp_error( $user ) )
+			return $user;
+
+		do_action( 'pre_' . __CLASS__ . '::' . __FUNCTION__, $user->ID );
+
+		$r = $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->users} WHERE ID = %d", $user->ID ) );
+		$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->usermeta} WHERE user_id = %d", $user->ID ) );
+
+		wp_cache_delete( $user->ID, 'users' );
+		wp_cache_delete( $user->user_nicename, 'usernicename' );
+		wp_cache_delete( $user->user_email, 'useremail' );
+		wp_cache_delete( $user->user_login, 'userlogins' );
+
+		do_action( __CLASS__ . '::' . __FUNCTION__, $user->ID );
+
+		return $r;
+	}
+
+	// Used for user meta, but can be used for other meta data (such as bbPress' topic meta)
+	// Should this be in the class or should it be it's own special function?
+	function append_meta( $object, $args = null ) {
+		$defaults = array( 'meta_table' => 'usermeta', 'meta_field' => 'user_id', 'id_field' => 'ID', 'cache_group' => 'users' );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		if ( is_array($object) ) {
+			$trans = array();
+			foreach ( array_keys($object) as $i )
+				$trans[$object[$i]->$id_field] =& $object[$i];
+			$ids = join(',', array_keys($trans));
+			if ( $ids && $metas = $this->db->get_results("SELECT $meta_field, meta_key, meta_value FROM {$this->db->$meta_table} WHERE $meta_field IN ($ids) /* WP_Users::append_meta */") ) {
+				usort( $metas, array(&$this, '_append_meta_sort') );
+				foreach ( $metas as $meta ) {
+					if ( empty( $meta->meta_key ) )
+						continue;
+					$trans[$meta->$meta_field]->{$meta->meta_key} = maybe_unserialize( $meta->meta_value );
+					if ( strpos($meta->meta_key, $this->db->prefix) === 0 )
+						$trans[$meta->$meta_field]->{substr($meta->meta_key, strlen($this->db->prefix))} = maybe_unserialize( $meta->meta_value );
+				}
+			}
+			foreach ( array_keys($trans) as $i ) {
+				wp_cache_set( $i, $trans[$i], $cache_group );
+				if ( 'users' == $cache_group ) {
+					wp_cache_set( $trans[$i]->user_login, $i, 'userlogins' );
+					wp_cache_set( $trans[$i]->user_email, $i, 'useremail' );
+					wp_cache_set( $trans[$i]->user_nicename, $i, 'usernicename' );
+				}
+			}
+			return $object;
+		} elseif ( $object ) {
+			if ( $metas = $this->db->get_results("SELECT meta_key, meta_value FROM {$this->db->$meta_table} WHERE $meta_field = '{$object->$id_field}' /* WP_Users::append_meta */") ) {
+				usort( $metas, array(&$this, '_append_meta_sort') );
+				foreach ( $metas as $meta ) {
+					if ( empty( $meta->meta_key ) )
+						continue;
+					$object->{$meta->meta_key} = maybe_unserialize( $meta->meta_value );
+					if ( strpos($meta->meta_key, $this->db->prefix) === 0 )
+						$object->{substr($meta->meta_key, strlen($this->db->prefix))} = maybe_unserialize( $meta->meta_value );
+				}
+			}
+			wp_cache_set( $object->$id_field, $object, $cache_group );
+			if ( 'users' == $cache_group ) {
+				wp_cache_set($object->user_login, $object->ID, 'userlogins');
+				wp_cache_set($object->user_email, $object->ID, 'useremail');
+				wp_cache_set($object->user_nicename, $object->ID, 'usernicename');
+			}
+			return $object;
+		}
+	}
+	
+	/** 
+	 * _append_meta_sort() - sorts meta keys by length to ensure $appended_object->{$bbdb->prefix}key overwrites $appended_object->key as desired
+	 *
+	 * @internal
+	 */
+	function _append_meta_sort( $a, $b ) {
+		return strlen( $a->meta_key ) - strlen( $b->meta_key );
+	}
+
+	function update_meta( $args = null ) {
+		$defaults = array( 'id' => 0, 'meta_key' => null, 'meta_value' => null, 'meta_table' => 'usermeta', 'meta_field' => 'user_id', 'cache_group' => 'users' );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		$user = $this->get_user( $id );
+		if ( !$user || is_wp_error($user) )
+			return $user;
+
+		$id = (int) $user->ID;
+
+		if ( is_null($meta_key) || is_null($meta_value) )
+			return false;
+
+		$meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key);
+		if ( 'usermeta' == $meta_table && 'capabilities' == $meta_key )
+			$meta_key = $this->db->prefix . 'capabilities';
+
+		$meta_tuple = compact('id', 'meta_key', 'meta_value', 'meta_table');
+		$meta_tuple = apply_filters( __CLASS__ . '::' . __FUNCTION__, $meta_tuple );
+		extract($meta_tuple, EXTR_OVERWRITE);
+
+		$_meta_value = maybe_serialize( $meta_value );
+
+		$cur = $this->db->get_row( $this->db->prepare( "SELECT * FROM {$this->db->$meta_table} WHERE $meta_field = %d AND meta_key = %s", $id, $meta_key ) );
+
+		if ( !$cur ) {
+			$this->db->insert( $this->db->$meta_table, array( $meta_field => $id, 'meta_key' => $meta_key, 'meta_value' => $_meta_value ) );
+		} elseif ( $cur->meta_value != $meta_value ) {
+			$this->db->update( $this->db->$meta_table, array( 'meta_value' => $_meta_value ), array( $meta_field => $id, 'meta_key' => $meta_key ) );
+		}
+
+
+		wp_cache_delete( $id, $cache_group );
+
+		return true;
+	}
+
+	function delete_meta( $args = null ) {
+		$defaults = array( 'id' => 0, 'meta_key' => null, 'meta_value' => null, 'meta_table' => 'usermeta', 'meta_field' => 'user_id', 'meta_id_field' => 'umeta_id', 'cache_group' => 'users' );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		$user = $this->get_user( $id );
+		if ( !$user || is_wp_error($user) )
+			return $user;
+
+		$id = (int) $id;
+
+		if ( is_null($meta_key) )
+			return false;
+
+		$meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key);
+
+		$meta_tuple = compact('id', 'meta_key', 'meta_value', 'meta_table');
+		$meta_tuple = apply_filters( __CLASS__ . '::' . __FUNCTION__, $meta_tuple );
+		extract($meta_tuple, EXTR_OVERWRITE);
+
+		$_meta_value = is_null($meta_value) ? null : maybe_serialize( $meta_value );
+
+		if ( is_null($_meta_value) )
+			$meta_id = $this->db->get_var( $this->db->prepare( "SELECT $meta_id_field FROM {$this->db->$meta_table} WHERE $meta_field = %d AND meta_key = %s", $id, $meta_key ) );
+		else
+			$meta_id = $this->db->get_var( $this->db->prepare( "SELECT $meta_id_field FROM {$this->db->$meta_table} WHERE $meta_field = %d AND meta_key = %s AND meta_value = %s", $id, $meta_key, $_meta_value ) );
+
+		if ( !$meta_id )
+			return false;
+
+		if ( is_null($_meta_value) )
+			$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->$meta_table} WHERE $meta_field = %d AND meta_key = %s", $id, $meta_key ) );
+		else
+			$this->db->query( $this->db->prepare( "DELETE FROM {$this->db->$meta_table} WHERE $meta_id_field = %d", $meta_id ) );
+
+		wp_cache_delete( $id, $cache_group );
+
+		return true;
+	}
+
+	function sanitize_user( $user_login, $strict = false ) {
+		return sanitize_user( $user_login, $strict );
+	}
+
+	function sanitize_nicename( $slug ) {
+		return sanitize_title( $slug );
+	}
+
+	function is_email( $email ) {
+		return is_email( $email );
+	}
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.bp-options.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.bp-options.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.bp-options.php	(revision 5057)
@@ -0,0 +1,64 @@
+<?php
+
+function backpress_get_option( $option )
+{
+	if ( !class_exists('BP_Options') ) {
+		return;
+	}
+
+	return BP_Options::get( $option );
+}
+
+function backpress_add_option( $option, $value )
+{
+	if ( !class_exists('BP_Options') ) {
+		return;
+	}
+
+	return BP_Options::add( $option, $value );
+}
+
+function backpress_update_option( $option, $value )
+{
+	if ( !class_exists('BP_Options') ) {
+		return;
+	}
+
+	return BP_Options::update( $option, $value );
+}
+
+function backpress_delete_option( $option )
+{
+	if ( !class_exists('BP_Options') ) {
+		return;
+	}
+
+	return BP_Options::delete( $option );
+}
+
+function backpress_get_transient( $transient )
+{
+	if ( !class_exists('BP_Transients') ) {
+		return;
+	}
+
+	return BP_Transients::get( $transient );
+}
+
+function backpress_set_transient( $transient, $value, $expiration = 0 )
+{
+	if ( !class_exists('BP_Transients') ) {
+		return;
+	}
+
+	return BP_Transients::set( $transient, $value, $expiration );
+}
+
+function backpress_delete_transient( $transient )
+{
+	if ( !class_exists('BP_Transients') ) {
+		return;
+	}
+
+	return BP_Transients::delete( $transient );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.compat.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.compat.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.compat.php	(revision 5057)
@@ -0,0 +1,140 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * BackPress implementation for PHP functions missing from older PHP versions.
+ *
+ * @package PHP
+ * @access private
+ */
+
+if ( !function_exists( 'http_build_query' ) ) {
+	// Added in PHP 5.0.0
+	function http_build_query( $data, $prefix = null, $sep = null )
+	{
+		return _http_build_query( $data, $prefix, $sep );
+	}
+}
+
+if ( !function_exists( '_http_build_query' ) ) {
+	// from php.net (modified by Mark Jaquith to behave like the native PHP5 function)
+	function _http_build_query($data, $prefix = null, $sep = null, $key = '', $urlencode = true)
+	{
+		$ret = array();
+
+		foreach ( (array) $data as $k => $v ) {
+			if ( $urlencode) {
+				$k = urlencode( $k );
+			}
+			if ( is_int( $k ) && $prefix != null ) {
+				$k = $prefix.$k;
+			}
+			if ( !empty( $key ) ) {
+				$k = $key . '%5B' . $k . '%5D';
+			}
+			if ( $v === NULL ) {
+				continue;
+			} elseif ( $v === FALSE ) {
+				$v = '0';
+			}
+
+			if ( is_array( $v ) || is_object( $v ) ) {
+				array_push( $ret, _http_build_query( $v, '', $sep, $k, $urlencode ) );
+			} elseif ( $urlencode ) {
+				array_push( $ret, $k . '=' . urlencode( $v ) );
+			} else {
+				array_push( $ret, $k . '=' . $v );
+			}
+		}
+
+		if ( NULL === $sep ) {
+			$sep = ini_get( 'arg_separator.output' );
+		}
+
+		return implode( $sep, $ret );
+	}
+}
+
+if ( !function_exists( '_' ) ) {
+	// Alias of gettext() - requires l10n functions
+	function _( $string )
+	{
+		return $string;
+	}
+}
+
+if ( !function_exists( 'stripos' ) ) {
+	// Added in PHP 5.0.0
+	function stripos( $haystack, $needle, $offset = 0 )
+	{
+		return strpos( strtolower( $haystack ), strtolower( $needle ), $offset );
+	}
+}
+
+if ( !function_exists( 'hash_hmac' ) ) {
+	// Added in PHP 5.1.2
+	function hash_hmac( $algo, $data, $key, $raw_output = false )
+	{
+		$packs = array( 'md5' => 'H32', 'sha1' => 'H40' );
+
+		if ( !isset( $packs[$algo] ) ) {
+			return false;
+		}
+
+		$pack = $packs[$algo];
+
+		if ( strlen( $key ) > 64 ) {
+			$key = pack( $pack, $algo( $key ) );
+		} elseif ( strlen($key) < 64 ) {
+			$key = str_pad( $key, 64, chr( 0 ) );
+		}
+
+		$ipad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x36 ), 64 ) );
+		$opad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x5C ), 64 ) );
+
+		return $algo( $opad . pack( $pack, $algo ( $ipad . $data ) ) );
+	}
+}
+
+if ( !function_exists( 'mb_substr' ) ) {
+	// Requires multi-byte support in PHP
+	function mb_substr( $str, $start, $length = null, $encoding = null )
+	{
+		return _mb_substr( $str, $start, $length, $encoding );
+	}
+}
+
+if ( !function_exists( '_mb_substr' ) ) {
+	function _mb_substr( $str, $start, $length = null, $encoding = null )
+	{
+		// the solution below, works only for utf-8, so in case of a different
+		// charset, just use built-in substr
+		$charset = backpress_get_option( 'charset' );
+		if ( !in_array( $charset, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
+			return is_null( $length )? substr( $str, $start ) : substr( $str, $start, $length);
+		}
+		// use the regex unicode support to separate the UTF-8 characters into an array
+		preg_match_all( '/./us', $str, $match );
+		$chars = is_null( $length )? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
+		return implode( '', $chars );
+	}
+}
+
+if ( !function_exists( 'htmlspecialchars_decode' ) ) {
+	// Added in PHP 5.1.0
+	// Error checks from PEAR::PHP_Compat
+	function htmlspecialchars_decode( $str, $quote_style = ENT_COMPAT )
+	{
+		if ( !is_scalar( $string ) ) {
+			trigger_error( 'htmlspecialchars_decode() expects parameter 1 to be string, ' . gettype( $string ) . ' given', E_USER_WARNING );
+			return;
+		}
+
+		if ( !is_int( $quote_style ) && $quote_style !== null ) {
+			trigger_error( 'htmlspecialchars_decode() expects parameter 2 to be integer, ' . gettype( $quote_style ) . ' given', E_USER_WARNING );
+			return;
+		}
+
+		return wp_specialchars_decode( $str, $quote_style );
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.core.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.core.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.core.php	(revision 5057)
@@ -0,0 +1,1249 @@
+<?php
+// Last sync [WP11544]
+
+/**
+ * From WP wp-includes/functions.php
+ *
+ * Missing functions are indicated in comments
+ */
+
+/**
+ * Main BackPress API
+ *
+ * @package BackPress
+ */
+
+// ! function mysql2date()
+
+if ( !function_exists('current_time') ) :
+/**
+ * Retrieve the current time based on specified type.
+ *
+ * The 'mysql' type will return the time in the format for MySQL DATETIME field.
+ * The 'timestamp' type will return the current timestamp.
+ *
+ * If $gmt is set to either '1' or 'true', then both types will use GMT time.
+ * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
+ *
+ * @since 1.0.0
+ *
+ * @param string $type Either 'mysql' or 'timestamp'.
+ * @param int|bool $gmt Optional. Whether to use GMT timezone. Default is false.
+ * @return int|string String if $type is 'gmt', int if $type is 'timestamp'.
+ */
+function current_time( $type, $gmt = 0 ) {
+	switch ( $type ) {
+		case 'mysql':
+			return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', ( time() + ( backpress_get_option( 'gmt_offset' ) * 3600 ) ) );
+			break;
+		case 'timestamp':
+			return ( $gmt ) ? time() : time() + ( backpress_get_option( 'gmt_offset' ) * 3600 );
+			break;
+	}
+}
+endif;
+
+// ! function date_i18n()
+// ! function number_format_i18n()
+// ! function size_format()
+// ! function get_weekstartend()
+
+if ( !function_exists('maybe_unserialize') ) :
+/**
+ * Unserialize value only if it was serialized.
+ *
+ * @since 2.0.0
+ *
+ * @param string $original Maybe unserialized original, if is needed.
+ * @return mixed Unserialized data can be any type.
+ */
+function maybe_unserialize( $original ) {
+	if ( is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in
+		return @unserialize( $original );
+	return $original;
+}
+endif;
+
+if ( !function_exists('is_serialized') ) :
+/**
+ * Check value to find if it was serialized.
+ *
+ * If $data is not an string, then returned value will always be false.
+ * Serialized data is always a string.
+ *
+ * @since 2.0.5
+ *
+ * @param mixed $data Value to check to see if was serialized.
+ * @return bool False if not serialized and true if it was.
+ */
+function is_serialized( $data ) {
+	// if it isn't a string, it isn't serialized
+	if ( !is_string( $data ) )
+		return false;
+	$data = trim( $data );
+	if ( 'N;' == $data )
+		return true;
+	if ( !preg_match( '/^([adObis]):/', $data, $badions ) )
+		return false;
+	switch ( $badions[1] ) {
+		case 'a' :
+		case 'O' :
+		case 's' :
+			if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )
+				return true;
+			break;
+		case 'b' :
+		case 'i' :
+		case 'd' :
+			if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )
+				return true;
+			break;
+	}
+	return false;
+}
+endif;
+
+if ( !function_exists('is_serialized_string') ) :
+/**
+ * Check whether serialized data is of string type.
+ *
+ * @since 2.0.5
+ *
+ * @param mixed $data Serialized data
+ * @return bool False if not a serialized string, true if it is.
+ */
+function is_serialized_string( $data ) {
+	// if it isn't a string, it isn't a serialized string
+	if ( !is_string( $data ) )
+		return false;
+	$data = trim( $data );
+	if ( preg_match( '/^s:[0-9]+:.*;$/s', $data ) ) // this should fetch all serialized strings
+		return true;
+	return false;
+}
+endif;
+
+// ! function get_option()
+// ! function wp_protect_special_option()
+// ! function form_option()
+// ! function get_alloptions()
+// ! function wp_load_alloptions()
+// ! function update_option()
+// ! function add_option()
+// ! function delete_option()
+// ! function delete_transient()
+// ! function get_transient()
+// ! function set_transient()
+// ! function wp_user_settings()
+// ! function get_user_setting()
+// ! function set_user_setting()
+// ! function delete_user_setting()
+// ! function get_all_user_settings()
+// ! function wp_set_all_user_settings()
+// ! function delete_all_user_settings()
+
+if ( !function_exists('maybe_serialize') ) :
+/**
+ * Serialize data, if needed.
+ *
+ * @since 2.0.5
+ *
+ * @param mixed $data Data that might be serialized.
+ * @return mixed A scalar data
+ */
+function maybe_serialize( $data ) {
+	if ( is_array( $data ) || is_object( $data ) )
+		return serialize( $data );
+
+	if ( is_serialized( $data ) )
+		return serialize( $data );
+
+	return $data;
+}
+endif;
+
+// ! function make_url_footnote()
+// ! function xmlrpc_getposttitle()
+// ! function xmlrpc_getpostcategory()
+// ! function xmlrpc_removepostdata()
+// ! function debug_fopen()
+// ! function debug_fwrite()
+// ! function debug_fclose()
+// ! function do_enclose()
+// ! function wp_get_http()
+// ! function wp_get_http_headers()
+// ! function is_new_day()
+
+if ( !function_exists( 'build_query' ) ) :
+/**
+ * Build URL query based on an associative and, or indexed array.
+ *
+ * This is a convenient function for easily building url queries. It sets the
+ * separator to '&' and uses _http_build_query() function.
+ *
+ * @see _http_build_query() Used to build the query
+ * @link http://us2.php.net/manual/en/function.http-build-query.php more on what
+ *		http_build_query() does.
+ *
+ * @since 2.3.0
+ *
+ * @param array $data URL-encode key/value pairs.
+ * @return string URL encoded string
+ */
+function build_query( $data ) {
+	return _http_build_query( $data, null, '&', '', false );
+}
+endif;
+
+if ( !function_exists( 'add_query_arg' ) ) :
+/**
+ * Retrieve a modified URL query string.
+ *
+ * You can rebuild the URL and append a new query variable to the URL query by
+ * using this function. You can also retrieve the full URL with query data.
+ *
+ * Adding a single key & value or an associative array. Setting a key value to
+ * emptystring removes the key. Omitting oldquery_or_uri uses the $_SERVER
+ * value.
+ *
+ * @since 1.5.0
+ *
+ * @param mixed $param1 Either newkey or an associative_array
+ * @param mixed $param2 Either newvalue or oldquery or uri
+ * @param mixed $param3 Optional. Old query or uri
+ * @return string New URL query string.
+ */
+function add_query_arg() {
+	$ret = '';
+	if ( is_array( func_get_arg(0) ) ) {
+		if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) )
+			$uri = $_SERVER['REQUEST_URI'];
+		else
+			$uri = @func_get_arg( 1 );
+	} else {
+		if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) )
+			$uri = $_SERVER['REQUEST_URI'];
+		else
+			$uri = @func_get_arg( 2 );
+	}
+
+	if ( $frag = strstr( $uri, '#' ) )
+		$uri = substr( $uri, 0, -strlen( $frag ) );
+	else
+		$frag = '';
+
+	if ( preg_match( '|^https?://|i', $uri, $matches ) ) {
+		$protocol = $matches[0];
+		$uri = substr( $uri, strlen( $protocol ) );
+	} else {
+		$protocol = '';
+	}
+
+	if ( strpos( $uri, '?' ) !== false ) {
+		$parts = explode( '?', $uri, 2 );
+		if ( 1 == count( $parts ) ) {
+			$base = '?';
+			$query = $parts[0];
+		} else {
+			$base = $parts[0] . '?';
+			$query = $parts[1];
+		}
+	} elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {
+		$base = $uri . '?';
+		$query = '';
+	} else {
+		$base = '';
+		$query = $uri;
+	}
+
+	wp_parse_str( $query, $qs );
+	$qs = urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
+	if ( is_array( func_get_arg( 0 ) ) ) {
+		$kayvees = func_get_arg( 0 );
+		$qs = array_merge( $qs, $kayvees );
+	} else {
+		$qs[func_get_arg( 0 )] = func_get_arg( 1 );
+	}
+
+	foreach ( (array) $qs as $k => $v ) {
+		if ( $v === false )
+			unset( $qs[$k] );
+	}
+
+	$ret = build_query( $qs );
+	$ret = trim( $ret, '?' );
+	$ret = preg_replace( '#=(&|$)#', '$1', $ret );
+	$ret = $protocol . $base . $ret . $frag;
+	$ret = rtrim( $ret, '?' );
+	return $ret;
+}
+endif;
+
+if ( !function_exists( 'remove_query_arg' ) ) :
+/**
+ * Removes an item or list from the query string.
+ *
+ * @since 1.5.0
+ *
+ * @param string|array $key Query key or keys to remove.
+ * @param bool $query When false uses the $_SERVER value.
+ * @return string New URL query string.
+ */
+function remove_query_arg( $key, $query=false ) {
+	if ( is_array( $key ) ) { // removing multiple keys
+		foreach ( $key as $k )
+			$query = add_query_arg( $k, false, $query );
+		return $query;
+	}
+	return add_query_arg( $key, false, $query );
+}
+endif;
+
+// ! function add_magic_quotes()
+
+if ( !function_exists( 'wp_remote_fopen' ) ) :
+/**
+ * HTTP request for URI to retrieve content.
+ *
+ * @since 1.5.1
+ * @uses wp_remote_get()
+ *
+ * @param string $uri URI/URL of web page to retrieve.
+ * @return bool|string HTTP content. False on failure.
+ */
+function wp_remote_fopen( $uri ) {
+	$parsed_url = @parse_url( $uri );
+
+	if ( !$parsed_url || !is_array( $parsed_url ) )
+		return false;
+
+	$options = array();
+	$options['timeout'] = 10;
+
+	$response = wp_remote_get( $uri, $options );
+
+	if ( is_wp_error( $response ) )
+		return false;
+
+	return $response['body'];
+}
+endif;
+
+// ! function wp()
+
+if ( !function_exists( 'get_status_header_desc' ) ) :
+/**
+ * Retrieve the description for the HTTP status.
+ *
+ * @since 2.3.0
+ *
+ * @param int $code HTTP status code.
+ * @return string Empty string if not found, or description if found.
+ */
+function get_status_header_desc( $code ) {
+	global $wp_header_to_desc;
+
+	$code = absint( $code );
+
+	if ( !isset( $wp_header_to_desc ) ) {
+		$wp_header_to_desc = array(
+			100 => 'Continue',
+			101 => 'Switching Protocols',
+			102 => 'Processing',
+
+			200 => 'OK',
+			201 => 'Created',
+			202 => 'Accepted',
+			203 => 'Non-Authoritative Information',
+			204 => 'No Content',
+			205 => 'Reset Content',
+			206 => 'Partial Content',
+			207 => 'Multi-Status',
+			226 => 'IM Used',
+
+			300 => 'Multiple Choices',
+			301 => 'Moved Permanently',
+			302 => 'Found',
+			303 => 'See Other',
+			304 => 'Not Modified',
+			305 => 'Use Proxy',
+			306 => 'Reserved',
+			307 => 'Temporary Redirect',
+
+			400 => 'Bad Request',
+			401 => 'Unauthorized',
+			402 => 'Payment Required',
+			403 => 'Forbidden',
+			404 => 'Not Found',
+			405 => 'Method Not Allowed',
+			406 => 'Not Acceptable',
+			407 => 'Proxy Authentication Required',
+			408 => 'Request Timeout',
+			409 => 'Conflict',
+			410 => 'Gone',
+			411 => 'Length Required',
+			412 => 'Precondition Failed',
+			413 => 'Request Entity Too Large',
+			414 => 'Request-URI Too Long',
+			415 => 'Unsupported Media Type',
+			416 => 'Requested Range Not Satisfiable',
+			417 => 'Expectation Failed',
+			422 => 'Unprocessable Entity',
+			423 => 'Locked',
+			424 => 'Failed Dependency',
+			426 => 'Upgrade Required',
+
+			500 => 'Internal Server Error',
+			501 => 'Not Implemented',
+			502 => 'Bad Gateway',
+			503 => 'Service Unavailable',
+			504 => 'Gateway Timeout',
+			505 => 'HTTP Version Not Supported',
+			506 => 'Variant Also Negotiates',
+			507 => 'Insufficient Storage',
+			510 => 'Not Extended'
+		);
+	}
+
+	if ( isset( $wp_header_to_desc[$code] ) )
+		return $wp_header_to_desc[$code];
+	else
+		return '';
+}
+endif;
+
+if ( !function_exists( 'status_header' ) ) :
+/**
+ * Set HTTP status header.
+ *
+ * @since 2.0.0
+ * @uses apply_filters() Calls 'status_header' on status header string, HTTP
+ *		HTTP code, HTTP code description, and protocol string as separate
+ *		parameters.
+ *
+ * @param int $header HTTP status code
+ * @return null Does not return anything.
+ */
+function status_header( $header ) {
+	$text = get_status_header_desc( $header );
+
+	if ( empty( $text ) )
+		return false;
+
+	$protocol = $_SERVER["SERVER_PROTOCOL"];
+	if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
+		$protocol = 'HTTP/1.0';
+	$status_header = "$protocol $header $text";
+	if ( function_exists( 'apply_filters' ) )
+		$status_header = apply_filters( 'status_header', $status_header, $header, $text, $protocol );
+
+	return @header( $status_header, true, $header );
+}
+endif;
+
+if ( !function_exists( 'wp_get_nocache_headers' ) ) :
+/**
+ * Gets the header information to prevent caching.
+ *
+ * The several different headers cover the different ways cache prevention is handled
+ * by different browsers
+ *
+ * @since 2.8
+ *
+ * @uses apply_filters()
+ * @return array The associative array of header names and field values.
+ */
+function wp_get_nocache_headers() {
+	$headers = array(
+		'Expires' => 'Wed, 11 Jan 1984 05:00:00 GMT',
+		'Last-Modified' => gmdate( 'D, d M Y H:i:s' ) . ' GMT',
+		'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
+		'Pragma' => 'no-cache',
+	);
+
+	if ( function_exists('apply_filters') ) {
+		$headers = apply_filters('nocache_headers', $headers);
+	}
+	return $headers;
+}
+endif;
+
+if ( !function_exists( 'nocache_headers' ) ) :
+/**
+ * Sets the headers to prevent caching for the different browsers.
+ *
+ * Different browsers support different nocache headers, so several headers must
+ * be sent so that all of them get the point that no caching should occur.
+ *
+ * @since 2.0.0
+ * @uses wp_get_nocache_headers()
+ */
+function nocache_headers() {
+	$headers = wp_get_nocache_headers();
+	foreach( (array) $headers as $name => $field_value )
+		@header("{$name}: {$field_value}");
+}
+endif;
+
+if ( !function_exists( 'cache_javascript_headers' ) ) :
+/**
+ * Set the headers for caching for 10 days with JavaScript content type.
+ *
+ * @since 2.1.0
+ */
+function cache_javascript_headers() {
+	$expiresOffset = 864000; // 10 days
+	header( "Content-Type: text/javascript; charset=" . backpress_get_option( 'charset' ) );
+	header( "Vary: Accept-Encoding" ); // Handle proxies
+	header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $expiresOffset ) . " GMT" );
+}
+endif;
+
+// ! function get_num_queries()
+// ! function bool_from_yn()
+// ! function do_feed()
+// ! function do_feed_rdf()
+// ! function do_feed_rss()
+// ! function do_feed_rss2()
+// ! function do_feed_atom()
+// ! function do_robots()
+// ! function is_blog_installed()
+// ! function wp_nonce_url()
+// ! function wp_nonce_field()
+
+if ( !function_exists( 'wp_referer_field' ) ) :
+/**
+ * Retrieve or display referer hidden field for forms.
+ *
+ * The referer link is the current Request URI from the server super global. The
+ * input name is '_wp_http_referer', in case you wanted to check manually.
+ *
+ * @package WordPress
+ * @subpackage Security
+ * @since 2.0.4
+ *
+ * @param bool $echo Whether to echo or return the referer field.
+ * @return string Referer field.
+ */
+function wp_referer_field( $echo = true) {
+	$ref = esc_attr( $_SERVER['REQUEST_URI'] );
+	$referer_field = '<input type="hidden" name="_wp_http_referer" value="'. $ref . '" />';
+
+	if ( $echo )
+		echo $referer_field;
+	return $referer_field;
+}
+endif;
+
+if ( !function_exists( 'wp_original_referer_field' ) ) :
+/**
+ * Retrieve or display original referer hidden field for forms.
+ *
+ * The input name is '_wp_original_http_referer' and will be either the same
+ * value of {@link wp_referer_field()}, if that was posted already or it will
+ * be the current page, if it doesn't exist.
+ *
+ * @package WordPress
+ * @subpackage Security
+ * @since 2.0.4
+ *
+ * @param bool $echo Whether to echo the original http referer
+ * @param string $jump_back_to Optional, default is 'current'. Can be 'previous' or page you want to jump back to.
+ * @return string Original referer field.
+ */
+function wp_original_referer_field( $echo = true, $jump_back_to = 'current' ) {
+	$jump_back_to = ( 'previous' == $jump_back_to ) ? wp_get_referer() : $_SERVER['REQUEST_URI'];
+	$ref = ( wp_get_original_referer() ) ? wp_get_original_referer() : $jump_back_to;
+	$orig_referer_field = '<input type="hidden" name="_wp_original_http_referer" value="' . esc_attr( stripslashes( $ref ) ) . '" />';
+	if ( $echo )
+		echo $orig_referer_field;
+	return $orig_referer_field;
+}
+endif;
+
+if ( !function_exists( 'wp_get_referer' ) ) :
+/**
+ * Retrieve referer from '_wp_http_referer', HTTP referer, or current page respectively.
+ *
+ * @package WordPress
+ * @subpackage Security
+ * @since 2.0.4
+ *
+ * @return string|bool False on failure. Referer URL on success.
+ */
+function wp_get_referer() {
+	$ref = '';
+	if ( ! empty( $_REQUEST['_wp_http_referer'] ) )
+		$ref = $_REQUEST['_wp_http_referer'];
+	else if ( ! empty( $_SERVER['HTTP_REFERER'] ) )
+		$ref = $_SERVER['HTTP_REFERER'];
+
+	if ( $ref !== $_SERVER['REQUEST_URI'] )
+		return $ref;
+	return false;
+}
+endif;
+
+if ( !function_exists( 'wp_get_original_referer' ) ) :
+/**
+ * Retrieve original referer that was posted, if it exists.
+ *
+ * @package WordPress
+ * @subpackage Security
+ * @since 2.0.4
+ *
+ * @return string|bool False if no original referer or original referer if set.
+ */
+function wp_get_original_referer() {
+	if ( !empty( $_REQUEST['_wp_original_http_referer'] ) )
+		return $_REQUEST['_wp_original_http_referer'];
+	return false;
+}
+endif;
+
+// ! function wp_mkdir_p()
+// ! function path_is_absolute()
+// ! function path_join()
+// ! function wp_upload_dir()
+// ! function wp_unique_filename()
+// ! function wp_upload_bits()
+// ! function wp_ext2type()
+// ! function wp_check_filetype()
+// ! function wp_explain_nonce()
+// ! function wp_nonce_ays()
+// ! function wp_die()
+// ! function _config_wp_home()
+// ! function _config_wp_siteurl()
+// ! function _mce_set_direction()
+// ! function smilies_init()
+
+if ( !function_exists('wp_parse_args') ) :
+/**
+ * Merge user defined arguments into defaults array.
+ *
+ * This function is used throughout WordPress to allow for both string or array
+ * to be merged into another array.
+ *
+ * @since 2.2.0
+ *
+ * @param string|array $args Value to merge with $defaults
+ * @param array $defaults Array that serves as the defaults.
+ * @return array Merged user defined values with defaults.
+ */
+function wp_parse_args( $args, $defaults = '' ) {
+	if ( is_object( $args ) )
+		$r = get_object_vars( $args );
+	elseif ( is_array( $args ) )
+		$r =& $args;
+	else
+		wp_parse_str( $args, $r );
+
+	if ( is_array( $defaults ) )
+		return array_merge( $defaults, $r );
+	return $r;
+}
+endif;
+
+// ! function wp_maybe_load_widgets()
+// ! function wp_widgets_add_menu()
+// ! function wp_ob_end_flush_all()
+// ! function require_wp_db()
+// ! function dead_db()
+
+if ( !function_exists('absint') ) :
+/**
+ * Converts value to nonnegative integer.
+ *
+ * @since 2.5.0
+ *
+ * @param mixed $maybeint Data you wish to have convered to an nonnegative integer
+ * @return int An nonnegative integer
+ */
+function absint( $maybeint ) {
+	return abs( intval( $maybeint ) );
+}
+endif;
+
+// ! function url_is_accessable_via_ssl()
+// ! function atom_service_url_filter()
+// ! function _deprecated_function()
+// ! function _deprecated_file()
+
+if ( !function_exists('is_lighttpd_before_150') ) :
+/**
+ * Is the server running earlier than 1.5.0 version of lighttpd
+ *
+ * @since 2.5.0
+ *
+ * @return bool Whether the server is running lighttpd < 1.5.0
+ */
+function is_lighttpd_before_150() {
+	$server_parts = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] )? $_SERVER['SERVER_SOFTWARE'] : '' );
+	$server_parts[1] = isset( $server_parts[1] )? $server_parts[1] : '';
+	return  'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' );
+}
+endif;
+
+if ( !function_exists('apache_mod_loaded') ) :
+/**
+ * Does the specified module exist in the apache config?
+ *
+ * @since 2.5.0
+ *
+ * @param string $mod e.g. mod_rewrite
+ * @param bool $default The default return value if the module is not found
+ * @return bool
+ */
+function apache_mod_loaded($mod, $default = false) {
+	global $is_apache;
+
+	if ( !$is_apache )
+		return false;
+
+	if ( function_exists('apache_get_modules') ) {
+		$mods = apache_get_modules();
+		if ( in_array($mod, $mods) )
+			return true;
+	} elseif ( function_exists('phpinfo') ) {
+			ob_start();
+			phpinfo(8);
+			$phpinfo = ob_get_clean();
+			if ( false !== strpos($phpinfo, $mod) )
+				return true;
+	}
+	return $default;
+}
+endif;
+
+if ( !function_exists('validate_file') ) :
+/**
+ * File validates against allowed set of defined rules.
+ *
+ * A return value of '1' means that the $file contains either '..' or './'. A
+ * return value of '2' means that the $file contains ':' after the first
+ * character. A return value of '3' means that the file is not in the allowed
+ * files list.
+ *
+ * @since 1.2.0
+ *
+ * @param string $file File path.
+ * @param array $allowed_files List of allowed files.
+ * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
+ */
+function validate_file( $file, $allowed_files = '' ) {
+	if ( false !== strpos( $file, '..' ))
+		return 1;
+
+	if ( false !== strpos( $file, './' ))
+		return 1;
+
+	if (':' == substr( $file, 1, 1 ))
+		return 2;
+
+	if (!empty ( $allowed_files ) && (!in_array( $file, $allowed_files ) ) )
+		return 3;
+
+	return 0;
+}
+endif;
+
+if ( !function_exists('is_ssl') ) :
+/**
+ * Determine if SSL is used.
+ *
+ * @since 2.6.0
+ *
+ * @return bool True if SSL, false if not used.
+ */
+function is_ssl() {
+	if ( isset($_SERVER['HTTPS']) ) {
+		if ( 'on' == strtolower($_SERVER['HTTPS']) )
+			return true;
+		if ( '1' == $_SERVER['HTTPS'] )
+			return true;
+	} elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
+		return true;
+	}
+	return false;
+}
+endif;
+
+if ( !function_exists('force_ssl_login') ) :
+/**
+ * Whether SSL login should be forced.
+ *
+ * @since 2.6.0
+ *
+ * @param string|bool $force Optional.
+ * @return bool True if forced, false if not forced.
+ */
+function force_ssl_login($force = '') {
+	static $forced;
+
+	if ( '' != $force ) {
+		$old_forced = $forced;
+		$forced = $force;
+		return $old_forced;
+	}
+
+	return $forced;
+}
+endif;
+
+if ( !function_exists('force_ssl_admin') ) :
+/**
+ * Whether to force SSL used for the Administration Panels.
+ *
+ * @since 2.6.0
+ *
+ * @param string|bool $force
+ * @return bool True if forced, false if not forced.
+ */
+function force_ssl_admin($force = '') {
+	static $forced;
+
+	if ( '' != $force ) {
+		$old_forced = $forced;
+		$forced = $force;
+		return $old_forced;
+	}
+
+	return $forced;
+}
+endif;
+
+// ! function wp_guess_url()
+// ! function wp_suspend_cache_invalidation()
+// ! function get_site_option()
+// ! function add_site_option()
+// ! function update_site_option()
+
+if ( !function_exists('wp_timezone_override_offset') ) :
+/**
+ * gmt_offset modification for smart timezone handling
+ *
+ * Overrides the gmt_offset option if we have a timezone_string available
+ */
+function wp_timezone_override_offset() {
+	if ( !wp_timezone_supported() ) {
+		return false;
+	}
+	if ( !$timezone_string = backpress_get_option( 'timezone_string' ) ) {
+		return false;
+	}
+
+	@date_default_timezone_set( $timezone_string );
+	$timezone_object = timezone_open( $timezone_string );
+	$datetime_object = date_create();
+	if ( false === $timezone_object || false === $datetime_object ) {
+		return false;
+	}
+	return round( timezone_offset_get( $timezone_object, $datetime_object ) / 3600, 2 );
+}
+endif;
+
+if ( !function_exists('wp_timezone_supported') ) :
+/**
+ * Check for PHP timezone support
+ */
+function wp_timezone_supported() {
+	$support = false;
+	if (
+		function_exists( 'date_default_timezone_set' ) &&
+		function_exists( 'timezone_identifiers_list' ) &&
+		function_exists( 'timezone_open' ) &&
+		function_exists( 'timezone_offset_get' )
+	) {
+		$support = true;
+	}
+	return apply_filters( 'timezone_support', $support );
+}
+endif;
+
+if ( !function_exists('_wp_timezone_choice_usort_callback') ) :
+function _wp_timezone_choice_usort_callback( $a, $b ) {
+	// Don't use translated versions of Etc
+	if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
+		// Make the order of these more like the old dropdown
+		if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
+			return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
+		}
+		if ( 'UTC' === $a['city'] ) {
+			if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
+				return 1;
+			}
+			return -1;
+		}
+		if ( 'UTC' === $b['city'] ) {
+			if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
+				return -1;
+			}
+			return 1;
+		}
+		return strnatcasecmp( $a['city'], $b['city'] );
+	}
+	if ( $a['t_continent'] == $b['t_continent'] ) {
+		if ( $a['t_city'] == $b['t_city'] ) {
+			return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
+		}
+		return strnatcasecmp( $a['t_city'], $b['t_city'] );
+	} else {
+		// Force Etc to the bottom of the list
+		if ( 'Etc' === $a['continent'] ) {
+			return 1;
+		}
+		if ( 'Etc' === $b['continent'] ) {
+			return -1;
+		}
+		return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
+	}
+}
+endif;
+
+if ( !function_exists('wp_timezone_choice') ) :
+/**
+ * Gives a nicely formatted list of timezone strings // temporary! Not in final
+ *
+ * @param $selected_zone string Selected Zone
+ *
+ */
+function wp_timezone_choice( $selected_zone ) {
+	static $mo_loaded = false;
+
+	$continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific', 'Etc' );
+
+	// Load translations for continents and cities
+	if ( !$mo_loaded ) {
+		$locale = backpress_get_option( 'language_locale' );
+		$mofile = backpress_get_option( 'language_directory' ) . 'continents-cities-' . $locale . '.mo';
+		load_textdomain( 'continents-cities', $mofile );
+		$mo_loaded = true;
+	}
+
+	$zonen = array();
+	foreach ( timezone_identifiers_list() as $zone ) {
+		$zone = explode( '/', $zone );
+		if ( !in_array( $zone[0], $continents ) ) {
+			continue;
+		}
+		if ( 'Etc' === $zone[0] && in_array( $zone[1], array( 'UCT', 'GMT', 'GMT0', 'GMT+0', 'GMT-0', 'Greenwich', 'Universal', 'Zulu' ) ) ) {
+			continue;
+		}
+
+		// This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
+		$exists = array(
+			0 => ( isset( $zone[0] ) && $zone[0] ) ? true : false,
+			1 => ( isset( $zone[1] ) && $zone[1] ) ? true : false,
+			2 => ( isset( $zone[2] ) && $zone[2] ) ? true : false
+		);
+		$exists[3] = ( $exists[0] && 'Etc' !== $zone[0] ) ? true : false;
+		$exists[4] = ( $exists[1] && $exists[3] ) ? true : false;
+		$exists[5] = ( $exists[2] && $exists[3] ) ? true : false;
+
+		$zonen[] = array(
+			'continent'   => ( $exists[0] ? $zone[0] : '' ),
+			'city'        => ( $exists[1] ? $zone[1] : '' ),
+			'subcity'     => ( $exists[2] ? $zone[2] : '' ),
+			't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
+			't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
+			't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' )
+		);
+	}
+	usort( $zonen, '_wp_timezone_choice_usort_callback' );
+
+	$structure = array();
+
+	if ( empty( $selected_zone ) ) {
+		$structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
+	}
+
+	foreach ( $zonen as $key => $zone ) {
+		// Build value in an array to join later
+		$value = array( $zone['continent'] );
+
+		if ( empty( $zone['city'] ) ) {
+			// It's at the continent level (generally won't happen)
+			$display = $zone['t_continent'];
+		} else {
+			// It's inside a continent group
+			
+			// Continent optgroup
+			if ( !isset( $zonen[$key - 1] ) || $zonen[$key - 1]['continent'] !== $zone['continent'] ) {
+				$label = ( 'Etc' === $zone['continent'] ) ? __( 'Manual offsets' ) : $zone['t_continent'];
+				$structure[] = '<optgroup label="'. esc_attr( $label ) .'">';
+			}
+			
+			// Add the city to the value
+			$value[] = $zone['city'];
+			if ( 'Etc' === $zone['continent'] ) {
+				if ( 'UTC' === $zone['city'] ) {
+					$display = '';
+				} else {
+					$display = str_replace( 'GMT', '', $zone['city'] );
+					$display = strtr( $display, '+-', '-+' ) . ':00';
+				}
+				$display = sprintf( __( 'UTC %s' ), $display );
+			} else {
+				$display = $zone['t_city'];
+				if ( !empty( $zone['subcity'] ) ) {
+					// Add the subcity to the value
+					$value[] = $zone['subcity'];
+					$display .= ' - ' . $zone['t_subcity'];
+				}
+			}
+		}
+
+		// Build the value
+		$value = join( '/', $value );
+		$selected = '';
+		if ( $value === $selected_zone ) {
+			$selected = 'selected="selected" ';
+		}
+		$structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . "</option>";
+		
+		// Close continent optgroup
+		if ( !empty( $zone['city'] ) && ( !isset($zonen[$key + 1]) || (isset( $zonen[$key + 1] ) && $zonen[$key + 1]['continent'] !== $zone['continent']) ) ) {
+			$structure[] = '</optgroup>';
+		}
+	}
+
+	return join( "\n", $structure );
+}
+endif;
+
+if ( !function_exists('_cleanup_header_comment') ) :
+/**
+ * Strip close comment and close php tags from file headers used by WP
+ * See http://core.trac.wordpress.org/ticket/8497
+ *
+ * @since 2.8
+ */
+function _cleanup_header_comment($str) {
+	return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $str));
+}
+endif;
+
+
+
+/**
+ * From WP wp-settings.php
+ */
+
+if ( !function_exists( 'wp_clone' ) ) :
+/**
+ * Copy an object.
+ *
+ * Returns a cloned copy of an object.
+ *
+ * @since 2.7.0
+ *
+ * @param object $object The object to clone
+ * @return object The cloned object
+ */
+function wp_clone( $object ) {
+	static $can_clone;
+	if ( !isset( $can_clone ) ) {
+		$can_clone = version_compare( phpversion(), '5.0', '>=' );
+	}
+	return $can_clone ? clone( $object ) : $object;
+}
+endif;
+
+
+
+/**
+ * BackPress only below
+ */
+
+if ( !function_exists('backpress_gmt_strtotime') ) :
+function backpress_gmt_strtotime( $string ) {
+	if ( is_numeric($string) )
+		return $string;
+	if ( !is_string($string) )
+		return -1;
+
+	if ( stristr($string, 'utc') || stristr($string, 'gmt') || stristr($string, '+0000') )
+		return strtotime($string);
+
+	if ( -1 == $time = strtotime($string . ' +0000') )
+		return strtotime($string);
+
+	return $time;
+}
+endif;
+
+if ( !function_exists('backpress_convert_object') ) :
+function backpress_convert_object( &$object, $output ) {
+	if ( is_array( $object ) ) {
+		foreach ( array_keys( $object ) as $key )
+			backpress_convert_object( $object[$key], $output );
+	} else {
+		switch ( $output ) {
+			case OBJECT  : break;
+			case ARRAY_A : $object = get_object_vars($object); break;
+			case ARRAY_N : $object = array_values(get_object_vars($object)); break;
+		}
+	}
+}
+endif;
+
+if ( !function_exists('backpress_die') ) :
+/**
+ * Kill BackPress execution and display HTML message with error message.
+ *
+ * This function calls the die() PHP function. The difference is that a message
+ * in HTML will be displayed to the user. It is recommended to use this function
+ * only when the execution should not continue any further. It is not
+ * recommended to call this function very often and normally you should try to
+ * handle as many errors as possible silently.
+ *
+ * @param string $message Error message.
+ * @param string $title Error title.
+ * @param string|array $args Optional arguments to control behaviour.
+ */
+function backpress_die( $message, $title = '', $args = array() )
+{
+	$defaults = array( 'response' => 500, 'language' => 'en-US', 'text_direction' => 'ltr' );
+	$r = wp_parse_args( $args, $defaults );
+
+	if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
+		if ( empty( $title ) ) {
+			$error_data = $message->get_error_data();
+			if ( is_array( $error_data ) && isset( $error_data['title'] ) ) {
+				$title = $error_data['title'];
+			}
+		}
+		$errors = $message->get_error_messages();
+		switch ( count( $errors ) ) {
+			case 0:
+				$message = '';
+				break;
+			case 1:
+				$message = '<p>' . $errors[0] . '</p>';
+				break;
+			default:
+				$message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $errors ) . "</li>\n\t</ul>";
+				break;
+		}
+	} elseif ( is_string( $message ) ) {
+		$message = '<p>' . $message . '</p>';
+	}
+
+	if ( !headers_sent() ) {
+		status_header( $r['response'] );
+		nocache_headers();
+		header( 'Content-Type: text/html; charset=utf-8' );
+	}
+
+	if ( empty( $title ) ) {
+		if ( function_exists( '__' ) ) {
+			$title = __( 'BackPress &rsaquo; Error' );
+		} else {
+			$title = 'BackPress &rsaquo; Error';
+		}
+	}
+
+	if ( $r['text_direction'] ) {
+		$language_attributes = ' dir="' . $r['text_direction'] . '"';
+	}
+
+	if ( $r['language'] ) {
+		// Always XHTML 1.1 style
+		$language_attributes .= ' lang="' . $r['language'] . '"';
+	}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"<?php echo $language_attributes; ?>>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title><?php echo $title ?></title>
+	<style type="text/css" media="screen">
+		html { background: #f7f7f7; }
+
+		body {
+			background: #fff;
+			color: #333;
+			font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
+			margin: 2em auto 0 auto;
+			width: 700px;
+			padding: 1em 2em;
+			-moz-border-radius: 11px;
+			-khtml-border-radius: 11px;
+			-webkit-border-radius: 11px;
+			border-radius: 11px;
+			border: 1px solid #dfdfdf;
+		}
+
+		a { color: #2583ad; text-decoration: none; }
+
+		a:hover { color: #d54e21; }
+
+		h1 {
+			border-bottom: 1px solid #dadada;
+			clear: both;
+			color: #666;
+			font: 24px Georgia, "Times New Roman", Times, serif;
+			margin: 5px 0 0 -4px;
+			padding: 0;
+			padding-bottom: 7px;
+		}
+
+		h2 { font-size: 16px; }
+
+		p, li {
+			padding-bottom: 2px;
+			font-size: 12px;
+			line-height: 18px;
+		}
+
+		code { font-size: 13px; }
+
+		ul, ol { padding: 5px 5px 5px 22px; }
+
+		#error-page { margin-top: 50px; }
+
+		#error-page p {
+			font-size: 12px;
+			line-height: 18px;
+			margin: 25px 0 20px;
+		}
+
+		#error-page code { font-family: Consolas, Monaco, Courier, monospace; }
+<?php
+	if ( $r['text_direction'] === 'rtl') {
+?>
+		body {
+			font-family: Tahoma, "Times New Roman";
+		}
+
+		h1 {
+			font-family: Tahoma, "Times New Roman";
+			margin: 5px -4px 0 0;
+		}
+
+		ul, ol { padding: 5px 22px 5px 5px; }
+<?php
+	}
+?>
+	</style>
+</head>
+<body id="error-page">
+	<?php echo $message; ?>
+</body>
+</html>
+<?php
+	die();
+}
+endif;
+
+/**
+ * Acts the same as core PHP setcookie() but its arguments are run through the backpress_set_cookie filter.
+ * 
+ * If the filter returns false, setcookie() isn't called.
+ */
+function backpress_set_cookie() {
+	$args = func_get_args();
+	$args = apply_filters( 'backpress_set_cookie', $args );
+	if ( $args === false ) return;
+	call_user_func_array( 'setcookie', $args );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.formatting.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.formatting.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.formatting.php	(revision 5057)
@@ -0,0 +1,1942 @@
+<?php
+// Last sync [WP12504]
+
+/**
+ * From WP wp-includes/formatting.php
+ *
+ * Missing functions are indicated in comments
+ */
+
+/**
+ * Main BackPress Formatting API.
+ *
+ * Handles many functions for formatting output.
+ *
+ * @package BackPress
+ **/
+
+if ( !function_exists( 'wptexturize' ) ) :
+/**
+ * Replaces common plain text characters into formatted entities
+ *
+ * As an example,
+ * <code>
+ * 'cause today's effort makes it worth tomorrow's "holiday"...
+ * </code>
+ * Becomes:
+ * <code>
+ * &#8217;cause today&#8217;s effort makes it worth tomorrow&#8217;s &#8220;holiday&#8221;&#8230;
+ * </code>
+ * Code within certain html blocks are skipped.
+ *
+ * @since 0.71
+ * @uses $wp_cockneyreplace Array of formatted entities for certain common phrases
+ *
+ * @param string $text The text to be formatted
+ * @return string The string replaced with html entities
+ */
+function wptexturize($text) {
+	global $wp_cockneyreplace;
+	static $static_setup = false, $opening_quote, $closing_quote, $default_no_texturize_tags, $default_no_texturize_shortcodes, $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements;
+	$output = '';
+	$curl = '';
+	$textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+	$stop = count($textarr);
+	
+	// No need to setup these variables more than once
+	if (!$static_setup) {
+		/* translators: opening curly quote */
+		$opening_quote = _x('&#8220;', 'opening curly quote');
+		/* translators: closing curly quote */
+		$closing_quote = _x('&#8221;', 'closing curly quote');
+
+		$default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
+		$default_no_texturize_shortcodes = array('code');
+
+		// if a plugin has provided an autocorrect array, use it
+		if ( isset($wp_cockneyreplace) ) {
+			$cockney = array_keys($wp_cockneyreplace);
+			$cockneyreplace = array_values($wp_cockneyreplace);
+		} else {
+			$cockney = array("'tain't","'twere","'twas","'tis","'twill","'til","'bout","'nuff","'round","'cause");
+			$cockneyreplace = array("&#8217;tain&#8217;t","&#8217;twere","&#8217;twas","&#8217;tis","&#8217;twill","&#8217;til","&#8217;bout","&#8217;nuff","&#8217;round","&#8217;cause");
+		}
+
+		$static_characters = array_merge(array('---', ' -- ', '--', ' - ', 'xn&#8211;', '...', '``', '\'s', '\'\'', ' (tm)'), $cockney);
+		$static_replacements = array_merge(array('&#8212;', ' &#8212; ', '&#8211;', ' &#8211; ', 'xn--', '&#8230;', $opening_quote, '&#8217;s', $closing_quote, ' &#8482;'), $cockneyreplace);
+
+		$dynamic_characters = array('/\'(\d\d(?:&#8217;|\')?s)/', '/(\s|\A|[([{<]|")\'/', '/(\d+)"/', '/(\d+)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A|[([{<])"(?!\s)/', '/"(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/(\d+)x(\d+)/');
+		$dynamic_replacements = array('&#8217;$1','$1&#8216;', '$1&#8243;', '$1&#8242;', '$1&#8217;$2', '$1' . $opening_quote . '$2', $closing_quote . '$1', '&#8217;$1', '$1&#215;$2');
+
+		$static_setup = true;
+	}
+
+	// Transform into regexp sub-expression used in _wptexturize_pushpop_element
+	// Must do this everytime in case plugins use these filters in a context sensitive manner
+	$no_texturize_tags = '(' . implode('|', apply_filters('no_texturize_tags', $default_no_texturize_tags) ) . ')';
+	$no_texturize_shortcodes = '(' . implode('|', apply_filters('no_texturize_shortcodes', $default_no_texturize_shortcodes) ) . ')';
+
+	$no_texturize_tags_stack = array();
+	$no_texturize_shortcodes_stack = array();
+
+	for ( $i = 0; $i < $stop; $i++ ) {
+		$curl = $textarr[$i];
+
+		if ( !empty($curl) && '<' != $curl{0} && '[' != $curl{0}
+				&& empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack)) { 
+			// This is not a tag, nor is the texturization disabled
+			// static strings
+			$curl = str_replace($static_characters, $static_replacements, $curl);
+			// regular expressions
+			$curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl);
+		} elseif (!empty($curl)) {
+			/*
+			 * Only call _wptexturize_pushpop_element if first char is correct
+			 * tag opening
+			 */
+			if ('<' == $curl{0})
+				_wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>');
+			elseif ('[' == $curl{0})
+				_wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']');
+		}
+
+		$curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&#038;$1', $curl);
+		$output .= $curl;
+	}
+
+	return $output;
+}
+endif;
+
+if ( !function_exists( '_wptexturize_pushpop_element' ) ) :
+/**
+ * Search for disabled element tags. Push element to stack on tag open and pop
+ * on tag close. Assumes first character of $text is tag opening.
+ *
+ * @access private
+ * @since 2.9.0
+ *
+ * @param string $text Text to check. First character is assumed to be $opening
+ * @param array $stack Array used as stack of opened tag elements
+ * @param string $disabled_elements Tags to match against formatted as regexp sub-expression
+ * @param string $opening Tag opening character, assumed to be 1 character long
+ * @param string $opening Tag closing  character
+ * @return object
+ */
+function _wptexturize_pushpop_element($text, &$stack, $disabled_elements, $opening = '<', $closing = '>') {
+	// Check if it is a closing tag -- otherwise assume opening tag
+	if (strncmp($opening . '/', $text, 2)) {
+		// Opening? Check $text+1 against disabled elements
+		if (preg_match('/^' . $disabled_elements . '\b/', substr($text, 1), $matches)) {
+			/*
+			 * This disables texturize until we find a closing tag of our type
+			 * (e.g. <pre>) even if there was invalid nesting before that
+			 * 
+			 * Example: in the case <pre>sadsadasd</code>"baba"</pre>
+			 *          "baba" won't be texturize
+			 */
+
+			array_push($stack, $matches[1]);
+		}
+	} else {
+		// Closing? Check $text+2 against disabled elements
+		$c = preg_quote($closing, '/');
+		if (preg_match('/^' . $disabled_elements . $c . '/', substr($text, 2), $matches)) {
+			$last = array_pop($stack);
+
+			// Make sure it matches the opening tag
+			if ($last != $matches[1])
+				array_push($stack, $last);
+		}
+	}
+}
+endif;
+
+if ( !function_exists( 'clean_pre' ) ) :
+/**
+ * Accepts matches array from preg_replace_callback in wpautop() or a string.
+ *
+ * Ensures that the contents of a <<pre>>...<</pre>> HTML block are not
+ * converted into paragraphs or line-breaks.
+ *
+ * @since 1.2.0
+ *
+ * @param array|string $matches The array or string
+ * @return string The pre block without paragraph/line-break conversion.
+ */
+function clean_pre($matches) {
+	if ( is_array($matches) )
+		$text = $matches[1] . $matches[2] . "</pre>";
+	else
+		$text = $matches;
+
+	$text = str_replace('<br />', '', $text);
+	$text = str_replace('<p>', "\n", $text);
+	$text = str_replace('</p>', '', $text);
+
+	return $text;
+}
+endif;
+
+// ! function wpautop()
+
+if ( !function_exists('seems_utf8') ) :
+/**
+ * Checks to see if a string is utf8 encoded.
+ *
+ * NOTE: This function checks for 5-Byte sequences, UTF8
+ *       has Bytes Sequences with a maximum length of 4.
+ *
+ * @author bmorel at ssi dot fr (modified)
+ * @since 1.2.1
+ *
+ * @param string $str The string to be checked
+ * @return bool True if $str fits a UTF-8 model, false otherwise.
+ */
+function seems_utf8($str) {
+	$length = strlen($str);
+	for ($i=0; $i < $length; $i++) {
+		$c = ord($str[$i]);
+		if ($c < 0x80) $n = 0; # 0bbbbbbb
+		elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
+		elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
+		elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
+		elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
+		elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
+		else return false; # Does not match any model
+		for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
+			if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
+				return false;
+		}
+	}
+	return true;
+}
+endif;
+
+
+
+if ( !function_exists('_wp_specialchars') ) :
+/**
+ * Converts a number of special characters into their HTML entities.
+ *
+ * Specifically deals with: &, <, >, ", and '.
+ *
+ * $quote_style can be set to ENT_COMPAT to encode " to
+ * &quot;, or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
+ *
+ * @since 1.2.2
+ *
+ * @param string $string The text which is to be encoded.
+ * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
+ * @param string $charset Optional. The character encoding of the string. Default is false.
+ * @param boolean $double_encode Optional. Whether or not to encode existing html entities. Default is false.
+ * @return string The encoded text with HTML entities.
+ */
+function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
+	$string = (string) $string;
+
+	if ( 0 === strlen( $string ) ) {
+		return '';
+	}
+
+	// Don't bother if there are no specialchars - saves some processing
+	if ( !preg_match( '/[&<>"\']/', $string ) ) {
+		return $string;
+	}
+
+	// Account for the previous behaviour of the function when the $quote_style is not an accepted value
+	if ( empty( $quote_style ) ) {
+		$quote_style = ENT_NOQUOTES;
+	} elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
+		$quote_style = ENT_QUOTES;
+	}
+
+	// Store the site charset as a static to avoid multiple calls to backpress_get_option()
+	if ( !$charset ) {
+		static $_charset;
+		if ( !isset( $_charset ) ) {
+			$_charset = backpress_get_option( 'charset' );
+		}
+		$charset = $_charset;
+	}
+	if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) ) {
+		$charset = 'UTF-8';
+	}
+
+	$_quote_style = $quote_style;
+
+	if ( $quote_style === 'double' ) {
+		$quote_style = ENT_COMPAT;
+		$_quote_style = ENT_COMPAT;
+	} elseif ( $quote_style === 'single' ) {
+		$quote_style = ENT_NOQUOTES;
+	}
+
+	// Handle double encoding ourselves
+	if ( !$double_encode ) {
+		$string = wp_specialchars_decode( $string, $_quote_style );
+		$string = preg_replace( '/&(#?x?[0-9a-z]+);/i', '|wp_entity|$1|/wp_entity|', $string );
+	}
+
+	$string = @htmlspecialchars( $string, $quote_style, $charset );
+
+	// Handle double encoding ourselves
+	if ( !$double_encode ) {
+		$string = str_replace( array( '|wp_entity|', '|/wp_entity|' ), array( '&', ';' ), $string );
+	}
+
+	// Backwards compatibility
+	if ( 'single' === $_quote_style ) {
+		$string = str_replace( "'", '&#039;', $string );
+	}
+
+	return $string;
+}
+endif;
+
+if ( !function_exists( 'wp_specialchars_decode' ) ) :
+/**
+ * Converts a number of HTML entities into their special characters.
+ *
+ * Specifically deals with: &, <, >, ", and '.
+ *
+ * $quote_style can be set to ENT_COMPAT to decode " entities,
+ * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
+ *
+ * @since 2.8
+ *
+ * @param string $string The text which is to be decoded.
+ * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old _wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
+ * @return string The decoded text without HTML entities.
+ */
+function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
+	$string = (string) $string;
+
+	if ( 0 === strlen( $string ) ) {
+		return '';
+	}
+
+	// Don't bother if there are no entities - saves a lot of processing
+	if ( strpos( $string, '&' ) === false ) {
+		return $string;
+	}
+
+	// Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
+	if ( empty( $quote_style ) ) {
+		$quote_style = ENT_NOQUOTES;
+	} elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
+		$quote_style = ENT_QUOTES;
+	}
+
+	// More complete than get_html_translation_table( HTML_SPECIALCHARS )
+	$single = array( '&#039;'  => '\'', '&#x27;' => '\'' );
+	$single_preg = array( '/&#0*39;/'  => '&#039;', '/&#x0*27;/i' => '&#x27;' );
+	$double = array( '&quot;' => '"', '&#034;'  => '"', '&#x22;' => '"' );
+	$double_preg = array( '/&#0*34;/'  => '&#034;', '/&#x0*22;/i' => '&#x22;' );
+	$others = array( '&lt;'   => '<', '&#060;'  => '<', '&gt;'   => '>', '&#062;'  => '>', '&amp;'  => '&', '&#038;'  => '&', '&#x26;' => '&' );
+	$others_preg = array( '/&#0*60;/'  => '&#060;', '/&#0*62;/'  => '&#062;', '/&#0*38;/'  => '&#038;', '/&#x0*26;/i' => '&#x26;' );
+
+	if ( $quote_style === ENT_QUOTES ) {
+		$translation = array_merge( $single, $double, $others );
+		$translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
+	} elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
+		$translation = array_merge( $double, $others );
+		$translation_preg = array_merge( $double_preg, $others_preg );
+	} elseif ( $quote_style === 'single' ) {
+		$translation = array_merge( $single, $others );
+		$translation_preg = array_merge( $single_preg, $others_preg );
+	} elseif ( $quote_style === ENT_NOQUOTES ) {
+		$translation = $others;
+		$translation_preg = $others_preg;
+	}
+
+	// Remove zero padding on numeric entities
+	$string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
+
+	// Replace characters according to translation table
+	return strtr( $string, $translation );
+}
+endif;
+
+if ( !function_exists( 'wp_check_invalid_utf8' ) ) :
+/**
+ * Checks for invalid UTF8 in a string.
+ *
+ * @since 2.8
+ *
+ * @param string $string The text which is to be checked.
+ * @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
+ * @return string The checked text.
+ */
+function wp_check_invalid_utf8( $string, $strip = false ) {
+	$string = (string) $string;
+
+	if ( 0 === strlen( $string ) ) {
+		return '';
+	}
+
+	// Store the site charset as a static to avoid multiple calls to backpress_get_option()
+	static $is_utf8;
+	if ( !isset( $is_utf8 ) ) {
+		$is_utf8 = in_array( backpress_get_option( 'charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
+	}
+	if ( !$is_utf8 ) {
+		return $string;
+	}
+
+	// Check for support for utf8 in the installed PCRE library once and store the result in a static
+	static $utf8_pcre;
+	if ( !isset( $utf8_pcre ) ) {
+		$utf8_pcre = @preg_match( '/^./u', 'a' );
+	}
+	// We can't demand utf8 in the PCRE installation, so just return the string in those cases
+	if ( !$utf8_pcre ) {
+		return $string;
+	}
+
+	// preg_match fails when it encounters invalid UTF8 in $string
+	if ( 1 === @preg_match( '/^./us', $string ) ) {
+		return $string;
+	}
+
+	// Attempt to strip the bad chars if requested (not recommended)
+	if ( $strip && function_exists( 'iconv' ) ) {
+		return iconv( 'utf-8', 'utf-8', $string );
+	}
+
+	return '';
+}
+endif;
+
+if ( !function_exists('utf8_uri_encode') ) :
+/**
+ * Encode the Unicode values to be used in the URI.
+ *
+ * @since 1.5.0
+ *
+ * @param string $utf8_string
+ * @param int $length Max length of the string
+ * @return string String with Unicode encoded for URI.
+ */
+function utf8_uri_encode( $utf8_string, $length = 0 ) {
+	$unicode = '';
+	$values = array();
+	$num_octets = 1;
+	$unicode_length = 0;
+
+	$string_length = strlen( $utf8_string );
+	for ($i = 0; $i < $string_length; $i++ ) {
+
+		$value = ord( $utf8_string[ $i ] );
+
+		if ( $value < 128 ) {
+			if ( $length && ( $unicode_length >= $length ) )
+				break;
+			$unicode .= chr($value);
+			$unicode_length++;
+		} else {
+			if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3;
+
+			$values[] = $value;
+
+			if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
+				break;
+			if ( count( $values ) == $num_octets ) {
+				if ($num_octets == 3) {
+					$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]);
+					$unicode_length += 9;
+				} else {
+					$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]);
+					$unicode_length += 6;
+				}
+
+				$values = array();
+				$num_octets = 1;
+			}
+		}
+	}
+
+	return $unicode;
+}
+endif;
+
+if ( !function_exists('remove_accents') ) :
+/**
+ * Converts all accent characters to ASCII characters.
+ *
+ * If there are no accent characters, then the string given is just returned.
+ *
+ * @since 1.2.1
+ *
+ * @param string $string Text that might have accent characters
+ * @return string Filtered string with replaced "nice" characters.
+ */
+function remove_accents($string) {
+	if ( !preg_match('/[\x80-\xff]/', $string) )
+		return $string;
+
+	if (seems_utf8($string)) {
+		$chars = array(
+		// Decompositions for Latin-1 Supplement
+		chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
+		chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
+		chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
+		chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
+		chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
+		chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
+		chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
+		chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
+		chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
+		chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
+		chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
+		chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
+		chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
+		chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
+		chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
+		chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
+		chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
+		chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
+		chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
+		chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
+		chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
+		chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
+		chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
+		chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
+		chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
+		chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
+		chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
+		chr(195).chr(191) => 'y',
+		// Decompositions for Latin Extended-A
+		chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
+		chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
+		chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
+		chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
+		chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
+		chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
+		chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
+		chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
+		chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
+		chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
+		chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
+		chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
+		chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
+		chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
+		chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
+		chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
+		chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
+		chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
+		chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
+		chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
+		chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
+		chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
+		chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
+		chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
+		chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
+		chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
+		chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
+		chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
+		chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
+		chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
+		chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
+		chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
+		chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
+		chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
+		chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
+		chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
+		chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
+		chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
+		chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
+		chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
+		chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
+		chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
+		chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
+		chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
+		chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
+		chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
+		chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
+		chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
+		chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
+		chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
+		chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
+		chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
+		chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
+		chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
+		chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
+		chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
+		chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
+		chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
+		chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
+		chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
+		chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
+		chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
+		chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
+		chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
+		// Euro Sign
+		chr(226).chr(130).chr(172) => 'E',
+		// GBP (Pound) Sign
+		chr(194).chr(163) => '');
+
+		$string = strtr($string, $chars);
+	} else {
+		// Assume ISO-8859-1 if not UTF-8
+		$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
+			.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
+			.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
+			.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
+			.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
+			.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
+			.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
+			.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
+			.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
+			.chr(252).chr(253).chr(255);
+
+		$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
+
+		$string = strtr($string, $chars['in'], $chars['out']);
+		$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
+		$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
+		$string = str_replace($double_chars['in'], $double_chars['out'], $string);
+	}
+
+	return $string;
+}
+endif;
+
+// ! function sanitize_file_name()
+
+if ( !function_exists('sanitize_user') ) :
+/**
+ * Sanitize username stripping out unsafe characters.
+ *
+ * If $strict is true, only alphanumeric characters (as well as _, space, ., -,
+ * @) are returned.
+ * Removes tags, octets, entities, and if strict is enabled, will remove all
+ * non-ASCII characters. After sanitizing, it passes the username, raw username
+ * (the username in the parameter), and the strict parameter as parameters for
+ * the filter.
+ *
+ * @since 2.0.0
+ * @uses apply_filters() Calls 'sanitize_user' hook on username, raw username,
+ *		and $strict parameter.
+ *
+ * @param string $username The username to be sanitized.
+ * @param bool $strict If set limits $username to specific characters. Default false.
+ * @return string The sanitized username, after passing through filters.
+ */
+function sanitize_user( $username, $strict = false ) {
+	$raw_username = $username;
+	$username = wp_strip_all_tags($username);
+	// Kill octets
+	$username = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $username);
+	$username = preg_replace('/&.+?;/', '', $username); // Kill entities
+
+	// If strict, reduce to ASCII for max portability.
+	if ( $strict )
+		$username = preg_replace('|[^a-z0-9 _.\-@]|i', '', $username);
+
+	// Consolidate contiguous whitespace
+	$username = preg_replace('|\s+|', ' ', $username);
+
+	return apply_filters('sanitize_user', $username, $raw_username, $strict);
+}
+endif;
+
+if ( !function_exists('sanitize_title') ) :
+/**
+ * Sanitizes title or use fallback title.
+ *
+ * Specifically, HTML and PHP tags are stripped. Further actions can be added
+ * via the plugin API. If $title is empty and $fallback_title is set, the latter
+ * will be used.
+ *
+ * @since 1.0.0
+ *
+ * @param string $title The string to be sanitized.
+ * @param string $fallback_title Optional. A title to use if $title is empty.
+ * @return string The sanitized string.
+ */
+function sanitize_title($title, $fallback_title = '') {
+	$raw_title = $title;
+	$title = strip_tags($title);
+	$title = apply_filters('sanitize_title', $title, $raw_title);
+
+	if ( '' === $title || false === $title )
+		$title = $fallback_title;
+
+	return $title;
+}
+endif;
+
+if ( !function_exists('sanitize_title_with_dashes') ) :
+/**
+ * Sanitizes title, replacing whitespace with dashes.
+ *
+ * Limits the output to alphanumeric characters, underscore (_) and dash (-).
+ * Whitespace becomes a dash.
+ *
+ * @since 1.2.0
+ *
+ * @param string $title The title to be sanitized.
+ * @return string The sanitized title.
+ */
+function sanitize_title_with_dashes($title) {
+	$title = strip_tags($title);
+	// Preserve escaped octets.
+	$title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
+	// Remove percent signs that are not part of an octet.
+	$title = str_replace('%', '', $title);
+	// Restore octets.
+	$title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
+
+	$title = remove_accents($title);
+	if (seems_utf8($title)) {
+		if (function_exists('mb_strtolower')) {
+			$title = mb_strtolower($title, 'UTF-8');
+		}
+		$title = utf8_uri_encode($title, 200);
+	}
+
+	$title = strtolower($title);
+	$title = preg_replace('/&.+?;/', '', $title); // kill entities
+	$title = str_replace('.', '-', $title);
+	$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
+	$title = preg_replace('/\s+/', '-', $title);
+	$title = preg_replace('|-+|', '-', $title);
+	$title = trim($title, '-');
+
+	return $title;
+}
+endif;
+
+// ! function sanitize_sql_orderby()
+// ! function sanitize_html_class()
+// ! function convert_chars()
+// ! function funky_javascript_callback()
+// ! function funky_javascript_fix()
+// ! function balanceTags()
+
+if ( !function_exists( 'force_balance_tags' ) ) :
+/**
+ * Balances tags of string using a modified stack.
+ *
+ * @since 2.0.4
+ *
+ * @author Leonard Lin <leonard@acm.org>
+ * @license GPL v2.0
+ * @copyright November 4, 2001
+ * @version 1.1
+ * @todo Make better - change loop condition to $text in 1.2
+ * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
+ *		1.1  Fixed handling of append/stack pop order of end text
+ *			 Added Cleaning Hooks
+ *		1.0  First Version
+ *
+ * @param string $text Text to be balanced.
+ * @return string Balanced text.
+ */
+function force_balance_tags( $text ) {
+	$tagstack = array(); $stacksize = 0; $tagqueue = ''; $newtext = '';
+	$single_tags = array('br', 'hr', 'img', 'input'); //Known single-entity/self-closing tags
+	$nestable_tags = array('blockquote', 'div', 'span'); //Tags that can be immediately nested within themselves
+
+	# WP bug fix for comments - in case you REALLY meant to type '< !--'
+	$text = str_replace('< !--', '<    !--', $text);
+	# WP bug fix for LOVE <3 (and other situations with '<' before a number)
+	$text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);
+
+	while (preg_match("/<(\/?\w*)\s*([^>]*)>/",$text,$regex)) {
+		$newtext .= $tagqueue;
+
+		$i = strpos($text,$regex[0]);
+		$l = strlen($regex[0]);
+
+		// clear the shifter
+		$tagqueue = '';
+		// Pop or Push
+		if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
+			$tag = strtolower(substr($regex[1],1));
+			// if too many closing tags
+			if($stacksize <= 0) {
+				$tag = '';
+				//or close to be safe $tag = '/' . $tag;
+			}
+			// if stacktop value = tag close value then pop
+			else if ($tagstack[$stacksize - 1] == $tag) { // found closing tag
+				$tag = '</' . $tag . '>'; // Close Tag
+				// Pop
+				array_pop ($tagstack);
+				$stacksize--;
+			} else { // closing tag not at top, search for it
+				for ($j=$stacksize-1;$j>=0;$j--) {
+					if ($tagstack[$j] == $tag) {
+					// add tag to tagqueue
+						for ($k=$stacksize-1;$k>=$j;$k--){
+							$tagqueue .= '</' . array_pop ($tagstack) . '>';
+							$stacksize--;
+						}
+						break;
+					}
+				}
+				$tag = '';
+			}
+		} else { // Begin Tag
+			$tag = strtolower($regex[1]);
+
+			// Tag Cleaning
+
+			// If self-closing or '', don't do anything.
+			if((substr($regex[2],-1) == '/') || ($tag == '')) {
+			}
+			// ElseIf it's a known single-entity tag but it doesn't close itself, do so
+			elseif ( in_array($tag, $single_tags) ) {
+				$regex[2] .= '/';
+			} else {	// Push the tag onto the stack
+				// If the top of the stack is the same as the tag we want to push, close previous tag
+				if (($stacksize > 0) && !in_array($tag, $nestable_tags) && ($tagstack[$stacksize - 1] == $tag)) {
+					$tagqueue = '</' . array_pop ($tagstack) . '>';
+					$stacksize--;
+				}
+				$stacksize = array_push ($tagstack, $tag);
+			}
+
+			// Attributes
+			$attributes = $regex[2];
+			if($attributes) {
+				$attributes = ' '.$attributes;
+			}
+			$tag = '<'.$tag.$attributes.'>';
+			//If already queuing a close tag, then put this tag on, too
+			if ($tagqueue) {
+				$tagqueue .= $tag;
+				$tag = '';
+			}
+		}
+		$newtext .= substr($text,0,$i) . $tag;
+		$text = substr($text,$i+$l);
+	}
+
+	// Clear Tag Queue
+	$newtext .= $tagqueue;
+
+	// Add Remaining text
+	$newtext .= $text;
+
+	// Empty Stack
+	while($x = array_pop($tagstack)) {
+		$newtext .= '</' . $x . '>'; // Add remaining tags to close
+	}
+
+	// WP fix for the bug with HTML comments
+	$newtext = str_replace("< !--","<!--",$newtext);
+	$newtext = str_replace("<    !--","< !--",$newtext);
+
+	return $newtext;
+}
+endif;
+
+if ( !function_exists('format_to_edit') ) :
+/**
+ * Acts on text which is about to be edited.
+ *
+ * Unless $richedit is set, it is simply a holder for the 'format_to_edit'
+ * filter. If $richedit is set true htmlspecialchars() will be run on the
+ * content, converting special characters to HTMl entities.
+ *
+ * @since 0.71
+ *
+ * @param string $content The text about to be edited.
+ * @param bool $richedit Whether or not the $content should pass through htmlspecialchars(). Default false.
+ * @return string The text after the filter (and possibly htmlspecialchars()) has been run.
+ */
+function format_to_edit($content, $richedit = false) {
+	$content = apply_filters('format_to_edit', $content);
+	if (! $richedit )
+		$content = htmlspecialchars($content);
+	return $content;
+}
+endif;
+
+// !format_to_post()
+
+if ( !function_exists( 'zeroise' ) ) :
+/**
+ * Add leading zeros when necessary.
+ *
+ * If you set the threshold to '4' and the number is '10', then you will get
+ * back '0010'. If you set the number to '4' and the number is '5000', then you
+ * will get back '5000'.
+ *
+ * Uses sprintf to append the amount of zeros based on the $threshold parameter
+ * and the size of the number. If the number is large enough, then no zeros will
+ * be appended.
+ *
+ * @since 0.71
+ *
+ * @param mixed $number Number to append zeros to if not greater than threshold.
+ * @param int $threshold Digit places number needs to be to not have zeros added.
+ * @return string Adds leading zeros to number if needed.
+ */
+function zeroise($number, $threshold) {
+	return sprintf('%0'.$threshold.'s', $number);
+}
+endif;
+
+if ( !function_exists( 'backslashit' ) ) :
+/**
+ * Adds backslashes before letters and before a number at the start of a string.
+ *
+ * @since 0.71
+ *
+ * @param string $string Value to which backslashes will be added.
+ * @return string String with backslashes inserted.
+ */
+function backslashit($string) {
+	$string = preg_replace('/^([0-9])/', '\\\\\\\\\1', $string);
+	$string = preg_replace('/([a-z])/i', '\\\\\1', $string);
+	return $string;
+}
+endif;
+
+if ( !function_exists( 'trailingslashit' ) ) :
+/**
+ * Appends a trailing slash.
+ *
+ * Will remove trailing slash if it exists already before adding a trailing
+ * slash. This prevents double slashing a string or path.
+ *
+ * The primary use of this is for paths and thus should be used for paths. It is
+ * not restricted to paths and offers no specific path support.
+ *
+ * @since 1.2.0
+ * @uses untrailingslashit() Unslashes string if it was slashed already.
+ *
+ * @param string $string What to add the trailing slash to.
+ * @return string String with trailing slash added.
+ */
+function trailingslashit($string) {
+	return untrailingslashit($string) . '/';
+}
+endif;
+
+if ( !function_exists( 'untrailingslashit' ) ) :
+/**
+ * Removes trailing slash if it exists.
+ *
+ * The primary use of this is for paths and thus should be used for paths. It is
+ * not restricted to paths and offers no specific path support.
+ *
+ * @since 2.2.0
+ *
+ * @param string $string What to remove the trailing slash from.
+ * @return string String without the trailing slash.
+ */
+function untrailingslashit($string) {
+	return rtrim($string, '/');
+}
+endif;
+
+// ! function addslashes_gpc()
+
+if ( !function_exists('stripslashes_deep') ) :
+/**
+ * Navigates through an array and removes slashes from the values.
+ *
+ * If an array is passed, the array_map() function causes a callback to pass the
+ * value back to the function. The slashes from this value will removed.
+ *
+ * @since 2.0.0
+ *
+ * @param array|string $value The array or string to be striped.
+ * @return array|string Stripped array (or string in the callback).
+ */
+function stripslashes_deep($value) {
+	$value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
+	return $value;
+}
+endif;
+
+if ( !function_exists( 'urlencode_deep' ) ) :
+/**
+ * Navigates through an array and encodes the values to be used in a URL.
+ *
+ * Uses a callback to pass the value of the array back to the function as a
+ * string.
+ *
+ * @since 2.2.0
+ *
+ * @param array|string $value The array or string to be encoded.
+ * @return array|string $value The encoded array (or string from the callback).
+ */
+function urlencode_deep($value) {
+	$value = is_array($value) ? array_map('urlencode_deep', $value) : urlencode($value);
+	return $value;
+}
+endif;
+
+// ! function antispambot()
+
+if ( !function_exists( '_make_url_clickable_cb' ) ) :
+/**
+ * Callback to convert URI match to HTML A element.
+ *
+ * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
+ * make_clickable()}.
+ *
+ * @since 2.3.2
+ * @access private
+ *
+ * @param array $matches Single Regex Match.
+ * @return string HTML A element with URI address.
+ */
+function _make_url_clickable_cb($matches) {
+	$url = $matches[2];
+
+	$url = esc_url($url);
+	if ( empty($url) )
+		return $matches[0];
+
+	return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>";
+}
+endif;
+
+if ( !function_exists( '_make_web_ftp_clickable_cb' ) ) :
+/**
+ * Callback to convert URL match to HTML A element.
+ *
+ * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
+ * make_clickable()}.
+ *
+ * @since 2.3.2
+ * @access private
+ *
+ * @param array $matches Single Regex Match.
+ * @return string HTML A element with URL address.
+ */
+function _make_web_ftp_clickable_cb($matches) {
+	$ret = '';
+	$dest = $matches[2];
+	$dest = 'http://' . $dest;
+	$dest = esc_url($dest);
+	if ( empty($dest) )
+		return $matches[0];
+
+	// removed trailing [.,;:)] from URL
+	if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
+		$ret = substr($dest, -1);
+		$dest = substr($dest, 0, strlen($dest)-1);
+	}
+	return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
+}
+endif;
+
+if ( !function_exists( '_make_email_clickable_cb' ) ) :
+/**
+ * Callback to convert email address match to HTML A element.
+ *
+ * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
+ * make_clickable()}.
+ *
+ * @since 2.3.2
+ * @access private
+ *
+ * @param array $matches Single Regex Match.
+ * @return string HTML A element with email address.
+ */
+function _make_email_clickable_cb($matches) {
+	$email = $matches[2] . '@' . $matches[3];
+	return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
+}
+endif;
+
+if ( !function_exists( 'make_clickable' ) ) :
+/**
+ * Convert plaintext URI to HTML links.
+ *
+ * Converts URI, www and ftp, and email addresses. Finishes by fixing links
+ * within links.
+ *
+ * @since 0.71
+ *
+ * @param string $ret Content to convert URIs.
+ * @return string Content with converted URIs.
+ */
+function make_clickable($ret) {
+	$ret = ' ' . $ret;
+	// in testing, using arrays here was found to be faster
+	$ret = preg_replace_callback('#(?<=[\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#$%&~/=?@\[\](+-]|[.,;:](?![\s<]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret);
+	$ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret);
+	$ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
+	// this one is not in an array because we need it to run last, for cleanup of accidental links within links
+	$ret = preg_replace("#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
+	$ret = trim($ret);
+	return $ret;
+}
+endif;
+
+// ! function wp_rel_nofollow()
+// ! function wp_rel_nofollow_callback()
+// ! function translate_smiley()
+// ! function convert_smilies()
+
+if ( !function_exists('is_email') ) :
+/**
+ * Verifies that an email is valid.
+ *
+ * Does not grok i18n domains. Not RFC compliant.
+ *
+ * @since 0.71
+ *
+ * @param string $email Email address to verify.
+ * @param boolean $check_dns Whether to check the DNS for the domain using checkdnsrr().
+ * @return string|bool Either false or the valid email address.
+ */
+function is_email( $email, $check_dns = false ) {
+	// Test for the minimum length the email can be
+	if ( strlen( $email ) < 3 ) {
+		return apply_filters( 'is_email', false, $email, 'email_too_short' );
+	}
+
+	// Test for an @ character after the first position
+	if ( strpos( $email, '@', 1 ) === false ) {
+		return apply_filters( 'is_email', false, $email, 'email_no_at' );
+	}
+
+	// Split out the local and domain parts
+	list( $local, $domain ) = explode( '@', $email, 2 );
+
+	// LOCAL PART
+	// Test for invalid characters
+	if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) {
+		return apply_filters( 'is_email', false, $email, 'local_invalid_chars' );
+	}
+
+	// DOMAIN PART
+	// Test for sequences of periods
+	if ( preg_match( '/\.{2,}/', $domain ) ) {
+		return apply_filters( 'is_email', false, $email, 'domain_period_sequence' );
+	}
+
+	// Test for leading and trailing periods and whitespace
+	if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) {
+		return apply_filters( 'is_email', false, $email, 'domain_period_limits' );
+	}
+
+	// Split the domain into subs
+	$subs = explode( '.', $domain );
+
+	// Assume the domain will have at least two subs
+	if ( 2 > count( $subs ) ) {
+		return apply_filters( 'is_email', false, $email, 'domain_no_periods' );
+	}
+
+	// Loop through each sub
+	foreach ( $subs as $sub ) {
+		// Test for leading and trailing hyphens and whitespace
+		if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) {
+			return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );
+		}
+
+		// Test for invalid characters
+		if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) {
+			return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' );
+		}
+	}
+
+	// DNS
+	// Check the domain has a valid MX and A resource record
+	if ( $check_dns && function_exists( 'checkdnsrr' ) && !( checkdnsrr( $domain . '.', 'MX' ) || checkdnsrr( $domain . '.', 'A' ) ) ) {
+		return apply_filters( 'is_email', false, $email, 'dns_no_rr' );
+	}
+
+	// Congratulations your email made it!
+	return apply_filters( 'is_email', $email, $email, null );
+}
+endif;
+
+// ! function wp_iso_descrambler()
+// ! function get_gmt_from_date()
+// ! function get_date_from_gmt()
+// ! function iso8601_timezone_to_offset()
+// ! function iso8601_to_datetime()
+// ! popuplinks()
+
+if ( !function_exists('sanitize_email') ) :
+/**
+ * Strips out all characters that are not allowable in an email.
+ *
+ * @since 1.5.0
+ *
+ * @param string $email Email address to filter.
+ * @return string Filtered email address.
+ */
+function sanitize_email( $email ) {
+	// Test for the minimum length the email can be
+	if ( strlen( $email ) < 3 ) {
+		return apply_filters( 'sanitize_email', '', $email, 'email_too_short' );
+	}
+
+	// Test for an @ character after the first position
+	if ( strpos( $email, '@', 1 ) === false ) {
+		return apply_filters( 'sanitize_email', '', $email, 'email_no_at' );
+	}
+
+	// Split out the local and domain parts
+	list( $local, $domain ) = explode( '@', $email, 2 );
+
+	// LOCAL PART
+	// Test for invalid characters
+	$local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
+	if ( '' === $local ) {
+		return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
+	}
+
+	// DOMAIN PART
+	// Test for sequences of periods
+	$domain = preg_replace( '/\.{2,}/', '', $domain );
+	if ( '' === $domain ) {
+		return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
+	}
+
+	// Test for leading and trailing periods and whitespace
+	$domain = trim( $domain, " \t\n\r\0\x0B." );
+	if ( '' === $domain ) {
+		return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' );
+	}
+
+	// Split the domain into subs
+	$subs = explode( '.', $domain );
+
+	// Assume the domain will have at least two subs
+	if ( 2 > count( $subs ) ) {
+		return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' );
+	}
+
+	// Create an array that will contain valid subs
+	$new_subs = array();
+
+	// Loop through each sub
+	foreach ( $subs as $sub ) {
+		// Test for leading and trailing hyphens
+		$sub = trim( $sub, " \t\n\r\0\x0B-" );
+
+		// Test for invalid characters
+		$sub = preg_replace( '/^[^a-z0-9-]+$/i', '', $sub );
+
+		// If there's anything left, add it to the valid subs
+		if ( '' !== $sub ) {
+			$new_subs[] = $sub;
+		}
+	}
+
+	// If there aren't 2 or more valid subs
+	if ( 2 > count( $new_subs ) ) {
+		return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' );
+	}
+
+	// Join valid subs into the new domain
+	$domain = join( '.', $new_subs );
+
+	// Put the email back together
+	$email = $local . '@' . $domain;
+
+	// Congratulations your email made it!
+	return apply_filters( 'sanitize_email', $email, $email, null );
+}
+endif;
+
+// ! function human_time_diff()
+// ! function wp_trim_excerpt()
+
+if ( !function_exists( 'ent2ncr' ) ) : // Current at [WP9840]
+/**
+ * Converts named entities into numbered entities.
+ *
+ * @since 1.5.1
+ *
+ * @param string $text The text within which entities will be converted.
+ * @return string Text with converted entities.
+ */
+function ent2ncr($text) {
+	$to_ncr = array(
+		'&quot;' => '&#34;',
+		'&amp;' => '&#38;',
+		'&frasl;' => '&#47;',
+		'&lt;' => '&#60;',
+		'&gt;' => '&#62;',
+		'|' => '&#124;',
+		'&nbsp;' => '&#160;',
+		'&iexcl;' => '&#161;',
+		'&cent;' => '&#162;',
+		'&pound;' => '&#163;',
+		'&curren;' => '&#164;',
+		'&yen;' => '&#165;',
+		'&brvbar;' => '&#166;',
+		'&brkbar;' => '&#166;',
+		'&sect;' => '&#167;',
+		'&uml;' => '&#168;',
+		'&die;' => '&#168;',
+		'&copy;' => '&#169;',
+		'&ordf;' => '&#170;',
+		'&laquo;' => '&#171;',
+		'&not;' => '&#172;',
+		'&shy;' => '&#173;',
+		'&reg;' => '&#174;',
+		'&macr;' => '&#175;',
+		'&hibar;' => '&#175;',
+		'&deg;' => '&#176;',
+		'&plusmn;' => '&#177;',
+		'&sup2;' => '&#178;',
+		'&sup3;' => '&#179;',
+		'&acute;' => '&#180;',
+		'&micro;' => '&#181;',
+		'&para;' => '&#182;',
+		'&middot;' => '&#183;',
+		'&cedil;' => '&#184;',
+		'&sup1;' => '&#185;',
+		'&ordm;' => '&#186;',
+		'&raquo;' => '&#187;',
+		'&frac14;' => '&#188;',
+		'&frac12;' => '&#189;',
+		'&frac34;' => '&#190;',
+		'&iquest;' => '&#191;',
+		'&Agrave;' => '&#192;',
+		'&Aacute;' => '&#193;',
+		'&Acirc;' => '&#194;',
+		'&Atilde;' => '&#195;',
+		'&Auml;' => '&#196;',
+		'&Aring;' => '&#197;',
+		'&AElig;' => '&#198;',
+		'&Ccedil;' => '&#199;',
+		'&Egrave;' => '&#200;',
+		'&Eacute;' => '&#201;',
+		'&Ecirc;' => '&#202;',
+		'&Euml;' => '&#203;',
+		'&Igrave;' => '&#204;',
+		'&Iacute;' => '&#205;',
+		'&Icirc;' => '&#206;',
+		'&Iuml;' => '&#207;',
+		'&ETH;' => '&#208;',
+		'&Ntilde;' => '&#209;',
+		'&Ograve;' => '&#210;',
+		'&Oacute;' => '&#211;',
+		'&Ocirc;' => '&#212;',
+		'&Otilde;' => '&#213;',
+		'&Ouml;' => '&#214;',
+		'&times;' => '&#215;',
+		'&Oslash;' => '&#216;',
+		'&Ugrave;' => '&#217;',
+		'&Uacute;' => '&#218;',
+		'&Ucirc;' => '&#219;',
+		'&Uuml;' => '&#220;',
+		'&Yacute;' => '&#221;',
+		'&THORN;' => '&#222;',
+		'&szlig;' => '&#223;',
+		'&agrave;' => '&#224;',
+		'&aacute;' => '&#225;',
+		'&acirc;' => '&#226;',
+		'&atilde;' => '&#227;',
+		'&auml;' => '&#228;',
+		'&aring;' => '&#229;',
+		'&aelig;' => '&#230;',
+		'&ccedil;' => '&#231;',
+		'&egrave;' => '&#232;',
+		'&eacute;' => '&#233;',
+		'&ecirc;' => '&#234;',
+		'&euml;' => '&#235;',
+		'&igrave;' => '&#236;',
+		'&iacute;' => '&#237;',
+		'&icirc;' => '&#238;',
+		'&iuml;' => '&#239;',
+		'&eth;' => '&#240;',
+		'&ntilde;' => '&#241;',
+		'&ograve;' => '&#242;',
+		'&oacute;' => '&#243;',
+		'&ocirc;' => '&#244;',
+		'&otilde;' => '&#245;',
+		'&ouml;' => '&#246;',
+		'&divide;' => '&#247;',
+		'&oslash;' => '&#248;',
+		'&ugrave;' => '&#249;',
+		'&uacute;' => '&#250;',
+		'&ucirc;' => '&#251;',
+		'&uuml;' => '&#252;',
+		'&yacute;' => '&#253;',
+		'&thorn;' => '&#254;',
+		'&yuml;' => '&#255;',
+		'&OElig;' => '&#338;',
+		'&oelig;' => '&#339;',
+		'&Scaron;' => '&#352;',
+		'&scaron;' => '&#353;',
+		'&Yuml;' => '&#376;',
+		'&fnof;' => '&#402;',
+		'&circ;' => '&#710;',
+		'&tilde;' => '&#732;',
+		'&Alpha;' => '&#913;',
+		'&Beta;' => '&#914;',
+		'&Gamma;' => '&#915;',
+		'&Delta;' => '&#916;',
+		'&Epsilon;' => '&#917;',
+		'&Zeta;' => '&#918;',
+		'&Eta;' => '&#919;',
+		'&Theta;' => '&#920;',
+		'&Iota;' => '&#921;',
+		'&Kappa;' => '&#922;',
+		'&Lambda;' => '&#923;',
+		'&Mu;' => '&#924;',
+		'&Nu;' => '&#925;',
+		'&Xi;' => '&#926;',
+		'&Omicron;' => '&#927;',
+		'&Pi;' => '&#928;',
+		'&Rho;' => '&#929;',
+		'&Sigma;' => '&#931;',
+		'&Tau;' => '&#932;',
+		'&Upsilon;' => '&#933;',
+		'&Phi;' => '&#934;',
+		'&Chi;' => '&#935;',
+		'&Psi;' => '&#936;',
+		'&Omega;' => '&#937;',
+		'&alpha;' => '&#945;',
+		'&beta;' => '&#946;',
+		'&gamma;' => '&#947;',
+		'&delta;' => '&#948;',
+		'&epsilon;' => '&#949;',
+		'&zeta;' => '&#950;',
+		'&eta;' => '&#951;',
+		'&theta;' => '&#952;',
+		'&iota;' => '&#953;',
+		'&kappa;' => '&#954;',
+		'&lambda;' => '&#955;',
+		'&mu;' => '&#956;',
+		'&nu;' => '&#957;',
+		'&xi;' => '&#958;',
+		'&omicron;' => '&#959;',
+		'&pi;' => '&#960;',
+		'&rho;' => '&#961;',
+		'&sigmaf;' => '&#962;',
+		'&sigma;' => '&#963;',
+		'&tau;' => '&#964;',
+		'&upsilon;' => '&#965;',
+		'&phi;' => '&#966;',
+		'&chi;' => '&#967;',
+		'&psi;' => '&#968;',
+		'&omega;' => '&#969;',
+		'&thetasym;' => '&#977;',
+		'&upsih;' => '&#978;',
+		'&piv;' => '&#982;',
+		'&ensp;' => '&#8194;',
+		'&emsp;' => '&#8195;',
+		'&thinsp;' => '&#8201;',
+		'&zwnj;' => '&#8204;',
+		'&zwj;' => '&#8205;',
+		'&lrm;' => '&#8206;',
+		'&rlm;' => '&#8207;',
+		'&ndash;' => '&#8211;',
+		'&mdash;' => '&#8212;',
+		'&lsquo;' => '&#8216;',
+		'&rsquo;' => '&#8217;',
+		'&sbquo;' => '&#8218;',
+		'&ldquo;' => '&#8220;',
+		'&rdquo;' => '&#8221;',
+		'&bdquo;' => '&#8222;',
+		'&dagger;' => '&#8224;',
+		'&Dagger;' => '&#8225;',
+		'&bull;' => '&#8226;',
+		'&hellip;' => '&#8230;',
+		'&permil;' => '&#8240;',
+		'&prime;' => '&#8242;',
+		'&Prime;' => '&#8243;',
+		'&lsaquo;' => '&#8249;',
+		'&rsaquo;' => '&#8250;',
+		'&oline;' => '&#8254;',
+		'&frasl;' => '&#8260;',
+		'&euro;' => '&#8364;',
+		'&image;' => '&#8465;',
+		'&weierp;' => '&#8472;',
+		'&real;' => '&#8476;',
+		'&trade;' => '&#8482;',
+		'&alefsym;' => '&#8501;',
+		'&crarr;' => '&#8629;',
+		'&lArr;' => '&#8656;',
+		'&uArr;' => '&#8657;',
+		'&rArr;' => '&#8658;',
+		'&dArr;' => '&#8659;',
+		'&hArr;' => '&#8660;',
+		'&forall;' => '&#8704;',
+		'&part;' => '&#8706;',
+		'&exist;' => '&#8707;',
+		'&empty;' => '&#8709;',
+		'&nabla;' => '&#8711;',
+		'&isin;' => '&#8712;',
+		'&notin;' => '&#8713;',
+		'&ni;' => '&#8715;',
+		'&prod;' => '&#8719;',
+		'&sum;' => '&#8721;',
+		'&minus;' => '&#8722;',
+		'&lowast;' => '&#8727;',
+		'&radic;' => '&#8730;',
+		'&prop;' => '&#8733;',
+		'&infin;' => '&#8734;',
+		'&ang;' => '&#8736;',
+		'&and;' => '&#8743;',
+		'&or;' => '&#8744;',
+		'&cap;' => '&#8745;',
+		'&cup;' => '&#8746;',
+		'&int;' => '&#8747;',
+		'&there4;' => '&#8756;',
+		'&sim;' => '&#8764;',
+		'&cong;' => '&#8773;',
+		'&asymp;' => '&#8776;',
+		'&ne;' => '&#8800;',
+		'&equiv;' => '&#8801;',
+		'&le;' => '&#8804;',
+		'&ge;' => '&#8805;',
+		'&sub;' => '&#8834;',
+		'&sup;' => '&#8835;',
+		'&nsub;' => '&#8836;',
+		'&sube;' => '&#8838;',
+		'&supe;' => '&#8839;',
+		'&oplus;' => '&#8853;',
+		'&otimes;' => '&#8855;',
+		'&perp;' => '&#8869;',
+		'&sdot;' => '&#8901;',
+		'&lceil;' => '&#8968;',
+		'&rceil;' => '&#8969;',
+		'&lfloor;' => '&#8970;',
+		'&rfloor;' => '&#8971;',
+		'&lang;' => '&#9001;',
+		'&rang;' => '&#9002;',
+		'&larr;' => '&#8592;',
+		'&uarr;' => '&#8593;',
+		'&rarr;' => '&#8594;',
+		'&darr;' => '&#8595;',
+		'&harr;' => '&#8596;',
+		'&loz;' => '&#9674;',
+		'&spades;' => '&#9824;',
+		'&clubs;' => '&#9827;',
+		'&hearts;' => '&#9829;',
+		'&diams;' => '&#9830;'
+	);
+
+	return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
+}
+endif;
+
+// ! function wp_richedit_pre()
+// ! function wp_htmledit_pre()
+
+if ( !function_exists('clean_url') ) :
+/**
+ * Checks and cleans a URL.
+ *
+ * A number of characters are removed from the URL. If the URL is for displaying
+ * (the default behaviour) amperstands are also replaced. The 'esc_url' filter
+ * is applied to the returned cleaned URL.
+ *
+ * @since 1.2.0
+ * @uses wp_kses_bad_protocol() To only permit protocols in the URL set
+ *		via $protocols or the common ones set in the function.
+ *
+ * @param string $url The URL to be cleaned.
+ * @param array $protocols Optional. An array of acceptable protocols.
+ *		Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set.
+ * @param string $context Optional. How the URL will be used. Default is 'display'.
+ * @return string The cleaned $url after the 'cleaned_url' filter is applied.
+ */
+function clean_url( $url, $protocols = null, $context = 'display' ) {
+	$original_url = $url;
+
+	if ( '' == $url )
+		return $url;
+	$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
+	$strip = array('%0d', '%0a', '%0D', '%0A');
+	$url = _deep_replace($strip, $url);
+	$url = str_replace(';//', '://', $url);
+	/* If the URL doesn't appear to contain a scheme, we
+	 * presume it needs http:// appended (unless a relative
+	 * link starting with / or a php file).
+	 */
+	if ( strpos($url, ':') === false &&
+		substr( $url, 0, 1 ) != '/' && substr( $url, 0, 1 ) != '#' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) )
+		$url = 'http://' . $url;
+
+	// Replace ampersands and single quotes only when displaying.
+	if ( 'display' == $context ) {
+		$url = wp_kses_normalize_entities( $url );
+		$url = str_replace( '&amp;', '&#038;', $url );
+		$url = str_replace( "'", '&#039;', $url );
+	}
+
+	if ( !is_array($protocols) )
+		$protocols = array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn');
+	if ( wp_kses_bad_protocol( $url, $protocols ) != $url )
+		return '';
+
+	return apply_filters('clean_url', $url, $original_url, $context);
+}
+endif;
+
+if ( !function_exists( '_deep_replace' ) ) :
+/**
+ * Perform a deep string replace operation to ensure the values in $search are no longer present
+ *
+ * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
+ * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
+ * str_replace would return
+ *
+ * @since 2.8.1
+ * @access private
+ *
+ * @param string|array $search
+ * @param string $subject
+ * @return string The processed string
+ */
+function _deep_replace($search, $subject){
+	$found = true;
+	while($found) {
+		$found = false;
+		foreach( (array) $search as $val ) {
+			while(strpos($subject, $val) !== false) {
+				$found = true;
+				$subject = str_replace($val, '', $subject);
+			}
+		}
+	}
+
+	return $subject;
+}
+endif;
+
+if ( !function_exists( 'esc_sql' ) ) :
+/**
+ * Escapes data for use in a MySQL query
+ *
+ * This is just a handy shortcut for $bpdb->escape(), for completeness' sake
+ *
+ * @since 2.8.0
+ * @param string $sql Unescaped SQL data
+ * @return string The cleaned $sql
+ */
+function esc_sql( $sql ) {
+	global $bpdb;
+	return $bpdb->escape( $sql );
+}
+endif;
+
+if ( !function_exists( 'esc_url' ) ) :
+/**
+ * Checks and cleans a URL.
+ *
+ * A number of characters are removed from the URL. If the URL is for displaying
+ * (the default behaviour) amperstands are also replaced. The 'esc_url' filter
+ * is applied to the returned cleaned URL.
+ *
+ * @since 2.8.0
+ * @uses esc_url()
+ * @uses wp_kses_bad_protocol() To only permit protocols in the URL set
+ *		via $protocols or the common ones set in the function.
+ *
+ * @param string $url The URL to be cleaned.
+ * @param array $protocols Optional. An array of acceptable protocols.
+ *		Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet' if not set.
+ * @return string The cleaned $url after the 'cleaned_url' filter is applied.
+ */
+function esc_url( $url, $protocols = null ) {
+	return clean_url( $url, $protocols, 'display' );
+}
+endif;
+
+if ( !function_exists( 'esc_url_raw' ) ) :
+/**
+ * Performs esc_url() for database usage.
+ *
+ * @see esc_url()
+ * @see esc_url()
+ *
+ * @since 2.8.0
+ *
+ * @param string $url The URL to be cleaned.
+ * @param array $protocols An array of acceptable protocols.
+ * @return string The cleaned URL.
+ */
+function esc_url_raw( $url, $protocols = null ) {
+	return clean_url( $url, $protocols, 'db' );
+}
+endif;
+
+// ! function sanitize_url()
+// ! function htmlentities2()
+
+if ( !function_exists( 'esc_js' ) ) :
+/**
+ * Escape single quotes, htmlspecialchar " < > &, and fix line endings.
+ *
+ * Escapes text strings for echoing in JS, both inline (for example in onclick="...")
+ * and inside <script> tag. Note that the strings have to be in single quotes.
+ * The filter 'js_escape' is also applied here.
+ *
+ * @since 2.8.0
+ *
+ * @param string $text The text to be escaped.
+ * @return string Escaped text.
+ */
+function esc_js( $text ) {
+	$safe_text = wp_check_invalid_utf8( $text );
+	$safe_text = _wp_specialchars( $safe_text, ENT_COMPAT );
+	$safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
+	$safe_text = str_replace( "\r", '', $safe_text );
+	$safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
+	return apply_filters( 'js_escape', $safe_text, $text );
+}
+endif;
+
+if ( !function_exists( 'js_escape' ) ) :
+/**
+ * Escape single quotes, specialchar double quotes, and fix line endings.
+ *
+ * The filter 'js_escape' is also applied by esc_js()
+ *
+ * @since 2.0.4
+ *
+ * @deprecated 2.8.0
+ * @see esc_js()
+ *
+ * @param string $text The text to be escaped.
+ * @return string Escaped text.
+ */
+function js_escape( $text ) {
+	return esc_js( $text );
+}
+endif;
+
+if ( !function_exists( 'esc_html' ) ) :
+/**
+ * Escaping for HTML blocks.
+ *
+ * @since 2.8.0
+ *
+ * @param string $text
+ * @return string
+ */
+function esc_html( $text ) {
+	$safe_text = wp_check_invalid_utf8( $text );
+	$safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
+	return apply_filters( 'esc_html', $safe_text, $text );
+	return $text;
+}
+endif;
+
+if ( !function_exists( 'wp_specialchars' ) ) :
+/**
+ * Escaping for HTML blocks
+ * @deprecated 2.8.0
+ * @see esc_html()
+ */
+function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
+	if ( func_num_args() > 1 ) { // Maintain backwards compat for people passing additional args
+		$args = func_get_args();
+		return call_user_func_array( '_wp_specialchars', $args );
+	} else {
+		return esc_html( $string );
+	}
+}
+endif;
+
+if ( !function_exists( 'esc_attr' ) ) :
+/**
+ * Escaping for HTML attributes.
+ *
+ * @since 2.8.0
+ *
+ * @param string $text
+ * @return string
+ */
+function esc_attr( $text ) {
+	$safe_text = wp_check_invalid_utf8( $text );
+	$safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
+	return apply_filters( 'attribute_escape', $safe_text, $text );
+}
+endif;
+
+if ( !function_exists( 'attribute_escape' ) ) :
+/**
+ * Escaping for HTML attributes.
+ *
+ * @since 2.0.6
+ *
+ * @deprecated 2.8.0
+ * @see esc_attr()
+ *
+ * @param string $text
+ * @return string
+ */
+function attribute_escape( $text ) {
+	return esc_attr( $text );
+}
+endif;
+
+// ! function tag_escape()
+
+if ( !function_exists('like_escape') ) :
+/**
+ * Escapes text for SQL LIKE special characters % and _.
+ *
+ * @since 2.5.0
+ *
+ * @param string $text The text to be escaped.
+ * @return string text, safe for inclusion in LIKE query.
+ */
+function like_escape($text) {
+	return addcslashes($text, '\\%_');
+}
+endif;
+
+// ! function wp_make_link_relative()
+// ! function sanitize_option()
+
+if ( !function_exists('wp_parse_str') ) :
+/**
+ * Parses a string into variables to be stored in an array.
+ *
+ * Uses {@link http://www.php.net/parse_str parse_str()} and stripslashes if
+ * {@link http://www.php.net/magic_quotes magic_quotes_gpc} is on.
+ *
+ * @since 2.2.1
+ * @uses apply_filters() for the 'wp_parse_str' filter.
+ *
+ * @param string $string The string to be parsed.
+ * @param array $array Variables will be stored in this array.
+ */
+function wp_parse_str( $string, &$array ) {
+	parse_str( $string, $array );
+	if ( get_magic_quotes_gpc() )
+		$array = stripslashes_deep( $array );
+	$array = apply_filters( 'wp_parse_str', $array );
+}
+endif;
+
+if ( !function_exists('wp_pre_kses_less_than') ) :
+/**
+ * Convert lone less than signs.
+ *
+ * KSES already converts lone greater than signs.
+ *
+ * @uses wp_pre_kses_less_than_callback in the callback function.
+ * @since 2.3.0
+ *
+ * @param string $text Text to be converted.
+ * @return string Converted text.
+ */
+function wp_pre_kses_less_than( $text ) {
+	return preg_replace_callback('%<[^>]*?((?=<)|>|$)%', 'wp_pre_kses_less_than_callback', $text);
+}
+endif;
+if ( !function_exists('wp_pre_kses_less_than_callback') ) :
+/**
+ * Callback function used by preg_replace.
+ *
+ * @uses esc_html to format the $matches text.
+ * @since 2.3.0
+ *
+ * @param array $matches Populated by matches to preg_replace.
+ * @return string The text returned after esc_html if needed.
+ */
+function wp_pre_kses_less_than_callback( $matches ) {
+	if ( false === strpos($matches[0], '>') )
+		return esc_html($matches[0]);
+	return $matches[0];
+}
+endif;
+// ! function wp_sprintf()
+// ! function wp_sprintf_l()
+
+if ( !function_exists('wp_html_excerpt') ) :
+/**
+ * Safely extracts not more than the first $count characters from html string.
+ *
+ * UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
+ * be counted as one character. For example &amp; will be counted as 4, &lt; as
+ * 3, etc.
+ *
+ * @since 2.5.0
+ *
+ * @param integer $str String to get the excerpt from.
+ * @param integer $count Maximum number of characters to take.
+ * @return string The excerpt.
+ */
+function wp_html_excerpt( $str, $count ) {
+	$str = wp_strip_all_tags( $str, true );
+	$str = mb_substr( $str, 0, $count );
+	// remove part of an entity at the end
+	$str = preg_replace( '/&[^;\s]{0,6}$/', '', $str );
+	return $str;
+}
+endif;
+
+// ! function links_add_base_url()
+// ! function _links_add_base()
+// ! function links_add_target()
+// ! function _links_add_target()
+// ! function normalize_whitespace()
+
+if ( !function_exists('wp_strip_all_tags') ) :
+/**
+ * Properly strip all HTML tags including script and style
+ *
+ * @since 2.9.0
+ *
+ * @param string $string String containing HTML tags
+ * @param bool $remove_breaks optional Whether to remove left over line breaks and white space chars
+ * @return string The processed string.
+ */
+function wp_strip_all_tags($string, $remove_breaks = false) {
+	$string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
+	$string = strip_tags($string);
+
+	if ( $remove_breaks )
+		$string = preg_replace('/[\r\n\t ]+/', ' ', $string);
+
+	return trim($string);
+}
+endif;
+
+if ( !function_exists('sanitize_text_field') ) :
+/**
+ * Sanitize a string from user input or from the db
+ *
+ * check for invalid UTF-8,
+ * Convert single < characters to entity,
+ * strip all tags,
+ * remove line breaks, tabs and extra whitre space,
+ * strip octets.
+ *
+ * @since 2.9
+ *
+ * @param string $str
+ * @return string
+ */
+function sanitize_text_field($str) {
+	$filtered = wp_check_invalid_utf8( $str );
+
+	if ( strpos($filtered, '<') !== false ) {
+		$filtered = wp_pre_kses_less_than( $filtered );
+		// This will strip extra whitespace for us.
+		$filtered = wp_strip_all_tags( $filtered, true );
+	} else {
+		$filtered = trim( preg_replace('/[\r\n\t ]+/', ' ', $filtered) );
+	}
+
+	$match = array();
+	$found = false;
+	while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
+		$filtered = str_replace($match[0], '', $filtered);
+		$found = true;
+	}
+
+	if ( $found ) {
+		// Strip out the whitespace that may now exist after removing the octets.
+		$filtered = trim( preg_replace('/ +/', ' ', $filtered) );
+	}
+
+	return apply_filters('sanitize_text_field', $filtered, $str);
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.kses.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.kses.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.kses.php	(revision 5057)
@@ -0,0 +1,1353 @@
+<?php
+// Last sync [WP17185]
+
+/**
+ * kses 0.2.2 - HTML/XHTML filter that only allows some elements and attributes
+ * Copyright (C) 2002, 2003, 2005  Ulf Harnhammar
+ *
+ * This program is free software and open source software; you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit
+ * http://www.gnu.org/licenses/gpl.html
+ * 
+ * [kses strips evil scripts!]
+ *
+ * Added wp_ prefix to avoid conflicts with existing kses users
+ *
+ * @version 0.2.2
+ * @copyright (C) 2002, 2003, 2005
+ * @author Ulf Harnhammar <http://advogato.org/person/metaur/>
+ *
+ * @package External
+ * @subpackage KSES
+ *
+ */
+
+/**
+ * You can override this in a plugin.
+ *
+ * @since 1.2.0
+ */
+if ( ! defined( 'BP_CUSTOM_TAGS' ) )
+	define( 'BP_CUSTOM_TAGS', false );
+
+if ( ! BP_CUSTOM_TAGS ) {
+	/**
+	 * Kses global for default allowable HTML tags.
+	 *
+	 * Can be override by using CUSTOM_TAGS constant.
+	 *
+	 * @global array $allowedposttags
+	 * @since 2.0.0
+	 */
+	$allowedposttags = array(
+		'address' => array(),
+		'a' => array(
+			'class' => array (),
+			'href' => array (),
+			'id' => array (),
+			'title' => array (),
+			'rel' => array (),
+			'rev' => array (),
+			'name' => array (),
+			'target' => array()),
+		'abbr' => array(
+			'class' => array (),
+			'title' => array ()),
+		'acronym' => array(
+			'title' => array ()),
+		'article' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'aside' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'b' => array(),
+		'big' => array(),
+		'blockquote' => array(
+			'id' => array (),
+			'cite' => array (),
+			'class' => array(),
+			'lang' => array(),
+			'xml:lang' => array()),
+		'br' => array (
+			'class' => array ()),
+		'button' => array(
+			'disabled' => array (),
+			'name' => array (),
+			'type' => array (),
+			'value' => array ()),
+		'caption' => array(
+			'align' => array (),
+			'class' => array ()),
+		'cite' => array (
+			'class' => array(),
+			'dir' => array(),
+			'lang' => array(),
+			'title' => array ()),
+		'code' => array (
+			'style' => array()),
+		'col' => array(
+			'align' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'span' => array (),
+			'dir' => array(),
+			'style' => array (),
+			'valign' => array (),
+			'width' => array ()),
+		'del' => array(
+			'datetime' => array ()),
+		'dd' => array(),
+		'details' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'open' => array (),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'div' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array()),
+		'dl' => array(),
+		'dt' => array(),
+		'em' => array(),
+		'fieldset' => array(),
+		'figure' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'figcaption' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'font' => array(
+			'color' => array (),
+			'face' => array (),
+			'size' => array ()),
+		'footer' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'form' => array(
+			'action' => array (),
+			'accept' => array (),
+			'accept-charset' => array (),
+			'enctype' => array (),
+			'method' => array (),
+			'name' => array (),
+			'target' => array ()),
+		'h1' => array(
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'h2' => array (
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'h3' => array (
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'h4' => array (
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'h5' => array (
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'h6' => array (
+			'align' => array (),
+			'class' => array (),
+			'id'    => array (),
+			'style' => array ()),
+		'header' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'hgroup' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'hr' => array (
+			'align' => array (),
+			'class' => array (),
+			'noshade' => array (),
+			'size' => array (),
+			'width' => array ()),
+		'i' => array(),
+		'img' => array(
+			'alt' => array (),
+			'align' => array (),
+			'border' => array (),
+			'class' => array (),
+			'height' => array (),
+			'hspace' => array (),
+			'longdesc' => array (),
+			'vspace' => array (),
+			'src' => array (),
+			'style' => array (),
+			'width' => array ()),
+		'ins' => array(
+			'datetime' => array (),
+			'cite' => array ()),
+		'kbd' => array(),
+		'label' => array(
+			'for' => array ()),
+		'legend' => array(
+			'align' => array ()),
+		'li' => array (
+			'align' => array (),
+			'class' => array ()),
+		'menu' => array (
+			'class' => array (),
+			'style' => array (),
+			'type' => array ()),
+		'nav' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'p' => array(
+			'class' => array (),
+			'align' => array (),
+			'dir' => array(),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array()),
+		'pre' => array(
+			'style' => array(),
+			'width' => array ()),
+		'q' => array(
+			'cite' => array ()),
+		's' => array(),
+		'span' => array (
+			'class' => array (),
+			'dir' => array (),
+			'align' => array (),
+			'lang' => array (),
+			'style' => array (),
+			'title' => array (),
+			'xml:lang' => array()),
+		'section' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'strike' => array(),
+		'strong' => array(),
+		'sub' => array(),
+		'summary' => array(
+			'align' => array (),
+			'class' => array (),
+			'dir' => array (),
+			'lang' => array(),
+			'style' => array (),
+			'xml:lang' => array(),
+		),
+		'sup' => array(),
+		'table' => array(
+			'align' => array (),
+			'bgcolor' => array (),
+			'border' => array (),
+			'cellpadding' => array (),
+			'cellspacing' => array (),
+			'class' => array (),
+			'dir' => array(),
+			'id' => array(),
+			'rules' => array (),
+			'style' => array (),
+			'summary' => array (),
+			'width' => array ()),
+		'tbody' => array(
+			'align' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'valign' => array ()),
+		'td' => array(
+			'abbr' => array (),
+			'align' => array (),
+			'axis' => array (),
+			'bgcolor' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'class' => array (),
+			'colspan' => array (),
+			'dir' => array(),
+			'headers' => array (),
+			'height' => array (),
+			'nowrap' => array (),
+			'rowspan' => array (),
+			'scope' => array (),
+			'style' => array (),
+			'valign' => array (),
+			'width' => array ()),
+		'textarea' => array(
+			'cols' => array (),
+			'rows' => array (),
+			'disabled' => array (),
+			'name' => array (),
+			'readonly' => array ()),
+		'tfoot' => array(
+			'align' => array (),
+			'char' => array (),
+			'class' => array (),
+			'charoff' => array (),
+			'valign' => array ()),
+		'th' => array(
+			'abbr' => array (),
+			'align' => array (),
+			'axis' => array (),
+			'bgcolor' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'class' => array (),
+			'colspan' => array (),
+			'headers' => array (),
+			'height' => array (),
+			'nowrap' => array (),
+			'rowspan' => array (),
+			'scope' => array (),
+			'valign' => array (),
+			'width' => array ()),
+		'thead' => array(
+			'align' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'class' => array (),
+			'valign' => array ()),
+		'title' => array(),
+		'tr' => array(
+			'align' => array (),
+			'bgcolor' => array (),
+			'char' => array (),
+			'charoff' => array (),
+			'class' => array (),
+			'style' => array (),
+			'valign' => array ()),
+		'tt' => array(),
+		'u' => array(),
+		'ul' => array (
+			'class' => array (),
+			'style' => array (),
+			'type' => array ()),
+		'ol' => array (
+			'class' => array (),
+			'start' => array (),
+			'style' => array (),
+			'type' => array ()),
+		'var' => array ());
+
+	/**
+	 * Kses allowed HTML elements.
+	 *
+	 * @global array $allowedtags
+	 * @since 1.0.0
+	 */
+	$allowedtags = array(
+		'a' => array(
+			'href' => array (),
+			'title' => array ()),
+		'abbr' => array(
+			'title' => array ()),
+		'acronym' => array(
+			'title' => array ()),
+		'b' => array(),
+		'blockquote' => array(
+			'cite' => array ()),
+		//	'br' => array(),
+		'cite' => array (),
+		'code' => array(),
+		'del' => array(
+			'datetime' => array ()),
+		//	'dd' => array(),
+		//	'dl' => array(),
+		//	'dt' => array(),
+		'em' => array (), 'i' => array (),
+		//	'ins' => array('datetime' => array(), 'cite' => array()),
+		//	'li' => array(),
+		//	'ol' => array(),
+		//	'p' => array(),
+		'q' => array(
+			'cite' => array ()),
+		'strike' => array(),
+		'strong' => array(),
+		//	'sub' => array(),
+		//	'sup' => array(),
+		//	'u' => array(),
+		//	'ul' => array(),
+	);
+
+	$allowedentitynames = array(
+		'nbsp',    'iexcl',  'cent',    'pound',  'curren', 'yen',
+		'brvbar',  'sect',   'uml',     'copy',   'ordf',   'laquo',
+		'not',     'shy',    'reg',     'macr',   'deg',    'plusmn',
+		'acute',   'micro',  'para',    'middot', 'cedil',  'ordm',
+		'raquo',   'iquest', 'Agrave',  'Aacute', 'Acirc',  'Atilde',
+		'Auml',    'Aring',  'AElig',   'Ccedil', 'Egrave', 'Eacute',
+		'Ecirc',   'Euml',   'Igrave',  'Iacute', 'Icirc',  'Iuml',
+		'ETH',     'Ntilde', 'Ograve',  'Oacute', 'Ocirc',  'Otilde',
+		'Ouml',    'times',  'Oslash',  'Ugrave', 'Uacute', 'Ucirc',
+		'Uuml',    'Yacute', 'THORN',   'szlig',  'agrave', 'aacute',
+		'acirc',   'atilde', 'auml',    'aring',  'aelig',  'ccedil',
+		'egrave',  'eacute', 'ecirc',   'euml',   'igrave', 'iacute',
+		'icirc',   'iuml',   'eth',     'ntilde', 'ograve', 'oacute',
+		'ocirc',   'otilde', 'ouml',    'divide', 'oslash', 'ugrave',
+		'uacute',  'ucirc',  'uuml',    'yacute', 'thorn',  'yuml',
+		'quot',    'amp',    'lt',      'gt',     'apos',   'OElig',
+		'oelig',   'Scaron', 'scaron',  'Yuml',   'circ',   'tilde',
+		'ensp',    'emsp',   'thinsp',  'zwnj',   'zwj',    'lrm',
+		'rlm',     'ndash',  'mdash',   'lsquo',  'rsquo',  'sbquo',
+		'ldquo',   'rdquo',  'bdquo',   'dagger', 'Dagger', 'permil',
+		'lsaquo',  'rsaquo', 'euro',    'fnof',   'Alpha',  'Beta',
+		'Gamma',   'Delta',  'Epsilon', 'Zeta',   'Eta',    'Theta',
+		'Iota',    'Kappa',  'Lambda',  'Mu',     'Nu',     'Xi',
+		'Omicron', 'Pi',     'Rho',     'Sigma',  'Tau',    'Upsilon',
+		'Phi',     'Chi',    'Psi',     'Omega',  'alpha',  'beta',
+		'gamma',   'delta',  'epsilon', 'zeta',   'eta',    'theta',
+		'iota',    'kappa',  'lambda',  'mu',     'nu',     'xi',
+		'omicron', 'pi',     'rho',     'sigmaf', 'sigma',  'tau',
+		'upsilon', 'phi',    'chi',     'psi',    'omega',  'thetasym',
+		'upsih',   'piv',    'bull',    'hellip', 'prime',  'Prime',
+		'oline',   'frasl',  'weierp',  'image',  'real',   'trade',
+		'alefsym', 'larr',   'uarr',    'rarr',   'darr',   'harr',
+		'crarr',   'lArr',   'uArr',    'rArr',   'dArr',   'hArr',
+		'forall',  'part',   'exist',   'empty',  'nabla',  'isin',
+		'notin',   'ni',     'prod',    'sum',    'minus',  'lowast',
+		'radic',   'prop',   'infin',   'ang',    'and',    'or',
+		'cap',     'cup',    'int',     'sim',    'cong',   'asymp',
+		'ne',      'equiv',  'le',      'ge',     'sub',    'sup',
+		'nsub',    'sube',   'supe',    'oplus',  'otimes', 'perp',
+		'sdot',    'lceil',  'rceil',   'lfloor', 'rfloor', 'lang',
+		'rang',    'loz',    'spades',  'clubs',  'hearts', 'diams',
+	);
+}
+
+/**
+ * Filters content and keeps only allowable HTML elements.
+ *
+ * This function makes sure that only the allowed HTML element names, attribute
+ * names and attribute values plus only sane HTML entities will occur in
+ * $string. You have to remove any slashes from PHP's magic quotes before you
+ * call this function.
+ *
+ * The default allowed protocols are 'http', 'https', 'ftp', 'mailto', 'news',
+ * 'irc', 'gopher', 'nntp', 'feed', 'telnet, 'mms', 'rtsp' and 'svn'. This
+ * covers all common link protocols, except for 'javascript' which should not
+ * be allowed for untrusted users.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to filter through kses
+ * @param array $allowed_html List of allowed HTML elements
+ * @param array $allowed_protocols Optional. Allowed protocol in links.
+ * @return string Filtered content with only allowed HTML elements
+ */
+function wp_kses($string, $allowed_html, $allowed_protocols = array ()) {
+	$allowed_protocols = wp_parse_args( $allowed_protocols, apply_filters('kses_allowed_protocols', array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn') ));
+	$string = wp_kses_no_null($string);
+	$string = wp_kses_js_entities($string);
+	$string = wp_kses_normalize_entities($string);
+	$allowed_html_fixed = wp_kses_array_lc($allowed_html);
+	$string = wp_kses_hook($string, $allowed_html_fixed, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
+	return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
+}
+
+/**
+ * You add any kses hooks here.
+ *
+ * There is currently only one kses WordPress hook and it is called here. All
+ * parameters are passed to the hooks and expected to recieve a string.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to filter through kses
+ * @param array $allowed_html List of allowed HTML elements
+ * @param array $allowed_protocols Allowed protocol in links
+ * @return string Filtered content through 'pre_kses' hook
+ */
+function wp_kses_hook($string, $allowed_html, $allowed_protocols) {
+	$string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
+	return $string;
+}
+
+/**
+ * This function returns kses' version number.
+ *
+ * @since 1.0.0
+ *
+ * @return string KSES Version Number
+ */
+function wp_kses_version() {
+	return '0.2.2';
+}
+
+/**
+ * Searches for HTML tags, no matter how malformed.
+ *
+ * It also matches stray ">" characters.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to filter
+ * @param array $allowed_html Allowed HTML elements
+ * @param array $allowed_protocols Allowed protocols to keep
+ * @return string Content with fixed HTML tags
+ */
+function wp_kses_split($string, $allowed_html, $allowed_protocols) {
+	global $pass_allowed_html, $pass_allowed_protocols;
+	$pass_allowed_html = $allowed_html;
+	$pass_allowed_protocols = $allowed_protocols;
+	return preg_replace_callback( '%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%', '_wp_kses_split_callback', $string );
+}
+
+/**
+ * Callback for wp_kses_split.
+ *
+ * @since 3.1.0
+ * @access private
+ */
+function _wp_kses_split_callback( $match ) {
+	global $pass_allowed_html, $pass_allowed_protocols;
+	return wp_kses_split2( $match[1], $pass_allowed_html, $pass_allowed_protocols );
+}
+
+/**
+ * Callback for wp_kses_split for fixing malformed HTML tags.
+ *
+ * This function does a lot of work. It rejects some very malformed things like
+ * <:::>. It returns an empty string, if the element isn't allowed (look ma, no
+ * strip_tags()!). Otherwise it splits the tag into an element and an attribute
+ * list.
+ *
+ * After the tag is split into an element and an attribute list, it is run
+ * through another filter which will remove illegal attributes and once that is
+ * completed, will be returned.
+ *
+ * @access private
+ * @since 1.0.0
+ * @uses wp_kses_attr()
+ *
+ * @param string $string Content to filter
+ * @param array $allowed_html Allowed HTML elements
+ * @param array $allowed_protocols Allowed protocols to keep
+ * @return string Fixed HTML element
+ */
+function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
+	$string = wp_kses_stripslashes($string);
+
+	if (substr($string, 0, 1) != '<')
+		return '&gt;';
+	# It matched a ">" character
+
+	if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
+		$string = str_replace(array('<!--', '-->'), '', $matches[1]);
+		while ( $string != $newstring = wp_kses($string, $allowed_html, $allowed_protocols) )
+			$string = $newstring;
+		if ( $string == '' )
+			return '';
+		// prevent multiple dashes in comments
+		$string = preg_replace('/--+/', '-', $string);
+		// prevent three dashes closing a comment
+		$string = preg_replace('/-$/', '', $string);
+		return "<!--{$string}-->";
+	}
+	# Allow HTML comments
+
+	if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
+		return '';
+	# It's seriously malformed
+
+	$slash = trim($matches[1]);
+	$elem = $matches[2];
+	$attrlist = $matches[3];
+
+	if (!@isset($allowed_html[strtolower($elem)]))
+		return '';
+	# They are using a not allowed HTML element
+
+	if ($slash != '')
+		return "<$slash$elem>";
+	# No attributes are allowed for closing elements
+
+	return wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
+}
+
+/**
+ * Removes all attributes, if none are allowed for this element.
+ *
+ * If some are allowed it calls wp_kses_hair() to split them further, and then
+ * it builds up new HTML code from the data that kses_hair() returns. It also
+ * removes "<" and ">" characters, if there are any left. One more thing it does
+ * is to check if the tag has a closing XHTML slash, and if it does, it puts one
+ * in the returned code as well.
+ *
+ * @since 1.0.0
+ *
+ * @param string $element HTML element/tag
+ * @param string $attr HTML attributes from HTML element to closing HTML element tag
+ * @param array $allowed_html Allowed HTML elements
+ * @param array $allowed_protocols Allowed protocols to keep
+ * @return string Sanitized HTML element
+ */
+function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
+	# Is there a closing XHTML slash at the end of the attributes?
+
+	$xhtml_slash = '';
+	if (preg_match('%\s*/\s*$%', $attr))
+		$xhtml_slash = ' /';
+
+	# Are any attributes allowed at all for this element?
+
+	if (@ count($allowed_html[strtolower($element)]) == 0)
+		return "<$element$xhtml_slash>";
+
+	# Split it
+
+	$attrarr = wp_kses_hair($attr, $allowed_protocols);
+
+	# Go through $attrarr, and save the allowed attributes for this element
+	# in $attr2
+
+	$attr2 = '';
+
+	foreach ($attrarr as $arreach) {
+		if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
+			continue; # the attribute is not allowed
+
+		$current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
+		if ($current == '')
+			continue; # the attribute is not allowed
+
+		if (!is_array($current))
+			$attr2 .= ' '.$arreach['whole'];
+		# there are no checks
+
+		else {
+			# there are some checks
+			$ok = true;
+			foreach ($current as $currkey => $currval)
+				if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
+					$ok = false;
+					break;
+				}
+
+			if ( strtolower($arreach['name']) == 'style' ) {
+				$orig_value = $arreach['value'];
+
+				$value = safecss_filter_attr($orig_value);
+
+				if ( empty($value) )
+					continue;
+
+				$arreach['value'] = $value;
+
+				$arreach['whole'] = str_replace($orig_value, $value, $arreach['whole']);
+			}
+
+			if ($ok)
+				$attr2 .= ' '.$arreach['whole']; # it passed them
+		} # if !is_array($current)
+	} # foreach
+
+	# Remove any "<" or ">" characters
+
+	$attr2 = preg_replace('/[<>]/', '', $attr2);
+
+	return "<$element$attr2$xhtml_slash>";
+}
+
+/**
+ * Builds an attribute list from string containing attributes.
+ *
+ * This function does a lot of work. It parses an attribute list into an array
+ * with attribute data, and tries to do the right thing even if it gets weird
+ * input. It will add quotes around attribute values that don't have any quotes
+ * or apostrophes around them, to make it easier to produce HTML code that will
+ * conform to W3C's HTML specification. It will also remove bad URL protocols
+ * from attribute values.  It also reduces duplicate attributes by using the
+ * attribute defined first (foo='bar' foo='baz' will result in foo='bar').
+ *
+ * @since 1.0.0
+ *
+ * @param string $attr Attribute list from HTML element to closing HTML element tag
+ * @param array $allowed_protocols Allowed protocols to keep
+ * @return array List of attributes after parsing
+ */
+function wp_kses_hair($attr, $allowed_protocols) {
+	$attrarr = array ();
+	$mode = 0;
+	$attrname = '';
+	$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
+
+	# Loop through the whole attribute list
+
+	while (strlen($attr) != 0) {
+		$working = 0; # Was the last operation successful?
+
+		switch ($mode) {
+			case 0 : # attribute name, href for instance
+
+				if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
+					$attrname = $match[1];
+					$working = $mode = 1;
+					$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
+				}
+
+				break;
+
+			case 1 : # equals sign or valueless ("selected")
+
+				if (preg_match('/^\s*=\s*/', $attr)) # equals sign
+					{
+					$working = 1;
+					$mode = 2;
+					$attr = preg_replace('/^\s*=\s*/', '', $attr);
+					break;
+				}
+
+				if (preg_match('/^\s+/', $attr)) # valueless
+					{
+					$working = 1;
+					$mode = 0;
+					if(FALSE === array_key_exists($attrname, $attrarr)) {
+						$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
+					}
+					$attr = preg_replace('/^\s+/', '', $attr);
+				}
+
+				break;
+
+			case 2 : # attribute value, a URL after href= for instance
+
+				if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
+					# "value"
+					{
+					$thisval = $match[1];
+					if ( in_array(strtolower($attrname), $uris) )
+						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
+
+					if(FALSE === array_key_exists($attrname, $attrarr)) {
+						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
+					}
+					$working = 1;
+					$mode = 0;
+					$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
+					break;
+				}
+
+				if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
+					# 'value'
+					{
+					$thisval = $match[1];
+					if ( in_array(strtolower($attrname), $uris) )
+						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
+
+					if(FALSE === array_key_exists($attrname, $attrarr)) {
+						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
+					}
+					$working = 1;
+					$mode = 0;
+					$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
+					break;
+				}
+
+				if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
+					# value
+					{
+					$thisval = $match[1];
+					if ( in_array(strtolower($attrname), $uris) )
+						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
+
+					if(FALSE === array_key_exists($attrname, $attrarr)) {
+						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
+					}
+					# We add quotes to conform to W3C's HTML spec.
+					$working = 1;
+					$mode = 0;
+					$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
+				}
+
+				break;
+		} # switch
+
+		if ($working == 0) # not well formed, remove and try again
+		{
+			$attr = wp_kses_html_error($attr);
+			$mode = 0;
+		}
+	} # while
+
+	if ($mode == 1 && FALSE === array_key_exists($attrname, $attrarr))
+		# special case, for when the attribute list ends with a valueless
+		# attribute like "selected"
+		$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
+
+	return $attrarr;
+}
+
+/**
+ * Performs different checks for attribute values.
+ *
+ * The currently implemented checks are "maxlen", "minlen", "maxval", "minval"
+ * and "valueless" with even more checks to come soon.
+ *
+ * @since 1.0.0
+ *
+ * @param string $value Attribute value
+ * @param string $vless Whether the value is valueless. Use 'y' or 'n'
+ * @param string $checkname What $checkvalue is checking for.
+ * @param mixed $checkvalue What constraint the value should pass
+ * @return bool Whether check passes
+ */
+function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
+	$ok = true;
+
+	switch (strtolower($checkname)) {
+		case 'maxlen' :
+			# The maxlen check makes sure that the attribute value has a length not
+			# greater than the given value. This can be used to avoid Buffer Overflows
+			# in WWW clients and various Internet servers.
+
+			if (strlen($value) > $checkvalue)
+				$ok = false;
+			break;
+
+		case 'minlen' :
+			# The minlen check makes sure that the attribute value has a length not
+			# smaller than the given value.
+
+			if (strlen($value) < $checkvalue)
+				$ok = false;
+			break;
+
+		case 'maxval' :
+			# The maxval check does two things: it checks that the attribute value is
+			# an integer from 0 and up, without an excessive amount of zeroes or
+			# whitespace (to avoid Buffer Overflows). It also checks that the attribute
+			# value is not greater than the given value.
+			# This check can be used to avoid Denial of Service attacks.
+
+			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
+				$ok = false;
+			if ($value > $checkvalue)
+				$ok = false;
+			break;
+
+		case 'minval' :
+			# The minval check checks that the attribute value is a positive integer,
+			# and that it is not smaller than the given value.
+
+			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
+				$ok = false;
+			if ($value < $checkvalue)
+				$ok = false;
+			break;
+
+		case 'valueless' :
+			# The valueless check checks if the attribute has a value
+			# (like <a href="blah">) or not (<option selected>). If the given value
+			# is a "y" or a "Y", the attribute must not have a value.
+			# If the given value is an "n" or an "N", the attribute must have one.
+
+			if (strtolower($checkvalue) != $vless)
+				$ok = false;
+			break;
+	} # switch
+
+	return $ok;
+}
+
+/**
+ * Sanitize string from bad protocols.
+ *
+ * This function removes all non-allowed protocols from the beginning of
+ * $string. It ignores whitespace and the case of the letters, and it does
+ * understand HTML entities. It does its work in a while loop, so it won't be
+ * fooled by a string like "javascript:javascript:alert(57)".
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to filter bad protocols from
+ * @param array $allowed_protocols Allowed protocols to keep
+ * @return string Filtered content
+ */
+function wp_kses_bad_protocol($string, $allowed_protocols) {
+	$string = wp_kses_no_null($string);
+	$string2 = $string.'a';
+
+	while ($string != $string2) {
+		$string2 = $string;
+		$string = wp_kses_bad_protocol_once($string, $allowed_protocols);
+	} # while
+
+	return $string;
+}
+
+/**
+ * Removes any NULL characters in $string.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string
+ * @return string
+ */
+function wp_kses_no_null($string) {
+	$string = preg_replace('/\0+/', '', $string);
+	$string = preg_replace('/(\\\\0)+/', '', $string);
+
+	return $string;
+}
+
+/**
+ * Strips slashes from in front of quotes.
+ *
+ * This function changes the character sequence  \"  to just  ". It leaves all
+ * other slashes alone. It's really weird, but the quoting from
+ * preg_replace(//e) seems to require this.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string String to strip slashes
+ * @return string Fixed strings with quoted slashes
+ */
+function wp_kses_stripslashes($string) {
+	return preg_replace('%\\\\"%', '"', $string);
+}
+
+/**
+ * Goes through an array and changes the keys to all lower case.
+ *
+ * @since 1.0.0
+ *
+ * @param array $inarray Unfiltered array
+ * @return array Fixed array with all lowercase keys
+ */
+function wp_kses_array_lc($inarray) {
+	$outarray = array ();
+
+	foreach ( (array) $inarray as $inkey => $inval) {
+		$outkey = strtolower($inkey);
+		$outarray[$outkey] = array ();
+
+		foreach ( (array) $inval as $inkey2 => $inval2) {
+			$outkey2 = strtolower($inkey2);
+			$outarray[$outkey][$outkey2] = $inval2;
+		} # foreach $inval
+	} # foreach $inarray
+
+	return $outarray;
+}
+
+/**
+ * Removes the HTML JavaScript entities found in early versions of Netscape 4.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string
+ * @return string
+ */
+function wp_kses_js_entities($string) {
+	return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
+}
+
+/**
+ * Handles parsing errors in wp_kses_hair().
+ *
+ * The general plan is to remove everything to and including some whitespace,
+ * but it deals with quotes and apostrophes as well.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string
+ * @return string
+ */
+function wp_kses_html_error($string) {
+	return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
+}
+
+/**
+ * Sanitizes content from bad protocols and other characters.
+ *
+ * This function searches for URL protocols at the beginning of $string, while
+ * handling whitespace and HTML entities.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to check for bad protocols
+ * @param string $allowed_protocols Allowed protocols
+ * @return string Sanitized content
+ */
+function wp_kses_bad_protocol_once($string, $allowed_protocols) {
+	$string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
+	if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) )
+		$string = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ) . trim( $string2[1] );
+
+	return $string;
+}
+
+/**
+ * Callback for wp_kses_bad_protocol_once() regular expression.
+ *
+ * This function processes URL protocols, checks to see if they're in the
+ * white-list or not, and returns different data depending on the answer.
+ *
+ * @access private
+ * @since 1.0.0
+ *
+ * @param string $string URI scheme to check against the whitelist
+ * @param string $allowed_protocols Allowed protocols
+ * @return string Sanitized content
+ */
+function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
+	$string2 = wp_kses_decode_entities($string);
+	$string2 = preg_replace('/\s/', '', $string2);
+	$string2 = wp_kses_no_null($string2);
+	$string2 = strtolower($string2);
+
+	$allowed = false;
+	foreach ( (array) $allowed_protocols as $one_protocol )
+		if ( strtolower($one_protocol) == $string2 ) {
+			$allowed = true;
+			break;
+		}
+
+	if ($allowed)
+		return "$string2:";
+	else
+		return '';
+}
+
+/**
+ * Converts and fixes HTML entities.
+ *
+ * This function normalizes HTML entities. It will convert "AT&T" to the correct
+ * "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to normalize entities
+ * @return string Content with normalized entities
+ */
+function wp_kses_normalize_entities($string) {
+	# Disarm all entities by converting & to &amp;
+
+	$string = str_replace('&', '&amp;', $string);
+
+	# Change back the allowed entities in our entity whitelist
+
+	$string = preg_replace_callback('/&amp;([A-Za-z]{2,8});/', 'wp_kses_named_entities', $string);
+	$string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
+	$string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
+
+	return $string;
+}
+
+/**
+ * Callback for wp_kses_normalize_entities() regular expression.
+ *
+ * This function only accepts valid named entity references, which are finite,
+ * case-sensitive, and highly scrutinized by HTML and XML validators.
+ *
+ * @since 3.0.0
+ *
+ * @param array $matches preg_replace_callback() matches array
+ * @return string Correctly encoded entity
+ */
+function wp_kses_named_entities($matches) {
+	global $allowedentitynames;
+
+	if ( empty($matches[1]) )
+		return '';
+
+	$i = $matches[1];
+	return ( ( ! in_array($i, $allowedentitynames) ) ? "&amp;$i;" : "&$i;" );
+}
+
+/**
+ * Callback for wp_kses_normalize_entities() regular expression.
+ *
+ * This function helps wp_kses_normalize_entities() to only accept 16 bit values
+ * and nothing more for &#number; entities.
+ *
+ * @access private
+ * @since 1.0.0
+ *
+ * @param array $matches preg_replace_callback() matches array
+ * @return string Correctly encoded entity
+ */
+function wp_kses_normalize_entities2($matches) {
+	if ( empty($matches[1]) )
+		return '';
+
+	$i = $matches[1];
+	if (valid_unicode($i)) {
+		$i = str_pad(ltrim($i,'0'), 3, '0', STR_PAD_LEFT);
+		$i = "&#$i;";
+	} else {
+		$i = "&amp;#$i;";
+	}
+
+	return $i;
+}
+
+/**
+ * Callback for wp_kses_normalize_entities() for regular expression.
+ *
+ * This function helps wp_kses_normalize_entities() to only accept valid Unicode
+ * numeric entities in hex form.
+ *
+ * @access private
+ *
+ * @param array $matches preg_replace_callback() matches array
+ * @return string Correctly encoded entity
+ */
+function wp_kses_normalize_entities3($matches) {
+	if ( empty($matches[1]) )
+		return '';
+
+	$hexchars = $matches[1];
+	return ( ( ! valid_unicode(hexdec($hexchars)) ) ? "&amp;#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';' );
+}
+
+/**
+ * Helper function to determine if a Unicode value is valid.
+ *
+ * @param int $i Unicode value
+ * @return bool true if the value was a valid Unicode number
+ */
+function valid_unicode($i) {
+	return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
+			($i >= 0x20 && $i <= 0xd7ff) ||
+			($i >= 0xe000 && $i <= 0xfffd) ||
+			($i >= 0x10000 && $i <= 0x10ffff) );
+}
+
+/**
+ * Convert all entities to their character counterparts.
+ *
+ * This function decodes numeric HTML entities (&#65; and &#x41;). It doesn't do
+ * anything with other entities like &auml;, but we don't need them in the URL
+ * protocol whitelisting system anyway.
+ *
+ * @since 1.0.0
+ *
+ * @param string $string Content to change entities
+ * @return string Content after decoded entities
+ */
+function wp_kses_decode_entities($string) {
+	$string = preg_replace_callback('/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string);
+	$string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string);
+
+	return $string;
+}
+
+/**
+ * Regex callback for wp_kses_decode_entities()
+ *
+ * @param array $match preg match
+ * @return string
+ */
+function _wp_kses_decode_entities_chr( $match ) {
+	return chr( $match[1] );
+}
+
+/**
+ * Regex callback for wp_kses_decode_entities()
+ *
+ * @param array $match preg match
+ * @return string
+ */
+function _wp_kses_decode_entities_chr_hexdec( $match ) {
+	return chr( hexdec( $match[1] ) );
+}
+
+/**
+ * Sanitize content with allowed HTML Kses rules.
+ *
+ * @since 1.0.0
+ * @uses $allowedtags
+ *
+ * @param string $data Content to filter, expected to be escaped with slashes
+ * @return string Filtered content
+ */
+function wp_filter_kses($data) {
+	global $allowedtags;
+	return addslashes( wp_kses(stripslashes( $data ), $allowedtags) );
+}
+
+/**
+ * Sanitize content with allowed HTML Kses rules.
+ *
+ * @since 2.9.0
+ * @uses $allowedtags
+ *
+ * @param string $data Content to filter, expected to not be escaped
+ * @return string Filtered content
+ */
+function wp_kses_data($data) {
+	global $allowedtags;
+	return wp_kses( $data , $allowedtags );
+}
+
+/**
+ * Sanitize content for allowed HTML tags for post content.
+ *
+ * Post content refers to the page contents of the 'post' type and not $_POST
+ * data from forms.
+ *
+ * @since 2.0.0
+ * @uses $allowedposttags
+ *
+ * @param string $data Post content to filter, expected to be escaped with slashes
+ * @return string Filtered post content with allowed HTML tags and attributes intact.
+ */
+function wp_filter_post_kses($data) {
+	global $allowedposttags;
+	return addslashes ( wp_kses(stripslashes( $data ), $allowedposttags) );
+}
+
+/**
+ * Sanitize content for allowed HTML tags for post content.
+ *
+ * Post content refers to the page contents of the 'post' type and not $_POST
+ * data from forms.
+ *
+ * @since 2.9.0
+ * @uses $allowedposttags
+ *
+ * @param string $data Post content to filter
+ * @return string Filtered post content with allowed HTML tags and attributes intact.
+ */
+function wp_kses_post($data) {
+	global $allowedposttags;
+	return wp_kses( $data , $allowedposttags );
+}
+
+/**
+ * Strips all of the HTML in the content.
+ *
+ * @since 2.1.0
+ *
+ * @param string $data Content to strip all HTML from
+ * @return string Filtered content without any HTML
+ */
+function wp_filter_nohtml_kses($data) {
+	return addslashes ( wp_kses(stripslashes( $data ), array()) );
+}
+
+// ! function kses_init_filters()
+// ! function kses_remove_filters()
+// ! function kses_init()
+
+/**
+ * Inline CSS filter
+ *
+ * @since 2.8.1
+ */
+function safecss_filter_attr( $css, $deprecated = '' ) {
+	if ( !empty( $deprecated ) )
+		_deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
+
+	$css = wp_kses_no_null($css);
+	$css = str_replace(array("\n","\r","\t"), '', $css);
+
+	if ( preg_match( '%[\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
+		return '';
+
+	$css_array = explode( ';', trim( $css ) );
+	$allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
+	'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
+	'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
+	'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
+	'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
+	'border-top-color', 'border-top-style', 'border-top-width', 'border-width', 'caption-side',
+	'clear', 'cursor', 'direction', 'font', 'font-family', 'font-size', 'font-style',
+	'font-variant', 'font-weight', 'height', 'letter-spacing', 'line-height', 'margin-bottom',
+	'margin-left', 'margin-right', 'margin-top', 'overflow', 'padding', 'padding-bottom',
+	'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'text-indent', 'vertical-align',
+	'width' ) );
+
+	if ( empty($allowed_attr) )
+		return $css;
+
+	$css = '';
+	foreach ( $css_array as $css_item ) {
+		if ( $css_item == '' )
+			continue;
+		$css_item = trim( $css_item );
+		$found = false;
+		if ( strpos( $css_item, ':' ) === false ) {
+			$found = true;
+		} else {
+			$parts = split( ':', $css_item );
+			if ( in_array( trim( $parts[0] ), $allowed_attr ) )
+				$found = true;
+		}
+		if ( $found ) {
+			if( $css != '' )
+				$css .= ';';
+			$css .= $css_item;
+		}
+	}
+
+	return $css;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.plugin-api.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.plugin-api.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.plugin-api.php	(revision 5057)
@@ -0,0 +1,771 @@
+<?php
+// Last sync [WP14924]
+
+/**
+ * The plugin API is located in this file, which allows for creating actions
+ * and filters and hooking functions, and methods. The functions or methods will
+ * then be run when the action or filter is called.
+ *
+ * The API callback examples reference functions, but can be methods of classes.
+ * To hook methods, you'll need to pass an array one of two ways.
+ *
+ * Any of the syntaxes explained in the PHP documentation for the
+ * {@link http://us2.php.net/manual/en/language.pseudo-types.php#language.types.callback 'callback'}
+ * type are valid.
+ *
+ * Also see the {@link http://codex.wordpress.org/Plugin_API Plugin API} for
+ * more information and examples on how to use a lot of these functions.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.5
+ */
+
+/**
+ * Hooks a function or method to a specific filter action.
+ *
+ * Filters are the hooks that WordPress launches to modify text of various types
+ * before adding it to the database or sending it to the browser screen. Plugins
+ * can specify that one or more of its PHP functions is executed to
+ * modify specific types of text at these times, using the Filter API.
+ *
+ * To use the API, the following code should be used to bind a callback to the
+ * filter.
+ *
+ * <code>
+ * function example_hook($example) { echo $example; }
+ * add_filter('example_filter', 'example_hook');
+ * </code>
+ *
+ * In WordPress 1.5.1+, hooked functions can take extra arguments that are set
+ * when the matching do_action() or apply_filters() call is run. The
+ * $accepted_args allow for calling functions only when the number of args
+ * match. Hooked functions can take extra arguments that are set when the
+ * matching do_action() or apply_filters() call is run. For example, the action
+ * comment_id_not_found will pass any functions that hook onto it the ID of the
+ * requested comment.
+ *
+ * <strong>Note:</strong> the function will return true no matter if the
+ * function was hooked fails or not. There are no checks for whether the
+ * function exists beforehand and no checks to whether the <tt>$function_to_add
+ * is even a string. It is up to you to take care and this is done for
+ * optimization purposes, so everything is as quick as possible.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 0.71
+ * @global array $wp_filter Stores all of the filters added in the form of
+ *	wp_filter['tag']['array of priorities']['array of functions serialized']['array of ['array (functions, accepted_args)']']
+ * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process.
+ *
+ * @param string $tag The name of the filter to hook the $function_to_add to.
+ * @param callback $function_to_add The name of the function to be called when the filter is applied.
+ * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
+ * @param int $accepted_args optional. The number of arguments the function accept (default 1).
+ * @return boolean true
+ */
+function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+	global $wp_filter, $merged_filters;
+
+	$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
+	$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
+	unset( $merged_filters[ $tag ] );
+	return true;
+}
+
+/**
+ * Check if any filter has been registered for a hook.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.5
+ * @global array $wp_filter Stores all of the filters
+ *
+ * @param string $tag The name of the filter hook.
+ * @param callback $function_to_check optional.  If specified, return the priority of that function on this hook or false if not attached.
+ * @return int|boolean Optionally returns the priority on that hook for the specified function.
+ */
+function has_filter($tag, $function_to_check = false) {
+	global $wp_filter;
+
+	$has = !empty($wp_filter[$tag]);
+	if ( false === $function_to_check || false == $has )
+		return $has;
+
+	if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) )
+		return false;
+
+	foreach ( (array) array_keys($wp_filter[$tag]) as $priority ) {
+		if ( isset($wp_filter[$tag][$priority][$idx]) )
+			return $priority;
+	}
+
+	return false;
+}
+
+/**
+ * Call the functions added to a filter hook.
+ *
+ * The callback functions attached to filter hook $tag are invoked by calling
+ * this function. This function can be used to create a new filter hook by
+ * simply calling this function with the name of the new hook specified using
+ * the $tag parameter.
+ *
+ * The function allows for additional arguments to be added and passed to hooks.
+ * <code>
+ * function example_hook($string, $arg1, $arg2)
+ * {
+ *		//Do stuff
+ *		return $string;
+ * }
+ * $value = apply_filters('example_filter', 'filter me', 'arg1', 'arg2');
+ * </code>
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 0.71
+ * @global array $wp_filter Stores all of the filters
+ * @global array $merged_filters Merges the filter hooks using this function.
+ * @global array $wp_current_filter stores the list of current filters with the current one last
+ *
+ * @param string $tag The name of the filter hook.
+ * @param mixed $value The value on which the filters hooked to <tt>$tag</tt> are applied on.
+ * @param mixed $var,... Additional variables passed to the functions hooked to <tt>$tag</tt>.
+ * @return mixed The filtered value after all hooked functions are applied to it.
+ */
+function apply_filters($tag, $value) {
+	global $wp_filter, $merged_filters, $wp_current_filter;
+
+	$args = array();
+	$wp_current_filter[] = $tag;
+
+	// Do 'all' actions first
+	if ( isset($wp_filter['all']) ) {
+		$args = func_get_args();
+		_wp_call_all_hook($args);
+	}
+
+	if ( !isset($wp_filter[$tag]) ) {
+		array_pop($wp_current_filter);
+		return $value;
+	}
+
+	// Sort
+	if ( !isset( $merged_filters[ $tag ] ) ) {
+		ksort($wp_filter[$tag]);
+		$merged_filters[ $tag ] = true;
+	}
+
+	reset( $wp_filter[ $tag ] );
+
+	if ( empty($args) )
+		$args = func_get_args();
+
+	do {
+		foreach( (array) current($wp_filter[$tag]) as $the_ )
+			if ( !is_null($the_['function']) ){
+				$args[1] = $value;
+				$value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
+			}
+
+	} while ( next($wp_filter[$tag]) !== false );
+
+	array_pop( $wp_current_filter );
+
+	return $value;
+}
+
+/**
+ * Execute functions hooked on a specific filter hook, specifying arguments in an array.
+ *
+ * @see apply_filters() This function is identical, but the arguments passed to the
+ * functions hooked to <tt>$tag</tt> are supplied using an array.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 3.0.0
+ * @global array $wp_filter Stores all of the filters
+ * @global array $merged_filters Merges the filter hooks using this function.
+ * @global array $wp_current_filter stores the list of current filters with the current one last
+ *
+ * @param string $tag The name of the filter hook.
+ * @param array $args The arguments supplied to the functions hooked to <tt>$tag</tt>
+ * @return mixed The filtered value after all hooked functions are applied to it.
+ */
+function apply_filters_ref_array($tag, $args) {
+	global $wp_filter, $merged_filters, $wp_current_filter;
+
+	$wp_current_filter[] = $tag;
+
+	// Do 'all' actions first
+	if ( isset($wp_filter['all']) ) {
+		$all_args = func_get_args();
+		_wp_call_all_hook($all_args);
+	}
+
+	if ( !isset($wp_filter[$tag]) ) {
+		array_pop($wp_current_filter);
+		return $args[0];
+	}
+
+	// Sort
+	if ( !isset( $merged_filters[ $tag ] ) ) {
+		ksort($wp_filter[$tag]);
+		$merged_filters[ $tag ] = true;
+	}
+
+	reset( $wp_filter[ $tag ] );
+
+	do {
+		foreach( (array) current($wp_filter[$tag]) as $the_ )
+			if ( !is_null($the_['function']) )
+				$args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
+
+	} while ( next($wp_filter[$tag]) !== false );
+
+	array_pop( $wp_current_filter );
+
+	return $args[0];
+}
+
+/**
+ * Removes a function from a specified filter hook.
+ *
+ * This function removes a function attached to a specified filter hook. This
+ * method can be used to remove default functions attached to a specific filter
+ * hook and possibly replace them with a substitute.
+ *
+ * To remove a hook, the $function_to_remove and $priority arguments must match
+ * when the hook was added. This goes for both filters and actions. No warning
+ * will be given on removal failure.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.2
+ *
+ * @param string $tag The filter hook to which the function to be removed is hooked.
+ * @param callback $function_to_remove The name of the function which should be removed.
+ * @param int $priority optional. The priority of the function (default: 10).
+ * @param int $accepted_args optional. The number of arguments the function accpets (default: 1).
+ * @return boolean Whether the function existed before it was removed.
+ */
+function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
+	$function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority);
+
+	$r = isset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
+
+	if ( true === $r) {
+		unset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
+		if ( empty($GLOBALS['wp_filter'][$tag][$priority]) )
+			unset($GLOBALS['wp_filter'][$tag][$priority]);
+		unset($GLOBALS['merged_filters'][$tag]);
+	}
+
+	return $r;
+}
+
+/**
+ * Remove all of the hooks from a filter.
+ *
+ * @since 2.7
+ *
+ * @param string $tag The filter to remove hooks from.
+ * @param int $priority The priority number to remove.
+ * @return bool True when finished.
+ */
+function remove_all_filters($tag, $priority = false) {
+	global $wp_filter, $merged_filters;
+
+	if( isset($wp_filter[$tag]) ) {
+		if( false !== $priority && isset($wp_filter[$tag][$priority]) )
+			unset($wp_filter[$tag][$priority]);
+		else
+			unset($wp_filter[$tag]);
+	}
+
+	if( isset($merged_filters[$tag]) )
+		unset($merged_filters[$tag]);
+
+	return true;
+}
+
+/**
+ * Retrieve the name of the current filter or action.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.5
+ *
+ * @return string Hook name of the current filter or action.
+ */
+function current_filter() {
+	global $wp_current_filter;
+	return end( $wp_current_filter );
+}
+
+
+/**
+ * Hooks a function on to a specific action.
+ *
+ * Actions are the hooks that the WordPress core launches at specific points
+ * during execution, or when specific events occur. Plugins can specify that
+ * one or more of its PHP functions are executed at these points, using the
+ * Action API.
+ *
+ * @uses add_filter() Adds an action. Parameter list and functionality are the same.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.2
+ *
+ * @param string $tag The name of the action to which the $function_to_add is hooked.
+ * @param callback $function_to_add The name of the function you wish to be called.
+ * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
+ * @param int $accepted_args optional. The number of arguments the function accept (default 1).
+ */
+function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+	return add_filter($tag, $function_to_add, $priority, $accepted_args);
+}
+
+
+/**
+ * Execute functions hooked on a specific action hook.
+ *
+ * This function invokes all functions attached to action hook $tag. It is
+ * possible to create new action hooks by simply calling this function,
+ * specifying the name of the new hook using the <tt>$tag</tt> parameter.
+ *
+ * You can pass extra arguments to the hooks, much like you can with
+ * apply_filters().
+ *
+ * @see apply_filters() This function works similar with the exception that
+ * nothing is returned and only the functions or methods are called.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.2
+ * @global array $wp_filter Stores all of the filters
+ * @global array $wp_actions Increments the amount of times action was triggered.
+ *
+ * @param string $tag The name of the action to be executed.
+ * @param mixed $arg,... Optional additional arguments which are passed on to the functions hooked to the action.
+ * @return null Will return null if $tag does not exist in $wp_filter array
+ */
+function do_action($tag, $arg = '') {
+	global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
+
+	if ( ! isset($wp_actions) )
+		$wp_actions = array();
+
+	if ( ! isset($wp_actions[$tag]) )
+		$wp_actions[$tag] = 1;
+	else
+		++$wp_actions[$tag];
+
+	$wp_current_filter[] = $tag;
+
+	// Do 'all' actions first
+	if ( isset($wp_filter['all']) ) {
+		$all_args = func_get_args();
+		_wp_call_all_hook($all_args);
+	}
+
+	if ( !isset($wp_filter[$tag]) ) {
+		array_pop($wp_current_filter);
+		return;
+	}
+
+	$args = array();
+	if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) ) // array(&$this)
+		$args[] =& $arg[0];
+	else
+		$args[] = $arg;
+	for ( $a = 2; $a < func_num_args(); $a++ )
+		$args[] = func_get_arg($a);
+
+	// Sort
+	if ( !isset( $merged_filters[ $tag ] ) ) {
+		ksort($wp_filter[$tag]);
+		$merged_filters[ $tag ] = true;
+	}
+
+	reset( $wp_filter[ $tag ] );
+
+	do {
+		foreach ( (array) current($wp_filter[$tag]) as $the_ )
+			if ( !is_null($the_['function']) )
+				call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
+
+	} while ( next($wp_filter[$tag]) !== false );
+
+	array_pop($wp_current_filter);
+}
+
+/**
+ * Retrieve the number times an action is fired.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.1
+ * @global array $wp_actions Increments the amount of times action was triggered.
+ *
+ * @param string $tag The name of the action hook.
+ * @return int The number of times action hook <tt>$tag</tt> is fired
+ */
+function did_action($tag) {
+	global $wp_actions;
+
+	if ( ! isset( $wp_actions ) || ! isset( $wp_actions[$tag] ) )
+		return 0;
+
+	return $wp_actions[$tag];
+}
+
+/**
+ * Execute functions hooked on a specific action hook, specifying arguments in an array.
+ *
+ * @see do_action() This function is identical, but the arguments passed to the
+ * functions hooked to <tt>$tag</tt> are supplied using an array.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.1
+ * @global array $wp_filter Stores all of the filters
+ * @global array $wp_actions Increments the amount of times action was triggered.
+ *
+ * @param string $tag The name of the action to be executed.
+ * @param array $args The arguments supplied to the functions hooked to <tt>$tag</tt>
+ * @return null Will return null if $tag does not exist in $wp_filter array
+ */
+function do_action_ref_array($tag, $args) {
+	global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
+
+	if ( ! isset($wp_actions) )
+		$wp_actions = array();
+
+	if ( ! isset($wp_actions[$tag]) )
+		$wp_actions[$tag] = 1;
+	else
+		++$wp_actions[$tag];
+
+	$wp_current_filter[] = $tag;
+
+	// Do 'all' actions first
+	if ( isset($wp_filter['all']) ) {
+		$all_args = func_get_args();
+		_wp_call_all_hook($all_args);
+	}
+
+	if ( !isset($wp_filter[$tag]) ) {
+		array_pop($wp_current_filter);
+		return;
+	}
+
+	// Sort
+	if ( !isset( $merged_filters[ $tag ] ) ) {
+		ksort($wp_filter[$tag]);
+		$merged_filters[ $tag ] = true;
+	}
+
+	reset( $wp_filter[ $tag ] );
+
+	do {
+		foreach( (array) current($wp_filter[$tag]) as $the_ )
+			if ( !is_null($the_['function']) )
+				call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
+
+	} while ( next($wp_filter[$tag]) !== false );
+
+	array_pop($wp_current_filter);
+}
+
+/**
+ * Check if any action has been registered for a hook.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.5
+ * @see has_filter() has_action() is an alias of has_filter().
+ *
+ * @param string $tag The name of the action hook.
+ * @param callback $function_to_check optional.  If specified, return the priority of that function on this hook or false if not attached.
+ * @return int|boolean Optionally returns the priority on that hook for the specified function.
+ */
+function has_action($tag, $function_to_check = false) {
+	return has_filter($tag, $function_to_check);
+}
+
+/**
+ * Removes a function from a specified action hook.
+ *
+ * This function removes a function attached to a specified action hook. This
+ * method can be used to remove default functions attached to a specific filter
+ * hook and possibly replace them with a substitute.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.2
+ *
+ * @param string $tag The action hook to which the function to be removed is hooked.
+ * @param callback $function_to_remove The name of the function which should be removed.
+ * @param int $priority optional The priority of the function (default: 10).
+ * @param int $accepted_args optional. The number of arguments the function accpets (default: 1).
+ * @return boolean Whether the function is removed.
+ */
+function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
+	return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
+}
+
+/**
+ * Remove all of the hooks from an action.
+ *
+ * @since 2.7
+ *
+ * @param string $tag The action to remove hooks from.
+ * @param int $priority The priority number to remove them from.
+ * @return bool True when finished.
+ */
+function remove_all_actions($tag, $priority = false) {
+	return remove_all_filters($tag, $priority);
+}
+
+//
+// Functions for handling plugins.
+//
+
+/**
+ * Gets the basename of a plugin.
+ *
+ * This method extracts the name of a plugin from its filename.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 1.5
+ *
+ * @access private
+ *
+ * @param string $file The filename of plugin.
+ * @return string The name of a plugin.
+ * @uses WP_PLUGIN_DIR
+ */
+function plugin_basename($file) {
+	$file = str_replace('\\','/',$file); // sanitize for Win32 installs
+	$file = preg_replace('|/+|','/', $file); // remove any duplicate slash
+	$plugin_dir = str_replace('\\','/',WP_PLUGIN_DIR); // sanitize for Win32 installs
+	$plugin_dir = preg_replace('|/+|','/', $plugin_dir); // remove any duplicate slash
+	$mu_plugin_dir = str_replace('\\','/',WPMU_PLUGIN_DIR); // sanitize for Win32 installs
+	$mu_plugin_dir = preg_replace('|/+|','/', $mu_plugin_dir); // remove any duplicate slash
+	$file = preg_replace('#^' . preg_quote($plugin_dir, '#') . '/|^' . preg_quote($mu_plugin_dir, '#') . '/#','',$file); // get relative path from plugins dir
+	$file = trim($file, '/');
+	return $file;
+}
+
+/**
+ * Gets the filesystem directory path (with trailing slash) for the plugin __FILE__ passed in
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.8
+ *
+ * @param string $file The filename of the plugin (__FILE__)
+ * @return string the filesystem path of the directory that contains the plugin
+ */
+function plugin_dir_path( $file ) {
+	return trailingslashit( dirname( $file ) );
+}
+
+/**
+ * Gets the URL directory path (with trailing slash) for the plugin __FILE__ passed in
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.8
+ *
+ * @param string $file The filename of the plugin (__FILE__)
+ * @return string the URL path of the directory that contains the plugin
+ */
+function plugin_dir_url( $file ) {
+	return trailingslashit( plugins_url( '', $file ) );
+}
+
+/**
+ * Set the activation hook for a plugin.
+ *
+ * When a plugin is activated, the action 'activate_PLUGINNAME' hook is
+ * activated. In the name of this hook, PLUGINNAME is replaced with the name of
+ * the plugin, including the optional subdirectory. For example, when the plugin
+ * is located in wp-content/plugin/sampleplugin/sample.php, then the name of
+ * this hook will become 'activate_sampleplugin/sample.php'. When the plugin
+ * consists of only one file and is (as by default) located at
+ * wp-content/plugin/sample.php the name of this hook will be
+ * 'activate_sample.php'.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.0
+ *
+ * @param string $file The filename of the plugin including the path.
+ * @param callback $function the function hooked to the 'activate_PLUGIN' action.
+ */
+function register_activation_hook($file, $function) {
+	$file = plugin_basename($file);
+	add_action('activate_' . $file, $function);
+}
+
+/**
+ * Set the deactivation hook for a plugin.
+ *
+ * When a plugin is deactivated, the action 'deactivate_PLUGINNAME' hook is
+ * deactivated. In the name of this hook, PLUGINNAME is replaced with the name
+ * of the plugin, including the optional subdirectory. For example, when the
+ * plugin is located in wp-content/plugin/sampleplugin/sample.php, then
+ * the name of this hook will become 'activate_sampleplugin/sample.php'.
+ *
+ * When the plugin consists of only one file and is (as by default) located at
+ * wp-content/plugin/sample.php the name of this hook will be
+ * 'activate_sample.php'.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.0
+ *
+ * @param string $file The filename of the plugin including the path.
+ * @param callback $function the function hooked to the 'activate_PLUGIN' action.
+ */
+function register_deactivation_hook($file, $function) {
+	$file = plugin_basename($file);
+	add_action('deactivate_' . $file, $function);
+}
+
+/**
+ * Set the uninstallation hook for a plugin.
+ *
+ * Registers the uninstall hook that will be called when the user clicks on the
+ * uninstall link that calls for the plugin to uninstall itself. The link won't
+ * be active unless the plugin hooks into the action.
+ *
+ * The plugin should not run arbitrary code outside of functions, when
+ * registering the uninstall hook. In order to run using the hook, the plugin
+ * will have to be included, which means that any code laying outside of a
+ * function will be run during the uninstall process. The plugin should not
+ * hinder the uninstall process.
+ *
+ * If the plugin can not be written without running code within the plugin, then
+ * the plugin should create a file named 'uninstall.php' in the base plugin
+ * folder. This file will be called, if it exists, during the uninstall process
+ * bypassing the uninstall hook. The plugin, when using the 'uninstall.php'
+ * should always check for the 'WP_UNINSTALL_PLUGIN' constant, before
+ * executing.
+ *
+ * @since 2.7
+ *
+ * @param string $file
+ * @param callback $callback The callback to run when the hook is called.
+ */
+function register_uninstall_hook($file, $callback) {
+	// The option should not be autoloaded, because it is not needed in most
+	// cases. Emphasis should be put on using the 'uninstall.php' way of
+	// uninstalling the plugin.
+	$uninstallable_plugins = (array) backpress_get_option('uninstall_plugins');
+	$uninstallable_plugins[plugin_basename($file)] = $callback;
+	backpress_update_option('uninstall_plugins', $uninstallable_plugins);
+}
+
+/**
+ * Calls the 'all' hook, which will process the functions hooked into it.
+ *
+ * The 'all' hook passes all of the arguments or parameters that were used for
+ * the hook, which this function was called for.
+ *
+ * This function is used internally for apply_filters(), do_action(), and
+ * do_action_ref_array() and is not meant to be used from outside those
+ * functions. This function does not check for the existence of the all hook, so
+ * it will fail unless the all hook exists prior to this function call.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @since 2.5
+ * @access private
+ *
+ * @uses $wp_filter Used to process all of the functions in the 'all' hook
+ *
+ * @param array $args The collected parameters from the hook that was called.
+ * @param string $hook Optional. The hook name that was used to call the 'all' hook.
+ */
+function _wp_call_all_hook($args) {
+	global $wp_filter;
+
+	reset( $wp_filter['all'] );
+	do {
+		foreach( (array) current($wp_filter['all']) as $the_ )
+			if ( !is_null($the_['function']) )
+				call_user_func_array($the_['function'], $args);
+
+	} while ( next($wp_filter['all']) !== false );
+}
+
+/**
+ * Build Unique ID for storage and retrieval.
+ *
+ * The old way to serialize the callback caused issues and this function is the
+ * solution. It works by checking for objects and creating an a new property in
+ * the class to keep track of the object and new objects of the same class that
+ * need to be added.
+ *
+ * It also allows for the removal of actions and filters for objects after they
+ * change class properties. It is possible to include the property $wp_filter_id
+ * in your class and set it to "null" or a number to bypass the workaround.
+ * However this will prevent you from adding new classes and any new classes
+ * will overwrite the previous hook by the same class.
+ *
+ * Functions and static method callbacks are just returned as strings and
+ * shouldn't have any speed penalty.
+ *
+ * @package WordPress
+ * @subpackage Plugin
+ * @access private
+ * @since 2.2.3
+ * @link http://trac.wordpress.org/ticket/3875
+ *
+ * @global array $wp_filter Storage for all of the filters and actions
+ * @param string $tag Used in counting how many hooks were applied
+ * @param callback $function Used for creating unique id
+ * @param int|bool $priority Used in counting how many hooks were applied.  If === false and $function is an object reference, we return the unique id only if it already has one, false otherwise.
+ * @param string $type filter or action
+ * @return string|bool Unique ID for usage as array key or false if $priority === false and $function is an object reference, and it does not already have a uniqe id.
+ */
+function _wp_filter_build_unique_id($tag, $function, $priority) {
+	global $wp_filter;
+	static $filter_id_count = 0;
+
+	if ( is_string($function) )
+		return $function;
+
+	if ( is_object($function) ) {
+		// Closures are currently implemented as objects
+		$function = array( $function, '' );
+	} else {
+		$function = (array) $function;
+	}
+
+	if (is_object($function[0]) ) {
+		// Object Class Calling
+		if ( function_exists('spl_object_hash') ) {
+			return spl_object_hash($function[0]) . $function[1];
+		} else {
+			$obj_idx = get_class($function[0]).$function[1];
+			if ( !isset($function[0]->wp_filter_id) ) {
+				if ( false === $priority )
+					return false;
+				$obj_idx .= isset($wp_filter[$tag][$priority]) ? count((array)$wp_filter[$tag][$priority]) : $filter_id_count;
+				$function[0]->wp_filter_id = $filter_id_count;
+				++$filter_id_count;
+			} else {
+				$obj_idx .= $function[0]->wp_filter_id;
+			}
+
+			return $obj_idx;
+		}
+	} else if ( is_string($function[0]) ) {
+		// Static Calling
+		return $function[0].$function[1];
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.shortcodes.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.shortcodes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.shortcodes.php	(revision 5057)
@@ -0,0 +1,296 @@
+<?php
+// Last sync [WP12206]
+
+/**
+ * WordPress API for creating bbcode like tags or what WordPress calls
+ * "shortcodes." The tag and attribute parsing or regular expression code is
+ * based on the Textpattern tag parser.
+ *
+ * A few examples are below:
+ *
+ * [shortcode /]
+ * [shortcode foo="bar" baz="bing" /]
+ * [shortcode foo="bar"]content[/shortcode]
+ *
+ * Shortcode tags support attributes and enclosed content, but does not entirely
+ * support inline shortcodes in other shortcodes. You will have to call the
+ * shortcode parser in your function to account for that.
+ *
+ * {@internal
+ * Please be aware that the above note was made during the beta of WordPress 2.6
+ * and in the future may not be accurate. Please update the note when it is no
+ * longer the case.}}
+ *
+ * To apply shortcode tags to content:
+ *
+ * <code>
+ * $out = do_shortcode($content);
+ * </code>
+ *
+ * @link http://codex.wordpress.org/Shortcode_API
+ *
+ * @package WordPress
+ * @subpackage Shortcodes
+ * @since 2.5
+ */
+
+/**
+ * Container for storing shortcode tags and their hook to call for the shortcode
+ *
+ * @since 2.5
+ * @name $shortcode_tags
+ * @var array
+ * @global array $shortcode_tags
+ */
+$shortcode_tags = array();
+
+/**
+ * Add hook for shortcode tag.
+ *
+ * There can only be one hook for each shortcode. Which means that if another
+ * plugin has a similar shortcode, it will override yours or yours will override
+ * theirs depending on which order the plugins are included and/or ran.
+ *
+ * Simplest example of a shortcode tag using the API:
+ *
+ * <code>
+ * // [footag foo="bar"]
+ * function footag_func($atts) {
+ * 	return "foo = {$atts[foo]}";
+ * }
+ * add_shortcode('footag', 'footag_func');
+ * </code>
+ *
+ * Example with nice attribute defaults:
+ *
+ * <code>
+ * // [bartag foo="bar"]
+ * function bartag_func($atts) {
+ * 	extract(shortcode_atts(array(
+ * 		'foo' => 'no foo',
+ * 		'baz' => 'default baz',
+ * 	), $atts));
+ *
+ * 	return "foo = {$foo}";
+ * }
+ * add_shortcode('bartag', 'bartag_func');
+ * </code>
+ *
+ * Example with enclosed content:
+ *
+ * <code>
+ * // [baztag]content[/baztag]
+ * function baztag_func($atts, $content='') {
+ * 	return "content = $content";
+ * }
+ * add_shortcode('baztag', 'baztag_func');
+ * </code>
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ *
+ * @param string $tag Shortcode tag to be searched in post content.
+ * @param callable $func Hook to run when shortcode is found.
+ */
+function add_shortcode($tag, $func) {
+	global $shortcode_tags;
+
+	if ( is_callable($func) )
+		$shortcode_tags[$tag] = $func;
+}
+
+/**
+ * Removes hook for shortcode.
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ *
+ * @param string $tag shortcode tag to remove hook for.
+ */
+function remove_shortcode($tag) {
+	global $shortcode_tags;
+
+	unset($shortcode_tags[$tag]);
+}
+
+/**
+ * Clear all shortcodes.
+ *
+ * This function is simple, it clears all of the shortcode tags by replacing the
+ * shortcodes global by a empty array. This is actually a very efficient method
+ * for removing all shortcodes.
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ */
+function remove_all_shortcodes() {
+	global $shortcode_tags;
+
+	$shortcode_tags = array();
+}
+
+/**
+ * Search content for shortcodes and filter shortcodes through their hooks.
+ *
+ * If there are no shortcode tags defined, then the content will be returned
+ * without any filtering. This might cause issues when plugins are disabled but
+ * the shortcode will still show up in the post or content.
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ * @uses get_shortcode_regex() Gets the search pattern for searching shortcodes.
+ *
+ * @param string $content Content to search for shortcodes
+ * @return string Content with shortcodes filtered out.
+ */
+function do_shortcode($content) {
+	global $shortcode_tags;
+
+	if (empty($shortcode_tags) || !is_array($shortcode_tags))
+		return $content;
+
+	$pattern = get_shortcode_regex();
+	return preg_replace_callback('/'.$pattern.'/s', 'do_shortcode_tag', $content);
+}
+
+/**
+ * Retrieve the shortcode regular expression for searching.
+ *
+ * The regular expression combines the shortcode tags in the regular expression
+ * in a regex class.
+ *
+ * The regular expresion contains 6 different sub matches to help with parsing.
+ *
+ * 1/6 - An extra [ or ] to allow for escaping shortcodes with double [[]]
+ * 2 - The shortcode name
+ * 3 - The shortcode argument list
+ * 4 - The self closing /
+ * 5 - The content of a shortcode when it wraps some content.
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ *
+ * @return string The shortcode search regular expression
+ */
+function get_shortcode_regex() {
+	global $shortcode_tags;
+	$tagnames = array_keys($shortcode_tags);
+	$tagregexp = join( '|', array_map('preg_quote', $tagnames) );
+
+	// WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcodes()
+	return '(.?)\[('.$tagregexp.')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)';
+}
+
+/**
+ * Regular Expression callable for do_shortcode() for calling shortcode hook.
+ * @see get_shortcode_regex for details of the match array contents.
+ *
+ * @since 2.5
+ * @access private
+ * @uses $shortcode_tags
+ *
+ * @param array $m Regular expression match array
+ * @return mixed False on failure.
+ */
+function do_shortcode_tag($m) {
+	global $shortcode_tags;
+
+	// allow [[foo]] syntax for escaping a tag
+	if ($m[1] == '[' && $m[6] == ']') {
+		return substr($m[0], 1, -1);
+	}
+
+	$tag = $m[2];
+	$attr = shortcode_parse_atts($m[3]);
+
+	if ( isset($m[5]) ) {
+		// enclosing tag - extra parameter
+		return $m[1] . call_user_func($shortcode_tags[$tag], $attr, $m[5], $m[2]) . $m[6];
+	} else {
+		// self-closing tag
+		return $m[1] . call_user_func($shortcode_tags[$tag], $attr, NULL, $m[2]) . $m[6];
+	}
+}
+
+/**
+ * Retrieve all attributes from the shortcodes tag.
+ *
+ * The attributes list has the attribute name as the key and the value of the
+ * attribute as the value in the key/value pair. This allows for easier
+ * retrieval of the attributes, since all attributes have to be known.
+ *
+ * @since 2.5
+ *
+ * @param string $text
+ * @return array List of attributes and their value.
+ */
+function shortcode_parse_atts($text) {
+	$atts = array();
+	$pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
+	$text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
+	if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
+		foreach ($match as $m) {
+			if (!empty($m[1]))
+				$atts[strtolower($m[1])] = stripcslashes($m[2]);
+			elseif (!empty($m[3]))
+				$atts[strtolower($m[3])] = stripcslashes($m[4]);
+			elseif (!empty($m[5]))
+				$atts[strtolower($m[5])] = stripcslashes($m[6]);
+			elseif (isset($m[7]) and strlen($m[7]))
+				$atts[] = stripcslashes($m[7]);
+			elseif (isset($m[8]))
+				$atts[] = stripcslashes($m[8]);
+		}
+	} else {
+		$atts = ltrim($text);
+	}
+	return $atts;
+}
+
+/**
+ * Combine user attributes with known attributes and fill in defaults when needed.
+ *
+ * The pairs should be considered to be all of the attributes which are
+ * supported by the caller and given as a list. The returned attributes will
+ * only contain the attributes in the $pairs list.
+ *
+ * If the $atts list has unsupported attributes, then they will be ignored and
+ * removed from the final returned list.
+ *
+ * @since 2.5
+ *
+ * @param array $pairs Entire list of supported attributes and their defaults.
+ * @param array $atts User defined attributes in shortcode tag.
+ * @return array Combined and filtered attribute list.
+ */
+function shortcode_atts($pairs, $atts) {
+	$atts = (array)$atts;
+	$out = array();
+	foreach($pairs as $name => $default) {
+		if ( array_key_exists($name, $atts) )
+			$out[$name] = $atts[$name];
+		else
+			$out[$name] = $default;
+	}
+	return $out;
+}
+
+/**
+ * Remove all shortcode tags from the given content.
+ *
+ * @since 2.5
+ * @uses $shortcode_tags
+ *
+ * @param string $content Content to remove shortcode tags.
+ * @return string Content without shortcode tags.
+ */
+function strip_shortcodes( $content ) {
+	global $shortcode_tags;
+
+	if (empty($shortcode_tags) || !is_array($shortcode_tags))
+		return $content;
+
+	$pattern = get_shortcode_regex();
+
+	return preg_replace('/'.$pattern.'/s', '$1$6', $content);
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-cron.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-cron.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-cron.php	(revision 5057)
@@ -0,0 +1,402 @@
+<?php
+// Last sync [WP12462]
+
+/**
+ * WordPress CRON API
+ *
+ * @package WordPress
+ */
+
+/**
+ * Schedules a hook to run only once.
+ *
+ * Schedules a hook which will be executed once by the WordPress actions core at
+ * a time which you specify. The action will fire off when someone visits your
+ * WordPress site, if the schedule time has passed.
+ *
+ * @since 2.1.0
+ * @link http://codex.wordpress.org/Function_Reference/wp_schedule_single_event
+ *
+ * @param int $timestamp Timestamp for when to run the event.
+ * @param string $hook Action hook to execute when cron is run.
+ * @param array $args Optional. Arguments to pass to the hook's callback function.
+ */
+function wp_schedule_single_event( $timestamp, $hook, $args = array()) {
+	// don't schedule a duplicate if there's already an identical event due in the next 10 minutes
+	$next = wp_next_scheduled($hook, $args);
+	if ( $next && $next <= $timestamp + 600 )
+		return;
+
+	$crons = _get_cron_array();
+	$key = md5(serialize($args));
+	$crons[$timestamp][$hook][$key] = array( 'schedule' => false, 'args' => $args );
+	uksort( $crons, "strnatcasecmp" );
+	_set_cron_array( $crons );
+}
+
+/**
+ * Schedule a periodic event.
+ *
+ * Schedules a hook which will be executed by the WordPress actions core on a
+ * specific interval, specified by you. The action will trigger when someone
+ * visits your WordPress site, if the scheduled time has passed.
+ *
+ * Valid values for the recurrence are hourly, daily and twicedaily.  These can
+ * be extended using the cron_schedules filter in wp_get_schedules().
+ *
+ * @since 2.1.0
+ *
+ * @param int $timestamp Timestamp for when to run the event.
+ * @param string $recurrence How often the event should recur.
+ * @param string $hook Action hook to execute when cron is run.
+ * @param array $args Optional. Arguments to pass to the hook's callback function.
+ * @return bool|null False on failure, null when complete with scheduling event.
+ */
+function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
+	$crons = _get_cron_array();
+	$schedules = wp_get_schedules();
+	$key = md5(serialize($args));
+	if ( !isset( $schedules[$recurrence] ) )
+		return false;
+	$crons[$timestamp][$hook][$key] = array( 'schedule' => $recurrence, 'args' => $args, 'interval' => $schedules[$recurrence]['interval'] );
+	uksort( $crons, "strnatcasecmp" );
+	_set_cron_array( $crons );
+}
+
+/**
+ * Reschedule a recurring event.
+ *
+ * @since 2.1.0
+ *
+ * @param int $timestamp Timestamp for when to run the event.
+ * @param string $recurrence How often the event should recur.
+ * @param string $hook Action hook to execute when cron is run.
+ * @param array $args Optional. Arguments to pass to the hook's callback function.
+ * @return bool|null False on failure. Null when event is rescheduled.
+ */
+function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array()) {
+	$crons = _get_cron_array();
+	$schedules = wp_get_schedules();
+	$key = md5(serialize($args));
+	$interval = 0;
+
+	// First we try to get it from the schedule
+	if ( 0 == $interval )
+		$interval = $schedules[$recurrence]['interval'];
+	// Now we try to get it from the saved interval in case the schedule disappears
+	if ( 0 == $interval )
+		$interval = $crons[$timestamp][$hook][$key]['interval'];
+	// Now we assume something is wrong and fail to schedule
+	if ( 0 == $interval )
+		return false;
+
+	$now = time();
+
+    if ( $timestamp >= $now )
+        $timestamp = $now + $interval;
+    else
+        $timestamp = $now + ($interval - (($now - $timestamp) % $interval));
+
+	wp_schedule_event( $timestamp, $recurrence, $hook, $args );
+}
+
+/**
+ * Unschedule a previously scheduled cron job.
+ *
+ * The $timestamp and $hook parameters are required, so that the event can be
+ * identified.
+ *
+ * @since 2.1.0
+ *
+ * @param int $timestamp Timestamp for when to run the event.
+ * @param string $hook Action hook, the execution of which will be unscheduled.
+ * @param array $args Arguments to pass to the hook's callback function.
+ * Although not passed to a callback function, these arguments are used
+ * to uniquely identify the scheduled event, so they should be the same
+ * as those used when originally scheduling the event.
+ */
+function wp_unschedule_event( $timestamp, $hook, $args = array() ) {
+	$crons = _get_cron_array();
+	$key = md5(serialize($args));
+	unset( $crons[$timestamp][$hook][$key] );
+	if ( empty($crons[$timestamp][$hook]) )
+		unset( $crons[$timestamp][$hook] );
+	if ( empty($crons[$timestamp]) )
+		unset( $crons[$timestamp] );
+	_set_cron_array( $crons );
+}
+
+/**
+ * Unschedule all cron jobs attached to a specific hook.
+ *
+ * @since 2.1.0
+ *
+ * @param string $hook Action hook, the execution of which will be unscheduled.
+ * @param array $args Optional. Arguments that were to be pass to the hook's callback function.
+ */
+function wp_clear_scheduled_hook( $hook, $args = array() ) {
+	// Backward compatibility
+	// Previously this function took the arguments as discrete vars rather than an array like the rest of the API
+	if ( !is_array($args) )
+		$args = array_slice( func_get_args(), 1 );
+
+	while ( $timestamp = wp_next_scheduled( $hook, $args ) )
+		wp_unschedule_event( $timestamp, $hook, $args );
+}
+
+/**
+ * Retrieve the next timestamp for a cron event.
+ *
+ * @since 2.1.0
+ *
+ * @param string $hook Action hook to execute when cron is run.
+ * @param array $args Optional. Arguments to pass to the hook's callback function.
+ * @return bool|int The UNIX timestamp of the next time the scheduled event will occur.
+ */
+function wp_next_scheduled( $hook, $args = array() ) {
+	$crons = _get_cron_array();
+	$key = md5(serialize($args));
+	if ( empty($crons) )
+		return false;
+	foreach ( $crons as $timestamp => $cron ) {
+		if ( isset( $cron[$hook][$key] ) )
+			return $timestamp;
+	}
+	return false;
+}
+
+/**
+ * Send request to run cron through HTTP request that doesn't halt page loading.
+ *
+ * @since 2.1.0
+ *
+ * @return null Cron could not be spawned, because it is not needed to run.
+ */
+function spawn_cron( $local_time = 0 ) {
+
+	if ( !$local_time )
+		$local_time = time();
+
+	if ( defined('DOING_CRON') || isset($_GET['doing_wp_cron']) )
+		return;
+
+	/*
+	 * do not even start the cron if local server timer has drifted
+	 * such as due to power failure, or misconfiguration
+	 */
+	$timer_accurate = check_server_timer( $local_time );
+	if ( !$timer_accurate )
+		return;
+
+	/*
+	* multiple processes on multiple web servers can run this code concurrently
+	* try to make this as atomic as possible by setting doing_cron switch
+	*/
+	$flag = backpress_get_transient('doing_cron');
+
+	if ( $flag > $local_time + 10*60 )
+		$flag = 0;
+
+	// don't run if another process is currently running it or more than once every 60 sec.
+	if ( $flag + 60 > $local_time )
+		return;
+
+	//sanity check
+	$crons = _get_cron_array();
+	if ( !is_array($crons) )
+		return;
+
+	$keys = array_keys( $crons );
+	if ( isset($keys[0]) && $keys[0] > $local_time )
+		return;
+
+	if ( defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON ) {
+		if ( !empty($_POST) || defined('DOING_AJAX') )
+			return;
+
+		backpress_set_transient( 'doing_cron', $local_time );
+
+		ob_start();
+		wp_redirect( add_query_arg('doing_wp_cron', '', stripslashes($_SERVER['REQUEST_URI'])) );
+		echo ' ';
+
+		// flush any buffers and send the headers
+		while ( @ob_end_flush() );
+		flush();
+
+		@include_once(ABSPATH . 'wp-cron.php');
+		return;
+	}
+
+	backpress_set_transient( 'doing_cron', $local_time );
+
+	$cron_url = remove_query_arg( 'check', backpress_get_option( 'cron_uri' ) );
+	$cron_url = add_query_arg( 'doing_wp_cron', '', $cron_url );
+	wp_remote_post( $cron_url, array('timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters('https_local_ssl_verify', true)) );
+}
+
+/**
+ * Run scheduled callbacks or spawn cron for all scheduled events.
+ *
+ * @since 2.1.0
+ *
+ * @return null When doesn't need to run Cron.
+ */
+function wp_cron() {
+	// Prevent infinite loops caused by cron page requesting itself
+	$cron_uri = parse_url( backpress_get_option( 'cron_uri' ) );
+
+	if ( strpos($_SERVER['REQUEST_URI'], $cron_uri['path'] ) !== false || ( defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ) )
+		return;
+
+	if ( false === $crons = _get_cron_array() )
+		return;
+
+	$local_time = time();
+	$keys = array_keys( $crons );
+	if ( isset($keys[0]) && $keys[0] > $local_time )
+		return;
+
+	$schedules = wp_get_schedules();
+	foreach ( $crons as $timestamp => $cronhooks ) {
+		if ( $timestamp > $local_time ) break;
+		foreach ( (array) $cronhooks as $hook => $args ) {
+			if ( isset($schedules[$hook]['callback']) && !call_user_func( $schedules[$hook]['callback'] ) )
+				continue;
+			spawn_cron( $local_time );
+			break 2;
+		}
+	}
+}
+
+/**
+ * Retrieve supported and filtered Cron recurrences.
+ *
+ * The supported recurrences are 'hourly' and 'daily'. A plugin may add more by
+ * hooking into the 'cron_schedules' filter. The filter accepts an array of
+ * arrays. The outer array has a key that is the name of the schedule or for
+ * example 'weekly'. The value is an array with two keys, one is 'interval' and
+ * the other is 'display'.
+ *
+ * The 'interval' is a number in seconds of when the cron job should run. So for
+ * 'hourly', the time is 3600 or 60*60. For weekly, the value would be
+ * 60*60*24*7 or 604800. The value of 'interval' would then be 604800.
+ *
+ * The 'display' is the description. For the 'weekly' key, the 'display' would
+ * be <code>__('Once Weekly')</code>.
+ *
+ * For your plugin, you will be passed an array. you can easily add your
+ * schedule by doing the following.
+ * <code>
+ * // filter parameter variable name is 'array'
+ *	$array['weekly'] = array(
+ *		'interval' => 604800,
+ *		'display' => __('Once Weekly')
+ *	);
+ * </code>
+ *
+ * @since 2.1.0
+ *
+ * @return array
+ */
+function wp_get_schedules() {
+	$schedules = array(
+		'hourly' => array( 'interval' => 3600, 'display' => __('Once Hourly') ),
+		'twicedaily' => array( 'interval' => 43200, 'display' => __('Twice Daily') ),
+		'daily' => array( 'interval' => 86400, 'display' => __('Once Daily') ),
+	);
+	return array_merge( apply_filters( 'cron_schedules', array() ), $schedules );
+}
+
+/**
+ * Retrieve Cron schedule for hook with arguments.
+ *
+ * @since 2.1.0
+ *
+ * @param string $hook Action hook to execute when cron is run.
+ * @param array $args Optional. Arguments to pass to the hook's callback function.
+ * @return string|bool False, if no schedule. Schedule on success.
+ */
+function wp_get_schedule($hook, $args = array()) {
+	$crons = _get_cron_array();
+	$key = md5(serialize($args));
+	if ( empty($crons) )
+		return false;
+	foreach ( $crons as $timestamp => $cron ) {
+		if ( isset( $cron[$hook][$key] ) )
+			return $cron[$hook][$key]['schedule'];
+	}
+	return false;
+}
+
+//
+// Private functions
+//
+
+/**
+ * Retrieve cron info array option.
+ *
+ * @since 2.1.0
+ * @access private
+ *
+ * @return array CRON info array.
+ */
+function _get_cron_array()  {
+	$cron = backpress_get_option('cron');
+	if ( ! is_array($cron) )
+		return false;
+
+	if ( !isset($cron['version']) )
+		$cron = _upgrade_cron_array($cron);
+
+	unset($cron['version']);
+
+	return $cron;
+}
+
+/**
+ * Updates the CRON option with the new CRON array.
+ *
+ * @since 2.1.0
+ * @access private
+ *
+ * @param array $cron Cron info array from {@link _get_cron_array()}.
+ */
+function _set_cron_array($cron) {
+	$cron['version'] = 2;
+	backpress_update_option( 'cron', $cron );
+}
+
+/**
+ * Upgrade a Cron info array.
+ *
+ * This function upgrades the Cron info array to version 2.
+ *
+ * @since 2.1.0
+ * @access private
+ *
+ * @param array $cron Cron info array from {@link _get_cron_array()}.
+ * @return array An upgraded Cron info array.
+ */
+function _upgrade_cron_array($cron) {
+	if ( isset($cron['version']) && 2 == $cron['version'])
+		return $cron;
+
+	$new_cron = array();
+
+	foreach ( (array) $cron as $timestamp => $hooks) {
+		foreach ( (array) $hooks as $hook => $args ) {
+			$key = md5(serialize($args['args']));
+			$new_cron[$timestamp][$hook][$key] = $args;
+		}
+	}
+
+	$new_cron['version'] = 2;
+	backpress_update_option( 'cron', $new_cron );
+	return $new_cron;
+}
+
+// stub for checking server timer accuracy, using outside standard time sources
+function check_server_timer( $local_time ) {
+	return true;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-object-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-object-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-object-cache.php	(revision 5057)
@@ -0,0 +1,173 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * Object Cache API
+ *
+ * @link http://codex.wordpress.org/Function_Reference/WP_Cache
+ *
+ * @package WordPress
+ * @subpackage Cache
+ */
+
+
+
+/**
+ * Adds data to the cache, if the cache key doesn't aleady exist.
+ *
+ * @since 2.0.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::add()
+ *
+ * @param int|string $key The cache ID to use for retrieval later
+ * @param mixed $data The data to add to the cache store
+ * @param string $flag The group to add the cache to
+ * @param int $expire When the cache data should be expired
+ * @return unknown
+ */
+function wp_cache_add($key, $data, $flag = '', $expire = 0) {
+	global $wp_object_cache;
+
+	return $wp_object_cache->add($key, $data, $flag, $expire);
+}
+
+/**
+ * Closes the cache.
+ *
+ * This function has ceased to do anything since WordPress 2.5. The
+ * functionality was removed along with the rest of the persistent cache. This
+ * does not mean that plugins can't implement this function when they need to
+ * make sure that the cache is cleaned up after WordPress no longer needs it.
+ *
+ * @since 2.0.0
+ *
+ * @return bool Always returns True
+ */
+function wp_cache_close() {
+	return true;
+}
+
+/**
+ * Removes the cache contents matching ID and flag.
+ *
+ * @since 2.0.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::delete()
+ *
+ * @param int|string $id What the contents in the cache are called
+ * @param string $flag Where the cache contents are grouped
+ * @return bool True on successful removal, false on failure
+ */
+function wp_cache_delete($id, $flag = '') {
+	global $wp_object_cache;
+
+	return $wp_object_cache->delete($id, $flag);
+}
+
+/**
+ * Removes all cache items.
+ *
+ * @since 2.0.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::flush()
+ *
+ * @return bool Always returns true
+ */
+function wp_cache_flush( $group = null ) {
+	// WP does not support group flushing
+	global $wp_object_cache;
+
+	return $wp_object_cache->flush( $group );
+}
+
+/**
+ * Retrieves the cache contents from the cache by ID and flag.
+ *
+ * @since 2.0.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::get()
+ *
+ * @param int|string $id What the contents in the cache are called
+ * @param string $flag Where the cache contents are grouped
+ * @return bool|mixed False on failure to retrieve contents or the cache
+ *		contents on success
+ */
+function wp_cache_get($id, $flag = '') {
+	global $wp_object_cache;
+
+	return $wp_object_cache->get($id, $flag);
+}
+
+/**
+ * Sets up Object Cache Global and assigns it.
+ *
+ * @since 2.0.0
+ * @global WP_Object_Cache $wp_object_cache WordPress Object Cache
+ */
+function wp_cache_init() {
+	$GLOBALS['wp_object_cache'] =& new WP_Object_Cache();
+}
+
+/**
+ * Replaces the contents of the cache with new data.
+ *
+ * @since 2.0.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::replace()
+ *
+ * @param int|string $id What to call the contents in the cache
+ * @param mixed $data The contents to store in the cache
+ * @param string $flag Where to group the cache contents
+ * @param int $expire When to expire the cache contents
+ * @return bool False if cache ID and group already exists, true on success
+ */
+function wp_cache_replace($key, $data, $flag = '', $expire = 0) {
+	global $wp_object_cache;
+
+	return $wp_object_cache->replace($key, $data, $flag, $expire);
+}
+
+/**
+ * Saves the data to the cache.
+ *
+ * @since 2.0
+ * @uses $wp_object_cache Object Cache Class
+ * @see WP_Object_Cache::set()
+ *
+ * @param int|string $id What to call the contents in the cache
+ * @param mixed $data The contents to store in the cache
+ * @param string $flag Where to group the cache contents
+ * @param int $expire When to expire the cache contents
+ * @return bool False if cache ID and group already exists, true on success
+ */
+function wp_cache_set($key, $data, $flag = '', $expire = 0) {
+	global $wp_object_cache;
+
+	return $wp_object_cache->set($key, $data, $flag, $expire);
+}
+
+/**
+ * Adds a group or set of groups to the list of global groups.
+ *
+ * @since 2.6.0
+ *
+ * @param string|array $groups A group or an array of groups to add
+ */
+function wp_cache_add_global_groups( $groups ) {
+	global $wp_object_cache;
+
+	return $wp_object_cache->add_global_groups( $groups );
+}
+
+/**
+ * Adds a group or set of groups to the list of non-persistent groups.
+ *
+ * @since 2.6.0
+ *
+ * @param string|array $groups A group or an array of groups to add
+ */
+function wp_cache_add_non_persistent_groups( $groups ) {
+	global $wp_object_cache;
+
+	return $wp_object_cache->add_non_persistent_groups( $groups );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-scripts.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-scripts.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-scripts.php	(revision 5057)
@@ -0,0 +1,133 @@
+<?php
+/**
+ * BackPress script procedural API.
+ *
+ * @package BackPress
+ * @since r16
+ */
+
+/**
+ * Prints script tags in document head.
+ *
+ * Called by admin-header.php and by wp_head hook. Since it is called by wp_head
+ * on every page load, the function does not instantiate the WP_Scripts object
+ * unless script names are explicitly passed. Does make use of already
+ * instantiated $wp_scripts if present. Use provided wp_print_scripts hook to
+ * register/enqueue new scripts.
+ *
+ * @since r16
+ * @see WP_Dependencies::print_scripts()
+ */
+function wp_print_scripts( $handles = false ) {
+	do_action( 'wp_print_scripts' );
+	if ( '' === $handles ) // for wp_head
+		$handles = false;
+
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') ) {
+		if ( !$handles )
+			return array(); // No need to instantiate if nothing's there.
+		else
+			$wp_scripts = new WP_Scripts();
+	}
+
+	return $wp_scripts->do_items( $handles );
+}
+
+/**
+ * Register new JavaScript file.
+ *
+ * @since r16
+ * @param string $handle Script name
+ * @param string $src Script url
+ * @param array $deps (optional) Array of script names on which this script depends
+ * @param string|bool $ver (optional) Script version (used for cache busting), set to NULL to disable
+ * @param bool (optional) Wether to enqueue the script before </head> or before </body>
+ * @return null
+ */
+function wp_register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') )
+		$wp_scripts = new WP_Scripts();
+
+	$wp_scripts->add( $handle, $src, $deps, $ver );
+	if ( $in_footer )
+		$wp_scripts->add_data( $handle, 'group', 1 );
+}
+
+/**
+ * Localizes a script.
+ *
+ * Localizes only if script has already been added.
+ *
+ * @since r16
+ * @see WP_Scripts::localize()
+ */
+function wp_localize_script( $handle, $object_name, $l10n ) {
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') )
+		return false;
+
+	return $wp_scripts->localize( $handle, $object_name, $l10n );
+}
+
+/**
+ * Remove a registered script.
+ *
+ * @since r16
+ * @see WP_Scripts::remove() For parameter information.
+ */
+function wp_deregister_script( $handle ) {
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') )
+		$wp_scripts = new WP_Scripts();
+
+	$wp_scripts->remove( $handle );
+}
+
+/**
+ * Enqueues script.
+ *
+ * Registers the script if src provided (does NOT overwrite) and enqueues.
+ *
+ * @since r16
+ * @see wp_register_script() For parameter information.
+ */
+function wp_enqueue_script( $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') )
+		$wp_scripts = new WP_Scripts();
+
+	if ( $src ) {
+		$_handle = explode('?', $handle);
+		$wp_scripts->add( $_handle[0], $src, $deps, $ver );
+		if ( $in_footer )
+			$wp_scripts->add_data( $_handle[0], 'group', 1 );
+	}
+	$wp_scripts->enqueue( $handle );
+}
+
+/**
+ * Check whether script has been added to WordPress Scripts.
+ *
+ * The values for list defaults to 'queue', which is the same as enqueue for
+ * scripts.
+ *
+ * @since WP unknown; BP unknown
+ *
+ * @param string $handle Handle used to add script.
+ * @param string $list Optional, defaults to 'queue'. Others values are 'registered', 'queue', 'done', 'to_do'
+ * @return bool
+ */
+function wp_script_is( $handle, $list = 'queue' ) {
+	global $wp_scripts;
+	if ( !is_a($wp_scripts, 'WP_Scripts') )
+		$wp_scripts = new WP_Scripts();
+
+	$query = $wp_scripts->query( $handle, $list );
+
+	if ( is_object( $query ) )
+		return true;
+
+	return $query;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-styles.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-styles.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-styles.php	(revision 5057)
@@ -0,0 +1,131 @@
+<?php
+/**
+ * BackPress styles procedural API.
+ *
+ * @package BackPress
+ * @since r79
+ */
+
+/**
+ * Display styles that are in the queue or part of $handles.
+ *
+ * @since r79
+ * @uses do_action() Calls 'wp_print_styles' hook.
+ * @global object $wp_styles The WP_Styles object for printing styles.
+ *
+ * @param array|bool $handles Styles to be printed. An empty array prints the queue,
+ *  an array with one string prints that style, and an array of strings prints those styles.
+ * @return bool True on success, false on failure.
+ */
+function wp_print_styles( $handles = false ) {
+	do_action( 'wp_print_styles' );
+	if ( '' === $handles ) // for wp_head
+		$handles = false;
+
+	global $wp_styles;
+	if ( !is_a($wp_styles, 'WP_Styles') ) {
+		if ( !$handles )
+			return array(); // No need to instantiate if nothing's there.
+		else
+			$wp_styles = new WP_Styles();
+	}
+
+	return $wp_styles->do_items( $handles );
+}
+
+/**
+ * Register CSS style file.
+ *
+ * @since r79
+ * @see WP_Styles::add() For additional information.
+ * @global object $wp_styles The WP_Styles object for printing styles.
+ * @link http://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types.
+ *
+ * @param string $handle Name of the stylesheet.
+ * @param string|bool $src Path to the stylesheet from the root directory of WordPress. Example: '/css/mystyle.css'.
+ * @param array $deps Array of handles of any stylesheet that this stylesheet depends on.
+ *  (Stylesheets that must be loaded before this stylesheet.) Pass an empty array if there are no dependencies.
+ * @param string|bool $ver String specifying the stylesheet version number. Set to NULL to disable.
+ *  Used to ensure that the correct version is sent to the client regardless of caching.
+ * @param string $media The media for which this stylesheet has been defined.
+ */
+function wp_register_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
+	global $wp_styles;
+	if ( !is_a($wp_styles, 'WP_Styles') )
+		$wp_styles = new WP_Styles();
+
+	$wp_styles->add( $handle, $src, $deps, $ver, $media );
+}
+
+/**
+ * Remove a registered CSS file.
+ *
+ * @since r79
+ * @see WP_Styles::remove() For additional information.
+ * @global object $wp_styles The WP_Styles object for printing styles.
+ *
+ * @param string $handle Name of the stylesheet.
+ */
+function wp_deregister_style( $handle ) {
+	global $wp_styles;
+	if ( !is_a($wp_styles, 'WP_Styles') )
+		$wp_styles = new WP_Styles();
+
+	$wp_styles->remove( $handle );
+}
+
+/**
+ * Enqueue a CSS style file.
+ *
+ * Registers the style if src provided (does NOT overwrite) and enqueues.
+ *
+ * @since r79
+ * @see WP_Styles::add(), WP_Styles::enqueue()
+ * @global object $wp_styles The WP_Styles object for printing styles.
+ * @link http://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types.
+ *
+ * @param string $handle Name of the stylesheet.
+ * @param string|bool $src Path to the stylesheet from the root directory of WordPress. Example: '/css/mystyle.css'.
+ * @param array $deps Array of handles (names) of any stylesheet that this stylesheet depends on.
+ *  (Stylesheets that must be loaded before this stylesheet.) Pass an empty array if there are no dependencies.
+ * @param string|bool $ver String specifying the stylesheet version number, if it has one. This parameter
+ *  is used to ensure that the correct version is sent to the client regardless of caching, and so should be included
+ *  if a version number is available and makes sense for the stylesheet.
+ * @param string $media The media for which this stylesheet has been defined.
+ */
+function wp_enqueue_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
+	global $wp_styles;
+	if ( !is_a($wp_styles, 'WP_Styles') )
+		$wp_styles = new WP_Styles();
+
+	if ( $src ) {
+		$_handle = explode('?', $handle);
+		$wp_styles->add( $_handle[0], $src, $deps, $ver, $media );
+	}
+	$wp_styles->enqueue( $handle );
+}
+
+/**
+ * Check whether style has been added to WordPress Styles.
+ *
+ * The values for list defaults to 'queue', which is the same as wp_enqueue_style().
+ *
+ * @since WP unknown; BP unknown
+ * @global object $wp_styles The WP_Styles object for printing styles.
+ *
+ * @param string $handle Name of the stylesheet.
+ * @param string $list Values are 'registered', 'done', 'queue' and 'to_do'.
+ * @return bool True on success, false on failure.
+ */
+function wp_style_is( $handle, $list = 'queue' ) {
+	global $wp_styles;
+	if ( !is_a($wp_styles, 'WP_Styles') )
+		$wp_styles = new WP_Styles();
+
+	$query = $wp_styles->query( $handle, $list );
+
+	if ( is_object( $query ) )
+		return true;
+
+	return $query;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-taxonomy.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-taxonomy.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/functions.wp-taxonomy.php	(revision 5057)
@@ -0,0 +1,185 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * Taxonomy API
+ *
+ * @package WordPress
+ * @subpackage Taxonomy
+ * @since 2.3.0
+ */
+
+function get_object_taxonomies($object_type) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_object_taxonomies($object_type);
+}
+
+function get_taxonomy( $taxonomy ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_taxonomy( $taxonomy );
+}
+
+function is_taxonomy( $taxonomy ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->is_taxonomy( $taxonomy );
+}
+
+function is_taxonomy_hierarchical($taxonomy) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->is_taxonomy_hierarchical($taxonomy);
+}
+
+function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->register_taxonomy( $taxonomy, $object_type, $args );
+}
+
+function get_objects_in_term( $terms, $taxonomies, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_objects_in_term( $terms, $taxonomies, $args );
+}
+
+function &get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_term($term, $taxonomy, $output, $filter);
+}
+
+function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_term_by($field, $value, $taxonomy, $output, $filter);
+}
+
+function get_term_children( $term, $taxonomy ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_term_children( $term, $taxonomy );
+}
+
+function get_term_field( $field, $term, $taxonomy, $context = 'display' ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_term_field( $field, $term, $taxonomy, $context );
+}
+
+function get_term_to_edit( $id, $taxonomy ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_term_to_edit( $id, $taxonomy );
+}
+
+function &get_terms($taxonomies, $args = '') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_terms($taxonomies, $args);
+}
+
+function is_term($term, $taxonomy = '') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->is_term($term, $taxonomy );
+}
+
+function sanitize_term($term, $taxonomy, $context = 'display') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->sanitize_term($term, $taxonomy, $context );
+}
+
+function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->sanitize_term_field($field, $value, $term_id, $taxonomy, $context);
+}
+
+function wp_count_terms( $taxonomy, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->count_terms( $taxonomy, $args );
+}
+
+function wp_delete_object_term_relationships( $object_id, $taxonomies ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->delete_object_term_relationships( $object_id, $taxonomies );
+}
+
+function wp_delete_term( $term, $taxonomy, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->delete_term( $term, $taxonomy, $args );
+}
+
+function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_object_terms($object_ids, $taxonomies, $args );
+}
+
+function wp_insert_term( $term, $taxonomy, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->insert_term( $term, $taxonomy, $args );
+}
+
+function wp_set_object_terms($object_id, $terms, $taxonomy, $append = false) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->set_object_terms($object_id, $terms, $taxonomy, $append);
+}
+
+function wp_unique_term_slug($slug, $term) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->unique_term_slug($slug, $term);
+}
+
+function wp_update_term( $term, $taxonomy, $args = array() ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->update_term( $term, $taxonomy, $args );
+}
+
+function wp_defer_term_counting($defer=NULL) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->defer_term_counting($defer);
+}
+
+function wp_update_term_count( $terms, $taxonomy, $do_deferred=false ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->update_term_count( $terms, $taxonomy, $do_deferred );
+}
+
+function wp_update_term_count_now( $terms, $taxonomy ) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->update_term_count_now( $terms, $taxonomy );
+}
+
+function clean_object_term_cache($object_ids, $object_type) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->clean_object_term_cache($object_ids, $object_type);
+}
+
+function clean_term_cache($ids, $taxonomy = '') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->clean_term_cache($ids, $taxonomy);
+}
+
+function &get_object_term_cache($id, $taxonomy) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->get_object_term_cache($id, $taxonomy);
+}
+
+function update_object_term_cache($object_ids, $object_type) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->update_object_term_cache($object_ids, $object_type);
+}
+
+function update_term_cache($terms, $taxonomy = '') {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->update_term_cache($terms, $taxonomy);
+}
+
+function _get_term_hierarchy($taxonomy) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->_get_term_hierarchy($taxonomy);
+}
+
+function &_get_term_children($term_id, $terms, $taxonomy) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->_get_term_children($term_id, $terms, $taxonomy);
+}
+
+function _pad_term_counts(&$terms, $taxonomy) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->_pad_term_counts(&$terms, $taxonomy);
+}
+
+function is_object_in_term($object_id, $taxonomy, $terms = null) {
+	global $wp_taxonomy_object;
+	return $wp_taxonomy_object->is_object_in_term($object_id, $taxonomy, $terms);
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/interface.bp-options.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/interface.bp-options.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/interface.bp-options.php	(revision 5057)
@@ -0,0 +1,128 @@
+<?php
+/**
+ * BackPress Options API.
+ *
+ * This is in place for the multiple projects that use BackPress to have options
+ * and transients but not rely on the WordPress options API.
+ *
+ * @since r132
+ */
+
+/**
+ * Interface for BP_Options;
+ *
+ * A BP_Options class must be implemented by the host application for
+ * BackPress to operate. This interface supplies a boilerplate for that
+ * class but can only be implemented in PHP 5 environments.
+ *
+ * @since r132
+ * @package BackPress
+ */
+interface BP_Options_Interface
+{
+	/**
+	 * Retrieve the prefix to be appended to the beginning of the option key.
+	 *
+	 * @since r132
+	 */
+	function prefix();
+
+	/**
+	 * Retrieve the value of the option.
+	 *
+	 * Here is a minimum set of values required (from bbPress)
+	 * - application_id     : An id for the application, use this when running multiple applications from the same code
+	 * - application_uri    : The base URI of the application
+	 * - cron_uri           : The URI that processes cron jobs
+	 * - wp_http_version    : This is the version sent when making remote HTTP requests
+	 * - hash_function_name : The function used to create unique hashes ( see wp_hash() )
+	 * - language_locale    : The current locale
+	 * - language_directory : The directory containing language (po, mo) files
+	 * - charset            : The charset to use when appropriate (usually UTF-8)
+	 * - gmt_offset         : The GMT offset as a positive or negative float
+	 * - timezone_string    : The timezone in Zoneinfo format
+	 *
+	 * @since r132
+	 *
+	 * @param string $option Option name.
+	 */
+	function get( $option );
+
+	/**
+	 * Adds an option with given value.
+	 *
+	 * @since r132
+	 *
+	 * @param string $option Option name.
+	 * @param mixed $value Option value.
+	 */
+	function add( $option, $value );
+
+	/**
+	 * Updates an existing option with a given value.
+	 *
+	 * @since r132
+	 *
+	 * @param string $option Option name.
+	 * @param mixed $value Option value.
+	 */
+	function update( $option, $value );
+
+	/**
+	 * Deletes an existing option.
+	 *
+	 * @since r132
+	 *
+	 * @param string $option Option name.
+	 */
+	function delete( $option );
+} // END interface BP_Options_Interface
+
+/**
+ * Interface for BP_Transients;
+ *
+ * A BP_Transients class must be implemented by the host application for
+ * BackPress to operate. This interface supplies a boilerplate for that
+ * class but can only be implemented in PHP 5 environments.
+ *
+ * @since r205
+ * @package BackPress
+ */
+interface BP_Transients_Interface
+{
+	/**
+	 * Retrieve the prefix to be appended to the beginning of the transient key.
+	 *
+	 * @since r205
+	 */
+	function prefix();
+
+	/**
+	 * Retrieve the value of the transient.
+	 *
+	 * @since r205
+	 *
+	 * @param string $transient Transient name.
+	 */
+	function get( $transient );
+
+	/**
+	 * Sets the value of a transient with a given value.
+	 *
+	 * @since r205
+	 *
+	 * @param string $transient Transient name.
+	 * @param mixed $value Transient value.
+	 * @param integer $expiration The time in seconds the transient will be held for. Default is 0, meaning it is held indefinitely.
+	 */
+	function set( $transient, $value, $expiration = 0 );
+
+	/**
+	 * Deletes an existing transient.
+	 *
+	 * @since r205
+	 *
+	 * @param string $transient Transient name.
+	 */
+	function delete( $transient );
+} // END Interface BP_Transients
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/license.txt
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/license.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/license.txt	(revision 5057)
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache-memcached.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache-memcached.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache-memcached.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+
+require_once( 'class.wp-object-cache-memcached.php' );
+require_once( 'functions.wp-object-cache.php' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/loader.wp-object-cache.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+
+require_once( 'class.wp-object-cache.php' );
+require_once( 'functions.wp-object-cache.php' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/entry.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/entry.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/entry.php	(revision 5057)
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Contains Translation_Entry class
+ *
+ * @version $Id: entry.php 621 2011-06-13 12:21:50Z nbachiyski $
+ * @package pomo
+ * @subpackage entry
+ */
+
+if ( !class_exists( 'Translation_Entry' ) ):
+/**
+ * Translation_Entry class encapsulates a translatable string
+ */
+class Translation_Entry {
+
+	/**
+	 * Whether the entry contains a string and its plural form, default is false
+	 *
+	 * @var boolean
+	 */
+	var $is_plural = false;
+
+	var $context = null;
+	var $singular = null;
+	var $plural = null;
+	var $translations = array();
+	var $translator_comments = '';
+	var $extracted_comments = '';
+	var $references = array();
+	var $flags = array();
+
+	/**
+	 * @param array $args associative array, support following keys:
+	 * 	- singular (string) -- the string to translate, if omitted and empty entry will be created
+	 * 	- plural (string) -- the plural form of the string, setting this will set {@link $is_plural} to true
+	 * 	- translations (array) -- translations of the string and possibly -- its plural forms
+	 * 	- context (string) -- a string differentiating two equal strings used in different contexts
+	 * 	- translator_comments (string) -- comments left by translators
+	 * 	- extracted_comments (string) -- comments left by developers
+	 * 	- references (array) -- places in the code this strings is used, in relative_to_root_path/file.php:linenum form
+	 * 	- flags (array) -- flags like php-format
+	 */
+	function Translation_Entry($args=array()) {
+		// if no singular -- empty object
+		if (!isset($args['singular'])) {
+			return;
+		}
+		// get member variable values from args hash
+		foreach ($args as $varname => $value) {
+			$this->$varname = $value;
+		}
+		if (isset($args['plural'])) $this->is_plural = true;
+		if (!is_array($this->translations)) $this->translations = array();
+		if (!is_array($this->references)) $this->references = array();
+		if (!is_array($this->flags)) $this->flags = array();
+	}
+
+	/**
+	 * Generates a unique key for this entry
+	 *
+	 * @return string|bool the key or false if the entry is empty
+	 */
+	function key() {
+		if (is_null($this->singular)) return false;
+		// prepend context and EOT, like in MO files
+		return is_null($this->context)? $this->singular : $this->context.chr(4).$this->singular;
+	}
+	
+	function merge_with(&$other) {
+		$this->flags = array_unique( array_merge( $this->flags, $other->flags ) );
+		$this->references = array_unique( array_merge( $this->references, $other->references ) );
+		if ( $this->extracted_comments != $other->extracted_comments ) {
+			$this->extracted_comments .= $other->extracted_comments;
+		}
+		
+	}
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/mo.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/mo.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/mo.php	(revision 5057)
@@ -0,0 +1,245 @@
+<?php
+/**
+ * Class for working with MO files
+ *
+ * @version $Id: mo.php 602 2011-01-30 12:43:29Z nbachiyski $
+ * @package pomo
+ * @subpackage mo
+ */
+
+require_once dirname(__FILE__) . '/translations.php';
+require_once dirname(__FILE__) . '/streams.php';
+
+if ( !class_exists( 'MO' ) ):
+class MO extends Gettext_Translations {
+
+	var $_nplurals = 2;
+
+	/**
+	 * Fills up with the entries from MO file $filename
+	 *
+	 * @param string $filename MO file to load
+	 */
+	function import_from_file($filename) {
+		$reader = new POMO_FileReader($filename);
+		if (!$reader->is_resource())
+			return false;
+		return $this->import_from_reader($reader);
+	}
+
+	function export_to_file($filename) {
+		$fh = fopen($filename, 'wb');
+		if ( !$fh ) return false;
+		$res = $this->export_to_file_handle( $fh );
+		fclose($fh);
+		return $res;
+	}
+	
+	function export() {
+		$tmp_fh = fopen("php://temp", 'r+');
+		if ( !$tmp_fh ) return false;
+		$this->export_to_file_handle( $tmp_fh );
+		rewind( $tmp_fh );
+		return stream_get_contents( $tmp_fh );
+	}
+	
+	function export_to_file_handle($fh) {
+		$entries = array_filter($this->entries, create_function('$e', 'return !empty($e->translations);'));
+		ksort($entries);
+		$magic = 0x950412de;
+		$revision = 0;
+		$total = count($entries) + 1; // all the headers are one entry
+		$originals_lenghts_addr = 28;
+		$translations_lenghts_addr = $originals_lenghts_addr + 8 * $total;
+		$size_of_hash = 0;
+		$hash_addr = $translations_lenghts_addr + 8 * $total;
+		$current_addr = $hash_addr;
+		fwrite($fh, pack('V*', $magic, $revision, $total, $originals_lenghts_addr,
+			$translations_lenghts_addr, $size_of_hash, $hash_addr));
+		fseek($fh, $originals_lenghts_addr);
+		
+		// headers' msgid is an empty string
+		fwrite($fh, pack('VV', 0, $current_addr));
+		$current_addr++;
+		$originals_table = chr(0);
+
+		foreach($entries as $entry) {
+			$originals_table .= $this->export_original($entry) . chr(0);
+			$length = strlen($this->export_original($entry));
+			fwrite($fh, pack('VV', $length, $current_addr));
+			$current_addr += $length + 1; // account for the NULL byte after
+		}
+		
+		$exported_headers = $this->export_headers();
+		fwrite($fh, pack('VV', strlen($exported_headers), $current_addr));
+		$current_addr += strlen($exported_headers) + 1;
+		$translations_table = $exported_headers . chr(0);
+		
+		foreach($entries as $entry) {
+			$translations_table .= $this->export_translations($entry) . chr(0);
+			$length = strlen($this->export_translations($entry));
+			fwrite($fh, pack('VV', $length, $current_addr));
+			$current_addr += $length + 1;
+		}
+		
+		fwrite($fh, $originals_table);
+		fwrite($fh, $translations_table);
+		return true;
+	}
+	
+	function export_original($entry) {
+		//TODO: warnings for control characters
+		$exported = $entry->singular;
+		if ($entry->is_plural) $exported .= chr(0).$entry->plural;
+		if (!is_null($entry->context)) $exported = $entry->context . chr(4) . $exported;
+		return $exported;
+	}
+	
+	function export_translations($entry) {
+		//TODO: warnings for control characters
+		return implode(chr(0), $entry->translations);
+	}
+	
+	function export_headers() {
+		$exported = '';
+		foreach($this->headers as $header => $value) {
+			$exported.= "$header: $value\n";
+		}
+		return $exported;
+	}
+
+	function get_byteorder($magic) {
+		// The magic is 0x950412de
+
+		// bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
+		$magic_little = (int) - 1794895138;
+		$magic_little_64 = (int) 2500072158;
+		// 0xde120495
+		$magic_big = ((int) - 569244523) & 0xFFFFFFFF;
+		if ($magic_little == $magic || $magic_little_64 == $magic) {
+			return 'little';
+		} else if ($magic_big == $magic) {
+			return 'big';
+		} else {
+			return false;
+		}
+	}
+
+	function import_from_reader($reader) {
+		$endian_string = MO::get_byteorder($reader->readint32());
+		if (false === $endian_string) {
+			return false;
+		}
+		$reader->setEndian($endian_string);
+
+		$endian = ('big' == $endian_string)? 'N' : 'V';
+
+		$header = $reader->read(24);
+		if ($reader->strlen($header) != 24)
+			return false;
+
+		// parse header
+		$header = unpack("{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header);
+		if (!is_array($header))
+			return false;
+
+		extract( $header );
+
+		// support revision 0 of MO format specs, only
+		if ($revision != 0)
+			return false;
+
+		// seek to data blocks
+		$reader->seekto($originals_lenghts_addr);
+
+		// read originals' indices
+		$originals_lengths_length = $translations_lenghts_addr - $originals_lenghts_addr;
+		if ( $originals_lengths_length != $total * 8 )
+			return false;
+
+		$originals = $reader->read($originals_lengths_length);
+		if ( $reader->strlen( $originals ) != $originals_lengths_length )
+			return false;
+
+		// read translations' indices
+		$translations_lenghts_length = $hash_addr - $translations_lenghts_addr;
+		if ( $translations_lenghts_length != $total * 8 )
+			return false;
+
+		$translations = $reader->read($translations_lenghts_length);
+		if ( $reader->strlen( $translations ) != $translations_lenghts_length )
+			return false;
+
+		// transform raw data into set of indices
+		$originals    = $reader->str_split( $originals, 8 );
+		$translations = $reader->str_split( $translations, 8 );
+
+		// skip hash table
+		$strings_addr = $hash_addr + $hash_length * 4;
+
+		$reader->seekto($strings_addr);
+
+		$strings = $reader->read_all();
+		$reader->close();
+
+		for ( $i = 0; $i < $total; $i++ ) {
+			$o = unpack( "{$endian}length/{$endian}pos", $originals[$i] );
+			$t = unpack( "{$endian}length/{$endian}pos", $translations[$i] );
+			if ( !$o || !$t ) return false;
+
+			// adjust offset due to reading strings to separate space before
+			$o['pos'] -= $strings_addr;
+			$t['pos'] -= $strings_addr;
+
+			$original    = $reader->substr( $strings, $o['pos'], $o['length'] );
+			$translation = $reader->substr( $strings, $t['pos'], $t['length'] );
+
+			if ('' === $original) {
+				$this->set_headers($this->make_headers($translation));
+			} else {
+				$entry = &$this->make_entry($original, $translation);
+				$this->entries[$entry->key()] = &$entry;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Build a Translation_Entry from original string and translation strings,
+	 * found in a MO file
+	 * 
+	 * @static
+	 * @param string $original original string to translate from MO file. Might contain
+	 * 	0x04 as context separator or 0x00 as singular/plural separator
+	 * @param string $translation translation string from MO file. Might contain
+	 * 	0x00 as a plural translations separator
+	 */
+	function &make_entry($original, $translation) {
+		$entry = new Translation_Entry();
+		// look for context
+		$parts = explode(chr(4), $original);
+		if (isset($parts[1])) {
+			$original = $parts[1];
+			$entry->context = $parts[0];
+		}
+		// look for plural original
+		$parts = explode(chr(0), $original);
+		$entry->singular = $parts[0];
+		if (isset($parts[1])) {
+			$entry->is_plural = true;
+			$entry->plural = $parts[1];
+		}
+		// plural translations are also separated by \0
+		$entry->translations = explode(chr(0), $translation);
+		return $entry;
+	}
+
+	function select_plural_form($count) {
+		return $this->gettext_select_plural_form($count);
+	}
+
+	function get_plural_forms_count() {
+		return $this->_nplurals;
+	}
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/po.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/po.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/po.php	(revision 5057)
@@ -0,0 +1,377 @@
+<?php
+/**
+ * Class for working with PO files
+ *
+ * @version $Id: po.php 589 2010-12-18 01:40:57Z nbachiyski $
+ * @package pomo
+ * @subpackage po
+ */
+
+require_once dirname(__FILE__) . '/translations.php';
+
+define('PO_MAX_LINE_LEN', 79);
+
+ini_set('auto_detect_line_endings', 1);
+
+/**
+ * Routines for working with PO files
+ */
+if ( !class_exists( 'PO' ) ):
+class PO extends Gettext_Translations {
+	
+	var $comments_before_headers = '';
+
+	/**
+	 * Exports headers to a PO entry
+	 *
+	 * @return string msgid/msgstr PO entry for this PO file headers, doesn't contain newline at the end
+	 */
+	function export_headers() {
+		$header_string = '';
+		foreach($this->headers as $header => $value) {
+			$header_string.= "$header: $value\n";
+		}
+		$poified = PO::poify($header_string);
+		if ($this->comments_before_headers)
+			$before_headers = $this->prepend_each_line(rtrim($this->comments_before_headers)."\n", '# ');
+		else
+			$before_headers = '';
+		return rtrim("{$before_headers}msgid \"\"\nmsgstr $poified");
+	}
+
+	/**
+	 * Exports all entries to PO format
+	 *
+	 * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end
+	 */
+	function export_entries() {
+		//TODO sorting
+		return implode("\n\n", array_map(array('PO', 'export_entry'), $this->entries));
+	}
+
+	/**
+	 * Exports the whole PO file as a string
+	 *
+	 * @param bool $include_headers whether to include the headers in the export
+	 * @return string ready for inclusion in PO file string for headers and all the enrtries
+	 */
+	function export($include_headers = true) {
+		$res = '';
+		if ($include_headers) {
+			$res .= $this->export_headers();
+			$res .= "\n\n";
+		}
+		$res .= $this->export_entries();
+		return $res;
+	}
+
+	/**
+	 * Same as {@link export}, but writes the result to a file
+	 *
+	 * @param string $filename where to write the PO string
+	 * @param bool $include_headers whether to include tje headers in the export
+	 * @return bool true on success, false on error
+	 */
+	function export_to_file($filename, $include_headers = true) {
+		$fh = fopen($filename, 'w');
+		if (false === $fh) return false;
+		$export = $this->export($include_headers);
+		$res = fwrite($fh, $export);
+		if (false === $res) return false;
+		return fclose($fh);
+	}
+	
+	/**
+	 * Text to include as a comment before the start of the PO contents
+	 * 
+	 * Doesn't need to include # in the beginning of lines, these are added automatically
+	 */
+	function set_comment_before_headers( $text ) {
+		$this->comments_before_headers = $text;
+	}
+
+	/**
+	 * Formats a string in PO-style
+	 *
+	 * @static
+	 * @param string $string the string to format
+	 * @return string the poified string
+	 */
+	function poify($string) {
+		$quote = '"';
+		$slash = '\\';
+		$newline = "\n";
+
+		$replaces = array(
+			"$slash" 	=> "$slash$slash",
+			"$quote"	=> "$slash$quote",
+			"\t" 		=> '\t',
+		);
+
+		$string = str_replace(array_keys($replaces), array_values($replaces), $string);
+
+		$po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote;
+		// add empty string on first line for readbility
+		if (false !== strpos($string, $newline) &&
+				(substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) {
+			$po = "$quote$quote$newline$po";
+		}
+		// remove empty strings
+		$po = str_replace("$newline$quote$quote", '', $po);
+		return $po;
+	}
+	
+	/**
+	 * Gives back the original string from a PO-formatted string
+	 * 
+	 * @static
+	 * @param string $string PO-formatted string
+	 * @return string enascaped string
+	 */
+	function unpoify($string) {
+		$escapes = array('t' => "\t", 'n' => "\n", '\\' => '\\');
+		$lines = array_map('trim', explode("\n", $string));
+		$lines = array_map(array('PO', 'trim_quotes'), $lines);
+		$unpoified = '';
+		$previous_is_backslash = false;
+		foreach($lines as $line) {
+			preg_match_all('/./u', $line, $chars);
+			$chars = $chars[0];
+			foreach($chars as $char) {
+				if (!$previous_is_backslash) {
+					if ('\\' == $char)
+						$previous_is_backslash = true;
+					else
+						$unpoified .= $char;
+				} else {
+					$previous_is_backslash = false;
+					$unpoified .= isset($escapes[$char])? $escapes[$char] : $char;
+				}
+			}
+		}
+		return $unpoified;
+	}
+
+	/**
+	 * Inserts $with in the beginning of every new line of $string and 
+	 * returns the modified string
+	 *
+	 * @static
+	 * @param string $string prepend lines in this string
+	 * @param string $with prepend lines with this string
+	 */
+	function prepend_each_line($string, $with) {
+		$php_with = var_export($with, true);
+		$lines = explode("\n", $string);
+		// do not prepend the string on the last empty line, artefact by explode
+		if ("\n" == substr($string, -1)) unset($lines[count($lines) - 1]);
+		$res = implode("\n", array_map(create_function('$x', "return $php_with.\$x;"), $lines));
+		// give back the empty line, we ignored above
+		if ("\n" == substr($string, -1)) $res .= "\n";
+		return $res;
+	}
+
+	/**
+	 * Prepare a text as a comment -- wraps the lines and prepends #
+	 * and a special character to each line
+	 *
+	 * @access private
+	 * @param string $text the comment text
+	 * @param string $char character to denote a special PO comment,
+	 * 	like :, default is a space
+	 */
+	function comment_block($text, $char=' ') {
+		$text = wordwrap($text, PO_MAX_LINE_LEN - 3);
+		return PO::prepend_each_line($text, "#$char ");
+	}
+
+	/**
+	 * Builds a string from the entry for inclusion in PO file
+	 *
+	 * @static
+	 * @param object &$entry the entry to convert to po string
+	 * @return string|bool PO-style formatted string for the entry or
+	 * 	false if the entry is empty
+	 */
+	function export_entry(&$entry) {
+		if (is_null($entry->singular)) return false;
+		$po = array();
+		if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments);
+		if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.');
+		if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':');
+		if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ',');
+		if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context);
+		$po[] = 'msgid '.PO::poify($entry->singular);
+		if (!$entry->is_plural) {
+			$translation = empty($entry->translations)? '' : $entry->translations[0];
+			$po[] = 'msgstr '.PO::poify($translation);
+		} else {
+			$po[] = 'msgid_plural '.PO::poify($entry->plural);
+			$translations = empty($entry->translations)? array('', '') : $entry->translations;
+			foreach($translations as $i => $translation) {
+				$po[] = "msgstr[$i] ".PO::poify($translation);
+			}
+		}
+		return implode("\n", $po);
+	}
+
+	function import_from_file($filename) {
+		$f = fopen($filename, 'r');
+		if (!$f) return false;
+		$lineno = 0;
+		while (true) {
+			$res = $this->read_entry($f, $lineno);
+			if (!$res) break;
+			if ($res['entry']->singular == '') {
+				$this->set_headers($this->make_headers($res['entry']->translations[0]));
+			} else {
+				$this->add_entry($res['entry']);
+			}
+		}
+		PO::read_line($f, 'clear');
+		return $res !== false;
+	}
+	
+	function read_entry($f, $lineno = 0) {
+		$entry = new Translation_Entry();
+		// where were we in the last step
+		// can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural
+		$context = '';
+		$msgstr_index = 0;
+		$is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";');
+		while (true) {
+			$lineno++;
+			$line = PO::read_line($f);
+			if (!$line)  {
+				if (feof($f)) {
+					if ($is_final($context))
+						break;
+					elseif (!$context) // we haven't read a line and eof came
+						return null;
+					else
+						return false;
+				} else {
+					return false;
+				}
+			}
+			if ($line == "\n") continue;
+			$line = trim($line);
+			if (preg_match('/^#/', $line, $m)) {
+				// the comment is the start of a new entry
+				if ($is_final($context)) {
+					PO::read_line($f, 'put-back');
+					$lineno--;
+					break;
+				}
+				// comments have to be at the beginning
+				if ($context && $context != 'comment') {
+					return false;
+				}
+				// add comment
+				$this->add_comment_to_entry($entry, $line);;
+			} elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) {
+				if ($is_final($context)) {
+					PO::read_line($f, 'put-back');
+					$lineno--;
+					break;
+				}
+				if ($context && $context != 'comment') {
+					return false;
+				}
+				$context = 'msgctxt';
+				$entry->context .= PO::unpoify($m[1]);
+			} elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) {
+				if ($is_final($context)) {
+					PO::read_line($f, 'put-back');
+					$lineno--;
+					break;
+				}
+				if ($context && $context != 'msgctxt' && $context != 'comment') {
+					return false;
+				}
+				$context = 'msgid';
+				$entry->singular .= PO::unpoify($m[1]);
+			} elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) {
+				if ($context != 'msgid') {
+					return false;
+				}
+				$context = 'msgid_plural';
+				$entry->is_plural = true;
+				$entry->plural .= PO::unpoify($m[1]);
+			} elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) {
+				if ($context != 'msgid') {
+					return false;
+				}
+				$context = 'msgstr';
+				$entry->translations = array(PO::unpoify($m[1]));
+			} elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) {
+				if ($context != 'msgid_plural' && $context != 'msgstr_plural') {
+					return false;
+				}
+				$context = 'msgstr_plural';
+				$msgstr_index = $m[1];
+				$entry->translations[$m[1]] = PO::unpoify($m[2]);
+			} elseif (preg_match('/^".*"$/', $line)) {
+				$unpoified = PO::unpoify($line);
+				switch ($context) {
+					case 'msgid':
+						$entry->singular .= $unpoified; break;
+					case 'msgctxt':
+						$entry->context .= $unpoified; break;
+					case 'msgid_plural':
+						$entry->plural .= $unpoified; break;
+					case 'msgstr':
+						$entry->translations[0] .= $unpoified; break;
+					case 'msgstr_plural':
+						$entry->translations[$msgstr_index] .= $unpoified; break;
+					default:
+						return false;
+				}
+			} else {
+				return false;
+			}
+		}
+		if (array() == array_filter($entry->translations, create_function('$t', 'return $t || "0" === $t;'))) {
+			$entry->translations = array();
+		}
+		return array('entry' => $entry, 'lineno' => $lineno);
+	}
+	
+	function read_line($f, $action = 'read') {
+		static $last_line = '';
+		static $use_last_line = false;
+		if ('clear' == $action) {
+			$last_line = '';
+			return true;
+		}
+		if ('put-back' == $action) {
+			$use_last_line = true;
+			return true;
+		}
+		$line = $use_last_line? $last_line : fgets($f);
+		$last_line = $line;
+		$use_last_line = false;
+		return $line;
+	}
+	
+	function add_comment_to_entry(&$entry, $po_comment_line) {
+		$first_two = substr($po_comment_line, 0, 2);
+		$comment = trim(substr($po_comment_line, 2));
+		if ('#:' == $first_two) {
+			$entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));
+		} elseif ('#.' == $first_two) {
+			$entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);
+		} elseif ('#,' == $first_two) {
+			$entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));
+		} else {
+			$entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);
+		}
+	}
+	
+	function trim_quotes($s) {
+		if ( substr($s, 0, 1) == '"') $s = substr($s, 1);
+		if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);
+		return $s;
+	}
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/app.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/app.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/app.php	(revision 5057)
@@ -0,0 +1,63 @@
+<?php
+
+require_once '../translations.php';
+require_once '../mo.php';
+
+
+function __($text, $domain = 'default') {
+	$translations = &get_translations($domain);
+	return $translations->translate($text);
+}
+
+function _e($text, $domain = 'default') {
+	$translations = &get_translations($domain);
+	echo $translations->translate($text);
+}
+
+function __n($singular, $plural, $count, $domain = 'default') {
+	$translations = &get_translations($domain);
+	return $translations->translate_plural($singular, $plural, $count);
+}
+
+function &load_translations($mo_filename) {
+	if (is_readable($mo_filename)) {
+		$translations = new MO();
+		$translations->import_from_file($mo_filename);
+	} else {
+		$translations = new Translations();
+	}
+	return $translations;
+}
+
+// get the locale from somewhere: subomain, config, GET var, etc.
+// it can be safely empty
+$locale = 'bg';
+$translations = array();
+$empty_translations = & new Translations();
+
+function load_textdomain($domain, $mofile) {
+	global $translations;
+	$translations[$domain] = &load_translations($mofile);
+}
+
+function &get_translations($domain) {
+	global $translations, $empty_translations;
+	return isset($translations[$domain])? $translations[$domain] : $empty_translations;
+}
+
+// load the translations
+load_textdomain('default', "languages/$locale.mo");
+load_textdomain('side', "languages/$locale-side.mo");
+
+//here comes the real app
+$user = 'apok';
+$messages = rand(0, 2);
+
+printf(__('Welcome %s!')."\n", $user);
+
+printf(__n('You have one new message.', 'You have %s new messages.', $messages)."\n", $messages);
+
+echo __("A string with low priority!", 'side')."\n";
+
+_e("Bye\n");
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app-side.pot
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app-side.pot	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app-side.pot	(revision 5057)
@@ -0,0 +1,21 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2008-06-06 23:10+0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+msgid "A string with low priority!"
+msgstr ""
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app.pot
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app.pot	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/app.pot	(revision 5057)
@@ -0,0 +1,34 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2008-06-06 23:10+0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: app.php:46
+#, php-format
+msgid "Welcome %s!"
+msgstr ""
+
+#: app.php:48
+#, php-format
+msgid "You have one new message."
+msgid_plural "You have %s new messages."
+msgstr[0] ""
+msgstr[1] ""
+
+#: app.php:50
+msgid "Bye\n"
+msgstr ""
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg-side.po
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg-side.po	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg-side.po	(revision 5057)
@@ -0,0 +1,19 @@
+# Bulgarian translations for PACKAGE package.
+# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Nikolay Bachiyski <nb@nikolay.bg>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2008-06-06 23:10+0300\n"
+"PO-Revision-Date: 2008-06-06 22:54+0300\n"
+"Last-Translator: Nikolay Bachiyski <nb@nikolay.bg>\n"
+"Language-Team: Bulgarian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "A string with low priority!"
+msgstr "Смотан низ!"
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg.po
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg.po	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/sample/languages/bg.po	(revision 5057)
@@ -0,0 +1,32 @@
+# Bulgarian translations for PACKAGE package.
+# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Nikolay Bachiyski <nb@nikolay.bg>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2008-06-06 23:10+0300\n"
+"PO-Revision-Date: 2008-06-06 22:54+0300\n"
+"Last-Translator: Nikolay Bachiyski <nb@nikolay.bg>\n"
+"Language-Team: Bulgarian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: app.php:46
+#, php-format
+msgid "Welcome %s!"
+msgstr "Добре дошъл, %s!"
+
+#: app.php:48
+#, php-format
+msgid "You have one new message."
+msgid_plural "You have %s new messages."
+msgstr[0] "Имате едно ново съобщение."
+msgstr[1] "Имате %s нови съобщения."
+
+#: app.php:50
+msgid "Bye\n"
+msgstr "Чао\n"
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/streams.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/streams.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/streams.php	(revision 5057)
@@ -0,0 +1,209 @@
+<?php
+/**
+ * Classes, which help reading streams of data from files.
+ * Based on the classes from Danilo Segan <danilo@kvota.net>
+ *
+ * @version $Id: streams.php 597 2011-01-16 20:14:36Z nbachiyski $
+ * @package pomo
+ * @subpackage streams
+ */
+
+if ( !class_exists( 'POMO_Reader' ) ):
+class POMO_Reader {
+	
+	var $endian = 'little';
+	var $_post = '';
+	
+	function POMO_Reader() {
+		$this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr');
+		$this->_pos = 0;
+	}
+	
+	/**
+	 * Sets the endianness of the file.
+	 *
+	 * @param $endian string 'big' or 'little'
+	 */
+	function setEndian($endian) {
+		$this->endian = $endian;
+	}
+
+	/**
+	 * Reads a 32bit Integer from the Stream
+	 *
+	 * @return mixed The integer, corresponding to the next 32 bits from
+	 * 	the stream of false if there are not enough bytes or on error
+	 */
+	function readint32() {
+		$bytes = $this->read(4);
+		if (4 != $this->strlen($bytes))
+			return false;
+		$endian_letter = ('big' == $this->endian)? 'N' : 'V';
+		$int = unpack($endian_letter, $bytes);
+		return array_shift($int);
+	}
+
+	/**
+	 * Reads an array of 32-bit Integers from the Stream
+	 *
+	 * @param integer count How many elements should be read
+	 * @return mixed Array of integers or false if there isn't
+	 * 	enough data or on error
+	 */
+	function readint32array($count) {
+		$bytes = $this->read(4 * $count);
+		if (4*$count != $this->strlen($bytes))
+			return false;
+		$endian_letter = ('big' == $this->endian)? 'N' : 'V';
+		return unpack($endian_letter.$count, $bytes);
+	}
+	
+	
+	function substr($string, $start, $length) {
+		if ($this->is_overloaded) {
+			return mb_substr($string, $start, $length, 'ascii');
+		} else {
+			return substr($string, $start, $length);
+		}
+	}
+	
+	function strlen($string) {
+		if ($this->is_overloaded) {
+			return mb_strlen($string, 'ascii');
+		} else {
+			return strlen($string);
+		}
+	}
+	
+	function str_split($string, $chunk_size) {
+		if (!function_exists('str_split')) {
+			$length = $this->strlen($string);
+			$out = array();
+			for ($i = 0; $i < $length; $i += $chunk_size)
+				$out[] = $this->substr($string, $i, $chunk_size);
+			return $out;
+		} else {
+			return str_split( $string, $chunk_size );
+		}
+	}
+	
+		
+	function pos() {
+		return $this->_pos;
+	}
+
+	function is_resource() {
+		return true;
+	}
+	
+	function close() {
+		return true;
+	}
+}
+endif;
+
+if ( !class_exists( 'POMO_FileReader' ) ):
+class POMO_FileReader extends POMO_Reader {
+	function POMO_FileReader($filename) {
+		parent::POMO_Reader();
+		$this->_f = fopen($filename, 'rb');
+	}
+	
+	function read($bytes) {
+		return fread($this->_f, $bytes);
+	}
+	
+	function seekto($pos) {
+		if ( -1 == fseek($this->_f, $pos, SEEK_SET)) {
+			return false;
+		}
+		$this->_pos = $pos;
+		return true;
+	}
+	
+	function is_resource() {
+		return is_resource($this->_f);
+	}
+	
+	function feof() {
+		return feof($this->_f);
+	}
+	
+	function close() {
+		return fclose($this->_f);
+	}
+	
+	function read_all() {
+		$all = '';
+		while ( !$this->feof() )
+			$all .= $this->read(4096);
+		return $all;
+	}
+}
+endif;
+
+if ( !class_exists( 'POMO_StringReader' ) ):
+/**
+ * Provides file-like methods for manipulating a string instead
+ * of a physical file.
+ */
+class POMO_StringReader extends POMO_Reader {
+	
+	var $_str = '';
+	
+	function POMO_StringReader($str = '') {
+		parent::POMO_Reader();
+		$this->_str = $str;
+		$this->_pos = 0;
+	}
+
+
+	function read($bytes) {
+		$data = $this->substr($this->_str, $this->_pos, $bytes);
+		$this->_pos += $bytes;
+		if ($this->strlen($this->_str) < $this->_pos) $this->_pos = $this->strlen($this->_str);
+		return $data;
+	}
+
+	function seekto($pos) {
+		$this->_pos = $pos;
+		if ($this->strlen($this->_str) < $this->_pos) $this->_pos = $this->strlen($this->_str);
+		return $this->_pos;
+	}
+
+	function length() {
+		return $this->strlen($this->_str);
+	}
+
+	function read_all() {
+		return $this->substr($this->_str, $this->_pos, $this->strlen($this->_str));
+	}
+	
+}
+endif;
+
+if ( !class_exists( 'POMO_CachedFileReader' ) ):
+/**
+ * Reads the contents of the file in the beginning.
+ */
+class POMO_CachedFileReader extends POMO_StringReader {
+	function POMO_CachedFileReader($filename) {
+		parent::POMO_StringReader();
+		$this->_str = file_get_contents($filename);
+		if (false === $this->_str)
+			return false;
+		$this->_pos = 0;
+	}
+}
+endif;
+
+if ( !class_exists( 'POMO_CachedIntFileReader' ) ):
+/**
+ * Reads the contents of the file in the beginning.
+ */
+class POMO_CachedIntFileReader extends POMO_CachedFileReader {
+	function POMO_CachedIntFileReader($filename) {
+		parent::POMO_CachedFileReader($filename);
+	}
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/translations.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/translations.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/backpress/pomo/translations.php	(revision 5057)
@@ -0,0 +1,275 @@
+<?php
+/**
+ * Class for a set of entries for translation and their associated headers
+ *
+ * @version $Id: translations.php 590 2010-12-20 19:58:37Z nbachiyski $
+ * @package pomo
+ * @subpackage translations
+ */
+
+require_once dirname(__FILE__) . '/entry.php';
+
+if ( !class_exists( 'Translations' ) ):
+class Translations {
+	var $entries = array();
+	var $headers = array();
+
+	/**
+	 * Add entry to the PO structure
+	 *
+	 * @param object &$entry
+	 * @return bool true on success, false if the entry doesn't have a key
+	 */
+	function add_entry($entry) {
+		if (is_array($entry)) {
+			$entry = new Translation_Entry($entry);
+		}
+		$key = $entry->key();
+		if (false === $key) return false;
+		$this->entries[$key] = &$entry;
+		return true;
+	}
+	
+	function add_entry_or_merge($entry) {
+		if (is_array($entry)) {
+			$entry = new Translation_Entry($entry);
+		}
+		$key = $entry->key();
+		if (false === $key) return false;
+		if (isset($this->entries[$key]))
+			$this->entries[$key]->merge_with($entry);
+		else
+			$this->entries[$key] = &$entry;
+		return true;
+	}
+
+	/**
+	 * Sets $header PO header to $value
+	 *
+	 * If the header already exists, it will be overwritten
+	 *
+	 * TODO: this should be out of this class, it is gettext specific
+	 *
+	 * @param string $header header name, without trailing :
+	 * @param string $value header value, without trailing \n
+	 */
+	function set_header($header, $value) {
+		$this->headers[$header] = $value;
+	}
+
+	function set_headers(&$headers) {
+		foreach($headers as $header => $value) {
+			$this->set_header($header, $value);
+		}
+	}
+
+	function get_header($header) {
+		return isset($this->headers[$header])? $this->headers[$header] : false;
+	}
+
+	function translate_entry(&$entry) {
+		$key = $entry->key();
+		return isset($this->entries[$key])? $this->entries[$key] : false;
+	}
+
+	function translate($singular, $context=null) {
+		$entry = new Translation_Entry(array('singular' => $singular, 'context' => $context));
+		$translated = $this->translate_entry($entry);
+		return ($translated && !empty($translated->translations))? $translated->translations[0] : $singular;
+	}
+
+	/**
+	 * Given the number of items, returns the 0-based index of the plural form to use
+	 *
+	 * Here, in the base Translations class, the commong logic for English is implmented:
+	 * 	0 if there is one element, 1 otherwise
+	 *
+	 * This function should be overrided by the sub-classes. For example MO/PO can derive the logic
+	 * from their headers.
+	 *
+	 * @param integer $count number of items
+	 */
+	function select_plural_form($count) {
+		return 1 == $count? 0 : 1;
+	}
+
+	function get_plural_forms_count() {
+		return 2;
+	}
+
+	function translate_plural($singular, $plural, $count, $context = null) {
+		$entry = new Translation_Entry(array('singular' => $singular, 'plural' => $plural, 'context' => $context));
+		$translated = $this->translate_entry($entry);
+		$index = $this->select_plural_form($count);
+		$total_plural_forms = $this->get_plural_forms_count();
+		if ($translated && 0 <= $index && $index < $total_plural_forms &&
+				is_array($translated->translations) &&
+				isset($translated->translations[$index]))
+			return $translated->translations[$index];
+		else
+			return 1 == $count? $singular : $plural;
+	}
+
+	/**
+	 * Merge $other in the current object.
+	 *
+	 * @param Object &$other Another Translation object, whose translations will be merged in this one
+	 * @return void
+	 **/
+	function merge_with(&$other) {
+		foreach( $other->entries as $entry ) {
+			$this->entries[$entry->key()] = $entry;
+		}
+	}
+	
+	function merge_originals_with(&$other) {
+		foreach( $other->entries as $entry ) {
+			if ( !isset( $this->entries[$entry->key()] ) )
+				$this->entries[$entry->key()] = $entry;
+			else
+				$this->entries[$entry->key()]->merge_with($entry);
+		}
+	}
+}
+
+class Gettext_Translations extends Translations {
+	/**
+	 * The gettext implmentation of select_plural_form.
+	 *
+	 * It lives in this class, because there are more than one descendand, which will use it and
+	 * they can't share it effectively.
+	 *
+	 */
+	function gettext_select_plural_form($count) {
+		if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) {
+			list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
+			$this->_nplurals = $nplurals;
+			$this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
+		}
+		return call_user_func($this->_gettext_select_plural_form, $count);
+	}
+	
+	function nplurals_and_expression_from_header($header) {
+		if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
+			$nplurals = (int)$matches[1];
+			$expression = trim($this->parenthesize_plural_exression($matches[2]));
+			return array($nplurals, $expression);
+		} else {
+			return array(2, 'n != 1');
+		}
+	}
+
+	/**
+	 * Makes a function, which will return the right translation index, according to the
+	 * plural forms header
+	 */
+	function make_plural_form_function($nplurals, $expression) {
+		$expression = str_replace('n', '$n', $expression);
+		$func_body = "
+			\$index = (int)($expression);
+			return (\$index < $nplurals)? \$index : $nplurals - 1;";
+		return create_function('$n', $func_body);
+	}
+
+	/**
+	 * Adds parantheses to the inner parts of ternary operators in
+	 * plural expressions, because PHP evaluates ternary oerators from left to right
+	 * 
+	 * @param string $expression the expression without parentheses
+	 * @return string the expression with parentheses added
+	 */
+	function parenthesize_plural_exression($expression) {
+		$expression .= ';';
+		$res = '';
+		$depth = 0;
+		for ($i = 0; $i < strlen($expression); ++$i) {
+			$char = $expression[$i];
+			switch ($char) {
+				case '?':
+					$res .= ' ? (';
+					$depth++;
+					break;
+				case ':':
+					$res .= ') : (';
+					break;
+				case ';':
+					$res .= str_repeat(')', $depth) . ';';
+					$depth= 0;
+					break;
+				default:
+					$res .= $char;
+			}
+		}
+		return rtrim($res, ';');
+	}
+	
+	function make_headers($translation) {
+		$headers = array();
+		// sometimes \ns are used instead of real new lines
+		$translation = str_replace('\n', "\n", $translation);
+		$lines = explode("\n", $translation);
+		foreach($lines as $line) {
+			$parts = explode(':', $line, 2);
+			if (!isset($parts[1])) continue;
+			$headers[trim($parts[0])] = trim($parts[1]);
+		}
+		return $headers;
+	}
+	
+	function set_header($header, $value) {
+		parent::set_header($header, $value);
+		if ('Plural-Forms' == $header) {
+			list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
+			$this->_nplurals = $nplurals;
+			$this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
+		}
+	}
+}
+endif;
+
+if ( !class_exists( 'NOOP_Translations' ) ):
+/**
+ * Provides the same interface as Translations, but doesn't do anything
+ */
+class NOOP_Translations {
+	var $entries = array();
+	var $headers = array();
+	
+	function add_entry($entry) {
+		return true;
+	}
+
+	function set_header($header, $value) {
+	}
+
+	function set_headers(&$headers) {
+	}
+
+	function get_header($header) {
+		return false;
+	}
+
+	function translate_entry(&$entry) {
+		return false;
+	}
+
+	function translate($singular, $context=null) {
+		return $singular;
+	}
+
+	function select_plural_form($count) {
+		return 1 == $count? 0 : 1;
+	}
+
+	function get_plural_forms_count() {
+		return 2;
+	}
+
+	function translate_plural($singular, $plural, $count, $context = null) {
+			return 1 == $count? $singular : $plural;
+	}
+
+	function merge_with(&$other) {
+	}
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-dir-map.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-dir-map.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-dir-map.php	(revision 5057)
@@ -0,0 +1,105 @@
+<?php
+
+class BB_Dir_Map {
+	var $root;
+	var $callback;
+	var $callback_args;
+	var $keep_empty;
+	var $apply_to;
+	var $recurse;
+	var $dots;
+	var $flat = array();
+	var $error = false;
+
+	var $_current_root;
+	var $_current_file;
+
+	function BB_Dir_Map( $root, $args = '' ) {
+		if ( !is_dir( $root ) ) {
+			$this->error = new WP_Error( 'bb_dir_map', __('Not a valid directory') );
+			return;
+		}
+
+		$this->parse_args( $args );
+		if ( is_null($this->apply_to) || is_null($this->dots) ) {
+			$this->error = new WP_Error( 'bb_dir_map', __('Invalid arguments') );
+			return;
+		}
+		$this->_current_root = $this->root = rtrim($root, '/\\');
+		$this->map();
+	}
+
+	function parse_args( $args ) {
+		// callback: should be callable
+		// callback_args: additional args to pass to callback
+		// apply_to: all, files, dirs
+		// keep_empty: (bool)
+		// recurse: (int) depth, -1 = infinite
+		// dots: true (everything), false (nothing), nosvn
+		$defaults = array( 'callback' => false, 'callback_args' => false, 'keep_empty' => false, 'apply_to' => 'files', 'recurse' => -1, 'dots' => false );
+		$this->callback = is_array($args) && isset($args['callback']) ? $args['callback'] : false;
+		$args = wp_parse_args( $args, $defaults );
+
+		foreach ( array('callback', 'keep_empty', 'dots') as $a )
+			if ( 'false' == $args[$a] )
+				$args[$a] = false;
+			elseif ( 'true' == $args[$a] )
+				$args[$a] = true;
+
+		if ( !isset($this->callback) )
+			$this->callback = $args['callback'];
+		if ( !is_callable($this->callback) )
+			$this->callback = false;
+		$this->callback_args = is_array($args['callback_args']) ? $args['callback_args'] : array();
+
+		$this->keep_empty = (bool) $args['keep_empty'];
+
+		$_apply_to = array( 'files' => 1, 'dirs' => 2, 'all' => 3 ); // This begs to be bitwise
+		$this->apply_to = @$_apply_to[$args['apply_to']];
+
+		$this->recurse = (int) $args['recurse'];
+
+		$_dots = array( 1 => 3, 0 => 0, 'nosvn' => 1 ); // bitwise here is a little silly
+		$this->dots = @$_dots[$args['dots']];
+	}
+
+	function map( $root = false ) {
+		$return = array();
+		$_dir = dir($root ? $root : $this->_current_root);
+		while ( $_dir && false !== ( $this->_current_file = $_dir->read() ) ) {
+			if ( in_array($this->_current_file, array('.', '..')) )
+				continue;
+			if ( !$this->dots && '.' == $this->_current_file{0} )
+				continue;
+
+			$item = $_dir->path . DIRECTORY_SEPARATOR . $this->_current_file;
+			$_item = substr( $item, strlen($this->root) + 1 );
+			$_callback_args = $this->callback_args;
+			array_push( $_callback_args, $item, $_item ); // $item, $_item will be last two args
+			if ( is_dir($item) )  { // dir stuff
+				if ( 1 & $this->dots && in_array($this->_current_file, array('.svn', 'CVS')) )
+					continue;
+				if ( 2 & $this->apply_to ) {
+					$result = $this->callback ? call_user_func_array($this->callback, $_callback_args) : true;
+					if ( $result || $this->keep_empty )
+						$this->flat[$_item] = $result;
+				}
+				if ( 0 > $this->recurse || $this->recurse ) {
+					$this->recurse--;
+					$this->map( $item );
+					$this->recurse++;
+				}
+			} else { // file stuff
+				if ( !(1 & $this->apply_to) )
+					continue;
+				$result = $this->callback ? call_user_func_array($this->callback, $_callback_args) : true;
+				if ( $result || $this->keep_empty )
+					$this->flat[$_item] = $result;
+			}
+		}
+	}
+
+	function get_results() {
+		return is_wp_error( $this->error ) ? $this->error : $this->flat;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-locale.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-locale.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-locale.php	(revision 5057)
@@ -0,0 +1,449 @@
+<?php
+// Last sync [WP11537]
+
+/**
+ * Date and Time Locale object
+ *
+ * @package bbPress
+ * @subpackage i18n
+ */
+
+/**
+ * Class that loads the calendar locale.
+ */
+class BB_Locale {
+	/**
+	 * Stores the translated strings for the full weekday names.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $weekday;
+
+	/**
+	 * Stores the translated strings for the one character weekday names.
+	 *
+	 * There is a hack to make sure that Tuesday and Thursday, as well
+	 * as Sunday and Saturday don't conflict. See init() method for more.
+	 *
+	 * @see BB_Locale::init() for how to handle the hack.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $weekday_initial;
+
+	/**
+	 * Stores the translated strings for the abbreviated weekday names.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $weekday_abbrev;
+
+	/**
+	 * Stores the translated strings for the full month names.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $month;
+
+	/**
+	 * Stores the translated strings for the abbreviated month names.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $month_abbrev;
+
+	/**
+	 * Stores the translated strings for 'am' and 'pm'.
+	 *
+	 * Also the capitalized versions.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $meridiem;
+
+	/**
+	 * Stores number formatting rules.
+	 *
+	 * @var array
+	 * @access public
+	 */
+	var $number_format;
+
+	/**
+	 * Stores date and time formatting strings.
+	 *
+	 * @var array
+	 * @access public
+	 */
+	var $datetime_formatstring;
+
+	/**
+	 * The text direction of the locale language.
+	 *
+	 * Default is left to right 'ltr'.
+	 *
+	 * @var string
+	 * @access private
+	 */
+	var $text_direction = '';
+
+	/**
+	 * Imports the global version to the class property.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	var $locale_vars = array('text_direction');
+
+	/**
+	 * Sets up the translated strings and object properties.
+	 *
+	 * The method creates the translatable strings for various
+	 * calendar elements. Which allows for specifying locale
+	 * specific calendar names and text direction.
+	 *
+	 * @access private
+	 */
+	function init() {
+		// The Weekdays
+		$this->weekday[0] = __('Sunday');
+		$this->weekday[1] = __('Monday');
+		$this->weekday[2] = __('Tuesday');
+		$this->weekday[3] = __('Wednesday');
+		$this->weekday[4] = __('Thursday');
+		$this->weekday[5] = __('Friday');
+		$this->weekday[6] = __('Saturday');
+
+		// The first letter of each day.  The _%day%_initial suffix is a hack to make
+		// sure the day initials are unique.
+		$this->weekday_initial[__('Sunday')]    = __('S_Sunday_initial');
+		$this->weekday_initial[__('Monday')]    = __('M_Monday_initial');
+		$this->weekday_initial[__('Tuesday')]   = __('T_Tuesday_initial');
+		$this->weekday_initial[__('Wednesday')] = __('W_Wednesday_initial');
+		$this->weekday_initial[__('Thursday')]  = __('T_Thursday_initial');
+		$this->weekday_initial[__('Friday')]    = __('F_Friday_initial');
+		$this->weekday_initial[__('Saturday')]  = __('S_Saturday_initial');
+
+		foreach ($this->weekday_initial as $weekday_ => $weekday_initial_) {
+			$this->weekday_initial[$weekday_] = preg_replace('/_.+_initial$/', '', $weekday_initial_);
+		}
+
+		// Abbreviations for each day.
+		$this->weekday_abbrev[__('Sunday')]    = __('Sun');
+		$this->weekday_abbrev[__('Monday')]    = __('Mon');
+		$this->weekday_abbrev[__('Tuesday')]   = __('Tue');
+		$this->weekday_abbrev[__('Wednesday')] = __('Wed');
+		$this->weekday_abbrev[__('Thursday')]  = __('Thu');
+		$this->weekday_abbrev[__('Friday')]    = __('Fri');
+		$this->weekday_abbrev[__('Saturday')]  = __('Sat');
+
+		// The Months
+		$this->month['01'] = __('January');
+		$this->month['02'] = __('February');
+		$this->month['03'] = __('March');
+		$this->month['04'] = __('April');
+		$this->month['05'] = __('May');
+		$this->month['06'] = __('June');
+		$this->month['07'] = __('July');
+		$this->month['08'] = __('August');
+		$this->month['09'] = __('September');
+		$this->month['10'] = __('October');
+		$this->month['11'] = __('November');
+		$this->month['12'] = __('December');
+
+		// Abbreviations for each month. Uses the same hack as above to get around the
+		// 'May' duplication.
+		$this->month_abbrev[__('January')] = __('Jan_January_abbreviation');
+		$this->month_abbrev[__('February')] = __('Feb_February_abbreviation');
+		$this->month_abbrev[__('March')] = __('Mar_March_abbreviation');
+		$this->month_abbrev[__('April')] = __('Apr_April_abbreviation');
+		$this->month_abbrev[__('May')] = __('May_May_abbreviation');
+		$this->month_abbrev[__('June')] = __('Jun_June_abbreviation');
+		$this->month_abbrev[__('July')] = __('Jul_July_abbreviation');
+		$this->month_abbrev[__('August')] = __('Aug_August_abbreviation');
+		$this->month_abbrev[__('September')] = __('Sep_September_abbreviation');
+		$this->month_abbrev[__('October')] = __('Oct_October_abbreviation');
+		$this->month_abbrev[__('November')] = __('Nov_November_abbreviation');
+		$this->month_abbrev[__('December')] = __('Dec_December_abbreviation');
+
+		foreach ($this->month_abbrev as $month_ => $month_abbrev_) {
+			$this->month_abbrev[$month_] = preg_replace('/_.+_abbreviation$/', '', $month_abbrev_);
+		}
+
+		// The Meridiems
+		$this->meridiem['am'] = __('am');
+		$this->meridiem['pm'] = __('pm');
+		$this->meridiem['AM'] = __('AM');
+		$this->meridiem['PM'] = __('PM');
+
+		// Numbers formatting
+		// See http://php.net/number_format
+
+		/* translators: $decimals argument for http://php.net/number_format, default is 0 */
+		$trans = __('number_format_decimals');
+		$this->number_format['decimals'] = ('number_format_decimals' == $trans) ? 0 : $trans;
+
+		/* translators: $dec_point argument for http://php.net/number_format, default is . */
+		$trans = __('number_format_decimal_point');
+		$this->number_format['decimal_point'] = ('number_format_decimal_point' == $trans) ? '.' : $trans;
+
+		/* translators: $thousands_sep argument for http://php.net/number_format, default is , */
+		$trans = __('number_format_thousands_sep');
+		$this->number_format['thousands_sep'] = ('number_format_thousands_sep' == $trans) ? ',' : $trans;
+		
+		// Date/Time formatting
+		$this->datetime_formatstring['datetime'] = __('F j, Y - h:i A');
+		$this->datetime_formatstring['date'] = __('F j, Y');
+		$this->datetime_formatstring['time'] = __('h:i A');
+		
+		$this->_load_locale_data();
+	}
+
+	/**
+	 * Imports global locale vars set during inclusion of $locale.php.
+	 *
+	 * @access private
+	 */
+	function _load_locale_data() {
+		$locale = bb_get_locale();
+		$locale_file = BB_LANG_DIR . $locale . '.php';
+		if ( !is_file( $locale_file ) ) {
+			return;
+		}
+
+		include( $locale_file );
+
+		foreach ( $this->locale_vars as $var ) {
+			$this->$var = $$var;
+		}
+	}
+
+	/**
+	 * Retrieve the full translated weekday word.
+	 *
+	 * Week starts on translated Sunday and can be fetched
+	 * by using 0 (zero). So the week starts with 0 (zero)
+	 * and ends on Saturday with is fetched by using 6 (six).
+	 *
+	 * @access public
+	 *
+	 * @param int $weekday_number 0 for Sunday through 6 Saturday
+	 * @return string Full translated weekday
+	 */
+	function get_weekday($weekday_number) {
+		return $this->weekday[$weekday_number];
+	}
+
+	/**
+	 * Retrieve the translated weekday initial.
+	 *
+	 * The weekday initial is retrieved by the translated
+	 * full weekday word. When translating the weekday initial
+	 * pay attention to make sure that the starting letter does
+	 * not conflict.
+	 *
+	 * @access public
+	 *
+	 * @param string $weekday_name
+	 * @return string
+	 */
+	function get_weekday_initial($weekday_name) {
+		return $this->weekday_initial[$weekday_name];
+	}
+
+	/**
+	 * Retrieve the translated weekday abbreviation.
+	 *
+	 * The weekday abbreviation is retrieved by the translated
+	 * full weekday word.
+	 *
+	 * @access public
+	 *
+	 * @param string $weekday_name Full translated weekday word
+	 * @return string Translated weekday abbreviation
+	 */
+	function get_weekday_abbrev($weekday_name) {
+		return $this->weekday_abbrev[$weekday_name];
+	}
+
+	/**
+	 * Retrieve the full translated month by month number.
+	 *
+	 * The $month_number parameter has to be a string
+	 * because it must have the '0' in front of any number
+	 * that is less than 10. Starts from '01' and ends at
+	 * '12'.
+	 *
+	 * You can use an integer instead and it will add the
+	 * '0' before the numbers less than 10 for you.
+	 *
+	 * @access public
+	 *
+	 * @param string|int $month_number '01' through '12'
+	 * @return string Translated full month name
+	 */
+	function get_month($month_number) {
+		return $this->month[zeroise($month_number, 2)];
+	}
+
+	function get_month_initial($month_name) {
+		return $this->month_initial[$month_name];
+	}
+
+	/**
+	 * Retrieve translated version of month abbreviation string.
+	 *
+	 * The $month_name parameter is expected to be the translated or
+	 * translatable version of the month.
+	 *
+	 * @access public
+	 *
+	 * @param string $month_name Translated month to get abbreviated version
+	 * @return string Translated abbreviated month
+	 */
+	function get_month_abbrev($month_name) {
+		return $this->month_abbrev[$month_name];
+	}
+
+	/**
+	 * Retrieve translated version of meridiem string.
+	 *
+	 * The $meridiem parameter is expected to not be translated.
+	 *
+	 * @access public
+	 *
+	 * @param string $meridiem Either 'am', 'pm', 'AM', or 'PM'. Not translated version.
+	 * @return string Translated version
+	 */
+	function get_meridiem($meridiem) {
+		return $this->meridiem[$meridiem];
+	}
+
+	function get_datetime_formatstring($type = 'datetime') {
+		return $this->datetime_formatstring[$type];
+	}
+
+	/**
+	 * Global variables are deprecated. For backwards compatibility only.
+	 *
+	 * @deprecated For backwards compatibility only.
+	 * @access private
+	 */
+	function register_globals() {
+		$GLOBALS['weekday']         = $this->weekday;
+		$GLOBALS['weekday_initial'] = $this->weekday_initial;
+		$GLOBALS['weekday_abbrev']  = $this->weekday_abbrev;
+		$GLOBALS['month']           = $this->month;
+		$GLOBALS['month_abbrev']    = $this->month_abbrev;
+	}
+
+	/**
+	 * PHP4 style constructor which calls helper methods to set up object variables
+	 *
+	 * @uses BB_Locale::init()
+	 * @uses BB_Locale::register_globals()
+	 *
+	 * @return BB_Locale
+	 */
+	function BB_Locale() {
+		$this->init();
+		$this->register_globals();
+	}
+}
+
+/**
+ * Retrieve the date in localized format, based on timestamp.
+ *
+ * If the locale specifies the locale month and weekday, then the locale will
+ * take over the format for the date. If it isn't, then the date format string
+ * will be used instead.
+ *
+ * @param string $dateformatstring Format to display the date.
+ * @param int $unixtimestamp Optional. Unix timestamp.
+ * @param bool $gmt Optional, default is true. Whether to convert to GMT for time.
+ * @return string The date, translated if locale specifies it.
+ */
+function bb_gmdate_i18n( $dateformatstring, $unixtimestamp = false, $gmt = true ) {
+	global $bb_locale;
+	$i = $unixtimestamp;
+	// Sanity check for PHP 5.1.0-
+	if ( false === $i || intval($i) < 0 ) {
+		if ( ! $gmt )
+			$i = current_time( 'timestamp' );
+		else
+			$i = time();
+		// we should not let date() interfere with our
+		// specially computed timestamp
+		$gmt = true;
+	}
+
+	// store original value for language with untypical grammars
+	// see http://core.trac.wordpress.org/ticket/9396
+	$req_format = $dateformatstring;
+
+	$datefunc = $gmt? 'gmdate' : 'date';
+
+	if ( ( !empty( $bb_locale->month ) ) && ( !empty( $bb_locale->weekday ) ) ) {
+		$datemonth = $bb_locale->get_month( $datefunc( 'm', $i ) );
+		$datemonth_abbrev = $bb_locale->get_month_abbrev( $datemonth );
+		$dateweekday = $bb_locale->get_weekday( $datefunc( 'w', $i ) );
+		$dateweekday_abbrev = $bb_locale->get_weekday_abbrev( $dateweekday );
+		$datemeridiem = $bb_locale->get_meridiem( $datefunc( 'a', $i ) );
+		$datemeridiem_capital = $bb_locale->get_meridiem( $datefunc( 'A', $i ) );
+		$dateformatstring = ' '.$dateformatstring;
+		$dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring );
+		$dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . backslashit( $datemonth ), $dateformatstring );
+		$dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . backslashit( $dateweekday ), $dateformatstring );
+		$dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring );
+		$dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . backslashit( $datemeridiem ), $dateformatstring );
+		$dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring );
+
+		$dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
+	}
+	$j = @$datefunc( $dateformatstring, $i );
+	// allow plugins to redo this entirely for languages with untypical grammars
+	$j = apply_filters('bb_gmdate_i18n', $j, $req_format, $i, $gmt);
+	return $j;
+}
+
+function bb_get_datetime_formatstring_i18n( $type = 'datetime' ) {
+	$formatstring = bb_get_option( $type . '_format' );
+	if ( empty($formatstring) ) {
+		global $bb_locale;
+		$formatstring = $bb_locale->get_datetime_formatstring( $type );
+	}
+	return $formatstring;
+}
+
+function bb_datetime_format_i18n( $unixtimestamp, $type = 'datetime', $formatstring = '', $gmt = true ) {
+	if ( empty($formatstring) ) {
+		$formatstring = bb_get_datetime_formatstring_i18n( $type );
+	}
+	return bb_gmdate_i18n( $formatstring, bb_offset_time( $unixtimestamp ), $gmt );
+}
+
+/**
+ * Convert number to format based on the locale.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $number The number to convert based on locale.
+ * @param int $decimals Precision of the number of decimal places.
+ * @return string Converted number in string format.
+ */
+function bb_number_format_i18n( $number, $decimals = null ) {
+	global $bb_locale;
+	// let the user override the precision only
+	$decimals = ( is_null( $decimals ) ) ? $bb_locale->number_format['decimals'] : intval( $decimals );
+
+	$num = number_format( $number, $decimals, $bb_locale->number_format['decimal_point'], $bb_locale->number_format['thousands_sep'] );
+
+	// let the user translate digits from latin to localized language
+	return apply_filters( 'bb_number_format_i18n', $num );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-pingbacks.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-pingbacks.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-pingbacks.php	(revision 5057)
@@ -0,0 +1,204 @@
+<?php
+/**
+ * bbPress class to handle pinging
+ *
+ * @package bbPress
+ */
+class BB_Pingbacks
+{
+	/**
+	 * Gets the Pingback endpoint URI provided by a web page specified by URL
+	 *
+	 * @return string|boolean Returns the Pingback endpoint URI if found or false
+	 */
+	function get_endpoint_uri( $url )
+	{
+		// First check for an X-pingback header
+		if ( !$response = wp_remote_head( $url ) ) {
+			return false;
+		}
+		if ( !$content_type = wp_remote_retrieve_header( $response, 'content-type' ) ) {
+			return false;
+		}
+		if ( preg_match( '#(image|audio|video|model)/#is', $content_type ) ) {
+			return false;
+		}
+		if ( $x_pingback = wp_remote_retrieve_header( $response, 'x-pingback' ) ) {
+			return trim( $x_pingback );
+		}
+
+		// Fall back to extracting it from the HTML link
+		if ( !$response = wp_remote_get( $url ) ) {
+			return false;
+		}
+		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+			return false;
+		}
+		if ( '' === $response_body = wp_remote_retrieve_body( $response ) ) {
+			return false;
+		}
+		if ( !preg_match_all( '@<link([^>]+)>@im', $response_body, $response_links ) ) {
+			return false;
+		}
+
+		foreach ( $response_links[1] as $response_link_attributes ) {
+			$_link = array( 'rel' => false, 'href' => false );
+			$response_link_attributes = preg_split( '@\s+@im', $response_link_attributes, -1, PREG_SPLIT_NO_EMPTY );
+			foreach ( $response_link_attributes as $response_link_attribute ) {
+				if ( $_link['rel'] == 'pingback' && $_link['href'] ) {
+					return $_link['href'];
+				}
+				if ( strpos( $response_link_attribute, '=', 1 ) !== false ) {
+					list( $_key, $_value ) = explode( '=', $response_link_attribute, 2 );
+					$_link[strtolower( $_key )] = trim( $_value, "'\"" );
+				}
+			}
+		}
+
+		// Fail
+		return false;
+	}
+
+	/**
+	 * Sends all pingbacks and other ping like requests
+	 *
+	 * At the moment only sends normal pingbacks, but may be
+	 * expanded in the future.
+	 *
+	 * @return integer The number of pings sent
+	 */
+	function send_all()
+	{
+		$pings = BB_Pingbacks::send_all_pingbacks();
+
+		return $pings;
+	}
+
+	/**
+	 * Sends all pingbacks
+	 *
+	 * @return integer The number of pings sent
+	 */
+	function send_all_pingbacks()
+	{
+		global $bbdb;
+
+		$posts = $bbdb->get_results(
+			"SELECT
+				{$bbdb->posts}.post_id,
+				{$bbdb->posts}.topic_id,
+				{$bbdb->posts}.post_text
+			FROM {$bbdb->posts}, {$bbdb->meta}
+			WHERE {$bbdb->posts}.post_id = {$bbdb->meta}.object_id
+				AND {$bbdb->meta}.object_type = 'bb_post'
+				AND {$bbdb->meta}.meta_key = 'pingback_queued';"
+		);
+
+		$pings = 0;
+		foreach ( $posts as $post ) {
+			if ( $sent = BB_Pingbacks::send_pingback( $post->topic_id, $post->post_text ) ) {
+				$pings += $sent;
+				bb_delete_postmeta( $post->post_id, 'pingback_queued' );
+			}
+		}
+
+		return $pings;
+	}
+
+	/**
+	 * Sends a single pingback if a link is found
+	 *
+	 * @return integer The number of pingbacks sent
+	 */
+	function send_pingback( $topic_id, $post_text )
+	{
+		if ( !$topic_id || !$post_text ) {
+			return 0;
+		}
+
+		// Get all links in the text and add them to an array
+		if ( !preg_match_all( '@<a ([^>]+)>@im', make_clickable( $post_text ), $post_links ) ) {
+			return 0;
+		}
+
+		$_links = array();
+		foreach ( $post_links[1] as $post_link_attributes ) {
+			$post_link_attributes = preg_split( '@\s+@im', $post_link_attributes, -1, PREG_SPLIT_NO_EMPTY );
+			foreach ( $post_link_attributes as $post_link_attribute ) {
+				if ( strpos( $post_link_attribute, '=', 1 ) !== false ) {
+					list( $_key, $_value ) = explode( '=', $post_link_attribute, 2 );
+					if ( strtolower( $_key ) === 'href' ) {
+						$_links[] = trim( $_value, "'\"" );
+					}
+				}
+			}
+		}
+
+		// Get pingbacks which have already been performed from this topic
+		$past_pingbacks = bb_get_topicmeta( $topic_id, 'pingback_performed' );
+		$new_pingbacks = array();
+
+		foreach ( $_links as $_link ) {
+			// If it's already been pingbacked, then skip it
+			if ( $past_pingbacks && in_array( $_link, $past_pingbacks ) ) {
+				continue;
+			}
+
+			// If it's trying to ping itself, then skip it
+			if ( $topic = bb_get_topic_from_uri( $_link ) ) {
+				if ( $topic->topic_id === $topic_id ) {
+					continue;
+				}
+			}
+
+			// Make sure it's a page on a site and not the root
+			if ( !$_url = parse_url( $_link ) ) {
+				continue;
+			}
+			if ( !isset( $_url['query'] ) ) {
+				if ( $_url['path'] == '' || $_url['path'] == '/' ) {
+					continue;
+				}
+			}
+
+			// Add the URL to the array of those to be pingbacked
+			$new_pingbacks[] = $_link;
+		}
+
+		include_once( BACKPRESS_PATH . '/class.ixr.php' );
+
+		$count = 0;
+		foreach ( $new_pingbacks as $pingback_to_url ) {
+			if ( !$pingback_endpoint_uri = BB_Pingbacks::get_endpoint_uri( $pingback_to_url ) ) {
+				continue;
+			}
+
+			// Stop this nonsense after 60 seconds
+			@set_time_limit( 60 );
+
+			// Get the URL to pingback from
+			$pingback_from_url = get_topic_link( $topic_id );
+
+			// Using a timeout of 3 seconds should be enough to cover slow servers
+			$client = new IXR_Client( $pingback_endpoint_uri );
+			$client->timeout = 3;
+			$client->useragent .= ' -- bbPress/' . bb_get_option( 'version' );
+
+			// When set to true, this outputs debug messages by itself
+			$client->debug = false;
+
+			// If successful or the ping already exists then add to the pingbacked list
+			if (
+				$client->query( 'pingback.ping', $pingback_from_url, $pingback_to_url ) ||
+				( isset( $client->error->code ) && 48 == $client->error->code )
+			) {
+				$count++;
+				$past_pingbacks[] = $pingback_to_url;
+			}
+		}
+
+		bb_update_topicmeta( $topic_id, 'pingback_performed', $past_pingbacks );
+
+		return $count;
+	}
+} // END class BB_Pingbacks
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-query.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-query.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-query.php	(revision 5057)
@@ -0,0 +1,1037 @@
+<?php
+
+class BB_Query {
+	var $type;
+	var $query;
+	var $query_id;
+
+	var $query_vars = array();
+	var $not_set = array();
+	var $request;
+	var $match_query = false;
+
+	var $results;
+	var $errors;
+	var $count = 0;
+	var $found_rows = false;
+
+	// Can optionally pass unique id string to help out filters
+	function BB_Query( $type = 'topic', $query = '', $id = '' ) {
+		$this->init( $type, $query, $id );
+
+		if ( !empty($this->query) )
+			$this->query();
+	}
+
+	function init( $type = null, $query = null, $id = null ) {
+		if ( !is_null($type) || !isset($this->type) )
+			$this->type = is_null($type) ? 'topic' : $type;
+		if ( !is_null($query) || !isset($this->query) )
+			$this->query = $query;
+		if ( !is_null($id) || !isset($this->query_id) )
+			$this->query_id = $id;
+
+		$this->query_vars = array();
+		$this->not_set = array();
+		unset($this->request);
+		$this->match_query = false;
+
+		unset($this->results, $this->errors);
+		$this->count = 0;
+		$this->found_rows = false;
+	}
+
+	function &query() {
+		global $bbdb;
+
+		if ( $args = func_get_args() )
+			call_user_func_array( array(&$this, 'init'), $args );
+
+		if ( !$this->generate_query() )
+			return;
+
+		do_action_ref_array( 'bb_query', array(&$this) );
+
+		$key = md5( $this->request );
+		if ( false === $cached_ids = wp_cache_get( $key, 'bb_query' ) ) {
+			if ( 'post' == $this->type ) {
+				$this->results = bb_cache_posts( $this->request, $this->query_vars['post_id_only'] ); // This always appends meta
+				$_the_id = 'post_id';
+				$this->query_vars['append_meta'] = false;
+			} else {
+				$this->results = $bbdb->get_results( $this->request );
+				$_the_id = 'topic_id';
+			}
+			$cached_ids = array();
+			if ( is_array($this->results) )
+				foreach ( $this->results as $object )
+					$cached_ids[] = $object->$_the_id;
+			wp_cache_set( $key, $cached_ids, 'bb_query' );
+		} else {
+			if ( 'post' == $this->type ) {
+				$_query_ids = array();
+				$_cached_posts = array();
+				foreach ( $cached_ids as $_cached_id ) {
+					if ( false !== $_post = wp_cache_get( $_cached_id, 'bb_post' ) ) {
+						$_cached_posts[$_post->post_id] = $_post;
+					} else {
+						$_query_ids[] = $_cached_id;
+					}
+				}
+				if ( count( $_query_ids ) ) {
+					$_query_ids = join( ',', array_map( 'intval', $_query_ids ) );
+					$results = $bbdb->get_results( "SELECT * FROM $bbdb->posts WHERE post_id IN($_query_ids)" );
+					$results = array_merge( $results, $_cached_posts );
+				} else {
+					$results = $_cached_posts;
+				}
+				$_the_id = 'post_id';
+			} else {
+				$_query_ids = array();
+				$_cached_topics = array();
+				foreach ( $cached_ids as $_cached_id ) {
+					if ( false !== $_topic = wp_cache_get( $_cached_id, 'bb_topic' ) ) {
+						$_cached_topics[$_topic->topic_id] = $_topic;
+					} else {
+						$_query_ids[] = $_cached_id;
+					}
+				}
+				if ( count( $_query_ids ) ) {
+					$_query_ids = join( ',', array_map( 'intval', $_query_ids ) );
+					$results = $bbdb->get_results( "SELECT * FROM $bbdb->topics WHERE topic_id IN($_query_ids)" );
+					$results = array_merge( $results, $_cached_topics );
+				} else {
+					$results = $_cached_topics;
+				}
+				$_the_id = 'topic_id';
+			}
+
+			$this->results = array();
+			$trans = array();
+
+			foreach ( $results as $object )
+				$trans[$object->$_the_id] = $object;
+			foreach ( $cached_ids as $cached_id )
+				$this->results[] = $trans[$cached_id];
+		}
+
+		$this->count = count( $this->results );
+
+		if ( false === $this->found_rows && $this->query_vars['count'] ) // handles FOUND_ROWS() or COUNT(*)
+			$this->found_rows = bb_count_last_query( $this->request );
+		if ( 'post' == $this->type ) {
+			if ( $this->query_vars['append_meta'] )
+				$this->results = bb_append_meta( $this->results, 'post' );
+			if ( $this->query_vars['cache_users'] )
+				bb_post_author_cache( $this->results );
+			if ( $this->query_vars['cache_topics'] )
+				bb_cache_post_topics( $this->results );
+		} else {
+			if ( $this->query_vars['append_meta'] )
+				$this->results = bb_append_meta( $this->results, 'topic' );
+		}
+
+		return $this->results;
+	}
+
+	function generate_query() {
+		if ( $args = func_get_args() )
+			call_user_func_array( array(&$this, 'init'), $args );
+
+		$this->parse_query();
+
+		// Allow filter to abort query
+		if ( false === $this->query_vars )
+			return false;
+
+		if ( 'post' == $this->type )
+			$this->generate_post_sql();
+		else
+			$this->generate_topic_sql();
+
+		return $this->request;
+	}
+
+	// $defaults = vars to use if not set in GET, POST or allowed
+	// $allowed = array( key_name => value, key_name, key_name, key_name => value );
+	// 	key_name => value pairs override anything from defaults, GET, POST
+	//	Lone key_names are a whitelist.  Only those can be set by defaults, GET, POST
+	//	If there are no lone key_names, allow everything but still override with key_name => value pairs
+	//	Ex: $allowed = array( 'topic_status' => 0, 'post_status' => 0, 'topic_author', 'started' );
+	//		Will only take topic_author and started values from defaults, GET, POST and will query with topic_status = 0 and post_status = 0
+	function &query_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) {
+		$this->init_from_env( $type, $defaults, $allowed, $id );
+		return $this->query();
+	}
+
+	function init_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) {
+		$vars = $this->fill_query_vars( array() );
+
+		$defaults  = wp_parse_args($defaults);
+		$get_vars  = stripslashes_deep( $_GET );
+		$post_vars = stripslashes_deep( $_POST );
+		$allowed   = wp_parse_args($allowed);
+
+		$_allowed = array();
+		foreach ( array_keys($allowed) as $k ) {
+			if ( is_numeric($k) ) {
+				$_allowed[] = $allowed[$k];
+				unset($allowed[$k]);
+			} elseif ( !isset($$k) ) {
+				$$k = $allowed[$k];
+			}
+		}
+
+		extract($post_vars, EXTR_SKIP);
+		extract($get_vars, EXTR_SKIP);
+		extract($defaults, EXTR_SKIP);
+
+		$vars = $_allowed ? compact($_allowed, array_keys($allowed)) : compact(array_keys($vars));
+
+		$this->init( $type, $vars, $id );
+	}
+
+	function fill_query_vars( $array ) {
+		// Should use 0, '' for empty values
+		// Function should return false iff not set
+
+		// parameters commented out are handled farther down
+
+		$ints = array(
+//			'page',		 // Defaults to global or number in URI
+//			'per_page',	 // Defaults to page_topics
+			'tag_id',	 // one tag ID
+			'favorites', // one user ID,
+			'offset',	 // first item to query
+			'number'	 // number of items to retrieve
+		);
+
+		$parse_ints = array(
+			// Both
+			'post_id',
+			'topic_id',
+			'forum_id',
+
+			// Topics
+			'topic_author_id',
+			'post_count',
+			'tag_count',
+
+			// Posts
+			'post_author_id',
+			'position'
+		);
+
+		$dates = array(
+			'started',	// topic
+			'updated',	// topic
+			'posted'	// post
+		);
+
+		$others = array(
+			// Both
+			'topic',	// one topic name
+			'forum',	// one forum name
+			'tag',		// one tag name
+
+			// Topics
+			'topic_author',	// one username
+			'topic_status',	// *normal, deleted, all, parse_int ( and - )
+			'open',			// *all, yes = open, no = closed, parse_int ( and - )
+			'sticky',		// *all, no = normal, forum, super = front, parse_int ( and - )
+			'meta_key',		// one meta_key ( and - )
+			'meta_value',	// range
+			'topic_title',	// LIKE search.  Understands "doublequoted strings"
+			'search',		// generic search: topic_title OR post_text
+							// Can ONLY be used in a topic query
+							// Returns additional search_score and (concatenated) post_text columns
+
+			// Posts
+			'post_author',	// one username
+			'post_status',	// *noraml, deleted, all, parse_int ( and - )
+			'post_text',	// FULLTEXT search
+							// Returns additional search_score column (and (concatenated) post_text column if topic query)
+			'poster_ip',	// one IPv4 address
+
+			// SQL
+			'index_hint',	// A full index hint using valid index hint syntax, can be multiple hints an array
+			'order_by',		// fieldname
+			'order',		// *DESC, ASC
+			'count',		// *false = none, true = COUNT(*), found_rows = FOUND_ROWS()
+			'_join_type',	// not implemented: For benchmarking only.  Will disappear. join (1 query), in (2 queries)
+
+			// Utility
+//			'append_meta',	// *true, false: topics only
+//			'cache_users',	// *true, false
+//			'cache_topics,	// *true, false: posts only
+//			'post_id_only', // true, *false: this query is only returning post IDs
+			'cache_posts'	 // not implemented: none, first, last
+		);
+
+		foreach ( $ints as $key )
+			if ( ( false === $array[$key] = isset($array[$key]) ? (int) $array[$key] : false ) && isset($this) )
+				$this->not_set[] = $key;
+
+		foreach ( $parse_ints as $key )
+			if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<=>0-9,-]/', '', $array[$key] ) : false ) && isset($this) )
+				$this->not_set[] = $key;
+
+		foreach ( $dates as $key )
+			if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<>0-9-]/', '', $array[$key] ) : false ) && isset($this) )
+				$this->not_set[] = $key;
+
+		foreach ( $others as $key ) {
+			if ( !isset($array[$key]) )
+				$array[$key] = false;
+			if ( isset($this) && false === $array[$key] )
+				$this->not_set[] = $key;
+		}
+
+		// Both
+		if ( isset($array['page']) )
+			$array['page'] = (int) $array['page'];
+		elseif ( isset($GLOBALS['page']) )
+			$array['page'] = (int) $GLOBALS['page'];
+		else
+			$array['page'] = bb_get_uri_page();
+
+		if ( $array['page'] < 1 )
+			$array['page'] = 1;
+
+		$array['per_page'] = isset($array['per_page']) ? (int) $array['per_page'] : 0;
+		if ( $array['per_page'] < -1 )
+			$array['per_page'] = 1;
+
+		// Posts
+		if ( ( !$array['poster_ip'] = isset($array['poster_ip']) ? preg_replace("@[^0-9a-f:.]@i", '', $array['poster_ip']) : false ) && isset($this) ) {
+			$this->not_set[] = 'poster_ip';
+			$array['poster_ip'] = false;
+		}
+
+		// Utility
+		$array['append_meta']  = isset($array['append_meta'])  ? (int) (bool) $array['append_meta']  : 1;
+		$array['cache_users']  = isset($array['cache_users'])  ? (int) (bool) $array['cache_users']  : 1;
+		$array['cache_topics'] = isset($array['cache_topics']) ? (int) (bool) $array['cache_topics'] : 1;
+		$array['post_id_only'] = isset($array['post_id_only']) ? (int) (bool) $array['post_id_only'] : 1;
+
+		// Only one FULLTEXT search per query please
+		if ( $array['search'] )
+			$array['post_text'] = false;
+
+		return $array;
+	}
+
+	// Parse a query string and set query flag booleans.
+	function parse_query() {
+		if ( $args = func_get_args() )
+			call_user_func_array( array(&$this, 'init'), $args );
+
+		if ( is_array($this->query) )
+			$this->query_vars = $this->query;
+		else
+			wp_parse_str($this->query, $this->query_vars);
+
+		do_action_ref_array('bb_parse_query', array(&$this));
+
+		if ( false === $this->query_vars )
+			return;
+
+		$this->query_vars = $this->fill_query_vars($this->query_vars);
+	}
+
+	function get($query_var) {
+		return isset($this->query_vars[$query_var]) ? $this->query_vars[$query_var] : null;
+	}
+
+	function set($query_var, $value) {
+		$this->query_vars[$query_var] = $value;
+	}
+
+	function generate_topic_sql( $_part_of_post_query = false ) {
+		global $bbdb;
+
+		$q =& $this->query_vars;
+		$distinct = '';
+		$sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered
+		$fields = 't.*';
+		$index_hint = '';
+		$join = '';
+		$where = '';
+		$group_by = '';
+		$having = '';
+		$order_by = '';
+
+		$post_where = '';
+		$post_queries = array('post_author_id', 'post_author', 'posted', 'post_status', 'position', 'post_text', 'poster_ip');
+
+		if ( !$_part_of_post_query && ( $q['search'] || array_diff($post_queries, $this->not_set) ) ) :
+			$join .= " JOIN $bbdb->posts as p ON ( t.topic_id = p.topic_id )";
+			$post_where = $this->generate_post_sql( true );
+			if ( $q['search'] ) {
+				$post_where .= ' AND ( ';
+				$post_where .= $this->generate_topic_title_sql( $q['search'] );
+				$post_where .= ' OR ';
+				$post_where .= $this->generate_post_text_sql( $q['search'] );
+				$post_where .= ' )';
+			}
+
+			$group_by = 't.topic_id';
+
+			$fields .= ", MIN(p.post_id) as post_id";
+
+			if ( $bbdb->has_cap( 'GROUP_CONCAT', $bbdb->posts ) )
+				$fields .= ", GROUP_CONCAT(p.post_text SEPARATOR ' ') AS post_text";
+			else
+				$fields .= ", p.post_text";
+
+			if ( $this->match_query ) {
+				$fields .= ", AVG($this->match_query) AS search_score";
+				if ( !$q['order_by'] )
+					$q['order_by'] = 'search_score';
+			} elseif ( $q['search'] || $q['post_text'] ) {
+				$fields .= ", 0 AS search_score";
+			}
+		endif;
+
+		if ( !$_part_of_post_query ) :
+			if ( $q['post_id'] ) :
+				$post_topics = $post_topics_no = array();
+				$op = substr($q['post_id'], 0, 1);
+				if ( in_array($op, array('>','<')) ) :
+					$post_topics = $bbdb->get_col( "SELECT DISTINCT topic_id FROM $bbdb->posts WHERE post_id $op '" . (int) substr($q['post_id'], 1) . "'" );
+				else :
+					$posts = explode(',', $q['post_id']);
+					$get_posts = array();
+					foreach ( $posts as $post_id ) {
+						$post_id = (int) $post_id;
+						$_post_id = abs($post_id);
+						$get_posts[] = $_post_id;
+					}
+					bb_cache_posts( $get_posts );
+
+					foreach ( $posts as $post_id ) :
+						$post = bb_get_post( abs($post_id) );
+						if ( $post_id < 0 )
+							$post_topics_no[] = $post->topic_id;
+						else
+							$post_topics[] = $post->topic_id;
+					endforeach;
+				endif;
+				if ( $post_topics )
+					$where .= " AND t.topic_id IN (" . join(',', $post_topics) . ")";
+				if ( $post_topics_no )
+					$where .= " AND t.topic_id NOT IN (" . join(',', $post_topics_no) . ")";
+			endif;
+
+			if ( $q['topic_id'] ) :
+				$where .= $this->parse_value( 't.topic_id', $q['topic_id'] );
+			elseif ( $q['topic'] ) :
+				$q['topic'] = bb_slug_sanitize( $q['topic'] );
+				$where .= " AND t.topic_slug = '$q[topic]'";
+			endif;
+
+			if ( $q['forum_id'] ) :
+				$where .= $this->parse_value( 't.forum_id', $q['forum_id'] );
+			elseif ( $q['forum'] ) :
+				if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) )
+					$this->error( 'query_var:forum', 'No forum by that name' );
+				$where .= " AND t.forum_id = $q[forum_id]";
+			endif;
+
+			if ( $q['tag'] && !is_int($q['tag_id']) )
+				$q['tag_id'] = (int) bb_get_tag_id( $q['tag'] );
+
+			if ( is_numeric($q['tag_id']) ) :
+				$join .= " JOIN `$bbdb->term_relationships` AS tr ON ( t.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )";
+			endif;
+
+			if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) )
+				$where .= $this->parse_value( 't.topic_id', $f_user->favorites );
+		endif; // !_part_of_post_query
+
+		if ( $q['topic_title'] )
+			$where .= ' AND ' . $this->generate_topic_title_sql( $q['topic_title'] );
+
+		if ( $q['started'] )
+			$where .= $this->date( 't.topic_start_time', $q['started'] );
+
+		if ( $q['updated'] )
+			$where .= $this->date( 't.topic_time', $q['updated'] );
+
+		if ( $q['topic_author_id'] ) :
+			$where .= $this->parse_value( 't.topic_poster', $q['topic_author_id'] );
+		elseif ( $q['topic_author'] ) :
+			$user = bb_get_user( $q['topic_author'], array( 'by' => 'login' ) );
+			if ( !$q['topic_author_id'] = (int) $user->ID )
+				$this->error( 'query_var:user', 'No user by that name' );
+			$where .= " AND t.topic_poster = $q[topic_author_id]";
+		endif;
+
+		if ( !$q['topic_status'] ) :
+			$where .= " AND t.topic_status = '0'";
+		elseif ( false === strpos($q['topic_status'], 'all') ) :
+			$stati = array( 'normal' => 0, 'deleted' => 1 );
+			$q['topic_status'] = str_replace(array_keys($stati), array_values($stati), $q['topic_status']);
+			$where .= $this->parse_value( 't.topic_status', $q['topic_status'] );
+		endif;
+
+		if ( false !== $q['open'] && false === strpos($q['open'], 'all') ) :
+			$stati = array( 'no' => 0, 'closed' => 0, 'yes' => 1, 'open' => 1 );
+			$q['open'] = str_replace(array_keys($stati), array_values($stati), $q['open']);
+			$where .= $this->parse_value( 't.topic_open', $q['open'] );
+		endif;
+
+		if ( false !== $q['sticky'] && false === strpos($q['sticky'], 'all') ) :
+			$stickies = array( 'no' => 0, 'normal' => 0, 'forum' => 1, 'super' => 2, 'front' => 2, 'sticky' => '-0' );
+			$q['sticky'] = str_replace(array_keys($stickies), array_values($stickies), $q['sticky']);
+			$where .= $this->parse_value( 't.topic_sticky', $q['sticky'] );
+		endif;
+
+		if ( false !== $q['post_count'] )
+			$where .= $this->parse_value( 't.topic_posts', $q['post_count'] );
+
+		if ( false !== $q['tag_count'] )
+			$where .= $this->parse_value( 't.tag_count', $q['tag_count'] );
+
+		if ( $q['meta_key'] && $q['meta_key'] = preg_replace('|[^a-z0-9_-]|i', '', $q['meta_key']) ) :
+			if ( '-' == substr($q['meta_key'], 0, 1) ) :
+				$join  .= " LEFT JOIN $bbdb->meta AS tm ON ( tm.object_type = 'bb_topic' AND t.topic_id = tm.object_id AND tm.meta_key = '" . substr( $q['meta_key'], 1 ) . "' )";
+				$where .= " AND tm.meta_key IS NULL";
+			else :
+				$join  .= " JOIN $bbdb->meta AS tm ON ( tm.object_type = 'bb_topic' AND t.topic_id = tm.object_id AND tm.meta_key = '$q[meta_key]' )";
+
+				if ( $q['meta_value'] ) :
+					$q['meta_value'] = maybe_serialize( $q['meta_value'] );
+					if ( strpos( $q['meta_value'], 'NULL' ) !== false )
+						$join = ' LEFT' . $join;
+					$where .= $this->parse_value( 'tm.meta_value', $q['meta_value'] );
+				endif;
+			endif;
+		endif;
+
+		// Just getting topic part for inclusion in post query
+		if ( $_part_of_post_query )
+			return $where;
+
+		$where .= $post_where;
+
+		if ( $where ) // Get rid of initial " AND " (this is pre-filters)
+			$where = substr($where, 5);
+
+		if ( $q['index_hint'] )
+			$index_hint = $q['index_hint'];
+
+		if ( $q['order_by'] )
+			$order_by = $q['order_by'];
+		else
+			$order_by = 't.topic_time';
+
+		$bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') );
+		$this->request = $this->_filter_sql( $bits, "$bbdb->topics AS t" );
+		return $this->request;
+	}
+
+	function generate_post_sql( $_part_of_topic_query = false ) {
+		global $bbdb;
+
+		$q =& $this->query_vars;
+		$distinct = '';
+		$sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered
+		$fields = 'p.*';
+		$index_hint = '';
+		$join = '';
+		$where = '';
+		$group_by = '';
+		$having = '';
+		$order_by = '';
+
+		$topic_where = '';
+		$topic_queries = array( 'topic_author_id', 'topic_author', 'topic_status', 'post_count', 'tag_count', 'started', 'updated', 'open', 'sticky', 'meta_key', 'meta_value', 'topic_title' );
+		if ( !$_part_of_topic_query && array_diff($topic_queries, $this->not_set) ) :
+			$join .= " JOIN $bbdb->topics as t ON ( t.topic_id = p.topic_id )";
+			$topic_where = $this->generate_topic_sql( true );
+		endif;
+		
+		if ( !$_part_of_topic_query ) :
+			if ( $q['post_id'] )
+				$where .= $this->parse_value( 'p.post_id', $q['post_id'] );
+
+			if ( $q['topic_id'] ) :
+				$where .= $this->parse_value( 'p.topic_id', $q['topic_id'] );
+			elseif ( $q['topic'] ) :
+				if ( !$q['topic_id'] = bb_get_id_from_slug( 'topic', $q['topic'] ) )
+					$this->error( 'query_var:topic', 'No topic by that name' );
+				$where .= " AND p.topic_id = " . $q['topic_id'];
+			endif;
+
+			if ( $q['forum_id'] ) :
+				$where .= $this->parse_value( 'p.forum_id', $q['forum_id'] );
+			elseif ( $q['forum'] ) :
+				if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) )
+					$this->error( 'query_var:forum', 'No forum by that name' );
+				$where .= " AND p.forum_id = " . $q['forum_id'];
+			endif;
+
+			if ( $q['tag'] && !is_int($q['tag_id']) )
+				$q['tag_id'] = (int) bb_get_tag_id( $q['tag'] );
+
+			if ( is_numeric($q['tag_id']) ) :
+				$join .= " JOIN `$bbdb->term_relationships` AS tr ON ( p.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )";
+			endif;
+
+			if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) )
+				$where .= $this->parse_value( 'p.topic_id', $f_user->favorites );
+		endif; // !_part_of_topic_query
+
+		if ( $q['post_text'] ) :
+			$where  .= ' AND ' . $this->generate_post_text_sql( $q['post_text'] );
+			if ( $this->match_query ) {
+				$fields .= ", $this->match_query AS search_score";
+				if ( !$q['order_by'] )
+					$q['order_by'] = 'search_score';
+			} else {
+				$fields .= ', 0 AS search_score';
+			}
+		endif;
+
+		if ( $q['posted'] )
+			$where .= $this->date( 'p.post_time', $q['posted'] );
+
+		if ( $q['post_author_id'] ) :
+			$where .= $this->parse_value( 'p.poster_id', $q['post_author_id'] );
+		elseif ( $q['post_author'] ) :
+			$user = bb_get_user( $q['post_author'], array( 'by' => 'login' ) );
+			if ( !$q['post_author_id'] = (int) $user->ID )
+				$this->error( 'query_var:user', 'No user by that name' );
+			$where .= " AND p.poster_id = $q[post_author_id]";
+		endif;
+
+		if ( !$q['post_status'] ) :
+			$where .= " AND p.post_status = '0'";
+		elseif ( false === strpos($q['post_status'], 'all') ) :
+			$stati = array( 'normal' => 0, 'deleted' => 1 );
+			$q['post_status'] = str_replace(array_keys($stati), array_values($stati), $q['post_status']);
+			$where .= $this->parse_value( 'p.post_status', $q['post_status'] );
+		endif;
+
+		if ( false !== $q['position'] )
+			$where .= $this->parse_value( 'p.post_position', $q['position'] );
+
+		if ( false !== $q['poster_ip'] )
+			$where .= " AND poster_ip = '" . $q['poster_ip'] . "'";
+
+		// Just getting post part for inclusion in topic query
+		if ( $_part_of_topic_query )
+			return $where;
+
+		$where .= $topic_where;
+
+		if ( $where ) // Get rid of initial " AND " (this is pre-filters)
+			$where = substr($where, 5);
+
+		if ( $q['index_hint'] )
+			$index_hint = $q['index_hint'];
+
+		if ( $q['order_by'] )
+			$order_by = $q['order_by'];
+		else
+			$order_by = 'p.post_time';
+
+		$bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') );
+		$this->request = $this->_filter_sql( $bits, "$bbdb->posts AS p" );
+
+		return $this->request;
+	}
+
+	function generate_topic_title_sql( $string ) {
+		global $bbdb;
+		$string = trim($string);
+
+		if ( !preg_match_all('/".*?("|$)|((?<=[\s",+])|^)[^\s",+]+/', $string, $matches) ) {
+			$string = $bbdb->escape($string);
+			return "(t.topic_title LIKE '%$string%')";
+		}
+
+		$where = '';
+
+		foreach ( $matches[0] as $match ) {
+			$term = trim($match, "\"\n\r ");
+			$term = $bbdb->escape($term);
+			$where .= " AND t.topic_title LIKE '%$term%'";
+		}
+
+		if ( count($matches[0]) > 1 && $string != $matches[0][0] ) {
+			$string = $bbdb->escape($string);
+			$where .= " OR t.topic_title LIKE '%$string%'";
+		}
+
+		return '(' . substr($where, 5) . ')';
+	}
+
+	function generate_post_text_sql( $string ) {
+		global $bbdb;
+		$string = trim($string);
+		$_string = $bbdb->escape( $string );
+		if ( strlen($string) < 5 )
+			return "p.post_text LIKE '%$_string%'";
+
+		return $this->match_query = "MATCH(p.post_text) AGAINST('$_string')";
+	}
+
+	function _filter_sql( $bits, $from ) {
+		global $bbdb;
+
+		$q =& $this->query_vars;
+
+		// MySQL 5.1 allows multiple index hints per query - earlier versions only get the first hint
+		if ( $bits['index_hint'] ) {
+			if ( !is_array( $bits['index_hint'] ) ) {
+				$bits['index_hint'] = array( (string) $bits['index_hint'] );
+			}
+			if ( $bbdb->has_cap( 'index_hint_for_any' ) ) {
+				// 5.1 <= MySQL
+				$_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+(FOR\s+(JOIN|ORDER\s+BY|GROUP\s+BY)\s+)?\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i';
+			} elseif ( $bbdb->has_cap( 'index_hint_for_join' ) ) {
+				// 5.0 <= MySQL < 5.1
+				$_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+(FOR\s+JOIN\s+)?\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i';
+			} else {
+				// MySQL < 5.0
+				$_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i';
+			}
+			$_index_hint = array();
+			foreach ( $bits['index_hint'] as $_hint ) {
+				if ( preg_match( $_regex, $_hint ) ) {
+					$_index_hint[] = trim( $_hint );
+				}
+			}
+			unset( $_regex, $_hint );
+			if ( $bbdb->has_cap( 'index_hint_lists' ) ) {
+				// 5.1 <= MySQL
+				$bits['index_hint'] = join( ' ', $_index_hint );
+			} else {
+				// MySQL < 5.1
+				$bits['index_hint'] = isset( $_index_hint[0] ) ? $_index_hint[0] : '';
+			}
+			unset( $_index_hint );
+		}
+
+		$q['order'] = strtoupper($q['order']);
+		if ( $q['order'] && in_array($q['order'], array('ASC', 'DESC')) )
+			$bits['order_by'] .= " $q[order]";
+		else
+			$bits['order_by'] .= " DESC";
+
+		$bits['limit'] = '';
+
+		// When offset and number are provided, skip per_page and limit checks
+		if ( !empty( $q['offset'] ) && !empty( $q['number'] ) ) {
+			$bits['limit'] .= $q['offset'] . ", " . $q['number'];
+
+		// Else proceed as normal
+		} else {
+			if ( !$q['per_page'] ) {
+				$q['per_page'] = (int) bb_get_option( 'page_topics' );
+			}
+	
+			if ( $q['per_page'] > 0 ) {
+				if ( $q['page'] > 1 ) {
+					$bits['limit'] .= $q['per_page'] * ( $q['page'] - 1 ) . ", ";
+				}
+				$bits['limit'] .= $q['per_page'];
+			}
+		}
+
+		$name = "get_{$this->type}s_";
+
+		// Unfiltered
+		$sql_calc_found_rows = $bits['sql_calc_found_rows'];
+		unset($bits['sql_calc_found_rows']);
+
+		foreach ( $bits as $bit => $value ) {
+			if ( $this->query_id )
+				$value = apply_filters( "{$this->query_id}_$bit", $value );
+			$$bit = apply_filters( "$name$bit", $value );
+		}
+
+		if ( $where )
+			$where = "WHERE $where";
+		if ( $group_by )
+			$group_by = "GROUP BY $group_by";
+		if ( $having )
+			$having = "HAVING $having";
+		if ( $order_by )
+			$order_by = "ORDER BY $order_by";
+		if ( $limit )
+			$limit = "LIMIT $limit";
+
+		return "SELECT $distinct $sql_calc_found_rows $fields FROM $from $index_hint $join $where $group_by $having $order_by $limit";
+	}
+
+	function parse_value( $field, $value = '' ) {
+		if ( !$value && !is_numeric($value) )
+			return '';
+
+		global $bbdb;
+
+		$op = substr($value, 0, 1);
+
+		// #, =whatever, <#, >#.  Cannot do < and > at same time
+		if ( in_array($op, array('<', '=', '>')) ) :
+			$value = substr($value, 1);
+			$value = is_numeric($value) ? (float) $value : $bbdb->escape( $value );
+			return " AND $field $op '$value'";
+		elseif ( false === strpos($value, ',') && 'NULL' !== $value && '-NULL' !== $value ) :
+			$value = is_numeric($value) ? (float) $value : $bbdb->escape( $value );
+			return '-' == $op ? " AND $field != '" . substr($value, 1) . "'" : " AND $field = '$value'";
+		endif;
+
+		$y = $n = array();
+		foreach ( explode(',', $value) as $v ) {
+			$v = is_numeric($v) ? (int) $v : $bbdb->escape( $v );
+			if ( '-' == substr($v, 0, 1) )
+				if ( $v === '-NULL' )
+					$not_null_flag = true;
+				else
+					$n[] = substr($v, 1);
+			else
+				if ( $v === 'NULL' )
+					$null_flag = true;
+				else
+					$y[] = $v;
+		}
+
+		$r = '';
+		if ( $y ) {
+			$r .= " AND ";
+			if ( $null_flag )
+				$r .= "(";
+			$r .= "$field IN ('" . join("','", $y) . "')";
+			if ( $null_flag )
+				$r .= " OR $field IS NULL)";
+		} elseif ( $null_flag ) {
+			$r .= " AND $field IS NULL";
+		}
+		
+		if ( $n ) {
+			$r .= " AND ";
+			if ( $not_null_flag )
+				$r .= "(";
+			$r .= "$field NOT IN ('" . join("','", $n) . "')";
+			if ( $not_null_flag )
+				$r .= " AND $field IS NOT NULL)";
+		} elseif ( $not_null_flag ) {
+			$r .= " AND $field IS NOT NULL";
+		}
+
+		return $r;
+	}
+
+	function date( $field, $date ) {
+		if ( !$date && !is_int($date) )
+			return '';
+
+		if ( $is_range = false !== strpos( $date, '--' ) )
+			$dates = explode( '--', $date, 2 );
+		else
+			$dates = array( $date );
+
+		$op = false;
+		$r = '';
+		foreach ( $dates as $date ) {
+			if ( $is_range ) {
+				$op = $op ? '<' : '>';
+				$date = (int) substr($date, 0, 14);
+			} else {
+				$op = substr($date, 0, 1);
+				if ( !in_array($op, array('>', '<')) )
+					break;
+				$date = (int) substr($date, 1, 14);
+			}
+			if ( strlen($date) < 14 )
+				$date .= str_repeat('0', 14 - strlen($date));
+			$r .= " AND $field $op $date";
+		}
+		if ( $r )
+			return $r;
+
+		$date = (int) $date;
+		$r = " AND YEAR($field) = " . substr($date, 0, 4);
+		if ( strlen($date) > 5 )
+			$r .= " AND MONTH($field) = " . substr($date, 4, 2);
+		if ( strlen($date) > 7 )
+			$r .= " AND DAYOFMONTH($field) = " . substr($date, 6, 2);
+		if ( strlen($date) > 9 )
+			$r .= " AND HOUR($field) = " . substr($date, 8, 2);
+		if ( strlen($date) > 11 )
+			$r .= " AND MINUTE($field) = " . substr($date, 10, 2);
+		if ( strlen($date) > 13 )
+			$r .= " AND SECOND($field) = " . substr($date, 12, 2);
+		return $r;
+	}
+
+	function error( $code, $message ) {
+		if ( is_wp_error($this->errors) )
+			$this->errors->add( $code, $message );
+		else
+			$this->errors = new WP_Error( $code, $message );
+	}
+}
+
+class BB_Query_Form extends BB_Query {
+	var $defaults;
+	var $allowed;
+
+	// Can optionally pass unique id string to help out filters
+	function BB_Query_Form( $type = 'topic', $defaults = '', $allowed = '', $id = '' ) {
+		$this->defaults = wp_parse_args( $defaults );
+		$this->allowed  = wp_parse_args( $allowed );
+		if ( !empty($defaults) || !empty($allowed) )
+			$this->query_from_env($type, $defaults, $allowed, $id);
+	}
+
+	function form( $args = null ) {
+		$_post = 'post' == $this->type;
+
+		$defaults = array(
+			'search' => true,
+			'forum'  => true,
+			'tag'    => false,
+			'open'   => false,
+			'topic_author' => false,
+			'post_author'  => false,
+			'topic_status' => false,
+			'post_status'  => false,
+			'topic_title'  => false,
+			'poster_ip'  => false,
+
+			'method' => 'get',
+			'submit' => __('Search &#187;'),
+			'action' => ''
+		);
+		$defaults['id'] = $_post ? 'post-search-form' : 'topic-search-form';
+
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		$id = esc_attr( $id );
+		$method = 'get' == strtolower($method) ? 'get' : 'post';
+		$submit = esc_attr( $submit );
+		if ( !$action = esc_url( $action ) )
+			$action = '';
+
+		if ( $this->query_vars )
+			$query_vars =& $this->query_vars;
+		else
+			$query_vars = $this->fill_query_vars( $this->defaults );
+
+		extract($query_vars, EXTR_PREFIX_ALL, 'q');
+
+		$r  = "<form action='$action' method='$method' id='$id' class='search-form'>\n";
+
+		$r .= "\t<fieldset>\n";
+
+		if ( $search ) {
+			if ( $_post ) {
+				$s_value = esc_attr( $q_post_text );
+				$s_name = 'post_text';
+				$s_id = 'post-text';
+			} else {
+				$s_value = esc_attr( $q_search );
+				$s_name = $s_id = 'search';
+			}
+			$r .= "\t<div><label for=\"$s_id\">" . __('Search term') . "</label>\n";
+			$r .= "\t\t<div><input name='$s_name' id='$s_id' type='text' class='text-input' value='$s_value' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $forum ) {
+			$r .= "\t<div><label for=\"forum-id\">" . __('Forum')  . "</label>\n";
+			$r .= "\t\t<div>" . bb_get_forum_dropdown( array( 'selected' => $q_forum_id, 'none' => __('Any') ) ) . "</div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $tag ) {
+			$q_tag = esc_attr( $q_tag );
+			$r .= "\t<div><label for=\"topic-tag\">" .  __('Tag') . "</label>\n";
+			$r .= "\t\t<div><input name='tag' id='topic-tag' type='text' class='text-input' value='$q_tag' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $topic_author ) {
+			$q_topic_author = esc_attr( $q_topic_author );
+			$r .= "\t<div><label for=\"topic-author\">" . __('Topic author') . "</label>\n";
+			$r .= "\t\t<div><input name='topic_author' id='topic-author' type='text' class='text-input' value='$q_topic_author' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $post_author ) {
+			$q_post_author = esc_attr( $q_post_author );
+			$r .= "\t<div><label for=\"post-author\">" . __('Post author') . "</label>\n";
+			$r .= "\t\t<div><input name='post_author' id='post-author' type='text' class='text-input' value='$q_post_author' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		$stati = apply_filters( 'bb_query_form_post_status', array( 'all' => _x( 'All', 'post status' ), '0' => __('Normal'), '1' => __('Deleted') ), $this->type );
+
+		if ( $topic_status ) {
+			$r .= "\t<div><label for=\"topic-status\">" . __('Topic status') . "</label>\n";
+			$r .= "\t\t<div><select name='topic_status' id='topic-status'>\n";
+			foreach ( $stati as $status => $label ) {
+				$selected = (string) $status == (string) $q_topic_status ? " selected='selected'" : '';
+				$r .= "\t\t\t<option value='$status'$selected>$label</option>\n";
+			}
+			$r .= "\t\t</select></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $post_status ) {
+			$r .= "\t<div><label for=\"post-status\">" . __('Post status') . "</label>\n";
+			$r .= "\t\t<div><select name='post_status' id='post-status'>\n";
+			foreach ( $stati as $status => $label ) {
+				$selected = (string) $status == (string) $q_post_status ? " selected='selected'" : '';
+				$r .= "\t\t\t<option value='$status'$selected>$label</option>\n";
+			}
+			$r .= "\t\t</select></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $poster_ip ) {
+			$r .= "\t<div><label for=\"poster-ip\">" . __('Poster IP address') . "</label>\n";
+			$r .= "\t\t<div><input name='poster_ip' id='poster-ip' type='text' class='text-input' value='$q_poster_ip' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $open ) {
+			$r .= "\t<div><label for=\"topic-open\">" . __('Open?') . "</label>\n";
+			$r .= "\t\t<div><select name='open' id='topic-open'>\n";
+			foreach ( array( 'all' => _x( 'All', 'posting status' ), '1' => _x( 'Open', 'posting status' ), '0' => __('Closed') ) as $status => $label ) {
+				$label = esc_html( $label );
+				$selected = (string) $status == (string) $q_open ? " selected='selected'" : '';
+				$r .= "\t\t\t<option value='$status'$selected>$label</option>\n";
+			}
+			$r .= "\t\t</select></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		if ( $topic_title ) {
+			$q_topic_title = esc_attr( $q_topic_title );
+			$r .= "\t<div><label for=\"topic-title\">" . __('Title') . "</label>\n";
+			$r .= "\t\t<div><input name='topic_title' id='topic-title' type='text' class='text-input' value='$q_topic_title' /></div>\n";
+			$r .= "\t</div>\n\n";
+		}
+
+		$r .= apply_filters( 'bb_query_form_inputs', '', $args, $query_vars );
+
+		$r .= "\t<div class=\"submit\"><label for=\"$id-submit\">" . __('Search') . "</label>\n";
+		$r .= "\t\t<div><input type='submit' class='button submit-input' value='$submit' id='$id-submit' /></div>\n";
+		$r .= "\t</div>\n";
+
+		$r .= "\t</fieldset>\n\n";
+
+		do_action( 'bb_query_form', $args, $query_vars );
+
+		$r .= "</form>\n\n";
+
+		echo $r;
+	}
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-taxonomy.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-taxonomy.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-taxonomy.php	(revision 5057)
@@ -0,0 +1,329 @@
+<?php
+/**
+ * Taxonomy API
+ *
+ * @package bbPress
+ * @subpackage Taxonomy
+ * @since 1.0
+ * @todo cache
+ */
+class BB_Taxonomy extends WP_Taxonomy
+{
+	/**
+	 * Retrieve object_ids of valid taxonomy and term.
+	 *
+	 * The strings of $taxonomies must exist before this function will continue. On
+	 * failure of finding a valid taxonomy, it will return an WP_Error class, kind
+	 * of like Exceptions in PHP 5, except you can't catch them. Even so, you can
+	 * still test for the WP_Error class and get the error message.
+	 *
+	 * The $terms aren't checked the same as $taxonomies, but still need to exist
+	 * for $object_ids to be returned.
+	 *
+	 * It is possible to change the order that object_ids is returned by either
+	 * using PHP sort family functions or using the database by using $args with
+	 * either ASC or DESC array. The value should be in the key named 'order'.
+	 *
+	 * @package bbPress
+	 * @subpackage Taxonomy
+	 * @since 1.0
+	 *
+	 * @uses wp_parse_args() Creates an array from string $args.
+	 *
+	 * @param string|array $terms String of term or array of string values of terms that will be used
+	 * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names
+	 * @param array|string $args Change the order of the object_ids, either ASC or DESC
+	 * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success
+	 *	the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
+	 */
+	function get_objects_in_term( $terms, $taxonomies, $args = null ) {
+		if ( !is_array($terms) )
+			$terms = array($terms);
+
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			if ( !$this->is_taxonomy($taxonomy) )
+				return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+		}
+
+		$defaults = array('order' => 'ASC', 'field' => 'term_id', 'user_id' => 0);
+		$args = wp_parse_args( $args, $defaults );
+		extract($args, EXTR_SKIP);
+
+		if ( 'tt_id' == $field )
+			$field = 'tt.term_taxonomy_id';
+		else
+			$field = 'tt.term_id';
+
+		$order = ( 'desc' == strtolower($order) ) ? 'DESC' : 'ASC';
+		$user_id = (int) $user_id;
+
+		$terms = array_map('intval', $terms);
+
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
+		$terms = "'" . implode("', '", $terms) . "'";
+
+		$sql = "SELECT tr.object_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND $field IN ($terms)";
+		if ( $user_id )
+			$sql .= " AND tr.user_id = '$user_id'";
+		$sql .= " ORDER BY tr.object_id $order";
+
+		$object_ids = $this->db->get_col( $sql );
+
+		if ( ! $object_ids )
+			return array();
+
+		return $object_ids;
+	}
+
+	/**
+	 * Will unlink the term from the taxonomy.
+	 *
+	 * Will remove the term's relationship to the taxonomy, not the term or taxonomy
+	 * itself. The term and taxonomy will still exist. Will require the term's
+	 * object ID to perform the operation.
+	 *
+	 * @package bbPress
+	 * @subpackage Taxonomy
+	 * @since 1.0
+	 *
+	 * @param int $object_id The term Object Id that refers to the term
+	 * @param string|array $taxonomy List of Taxonomy Names or single Taxonomy name.
+	 * @param int $user_id The ID of the user who created the relationship.
+	 */
+	function delete_object_term_relationships( $object_id, $taxonomies, $user_id = 0 ) {
+		$object_id = (int) $object_id;
+		$user_id = (int) $user_id;
+
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			$terms = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'user_id' => $user_id));
+			$in_terms = "'" . implode("', '", $terms) . "'";
+			$sql = "DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_terms)";
+			if ( $user_id )
+				$sql .= " AND user_id = %d";
+			$this->db->query( $this->db->prepare( $sql, $object_id, $user_id ) );
+			$this->update_term_count($terms, $taxonomy);
+		}
+	}
+
+	/**
+	 * Retrieves the terms associated with the given object(s), in the supplied taxonomies.
+	 *
+	 * The following information has to do the $args parameter and for what can be
+	 * contained in the string or array of that parameter, if it exists.
+	 *
+	 * The first argument is called, 'orderby' and has the default value of 'name'.
+	 * The other value that is supported is 'count'.
+	 *
+	 * The second argument is called, 'order' and has the default value of 'ASC'.
+	 * The only other value that will be acceptable is 'DESC'.
+	 *
+	 * The final argument supported is called, 'fields' and has the default value of
+	 * 'all'. There are multiple other options that can be used instead. Supported
+	 * values are as follows: 'all', 'ids', 'names', and finally
+	 * 'all_with_object_id'.
+	 *
+	 * The fields argument also decides what will be returned. If 'all' or
+	 * 'all_with_object_id' is choosen or the default kept intact, then all matching
+	 * terms objects will be returned. If either 'ids' or 'names' is used, then an
+	 * array of all matching term ids or term names will be returned respectively.
+	 *
+	 * @package bbPress
+	 * @subpackage Taxonomy
+	 * @since 1.0
+	 *
+	 * @param int|array $object_id The id of the object(s) to retrieve.
+	 * @param string|array $taxonomies The taxonomies to retrieve terms from.
+	 * @param array|string $args Change what is returned
+	 * @return array|WP_Error The requested term data or empty array if no terms found. WP_Error if $taxonomy does not exist.
+	 */
+	function get_object_terms($object_ids, $taxonomies, $args = array()) {
+		if ( !is_array($taxonomies) )
+			$taxonomies = array($taxonomies);
+
+		foreach ( (array) $taxonomies as $taxonomy ) {
+			if ( !$this->is_taxonomy($taxonomy) )
+				return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+		}
+
+		if ( !is_array($object_ids) )
+			$object_ids = array($object_ids);
+		$object_ids = array_map('intval', $object_ids);
+
+		$defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all', 'user_id' => 0);
+		$args = wp_parse_args( $args, $defaults );
+		$args['user_id'] = (int) $args['user_id'];
+
+		$terms = array();
+		if ( count($taxonomies) > 1 ) {
+			foreach ( $taxonomies as $index => $taxonomy ) {
+				$t = $this->get_taxonomy($taxonomy);
+				if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) {
+					unset($taxonomies[$index]);
+					$terms = array_merge($terms, $this->get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
+				}
+			}
+		} else {
+			$t = $this->get_taxonomy($taxonomies[0]);
+			if ( isset($t->args) && is_array($t->args) )
+				$args = array_merge($args, $t->args);
+		}
+
+		extract($args, EXTR_SKIP);
+		$user_id = (int) $user_id;
+
+		if ( 'count' == $orderby )
+			$orderby = 'tt.count';
+		else if ( 'name' == $orderby )
+			$orderby = 't.name';
+		else if ( 'slug' == $orderby )
+			$orderby = 't.slug';
+		else if ( 'term_group' == $orderby )
+			$orderby = 't.term_group';
+		else if ( 'term_order' == $orderby )
+			$orderby = 'tr.term_order';
+		else if ( 'none' == $orderby ) {
+			$orderby = '';
+			$order = '';
+		} else {
+			$orderby = 't.term_id';
+		}
+
+		// tt_ids queries can only be none or tr.term_taxonomy_id
+		if ( ('tt_ids' == $fields) && !empty($orderby) )
+			$orderby = 'tr.term_taxonomy_id';
+
+		if ( !empty($orderby) )
+			$orderby = "ORDER BY $orderby";
+
+		$taxonomies = "'" . implode("', '", $taxonomies) . "'";
+		$object_ids = implode(', ', $object_ids);
+
+		$select_this = '';
+		if ( 'all' == $fields )
+			$select_this = 't.*, tt.*, tr.user_id';
+		else if ( 'ids' == $fields )
+			$select_this = 't.term_id';
+		else if ( 'names' == $fields )
+			$select_this = 't.name';
+		else if ( 'all_with_object_id' == $fields )
+			$select_this = 't.*, tt.*, tr.user_id, tr.object_id';
+
+		$query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON tt.term_id = t.term_id INNER JOIN {$this->db->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids)";
+		if ( $user_id )
+			$query .= " AND user_id = '$user_id'";
+		$query .= " $orderby $order";
+
+		if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
+			$terms = array_merge($terms, $this->db->get_results($query));
+			$this->update_term_cache($terms);
+		} else if ( 'ids' == $fields || 'names' == $fields ) {
+			$terms = array_merge($terms, $this->db->get_col($query));
+		} else if ( 'tt_ids' == $fields ) {
+			$query = "SELECT tr.term_taxonomy_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies)";
+			if ( $user_id )
+				$query .= " AND tr.user_id = '$user_id'";
+			$query .= " $orderby $order";
+			$terms = $this->db->get_col( $query );
+		}
+
+		if ( ! $terms )
+			$terms = array();
+
+		return apply_filters('wp_get_object_terms', $terms, $object_ids, $taxonomies, $args);
+	}
+
+	/**
+	 * Create Term and Taxonomy Relationships.
+	 *
+	 * Relates an object (post, link etc) to a term and taxonomy type. Creates the
+	 * term and taxonomy relationship if it doesn't already exist. Creates a term if
+	 * it doesn't exist (using the slug).
+	 *
+	 * A relationship means that the term is grouped in or belongs to the taxonomy.
+	 * A term has no meaning until it is given context by defining which taxonomy it
+	 * exists under.
+	 *
+	 * @package bbPress
+	 * @subpackage Taxonomy
+	 * @since 1.0
+	 *
+	 * @param int $object_id The object to relate to.
+	 * @param array|int|string $term The slug or id of the term, will replace all existing
+	 * related terms in this taxonomy.
+	 * @param array|string $taxonomy The context in which to relate the term to the object.
+	 * @param bool $append If false will delete difference of terms.
+	 * @return array|WP_Error Affected Term IDs
+	 */
+	function set_object_terms($object_id, $terms, $taxonomy, $args = null) {
+		$object_id = (int) $object_id;
+
+		$defaults = array( 'append' => false, 'user_id' => 0 );
+		if ( is_scalar( $args ) )
+			$args = array( 'append' => (bool) $args );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+		if ( !$user_id = (int) $user_id )
+			return new WP_Error('invalid_user_id', __('Invalid User ID'));
+
+		if ( !$this->is_taxonomy($taxonomy) )
+			return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
+
+		if ( !is_array($terms) )
+			$terms = array($terms);
+
+		if ( ! $append )
+			$old_tt_ids = $this->get_object_terms($object_id, $taxonomy, array('user_id' => $user_id, 'fields' => 'tt_ids', 'orderby' => 'none'));
+
+		$tt_ids = array();
+		$term_ids = array();
+
+		foreach ( (array) $terms as $term ) {
+			if ( !strlen(trim($term)) )
+				continue;
+
+			if ( !$id = $this->is_term($term, $taxonomy) )
+				$id = $this->insert_term($term, $taxonomy);
+			if ( is_wp_error($id) )
+				return $id;
+			$term_ids[] = $id['term_id'];
+			$id = $id['term_taxonomy_id'];
+			$tt_ids[] = $id;
+
+			if ( $this->db->get_var( $this->db->prepare( "SELECT term_taxonomy_id FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d AND user_id = %d", $object_id, $id, $user_id ) ) )
+				continue;
+			$this->db->insert( $this->db->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $id, 'user_id' => $user_id ) );
+		}
+
+		$this->update_term_count($tt_ids, $taxonomy);
+
+		if ( ! $append ) {
+			$delete_terms = array_diff($old_tt_ids, $tt_ids);
+			if ( $delete_terms ) {
+				$in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
+				$this->db->query( $this->db->prepare("DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND user_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id, $user_id) );
+				$this->update_term_count($delete_terms, $taxonomy);
+			}
+		}
+
+		$t = $this->get_taxonomy($taxonomy);
+		if ( ! $append && isset($t->sort) && $t->sort ) {
+			$values = array();
+			$term_order = 0;
+			$final_tt_ids = $this->get_object_terms($object_id, $taxonomy, array( 'user_id' => $user_id, 'fields' => 'tt_ids' ));
+			foreach ( $tt_ids as $tt_id )
+				if ( in_array($tt_id, $final_tt_ids) )
+					$values[] = $this->db->prepare( "(%d, %d, %d, %d)", $object_id, $tt_id, $user_id, ++$term_order);
+			if ( $values )
+				$this->db->query("INSERT INTO {$this->db->term_relationships} (object_id, term_taxonomy_id, user_id, term_order) VALUES " . join(',', $values) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)");
+		}
+
+		do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append);
+		return $tt_ids;
+	}
+} // END class BB_Taxonomy extends WP_Taxonomy
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-walker.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-walker.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bb-walker.php	(revision 5057)
@@ -0,0 +1,253 @@
+<?php
+
+class BB_Walker {
+	var $tree_type;
+	var $db_fields;
+
+	//abstract callbacks
+	function start_lvl($output) { return $output; }
+	function end_lvl($output)   { return $output; }
+	function start_el($output)  { return $output; }
+	function end_el($output)    { return $output; }
+
+	function _init() {
+		$this->parents = array();
+		$this->depth = 1;
+		$this->previous_element = '';
+	}		
+
+	function walk($elements, $to_depth) {
+		$args = array_slice(func_get_args(), 2);
+		$output = '';
+
+		// padding at the end
+		$last_element->{$this->db_fields['parent']} = 0;
+		$last_element->{$this->db_fields['id']} = 0;
+		$elements[] = $last_element;
+
+		$flat = (-1 == $to_depth) ? true : false;
+		foreach ( $elements as $element )
+			$output .= call_user_func_array( array(&$this, 'step'), array_merge( array($element, $to_depth), $args ) );
+
+		return $output;
+	}
+
+	function step( $element, $to_depth ) {
+		if ( !isset($this->depth) )
+			$this->_init();
+
+		$args = array_slice(func_get_args(), 2);
+		$id_field = $this->db_fields['id'];
+		$parent_field = $this->db_fields['parent'];
+
+		$flat = (-1 == $to_depth) ? true : false;
+
+		$output = '';
+
+		// If flat, start and end the element and skip the level checks.
+		if ( $flat ) {
+			// Start the element.
+			if ( isset($element->$id_field) && $element->$id_field != 0 ) {
+				$cb_args = array_merge( array(&$output, $element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'start_el'), $cb_args);
+			}
+
+			// End the element.
+			if ( isset($element->$id_field) && $element->$id_field != 0 ) {
+				$cb_args = array_merge( array(&$output, $element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'end_el'), $cb_args);
+			}
+
+			return;
+		}
+
+		// Walk the tree.
+		if ( !empty($element) && !empty($this->previous_element) && $element->$parent_field == $this->previous_element->$id_field ) {
+			// Previous element is my parent. Descend a level.
+			array_unshift($this->parents, $this->previous_element);
+			if ( !$to_depth || ($this->depth < $to_depth) ) { //only descend if we're below $to_depth
+				$cb_args = array_merge( array(&$output, $this->depth), $args);
+				call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
+			} else if ( $to_depth && $this->depth == $to_depth  ) {  // If we've reached depth, end the previous element.
+				$cb_args = array_merge( array(&$output, $this->previous_element, $this->depth), $args);
+				call_user_func_array(array(&$this, 'end_el'), $cb_args);
+			}
+			$this->depth++; //always do this so when we start the element further down, we know where we are
+		} else if ( !empty($element) && !empty($this->previous_element) && $element->$parent_field == $this->previous_element->$parent_field) {
+			// On the same level as previous element.
+			if ( !$to_depth || ($this->depth <= $to_depth) ) {
+				$cb_args = array_merge( array(&$output, $this->previous_element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'end_el'), $cb_args);
+			}
+		} else if ( $this->depth > 1 ) {
+			// Ascend one or more levels.
+			if ( !$to_depth || ($this->depth <= $to_depth) ) {
+				$cb_args = array_merge( array(&$output, $this->previous_element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'end_el'), $cb_args);
+			}
+
+			while ( $parent = array_shift($this->parents) ) {
+				$this->depth--;
+				if ( !$to_depth || ($this->depth < $to_depth) ) {
+					$cb_args = array_merge( array(&$output, $this->depth), $args);
+					call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
+					$cb_args = array_merge( array(&$output, $parent, $this->depth - 1), $args);
+					call_user_func_array(array(&$this, 'end_el'), $cb_args);
+				}
+				if ( !empty($element) && isset($this->parents[0]) && $element->$parent_field == $this->parents[0]->$id_field ) {
+					break;
+				}
+			}
+		} else if ( !empty($this->previous_element) ) {
+			// Close off previous element.
+			if ( !$to_depth || ($this->depth <= $to_depth) ) {
+				$cb_args = array_merge( array(&$output, $this->previous_element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'end_el'), $cb_args);
+			}
+		}
+
+		// Start the element.
+		if ( !$to_depth || ($this->depth <= $to_depth) ) {
+			if ( !empty($element) && $element->$id_field != 0 ) {
+				$cb_args = array_merge( array(&$output, $element, $this->depth - 1), $args);
+				call_user_func_array(array(&$this, 'start_el'), $cb_args);
+			}
+		}
+
+		$this->previous_element = $element;
+		return $output;
+	}
+}
+
+class BB_Walker_Blank extends BB_Walker { // Used for template functions
+	var $tree_type;
+	var $db_fields = array( 'id' => '', 'parent' => '' );
+
+	var $start_lvl = '';
+	var $end_lvl   = '';
+
+	//abstract callbacks
+	function start_lvl( $output, $depth ) { 
+		if ( !$this->start_lvl )
+			return '';
+		$indent = str_repeat("\t", $depth);
+		$output .= $indent . "$this->start_lvl\n";
+		return $output;
+	}
+
+	function end_lvl( $output, $depth )   {
+		if ( !$this->end_lvl )
+			return '';
+		$indent = str_repeat("\t", $depth);
+		$output .= $indent . "$this->end_lvl\n";
+		return $output;
+	}
+
+	function start_el()  { return ''; }
+	function end_el()    { return ''; }
+}
+
+class BB_Loop {
+	var $elements;
+	var $walker;
+	var $_preserve = array();
+	var $_looping = false;
+
+	function &start( $elements, $walker = 'BB_Walker_Blank' ) {
+		$null = null;
+		$a = new BB_Loop( $elements );
+		if ( !$a->elements )
+			return $null;
+		$a->walker = new $walker;
+		return $a;
+	}
+
+	function BB_Loop( &$elements ) {
+		$this->elements = $elements;
+		if ( !is_array($this->elements) || empty($this->elements) )
+			return $this->elements = false;
+	}
+
+	function step() {
+		if ( !is_array($this->elements) || !current($this->elements) || !is_object($this->walker) )
+			return false;
+
+		if ( !$this->_looping ) {
+			$r = reset($this->elements);
+			$this->_looping = true;
+		} else {
+			$r = next($this->elements);
+		}
+
+		if ( !$args = func_get_args() )
+			$args = array( 0 );
+		echo call_user_func_array( array(&$this->walker, 'step'), array_merge(array(current($this->elements)), $args) );
+		return $r;
+	}
+
+	function pad( $pad, $offset = 0 ) {
+		if ( !is_array($this->elements) || !is_object($this->walker) )
+			return false;
+
+		if ( is_numeric($pad) )
+			return $pad * ($this->walker->depth - 1) + (int) $offset;
+
+		return str_repeat( $pad, $this->walker->depth - 1 );
+	}
+
+	function preserve( $array ) {
+		if ( !is_array( $array ) )
+			return false;
+
+		foreach ( $array as $key )
+			$this->_preserve[$key] = isset( $GLOBALS[$key] ) ? $GLOBALS[$key] : null;
+	}
+
+	function reinstate() {
+		foreach ( $this->_preserve as $key => $value )
+			$GLOBALS[$key] = $value;
+	}
+
+	function classes( $output = 'string' ) {
+		if ( !is_array($this->elements) || !is_object($this->walker) )
+			return false;
+		$classes = array();
+
+		$current = current($this->elements);
+
+		if ( $prev = prev($this->elements) )
+			next($this->elements);
+		else		
+			reset($this->elements);
+
+		if ( $next = next($this->elements) )
+			prev($this->elements);
+		else
+			end($this->elements);
+
+		if ( !empty($next) && $next->{$this->walker->db_fields['parent']} == $current->{$this->walker->db_fields['id']} )
+			$classes[] = 'bb-parent';
+		elseif ( !empty($next) && $next->{$this->walker->db_fields['parent']} == $current->{$this->walker->db_fields['parent']} )
+			$classes[] = 'bb-precedes-sibling';
+		else
+			$classes[] = 'bb-last-child';
+
+		if ( !empty($prev) && $current->{$this->walker->db_fields['parent']} == $prev->{$this->walker->db_fields['id']} )
+			$classes[] = 'bb-first-child';
+		elseif ( !empty($prev) && $current->{$this->walker->db_fields['parent']} == $prev->{$this->walker->db_fields['parent']} )
+			$classes[] = 'bb-follows-sibling';
+		elseif ( $prev )
+			$classes[] = 'bb-follows-niece';
+
+		if ( $this->walker->depth > 1 )
+			$classes[] = 'bb-child';
+		else
+			$classes[] = 'bb-root';
+
+		if ( $output === 'string' )
+			$classes = join(' ', $classes);
+
+		return $classes;
+	}
+
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bp-options.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bp-options.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/class.bp-options.php	(revision 5057)
@@ -0,0 +1,94 @@
+<?php
+/**
+ * BP_Options allows storage of options for BackPress
+ * in the bbPress database
+ *
+ * @see BP_Options_Interface
+ * @package bbPress
+ */
+class BP_Options
+{
+	function prefix()
+	{
+		return 'bp_bbpress_';
+	}
+	
+	function get( $option )
+	{
+		switch ( $option ) {
+			case 'application_id':
+				return bb_get_option( 'site_id' );
+				break;
+			case 'application_uri':
+				return bb_get_uri( null, null, BB_URI_CONTEXT_NONE );
+				break;
+			case 'cron_uri':
+				return bb_get_uri( 'bb-cron.php', array( 'check' => BP_Options::get( 'cron_check' ) ), BB_URI_CONTEXT_WP_HTTP_REQUEST );
+				break;
+			case 'wp_http_version':
+				return 'bbPress/' . bb_get_option( 'version' );
+				break;
+			case 'hash_function_name':
+				return 'bb_hash';
+				break;
+			case 'language_locale':
+				return bb_get_locale();
+				break;
+			case 'language_directory':
+				return BB_LANG_DIR;
+				break;
+			case 'charset':
+			case 'gmt_offset':
+			case 'timezone_string':
+				return bb_get_option( $option );
+				break;
+			default:
+				return bb_get_option( BP_Options::prefix() . $option );
+				break;
+		}
+	}
+	
+	function add( $option, $value )
+	{
+		return BP_Options::update( $option, $value );
+	}
+	
+	function update( $option, $value )
+	{
+		return bb_update_option( BP_Options::prefix() . $option, $value );
+	}
+	
+	function delete( $option )
+	{
+		return bb_delete_option( BP_Options::prefix() . $option );
+	}
+} // END class BP_Options
+
+/**
+ * Allows storage of transients for BackPress
+ *
+ * @see BP_Transients_Interface
+ * @package bbPress
+ */
+class BP_Transients
+{
+	function prefix()
+	{
+		return 'bp_bbpress_';
+	}
+	
+	function get( $transient )
+	{
+		return bb_get_transient( BP_Transients::prefix() . $transient );
+	}
+	
+	function set( $transient, $value, $expiration = 0 )
+	{
+		return bb_set_transient( BP_Transients::prefix() . $transient, $value, $expiration );
+	}
+	
+	function delete( $transient )
+	{
+		return bb_delete_transient( BP_Transients::prefix() . $transient );
+	}
+} // END class BP_Transients
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/defaults.bb-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/defaults.bb-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/defaults.bb-filters.php	(revision 5057)
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Sets up the default filters and actions for most
+ * of the bbPress hooks.
+ *
+ * If you need to remove a default hook, this file will
+ * give you the priority for which to use to remove the
+ * hook.
+ *
+ * Not all of the default hooks are found in this files
+ *
+ * @package bbPress
+ */
+
+// Strip, trim, kses, special chars for string saves
+$filters = array( 'pre_term_name', 'bb_pre_forum_name', 'pre_topic_title' );
+foreach ( $filters as $filter ) {
+	add_filter( $filter, 'strip_tags' );
+	add_filter( $filter, 'trim' );
+	add_filter( $filter, 'bb_filter_kses' );
+	add_filter( $filter, 'esc_html', 30 );
+}
+
+// Kses only for textarea saves
+$filters = array( 'pre_term_description', 'bb_pre_forum_desc' );
+foreach ( $filters as $filter ) {
+	add_filter( $filter, 'bb_filter_kses' );
+}
+
+// Slugs
+add_filter( 'pre_term_slug', 'bb_pre_term_slug' );
+add_filter( 'editable_slug', 'urldecode');
+
+// DB truncations
+add_filter( 'pre_topic_title', 'bb_trim_for_db_150', 9999 );
+add_filter( 'bb_pre_forum_name', 'bb_trim_for_db_150', 9999 );
+add_filter( 'pre_term_name', 'bb_trim_for_db_55', 9999 );
+
+// Format Strings for Display
+$filters = array( 'forum_name', 'topic_title', 'bb_title', 'bb_option_name' );
+foreach ( $filters as $filter ) {
+	add_filter( $filter, 'esc_html' );
+}
+
+// Numbers
+$filters = array( 'forum_topics', 'forum_posts', 'total_posts', 'total_users', 'total_topics' );
+foreach ( $filters as $filter ) {
+	add_filter( $filter, 'bb_number_format_i18n' );
+}
+
+// Offset Times
+$filters = array( 'topic_time', 'topic_start_time', 'bb_post_time' );
+foreach ( $filters as $filter ) {
+	add_filter( $filter, 'bb_offset_time', 10, 2 );
+}
+
+add_filter('bb_topic_labels', 'bb_closed_label', 10);
+add_filter('bb_topic_labels', 'bb_sticky_label', 20);
+
+add_filter('pre_post', 'trim');
+add_filter('pre_post', 'bb_encode_bad');
+add_filter('pre_post', 'bb_code_trick');
+add_filter('pre_post', 'force_balance_tags');
+add_filter('pre_post', 'bb_filter_kses', 50);
+add_filter('pre_post', 'bb_autop', 60);
+
+add_filter('post_text', 'do_shortcode');
+
+function bb_contextualise_search_post_text()
+{
+	if ( bb_is_search() ) {
+		add_filter( 'get_post_text', 'bb_post_text_context' );
+	}
+}
+add_action( 'bb_init', 'bb_contextualise_search_post_text' );
+
+add_filter('post_text', 'make_clickable');
+
+add_filter('edit_text', 'bb_code_trick_reverse');
+add_filter('edit_text', 'wp_specialchars');
+add_filter('edit_text', 'trim', 15);
+
+add_filter('pre_sanitize_with_dashes', 'bb_pre_sanitize_with_dashes_utf8', 10, 3 );
+
+add_filter('get_user_link', 'bb_fix_link');
+
+add_filter('sanitize_profile_info', 'esc_html');
+add_filter('sanitize_profile_admin', 'esc_html');
+
+add_filter( 'get_recent_user_replies_fields', 'bb_get_recent_user_replies_fields' );
+add_filter( 'get_recent_user_replies_group_by', 'bb_get_recent_user_replies_group_by' );
+
+add_filter('sort_tag_heat_map', 'bb_sort_tag_heat_map');
+
+// URLS
+
+if ( !bb_get_option( 'mod_rewrite' ) ) {
+	add_filter( 'bb_stylesheet_uri', 'esc_attr', 1, 9999 );
+	add_filter( 'forum_link', 'esc_attr', 1, 9999 );
+	add_filter( 'bb_forum_posts_rss_link', 'esc_attr', 1, 9999 );
+	add_filter( 'bb_forum_topics_rss_link', 'esc_attr', 1, 9999 );
+	add_filter( 'bb_tag_link', 'esc_attr', 1, 9999 );
+	add_filter( 'tag_rss_link', 'esc_attr', 1, 9999 );
+	add_filter( 'topic_link', 'esc_attr', 1, 9999 );
+	add_filter( 'topic_rss_link', 'esc_attr', 1, 9999 );
+	add_filter( 'post_link', 'esc_attr', 1, 9999 );
+	add_filter( 'post_anchor_link', 'esc_attr', 1, 9999 );
+	add_filter( 'user_profile_link', 'esc_attr', 1, 9999 );
+	add_filter( 'profile_tab_link', 'esc_attr', 1, 9999 );
+	add_filter( 'favorites_link', 'esc_attr', 1, 9999 );
+	add_filter( 'view_link', 'esc_attr', 1, 9999 );
+}
+
+// Feed Stuff
+
+function bb_filter_feed_content()
+{
+	if ( bb_is_feed() ) {
+		add_filter( 'bb_title_rss', 'strip_tags' );
+		add_filter( 'bb_title_rss', 'ent2ncr', 8 );
+		add_filter( 'bb_title_rss', 'esc_html' );
+
+		add_filter( 'bb_description_rss', 'strip_tags' );
+		add_filter( 'bb_description_rss', 'ent2ncr', 8 );
+		add_filter( 'bb_description_rss', 'esc_html' );
+
+		add_filter( 'post_author', 'ent2ncr', 8 );
+		add_filter( 'post_link', 'esc_html' );
+		add_filter( 'post_text', 'ent2ncr', 8 );
+		add_filter( 'post_text', 'bb_convert_chars' );
+	}
+}
+add_action( 'bb_init', 'bb_filter_feed_content' );
+
+add_action( 'init_roles', 'bb_init_roles' );
+add_filter( 'map_meta_cap', 'bb_map_meta_cap', 1, 4 );
+
+// Actions
+
+add_action( 'bb_head', 'bb_generator' );
+add_action('bb_head', 'bb_template_scripts');
+add_action('bb_head', 'wp_print_scripts');
+add_action('bb_head', 'wp_print_styles');
+add_action('bb_head', 'bb_rsd_link');
+add_action('bb_head', 'bb_pingback_link');
+if ( $bb_log->type === 'console' ) {
+	add_action('bb_head', array(&$bb_log, 'console_javascript'));
+	add_action('bb_admin_head', array(&$bb_log, 'console_javascript'));
+}
+add_action('bb_send_headers', 'bb_pingback_header');
+add_action('bb_admin_print_scripts', 'wp_print_scripts');
+
+add_action('bb_user_has_no_caps', 'bb_give_user_default_role');
+
+add_action('do_pingbacks', array('BB_Pingbacks', 'send_all'), 10, 1);
+
+add_action( 'bb_init', 'bb_register_default_views' );
+
+add_action( 'set_current_user', 'bb_apply_wp_role_map_to_user' );
+
+add_filter( 'bb_pre_get_option_gmt_offset', 'wp_timezone_override_offset' );
+
+// Subscriptions
+
+if ( bb_is_subscriptions_active() ) {
+	add_action( 'bb_new_post', 'bb_notify_subscribers' );
+	add_action( 'bb_insert_post', 'bb_user_subscribe_checkbox_update' );
+	add_action( 'topicmeta', 'bb_user_subscribe_link' );
+	add_action( 'edit_form', 'bb_user_subscribe_checkbox' ); 
+	add_action( 'post_form', 'bb_user_subscribe_checkbox' );
+}
+
+add_action( 'bb_post-form.php', 'bb_anonymous_post_form' );
+
+unset( $filters, $filter );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-capabilities.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-capabilities.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-capabilities.php	(revision 5057)
@@ -0,0 +1,348 @@
+<?php
+/**
+ * bbPress Roles and Capabilities Wrapping Functions.
+ *
+ * @package bbPress
+ * @subpackage User
+ */
+
+
+
+/**
+ * Whether current user has capability or role.
+ *
+ * @since 0.7.2
+ * @uses $bb_current_user Current User Object
+ *
+ * @param string $capability Capability or role name.
+ * @return bool
+ */
+function bb_current_user_can($capability) {
+	global $bb_current_user;
+
+	$args = array_slice(func_get_args(), 1);
+	$args = array_merge(array($capability), $args);
+
+	if ( empty($bb_current_user) ) {
+		$retvalue = false;
+		if ( ( $capability == 'write_topic' || $capability == 'write_topics' ) && !bb_is_login_required() )
+			$retvalue = true;
+	} else {
+		$retvalue = call_user_func_array(array(&$bb_current_user, 'has_cap'), $args);
+	}
+	
+	// Use bb_user_has_cap whenever possible!  This will not work everywhere.
+	return apply_filters('bb_current_user_can', $retvalue, $capability, $args);
+}
+
+/**
+ * Give a user the default role
+ *
+ * @since 0.7.2
+ *
+ * @param BP_User $user User object to give default role to
+ */
+function bb_give_user_default_role( $user ) {
+	if ( !( is_object($user) && is_a($user, 'BP_User') ) )
+		return;
+	$user->set_role('member');
+}
+
+/**
+ * Setup all default roles and associate them with capabilities
+ *
+ * @since 0.7.2
+ *
+ * @param BP_Roles $roles Roles object to add default roles to
+ */
+function bb_init_roles( &$roles ) {
+	$roles->add_role( 'keymaster', __('Key Master'), array(
+		'use_keys' => true,				// Verb forms of roles - keymaster
+		'administrate' => true,			// administrator
+		'moderate' => true, 			// moderator
+		'participate' => true,			// member
+
+		'keep_gate' => true,			// Make new Key Masters		//+
+		'import_export' => true,		// Import and export data	//+
+		'recount' => true,				// bb-do-counts.php			//+
+		'manage_options' => true,		// backend					//+
+		'manage_themes' => true,		// Themes					//+
+		'manage_plugins' => true,		// Plugins					//+
+		'manage_options' => true,		// Options					//+
+		'edit_users' => true,
+		'manage_tags' => true,			// Rename, Merge, Destroy
+		'edit_others_favorites' => true,
+		'manage_forums' => true,		// Add/Rename forum
+		'delete_forums' => true,		// Delete forum
+		'delete_topics' => true,
+		'close_topics' => true,
+		'stick_topics' => true,
+		'move_topics' => true,
+		'view_by_ip' => true,			// view-ip.php
+		'edit_closed' => true,			// Edit closed topics
+		'edit_deleted' => true,			// Edit deleted topics/posts
+		'browse_deleted' => true,		// Use 'deleted' view
+		'edit_others_tags' => true,
+		'edit_others_topics' => true,
+		'delete_posts' => true,
+		'throttle' => true,				// Post back to back arbitrarily quickly
+		'ignore_edit_lock' => true,
+		'edit_others_posts' => true,
+		'edit_favorites' => true,
+		'edit_tags' => true,
+		'edit_topics' => true,			// Edit title, resolution status
+		'edit_posts' => true,
+		'edit_profile' => true,
+		'write_topics' => true,
+		'write_posts' => true,
+		'change_password' => true,
+		'read' => true
+	) );
+
+	$roles->add_role( 'administrator', __('Administrator'), array(
+		'administrate' => true,
+		'moderate' => true,
+		'participate' => true,
+
+		'edit_users' => true,				//+
+		'edit_others_favorites' => true,	//+
+		'manage_forums' => true,			//+
+		'delete_forums' => true,			//+
+		'manage_tags' => true,
+		'delete_topics' => true,
+		'close_topics' => true,
+		'stick_topics' => true,
+		'move_topics' => true,
+		'view_by_ip' => true,
+		'edit_closed' => true,
+		'edit_deleted' => true,
+		'browse_deleted' => true,
+		'edit_others_tags' => true,
+		'edit_others_topics' => true,
+		'delete_posts' => true,
+		'throttle' => true,
+		'ignore_edit_lock' => true,
+		'edit_others_posts' => true,
+		'edit_favorites' => true,
+		'edit_tags' => true,
+		'edit_topics' => true,
+		'edit_posts' => true,
+		'edit_profile' => true,
+		'write_topics' => true,
+		'write_posts' => true,
+		'change_password' => true,
+		'read' => true
+	) );
+
+	$roles->add_role( 'moderator', __('Moderator'), array(
+		'moderate' => true,
+		'participate' => true,
+
+		'manage_tags' => true,			//+
+		'delete_topics' => true,		//+
+		'close_topics' => true,			//+
+		'stick_topics' => true,			//+
+		'move_topics' => true,			//+
+		'view_by_ip' => true,			//+
+		'edit_closed' => true,			//+
+		'edit_deleted' => true,			//+
+		'browse_deleted' => true,		//+
+		'edit_others_tags' => true,		//+
+		'edit_others_topics' => true,	//+
+		'delete_posts' => true,			//+
+		'throttle' => true,				//+
+		'ignore_edit_lock' => true,		//+
+		'edit_others_posts' => true,	//+
+		'edit_favorites' => true,
+		'edit_tags' => true,
+		'edit_topics' => true,
+		'edit_posts' => true,
+		'edit_profile' => true,
+		'write_topics' => true,
+		'write_posts' => true,
+		'change_password' => true,
+		'read' => true
+	) );
+
+
+	$roles->add_role( 'member', __('Member'), array(
+		'participate' => true,
+
+		'edit_favorites' => true,
+		'edit_tags' => true,
+		'edit_topics' => true,
+		'edit_posts' => true,
+		'edit_profile' => true,
+		'write_topics' => true,
+		'write_posts' => true,
+		'change_password' => true,
+		'read' => true
+	) );
+
+	$roles->add_role( 'inactive', __('Inactive'), array(
+		'change_password' => true,
+		'read' => true
+	) );
+
+	$roles->add_role( 'blocked', __('Blocked'), array(
+		'not_play_nice' => true // Madness - a negative capability.  Don't try this at home.
+	) );
+}
+
+/**
+ * Map meta capabilities to primitive capabilities.
+ *
+ * This does not actually compare whether the user ID has the actual capability,
+ * just what the capability or capabilities are. Meta capability list value can
+ * be 'delete_user', 'edit_user', 'delete_post', 'delete_page', 'edit_post',
+ * 'edit_page', 'read_post', or 'read_page'.
+ *
+ * @since 0.7.2
+ *
+ * @param array $caps Previously existing capabilities
+ * @param string $cap Capability name.
+ * @param int $user_id User ID.
+ * @return array Actual capabilities for meta capability.
+ */
+function bb_map_meta_cap( $caps, $cap, $user_id, $args ) {
+	// Unset the meta cap
+	if ( false !== $cap_pos = array_search( $cap, $caps ) )
+		unset( $caps[$cap_pos] );
+
+	switch ( $cap ) {
+		case 'write_post':
+			$caps[] = 'write_posts';
+			break;
+		case 'edit_post':
+			// edit_posts, edit_others_posts, edit_deleted, edit_closed, ignore_edit_lock
+			if ( !$bb_post = bb_get_post( $args[0] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( $user_id == $bb_post->poster_id )
+				$caps[] = 'edit_posts';
+			else
+				$caps[] = 'edit_others_posts';
+			if ( $bb_post->post_status == '1' )
+				$caps[] = 'edit_deleted';
+			if ( !topic_is_open( $bb_post->topic_id ) )
+				$caps[] = 'edit_closed';
+			$post_time = bb_gmtstrtotime( $bb_post->post_time );
+			$curr_time = time() + 1;
+			$edit_lock = bb_get_option( 'edit_lock' );
+			if ( $edit_lock >= 0 && $curr_time - $post_time > $edit_lock * 60 )
+				$caps[] = 'ignore_edit_lock';
+			break;
+		case 'delete_post' :
+			// edit_deleted, delete_posts
+			if ( !$bb_post = bb_get_post( $args[0] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( 0 != $bb_post->post_status )
+				$caps[] = 'edit_deleted';
+			// NO BREAK
+		case 'manage_posts' : // back compat
+			$caps[] = 'delete_posts';
+			break;
+		case 'write_topic':
+			$caps[] = 'write_topics';
+			break;
+		case 'edit_topic':
+			// edit_closed, edit_deleted, edit_topics, edit_others_topics
+			if ( !$topic = get_topic( $args[0] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( !topic_is_open( $args[0]) )
+				$caps[] = 'edit_closed';
+			if ( '1' == $topic->topic_status )
+				$caps[] = 'edit_deleted';
+			if ( $user_id == $topic->topic_poster )
+				$caps[] = 'edit_topics';
+			else
+				$caps[] = 'edit_others_topics';
+			break;
+		case 'move_topic' :
+			$caps[] = 'move_topics';
+			break;
+		case 'stick_topic' :
+			$caps[] = 'stick_topics';
+			break;
+		case 'close_topic' :
+			$caps[] = 'close_topics';
+			break;
+		case 'delete_topic' :
+			$caps[] = 'delete_topics';
+			add_filter( 'get_topic_where', 'bb_no_where', 9999 );
+			if ( !$topic = get_topic( $args[0] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( 0 != $topic->topic_status )
+				$caps[] = 'edit_deleted';
+			remove_filter( 'get_topic_where', 'bb_no_where', 9999 );
+			break;
+		case 'manage_topics' :
+			// back compat
+			$caps[] = 'move_topics';
+			$caps[] = 'stick_topics';
+			$caps[] = 'close_topics';
+			$caps[] = 'delete_topics';
+			break;
+		case 'add_tag_to':
+			// edit_closed, edit_deleted, edit_tags;
+			if ( !$topic = get_topic( $args[0] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( !topic_is_open( $topic->topic_id ) )
+				$caps[] = 'edit_closed';
+			if ( '1' == $topic->topic_status )
+				$caps[] = 'edit_deleted';
+			$caps[] = 'edit_tags';
+			break;
+		case 'edit_tag_by_on':
+			// edit_closed, edit_deleted, edit_tags, edit_others_tags
+			if ( !$topic = get_topic( $args[1] ) ) {
+				$caps[] = 'magically_provide_data_given_bad_input';
+				return $caps;
+			}
+			if ( !topic_is_open( $topic->topic_id ) )
+				$caps[] = 'edit_closed';
+			if ( '1' == $topic->topic_status )
+				$caps[] = 'edit_deleted';
+			if ( $user_id == $args[0] )
+				$caps[] = 'edit_tags';
+			else
+				$caps[] = 'edit_others_tags';
+			break;
+		case 'edit_user':
+			// edit_profile, edit_users;
+			if ( $user_id == $args[0] )
+				$caps[] = 'edit_profile';
+			else
+				$caps[] = 'edit_users';
+			break;
+		case 'edit_favorites_of':
+			// edit_favorites, edit_others_favorites;
+			if ( $user_id == $args[0] )
+				$caps[] = 'edit_favorites';
+			else
+				$caps[] = 'edit_others_favorites';
+			break;
+		case 'delete_forum':
+			$caps[] = 'delete_forums';
+			break;
+		case 'change_user_password':
+			// change_password, edit_users
+			$caps[] = 'change_password';
+			if ( $user_id != $args[0] )
+				$caps[] = 'edit_users';
+			break;
+		default:
+			// If no meta caps match, return the original cap.
+			$caps[] = $cap;
+	}
+	return $caps;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-core.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-core.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-core.php	(revision 5057)
@@ -0,0 +1,1836 @@
+<?php
+/**
+ * Core bbPress functions.
+ *
+ * @package bbPress
+ */
+
+
+
+/**
+ * Initialization functions mostly called in bb-settings.php
+ */
+
+/**
+ * Marks things as deprecated and informs when they have been used.
+ *
+ * @since 0.9
+ *
+ * @param string $type The type of thing that was attempted: function, class::function, constant, variable or page.
+ * @param string $name The thing that was called.
+ * @param string $replacement Optional. The thing that should have been called.
+ * @uses $bb_log BP_Log logging object.
+ */
+function bb_log_deprecated( $type, $name, $replacement = 'none' ) {
+	global $bb_log;
+	$bb_log->notice( sprintf( __( 'Using deprecated bbPress %1$s - %2$s - replace with - %3$s' ), $type, $name, $replacement ) );
+
+	if ( $bb_log->level & BP_LOG_DEBUG && $bb_log->level & BP_LOG_NOTICE ) { // Only compute the location if we're going to log it.
+		$backtrace = debug_backtrace();
+
+		$file = $backtrace[2]['file'];
+
+		if ( substr( $file, 0, strlen( BB_PATH ) - 1 ) == rtrim( BB_PATH, '\\/') )
+			$file = substr( $file, strlen( BB_PATH ) );
+
+		$file = str_replace( '\\', '/', $file );
+
+		// 0 = this function, 1 = the deprecated function
+		$bb_log->notice( '    ' . sprintf( __( 'on line %1$d of file %2$s' ), $backtrace[2]['line'], $file ) );
+	}
+}
+
+/**
+ * Sanitizes user input en-masse.
+ *
+ * @param mixed $array The array of values or a single value to sanitize, usually a global variable like $_GET or $_POST.
+ * @param boolean $trim Optional. Whether to trim the value or not. Default is true.
+ * @return mixed The sanitized data.
+ */
+function bb_global_sanitize( $array, $trim = true )
+{
+	foreach ( $array as $k => $v ) {
+		if ( is_array( $v ) ) {
+			$array[$k] = bb_global_sanitize( $v );
+		} else {
+			if ( !get_magic_quotes_gpc() ) {
+				$array[$k] = addslashes( $v );
+			}
+			if ( $trim ) {
+				$array[$k] = trim( $array[$k] );
+			}
+		}
+	}
+
+	return $array;
+}
+
+/**
+ * Reports whether bbPress is installed by getting forums.
+ *
+ * @return boolean True if there are forums, otherwise false.
+ */
+function bb_is_installed()
+{
+	// Maybe grab all the forums and cache them
+	global $bbdb;
+	$bbdb->suppress_errors();
+	$forums = (array) @bb_get_forums();
+	$bbdb->suppress_errors(false);
+
+	if ( !$forums ) {
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * Sets the required variables to connect to custom user tables.
+ *
+ * @return boolean Always returns true.
+ */
+function bb_set_custom_user_tables()
+{
+	global $bb;
+
+	// Check for older style custom user table
+	if ( !isset( $bb->custom_tables['users'] ) ) { // Don't stomp new setting style
+		if ( $bb->custom_user_table = bb_get_option( 'custom_user_table' ) ) {
+			if ( !isset( $bb->custom_tables ) ) {
+				$bb->custom_tables = array();
+			}
+			$bb->custom_tables['users'] = $bb->custom_user_table;
+		}
+	}
+
+	// Check for older style custom user meta table
+	if ( !isset( $bb->custom_tables['usermeta'] ) ) { // Don't stomp new setting style
+		if ( $bb->custom_user_meta_table = bb_get_option( 'custom_user_meta_table' ) ) {
+			if ( !isset( $bb->custom_tables ) ) {
+				$bb->custom_tables = array();
+			}
+			$bb->custom_tables['usermeta'] = $bb->custom_user_meta_table;
+		}
+	}
+
+	// Check for older style wp_table_prefix
+	if ( $bb->wp_table_prefix = bb_get_option( 'wp_table_prefix' ) ) { // User has set old constant
+		if ( !isset( $bb->custom_tables ) ) {
+			$bb->custom_tables = array(
+				'users'    => $bb->wp_table_prefix . 'users',
+				'usermeta' => $bb->wp_table_prefix . 'usermeta'
+			);
+		} else {
+			if ( !isset( $bb->custom_tables['users'] ) ) { // Don't stomp new setting style
+				$bb->custom_tables['users'] = $bb->wp_table_prefix . 'users';
+			}
+			if ( !isset( $bb->custom_tables['usermeta'] ) ) {
+				$bb->custom_tables['usermeta'] = $bb->wp_table_prefix . 'usermeta';
+			}
+		}
+	}
+
+	if ( bb_get_option( 'wordpress_mu_primary_blog_id' ) ) {
+		$bb->wordpress_mu_primary_blog_id = bb_get_option( 'wordpress_mu_primary_blog_id' );
+	}
+
+	// Check for older style user database
+	if ( !isset( $bb->custom_databases ) ) {
+		$bb->custom_databases = array();
+	}
+	if ( !isset( $bb->custom_databases['user'] ) ) {
+		if ( $bb->user_bbdb_name = bb_get_option( 'user_bbdb_name' ) ) {
+			$bb->custom_databases['user']['name'] = $bb->user_bbdb_name;
+		}
+		if ( $bb->user_bbdb_user = bb_get_option( 'user_bbdb_user' ) ) {
+			$bb->custom_databases['user']['user'] = $bb->user_bbdb_user;
+		}
+		if ( $bb->user_bbdb_password = bb_get_option( 'user_bbdb_password' ) ) {
+			$bb->custom_databases['user']['password'] = $bb->user_bbdb_password;
+		}
+		if ( $bb->user_bbdb_host = bb_get_option( 'user_bbdb_host' ) ) {
+			$bb->custom_databases['user']['host'] = $bb->user_bbdb_host;
+		}
+		if ( $bb->user_bbdb_charset = bb_get_option( 'user_bbdb_charset' ) ) {
+			$bb->custom_databases['user']['charset'] = $bb->user_bbdb_charset;
+		}
+		if ( $bb->user_bbdb_collate = bb_get_option( 'user_bbdb_collate' ) ) {
+			$bb->custom_databases['user']['collate'] = $bb->user_bbdb_collate;
+		}
+		if ( isset( $bb->custom_databases['user'] ) ) {
+			if ( isset( $bb->custom_tables['users'] ) ) {
+				$bb->custom_tables['users'] = array( 'user', $bb->custom_tables['users'] );
+			}
+			if ( isset( $bb->custom_tables['usermeta'] ) ) {
+				$bb->custom_tables['usermeta'] = array( 'user', $bb->custom_tables['usermeta'] );
+			}
+		}
+	}
+
+	return true;
+}
+
+
+/* Pagination */
+
+/**
+ * Retrieve paginated links for pages.
+ *
+ * Technically, the function can be used to create paginated link list for any
+ * area. The 'base' argument is used to reference the url, which will be used to
+ * create the paginated links. The 'format' argument is then used for replacing
+ * the page number. It is however, most likely and by default, to be used on the
+ * archive post pages.
+ *
+ * The 'type' argument controls format of the returned value. The default is
+ * 'plain', which is just a string with the links separated by a newline
+ * character. The other possible values are either 'array' or 'list'. The
+ * 'array' value will return an array of the paginated link list to offer full
+ * control of display. The 'list' value will place all of the paginated links in
+ * an unordered HTML list.
+ *
+ * The 'total' argument is the total amount of pages and is an integer. The
+ * 'current' argument is the current page number and is also an integer.
+ *
+ * An example of the 'base' argument is "http://example.com/all_posts.php%_%"
+ * and the '%_%' is required. The '%_%' will be replaced by the contents of in
+ * the 'format' argument. An example for the 'format' argument is "?page=%#%"
+ * and the '%#%' is also required. The '%#%' will be replaced with the page
+ * number.
+ *
+ * You can include the previous and next links in the list by setting the
+ * 'prev_next' argument to true, which it is by default. You can set the
+ * previous text, by using the 'prev_text' argument. You can set the next text
+ * by setting the 'next_text' argument.
+ *
+ * If the 'show_all' argument is set to true, then it will show all of the pages
+ * instead of a short list of the pages near the current page. By default, the
+ * 'show_all' is set to false and controlled by the 'end_size' and 'mid_size'
+ * arguments. The 'end_size' argument is how many numbers on either the start
+ * and the end list edges, by default is 1. The 'mid_size' argument is how many
+ * numbers to either side of current page, but not including current page.
+ *
+ * It is possible to add query vars to the link by using the 'add_args' argument
+ * and see {@link add_query_arg()} for more information.
+ *
+ * @since 1.0
+ *
+ * @param string|array $args Optional. Override defaults.
+ * @return array|string String of page links or array of page links.
+ */
+function bb_paginate_links( $args = '' ) {
+	$defaults = array(
+		'base'         => '%_%', // http://example.com/all_posts.php%_% : %_% is replaced by format (below)
+		'format'       => '?page=%#%', // ?page=%#% : %#% is replaced by the page number
+		'total'        => 1,
+		'current'      => 0,
+		'show_all'     => false,
+		'prev_next'    => true,
+		'prev_text'    => __( '&laquo; Previous' ),
+		'next_text'    => __( 'Next &raquo;' ),
+		'end_size'     => 1, // How many numbers on either end including the end
+		'mid_size'     => 2, // How many numbers to either side of current not including current
+		'type'         => 'plain',
+		'add_args'     => false, // array of query args to add
+		'add_fragment' => '',
+		'n_title'      => __( 'Page %d' ), // Not from WP version
+		'prev_title'   => __( 'Previous page' ), // Not from WP version
+		'next_title'   => __( 'Next page' ) // Not from WP version
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	// Who knows what else people pass in $args
+	$total = (int) $total;
+	if ( $total < 2 )
+		return;
+	$current  = (int) $current;
+	$end_size = 0 < (int) $end_size ? (int) $end_size : 1; // Out of bounds?  Make it the default.
+	$mid_size = 0 <= (int) $mid_size ? (int) $mid_size : 2;
+	$add_args = is_array($add_args) ? $add_args : false;
+	$r = '';
+	$page_links = array();
+	$n = 0;
+	$dots = false;
+
+	$empty_format = '';
+	if ( strpos( $format, '?' ) === 0 ) {
+		$empty_format = '?';
+	}
+
+	if ( $prev_next && $current && 1 < $current ) {
+		$link = str_replace( '%_%', 2 == $current ? $empty_format : $format, $base );
+		$link = str_replace( '%#%', $current - 1, $link );
+		$link = str_replace( '?&', '?', $link );
+		if ( $add_args )
+			$link = add_query_arg( $add_args, $link );
+		$link .= $add_fragment;
+		$page_links[] = '<a class="prev page-numbers" href="' . esc_url( $link ) . '" title="' . esc_attr( $prev_title ) . '">' . $prev_text . '</a>';
+	}
+
+	for ( $n = 1; $n <= $total; $n++ ) {
+		if ( $n == $current ) {
+			$n_display = bb_number_format_i18n( $n );
+			$n_display_title =  esc_attr( sprintf( $n_title, $n ) );
+			$page_links[] = '<span class="page-numbers current" title="' . $n_display_title . '">' . $n_display . '</span>';
+			$dots = true;
+		} else {
+			if ( $show_all || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) {
+				$n_display = bb_number_format_i18n( $n );
+				$n_display_title =  esc_attr( sprintf( $n_title, $n ) );
+				$link = str_replace( '%_%', 1 == $n ? $empty_format : $format, $base );
+				$link = str_replace( '%#%', $n, $link );
+				$link = str_replace( '?&', '?', $link );
+				if ( $add_args )
+					$link = add_query_arg( $add_args, $link );
+				$link .= $add_fragment;
+				$page_links[] = '<a class="page-numbers" href="' . esc_url( $link ) . '" title="' . $n_display_title . '">' . $n_display . '</a>';
+				$dots = true;
+			} elseif ( $dots && !$show_all ) {
+				$page_links[] = '<span class="page-numbers dots">&hellip;</span>';
+				$dots = false;
+			}
+		}
+	}
+	if ( $prev_next && $current && ( $current < $total || -1 == $total ) ) {
+		$link = str_replace( '%_%', $format, $base );
+		$link = str_replace( '%#%', $current + 1, $link );
+		if ( $add_args )
+			$link = add_query_arg( $add_args, $link );
+		$link .= $add_fragment;
+		$page_links[] = '<a class="next page-numbers" href="' . esc_url( $link ) . '" title="' . esc_attr( $next_title ) . '">' . $next_text . '</a>';
+	}
+	switch ( $type ) {
+		case 'array':
+			return $page_links;
+			break;
+		case 'list':
+			$r .= '<ul class="page-numbers">' . "\n\t" . '<li>';
+			$r .= join( '</li>' . "\n\t" . '<li>', $page_links );
+			$r .= '</li>' . "\n" . '</ul>' . "\n";
+			break;
+		default:
+			$r = join( "\n", $page_links );
+			break;
+	}
+	return $r;
+}
+
+function bb_get_uri_page() {
+	if ( isset($_GET['page']) && is_numeric($_GET['page']) && 1 < (int) $_GET['page'] )
+		return (int) $_GET['page'];
+
+	if ( isset($_SERVER['PATH_INFO']) )
+		$path = $_SERVER['PATH_INFO'];
+	else
+		if ( !$path = strtok($_SERVER['REQUEST_URI'], '?') )
+			return 1;
+
+	if ( preg_match( '/^\/([0-9]+)\/?$/', $path, $matches ) ) {
+		$page = (int) $matches[1];
+		if ( 1 < $page ) {
+			return $page;
+		}
+	}
+
+	if ( $page = strstr($path, '/page/') ) {
+		$page = (int) substr($page, 6);
+		if ( 1 < $page )
+			return $page;
+	}
+	return 1;
+}
+
+//expects $item = 1 to be the first, not 0
+function bb_get_page_number( $item, $per_page = 0 ) {
+	if ( !$per_page )
+		$per_page = bb_get_option('page_topics');
+	return intval( ceil( $item / $per_page ) ); // page 1 is the first page
+}
+
+
+
+/* Time */
+
+function bb_timer_stop($display = 0, $precision = 3) { //if called like bb_timer_stop(1), will echo $timetotal
+	global $bb_timestart, $timeend;
+	$mtime = explode(' ', microtime());
+	$timeend = $mtime[1] + $mtime[0];
+	$timetotal = $timeend - $bb_timestart;
+	if ($display)
+		echo bb_number_format_i18n($timetotal, $precision);
+	return bb_number_format_i18n($timetotal, $precision);
+}
+
+// GMT -> so many minutes ago
+function bb_since( $original, $args = '' )
+{
+	$defaults = array(
+		'levels'    => 1,
+		'separator' => ', '
+	);
+
+	// $args used to be $do_more
+	// $do_more = 0 is equivalent to $args['levels'] = 1
+	// $do_more = 1 is equivalent to $args['levels'] = 2
+	if ( !is_array( $args ) ) {
+		$args = array(
+			'levels' => abs( (integer) $args ) + 1
+		);
+	}
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$today = (integer) time();
+
+	if ( !is_numeric( $original ) ) {
+		if ( $today < $_original = bb_gmtstrtotime( str_replace( ',', ' ', $original ) ) ) { // Looks like bb_since was called twice
+			return $original;
+		} else {
+			$original = $_original;
+		}
+	}
+
+	$seconds = $today - ( (integer) $original );
+	if ( 0 === $seconds ) {
+		return sprintf( _n( '%d second', '%d seconds', 0 ), 0 );
+	}
+
+	$levels = abs( (integer) $levels );
+	if ( 0 === $levels ) {
+		return '';
+	}
+
+	// array of time period chunks
+	$chunks = array(
+		( 60 * 60 * 24 * 365 ), // years
+		( 60 * 60 * 24 * 30 ),  // months
+		( 60 * 60 * 24 * 7 ),   // weeks
+		( 60 * 60 * 24 ),       // days
+		( 60 * 60 ),            // hours
+		( 60 ),                 // minutes
+		( 1 )                   // seconds
+	);
+
+	$caught = 0;
+	$parts = array();
+	for ( $i = 0; $i < count( $chunks ); $i++ ) {
+		if ( ( $count = floor( $seconds / $chunks[$i] ) ) || $caught ) {
+			if ( $count ) {
+				$trans = array(
+					_n( '%d year', '%d years', $count ),
+					_n( '%d month', '%d months', $count ),
+					_n( '%d week', '%d weeks', $count ),
+					_n( '%d day', '%d days', $count ),
+					_n( '%d hour', '%d hours', $count ),
+					_n( '%d minute', '%d minutes', $count ),
+					_n( '%d second', '%d seconds', $count )
+				);
+				$parts[] = sprintf( $trans[$i], $count );
+			}
+			$caught++;
+			$seconds = $seconds - ( $count * $chunks[$i] );
+		}
+		if ( $caught === $levels ) {
+			break;
+		}
+	}
+
+	if ( empty( $parts ) ) {
+		return sprintf( _n( '%d second', '%d seconds', 0 ), 0 );
+	}
+
+	return join( $separator, $parts );
+}
+
+function bb_current_time( $type = 'timestamp' ) {
+	return current_time( $type, true );
+}
+
+// GMT -> Local
+// in future versions this could eaily become a user option.
+function bb_offset_time( $time, $args = null ) {
+	if ( isset($args['format']) && 'since' == $args['format'] )
+		return $time;
+	if ( !is_numeric($time) ) {
+		if ( -1 !== $_time = bb_gmtstrtotime( $time ) )
+			return gmdate('Y-m-d H:i:s', $_time + bb_get_option( 'gmt_offset' ) * 3600);
+		else
+			return $time; // Perhaps should return -1 here
+	} else {
+		return $time + bb_get_option( 'gmt_offset' ) * 3600;
+	}
+}
+
+
+
+/* Permalinking / URLs / Paths */
+
+/**
+ * BB_URI_CONTEXT_* - Bitwise definitions for bb_uri() and bb_get_uri() contexts
+ *
+ * @since 1.0
+ */
+define( 'BB_URI_CONTEXT_NONE',                 0 );
+define( 'BB_URI_CONTEXT_HEADER',               1 );
+define( 'BB_URI_CONTEXT_TEXT',                 2 );
+define( 'BB_URI_CONTEXT_A_HREF',               4 );
+define( 'BB_URI_CONTEXT_FORM_ACTION',          8 );
+define( 'BB_URI_CONTEXT_IMG_SRC',              16 );
+define( 'BB_URI_CONTEXT_LINK_STYLESHEET_HREF', 32 );
+define( 'BB_URI_CONTEXT_LINK_ALTERNATE_HREF',  64 );
+define( 'BB_URI_CONTEXT_LINK_OTHER',           128 );
+define( 'BB_URI_CONTEXT_SCRIPT_SRC',           256 );
+define( 'BB_URI_CONTEXT_IFRAME_SRC',           512 );
+define( 'BB_URI_CONTEXT_BB_FEED',              1024 );
+define( 'BB_URI_CONTEXT_BB_USER_FORMS',        2048 );
+define( 'BB_URI_CONTEXT_BB_ADMIN',             4096 );
+define( 'BB_URI_CONTEXT_BB_XMLRPC',            8192 );
+define( 'BB_URI_CONTEXT_WP_HTTP_REQUEST',      16384 );
+//define( 'BB_URI_CONTEXT_*',                    32768 );  // Reserved for future definitions
+//define( 'BB_URI_CONTEXT_*',                    65536 );  // Reserved for future definitions
+//define( 'BB_URI_CONTEXT_*',                    131072 ); // Reserved for future definitions
+//define( 'BB_URI_CONTEXT_*',                    262144 ); // Reserved for future definitions
+define( 'BB_URI_CONTEXT_AKISMET',              524288 );
+
+/**
+ * Echo a URI based on the URI setting
+ *
+ * @since 1.0
+ *
+ * @param $resource string The directory, may include a querystring
+ * @param $query mixed The query arguments as a querystring or an associative array
+ * @param $context integer The context of the URI, use BB_URI_CONTEXT_*
+ * @return void
+ */
+function bb_uri( $resource = null, $query = null, $context = BB_URI_CONTEXT_A_HREF )
+{
+	echo apply_filters( 'bb_uri', bb_get_uri( $resource, $query, $context ), $resource, $query, $context );
+}
+
+/**
+ * Return a URI based on the URI setting
+ *
+ * @since 1.0
+ *
+ * @param $resource string The directory, may include a querystring
+ * @param $query mixed The query arguments as a querystring or an associative array
+ * @param $context integer The context of the URI, use BB_URI_CONTEXT_*
+ * @return string The complete URI
+ */
+function bb_get_uri( $resource = null, $query = null, $context = BB_URI_CONTEXT_A_HREF )
+{
+	// If there is a querystring in the resource then extract it
+	if ( $resource && strpos( $resource, '?' ) !== false ) {
+		list( $_resource, $_query ) = explode( '?', trim( $resource ), 2 );
+		$resource = $_resource;
+		$_query = wp_parse_args( $_query );
+	} else {
+		// Make sure $_query is an array for array_merge()
+		$_query = array();
+	}
+
+	// $query can be an array as well as a string
+	if ( $query ) {
+		if ( is_string( $query ) ) {
+			$query = ltrim( trim( $query ), '?' );
+		}
+		$query = wp_parse_args( $query );
+	}
+
+	// Make sure $query is an array for array_merge()
+	if ( !$query ) {
+		$query = array();
+	}
+
+	// Merge the queries into a single array
+	$query = array_merge( $_query, $query );
+
+	// Make sure context is an integer
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+
+	// Get the base URI
+	static $_uri;
+	if( !isset( $_uri ) ) {
+		$_uri = bb_get_option( 'uri' );
+	}
+	$uri = $_uri;
+
+	// Use https?
+	if (
+		( ( $context & BB_URI_CONTEXT_BB_USER_FORMS ) && force_ssl_login() ) // Force https when required on user forms
+	||
+		( ( $context & BB_URI_CONTEXT_BB_ADMIN ) && force_ssl_admin() ) // Force https when required in admin
+	) {
+		static $_uri_ssl;
+		if( !isset( $_uri_ssl ) ) {
+			$_uri_ssl = bb_get_option( 'uri_ssl' );
+		}
+		$uri = $_uri_ssl;
+	}
+
+	// Add the directory
+	$uri .= ltrim( $resource, '/' );
+
+	// Add the query string to the URI
+	$uri = add_query_arg( $query, $uri );
+
+	return apply_filters( 'bb_get_uri', $uri, $resource, $context );
+}
+
+/**
+ * Forces redirection to an SSL page when required
+ *
+ * @since 1.0
+ *
+ * @return void
+ */
+function bb_ssl_redirect()
+{
+	$page = bb_get_location();
+
+	do_action( 'bb_ssl_redirect' );
+
+	if ( BB_IS_ADMIN ) {
+		if ( !force_ssl_admin() ) {
+			return;
+		}
+	} else {
+		switch ( $page ) {
+			case 'login-page':
+			case 'register-page':
+				if ( !force_ssl_login() ) {
+					return;
+				}
+				break;
+			case 'profile-page':
+				global $self;
+				if ( $self == 'profile-edit.php' ) {
+					if ( !force_ssl_login() ) {
+						return;
+					}
+				} else {
+					return;
+				}
+				break;
+			default:
+				return;
+				break;
+		}
+	}
+
+	if ( is_ssl() ) {
+		return;
+	}
+
+	$uri_ssl = parse_url( bb_get_option( 'uri_ssl' ) );
+	$uri = $uri_ssl['scheme'] . '://' . $uri_ssl['host'] . $_SERVER['REQUEST_URI'];
+	bb_safe_redirect( $uri );
+	exit;
+}
+
+function bb_get_path( $level = 1, $base = false, $request = false ) {
+	if ( !$request )
+		$request = $_SERVER['REQUEST_URI'];
+	if ( is_string($request) )
+		$request = parse_url($request);
+	if ( !is_array($request) || !isset($request['path']) )
+		return '';
+
+	$path = rtrim($request['path'], " \t\n\r\0\x0B/");
+	if ( !$base )
+		$base = rtrim(bb_get_option('path'), " \t\n\r\0\x0B/");
+	$path = preg_replace('|' . preg_quote($base, '|') . '/?|','',$path,1);
+	if ( !$path )
+		return '';
+	if ( strpos($path, '/') === false )
+		return '';
+
+	$url = explode('/',$path);
+	if ( !isset($url[$level]) )
+		return '';
+
+	return urldecode($url[$level]);
+}
+
+function bb_find_filename( $text ) {
+	if ( preg_match('|.*?/([a-z\-]+\.php)/?.*|', $text, $matches) )
+		return $matches[1];
+	else {
+		$path = bb_get_option( 'path' );
+		$text = preg_replace("#^$path#", '', $text);
+		$text = preg_replace('#/.+$#', '', $text);
+		return $text . '.php';
+	}
+	return false;
+}
+
+function bb_send_headers() {
+	if ( bb_is_user_logged_in() )
+		nocache_headers();
+	@header('Content-Type: ' . bb_get_option( 'html_type' ) . '; charset=' . bb_get_option( 'charset' ));
+	do_action( 'bb_send_headers' );
+}
+
+function bb_pingback_header() {
+	if (bb_get_option('enable_pingback'))
+		@header('X-Pingback: '. bb_get_uri('xmlrpc.php', null, BB_URI_CONTEXT_HEADER + BB_URI_CONTEXT_BB_XMLRPC));
+}
+
+// Inspired by and adapted from Yung-Lung Scott YANG's http://scott.yang.id.au/2005/05/permalink-redirect/ (GPL)
+function bb_repermalink() {
+	global $page;
+	$location = bb_get_location();
+	$uri = $_SERVER['REQUEST_URI'];
+	if ( isset($_GET['id']) )
+		$id = $_GET['id'];
+	else
+		$id = bb_get_path();
+	$_original_id = $id;
+
+	do_action( 'pre_permalink', $id );
+
+	$id = apply_filters( 'bb_repermalink', $id );
+
+	switch ($location) {
+		case 'front-page':
+			$path = null;
+			$querystring = null;
+			if ($page > 1) {
+				if (bb_get_option( 'mod_rewrite' )) {
+					$path = 'page/' . $page;
+				} else {
+					$querystring = array('page' => $page);
+				}
+			}
+			$permalink = bb_get_uri($path, $querystring, BB_URI_CONTEXT_HEADER);
+			$issue_404 = true;
+			break;
+		case 'forum-page':
+			if (empty($id)) {
+				$permalink = bb_get_uri(null, null, BB_URI_CONTEXT_HEADER);
+				break;
+			}
+			global $forum_id, $forum;
+			$forum     = bb_get_forum( $id );
+			$forum_id  = $forum->forum_id;
+			$permalink = get_forum_link( $forum->forum_id, $page );
+			break;
+		case 'topic-edit-page':
+		case 'topic-page':
+			if (empty($id)) {
+				$permalink = bb_get_uri(null, null, BB_URI_CONTEXT_HEADER);
+				break;
+			}
+			global $topic_id, $topic;
+			$topic     = get_topic( $id );
+			$topic_id  = $topic->topic_id;
+			$permalink = get_topic_link( $topic->topic_id, $page );
+			break;
+		case 'profile-page': // This handles the admin side of the profile as well.
+			global $user_id, $user, $profile_hooks, $self;
+			if ( isset($_GET['id']) )
+				$id = $_GET['id'];
+			elseif ( isset($_GET['username']) )
+				$id = $_GET['username'];
+			else
+				$id = bb_get_path();
+			$_original_id = $id;
+			
+			if ( !$id ) {
+				$user = bb_get_current_user(); // Attempt to go to the current users profile
+			} else {
+				if ( bb_get_option( 'mod_rewrite' ) === 'slugs') {
+					if ( !$user = bb_get_user_by_nicename( $id ) ) {
+						$user = bb_get_user( $id );
+					}
+				} else {
+					if ( !$user = bb_get_user( $id ) ) {
+						$user = bb_get_user_by_nicename( $id );
+					}
+				}
+			}
+
+			if ( !$user || ( 1 == $user->user_status && !bb_current_user_can( 'moderate' ) ) )
+				bb_die(__('User not found.'), '', 404);
+
+			$user_id = $user->ID;
+			bb_global_profile_menu_structure();
+			$valid = false;
+			if ( $tab = isset($_GET['tab']) ? $_GET['tab'] : bb_get_path(2) ) {
+				foreach ( $profile_hooks as $valid_tab => $valid_file ) {
+					if ( $tab == $valid_tab ) {
+						$valid = true;
+						$self = $valid_file;
+					}
+				}
+			}
+			if ( $valid ) {
+				$permalink = get_profile_tab_link( $user->ID, $tab, $page );
+			} else {
+				$permalink = get_user_profile_link( $user->ID, $page );
+				unset($self, $tab);
+			}
+			break;
+		case 'favorites-page':
+			$permalink = get_favorites_link();
+			break;
+		case 'tag-page': // It's not an integer and tags.php pulls double duty.
+			$id = ( isset($_GET['tag']) ) ? $_GET['tag'] : false;
+			if ( ! $id || ! bb_get_tag( (string) $id ) )
+				$permalink = bb_get_tag_page_link();
+			else {
+				global $tag, $tag_name;
+				$tag_name = $id;
+				$tag = bb_get_tag( (string) $id );
+				$permalink = bb_get_tag_link( 0, $page ); // 0 => grabs $tag from global.
+			}
+			break;
+		case 'view-page': // Not an integer
+			if ( isset($_GET['view']) )
+				$id = $_GET['view'];
+			else
+				$id = bb_get_path();
+			$_original_id = $id;
+			global $view;
+			$view = $id;
+			$permalink = get_view_link( $view, $page );
+			break;
+		default:
+			return;
+			break;
+	}
+	
+	wp_parse_str($_SERVER['QUERY_STRING'], $args);
+	$args = urlencode_deep($args);
+	if ( $args ) {
+		$permalink = add_query_arg($args, $permalink);
+		if ( bb_get_option('mod_rewrite') ) {
+			$pretty_args = array('id', 'page', 'tag', 'tab', 'username'); // these are already specified in the path
+			if ( $location == 'view-page' )
+				$pretty_args[] = 'view';
+			foreach ( $pretty_args as $pretty_arg )
+				$permalink = remove_query_arg( $pretty_arg, $permalink );
+		}
+	}
+
+	$permalink = apply_filters( 'bb_repermalink_result', $permalink, $location );
+
+	$domain = bb_get_option('domain');
+	$domain = preg_replace('/^https?/', '', $domain);
+	$check = preg_replace( '|^.*' . trim($domain, ' /' ) . '|', '', $permalink, 1 );
+	$uri = rtrim( $uri, " \t\n\r\0\x0B?" );
+	$uri = str_replace( '/index.php', '/', $uri );
+
+	global $bb_log;
+	$bb_log->debug($uri, 'bb_repermalink() ' . __('REQUEST_URI'));
+	$bb_log->debug($check, 'bb_repermalink() ' . __('should be'));
+	$bb_log->debug($permalink, 'bb_repermalink() ' . __('full permalink'));
+	$bb_log->debug(isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : null, 'bb_repermalink() ' . __('PATH_INFO'));
+
+	if ( $check != $uri && $check != str_replace(urlencode($_original_id), $_original_id, $uri) ) {
+		if ( $issue_404 && rtrim( $check, " \t\n\r\0\x0B/" ) !== rtrim( $uri, " \t\n\r\0\x0B/" ) ) {
+			status_header( 404 );
+			bb_load_template( '404.php' );
+		} else {
+			wp_redirect( $permalink );
+		}
+		exit;
+	}
+
+	do_action( 'post_permalink', $permalink );
+}
+
+/* Profile/Admin */
+
+function bb_global_profile_menu_structure() {
+	global $user_id, $profile_menu, $profile_hooks;
+	// Menu item name
+	// The capability required for own user to view the tab ('' to allow non logged in access)
+	// The capability required for other users to view the tab ('' to allow non logged in access)
+	// The URL of the item's file
+	// Item name for URL (nontranslated)
+	$profile_menu[0] = array(__('Edit'), 'edit_profile', 'edit_users', 'profile-edit.php', 'edit');
+	$profile_menu[5] = array(__('Favorites'), '', '', 'favorites.php', 'favorites');
+
+	// Create list of page plugin hook names the current user can access
+	$profile_hooks = array();
+	foreach ($profile_menu as $profile_tab)
+		if ( bb_can_access_tab( $profile_tab, bb_get_current_user_info( 'id' ), $user_id ) )
+			$profile_hooks[bb_sanitize_with_dashes($profile_tab[4])] = $profile_tab[3];
+
+	do_action('bb_profile_menu');
+	ksort($profile_menu);
+}
+
+function bb_add_profile_tab($tab_title, $users_cap, $others_cap, $file, $arg = false) {
+	global $profile_menu, $profile_hooks, $user_id;
+
+	$arg = $arg ? $arg : $tab_title;
+
+	$profile_tab = array($tab_title, $users_cap, $others_cap, $file, $arg);
+	$profile_menu[] = $profile_tab;
+	if ( bb_can_access_tab( $profile_tab, bb_get_current_user_info( 'id' ), $user_id ) )
+		$profile_hooks[bb_sanitize_with_dashes($arg)] = $file;
+}
+
+function bb_can_access_tab( $profile_tab, $viewer_id, $owner_id ) {
+	global $bb_current_user;
+	$viewer_id = (int) $viewer_id;
+	$owner_id = (int) $owner_id;
+	if ( $viewer_id == bb_get_current_user_info( 'id' ) )
+		$viewer =& $bb_current_user;
+	else
+		$viewer = new BP_User( $viewer_id );
+	if ( !$viewer )
+		return '' === $profile_tab[2];
+
+	if ( $owner_id == $viewer_id ) {
+		if ( '' === $profile_tab[1] )
+			return true;
+		else
+			return $viewer->has_cap($profile_tab[1]);
+	} else {
+		if ( '' === $profile_tab[2] )
+			return true;
+		else
+			return $viewer->has_cap($profile_tab[2]);
+	}
+}
+
+//meta_key => (required?, Label, hCard property).  Don't use user_{anything} as the name of your meta_key.
+function bb_get_profile_info_keys( $context = null ) {
+	return apply_filters( 'get_profile_info_keys', array(
+		'first_name' => array(0, __('First name')),
+		'last_name' => array(0, __('Last name')),
+		'display_name' => array(1, __('Display name as')),
+		'user_email' => array(1, __('Email'), 'email'),
+		'user_url' => array(0, __('Website'), 'url'),
+		'from' => array(0, __('Location')),
+		'occ' => array(0, __('Occupation'), 'role'),
+		'interest' => array(0, __('Interests')),
+	), $context );
+}
+
+function bb_get_profile_admin_keys( $context = null ) {
+	global $bbdb;
+	return apply_filters( 'get_profile_admin_keys', array(
+		$bbdb->prefix . 'title' => array(0, __('Custom Title'))
+	), $context );
+}
+
+function bb_get_assignable_caps() {
+	$caps = array();
+	if ( $throttle_time = bb_get_option( 'throttle_time' ) )
+		$caps['throttle'] = sprintf( __('Ignore the %d second post throttling limit'), $throttle_time );
+	return apply_filters( 'get_assignable_caps', $caps );
+}
+
+/* Views */
+
+function bb_get_views() {
+	global $bb_views;
+
+	$views = array();
+	foreach ( (array) $bb_views as $view => $array )
+		$views[$view] = $array['title'];
+
+	return $views;
+}
+
+function bb_register_view( $view, $title, $query_args = '', $feed = TRUE ) {
+	global $bb_views;
+
+	$view  = bb_slug_sanitize( $view );
+	$title = esc_html( $title );
+
+	if ( !$view || !$title )
+		return false;
+
+	$query_args = wp_parse_args( $query_args );
+
+	if ( !$sticky_set = isset($query_args['sticky']) )
+		$query_args['sticky'] = 'no';
+
+	$bb_views[$view]['title']  = $title;
+	$bb_views[$view]['query']  = $query_args;
+	$bb_views[$view]['sticky'] = !$sticky_set; // No sticky set => split into stickies and not
+	$bb_views[$view]['feed'] = $feed;
+	return $bb_views[$view];
+}
+
+function bb_deregister_view( $view ) {
+	global $bb_views;
+
+	$view = bb_slug_sanitize( $view );
+	if ( !isset($bb_views[$view]) )
+		return false;
+
+	unset($GLOBALS['bb_views'][$view]);
+	return true;
+}
+
+function bb_view_query( $view, $new_args = '' ) {
+	global $bb_views;
+
+	$view = bb_slug_sanitize( $view );
+	if ( !isset($bb_views[$view]) )
+		return false;
+
+	if ( $new_args ) {
+		$new_args = wp_parse_args( $new_args );
+		$query_args = array_merge( $bb_views[$view]['query'], $new_args );
+	} else {
+		$query_args = $bb_views[$view]['query'];
+	}
+
+	return new BB_Query( 'topic', $query_args, "bb_view_$view" );
+}
+
+function bb_get_view_query_args( $view ) {
+	global $bb_views;
+
+	$view = bb_slug_sanitize( $view );
+	if ( !isset($bb_views[$view]) )
+		return false;
+
+	return $bb_views[$view]['query'];
+}
+
+function bb_register_default_views() {
+	// no posts (besides the first one), older than 2 hours
+	bb_register_view( 'no-replies', __('Topics with no replies'), array( 'post_count' => 1, 'started' => '<' . gmdate( 'YmdH', time() - 7200 ) ) );
+	bb_register_view( 'untagged'  , __('Topics with no tags')   , array( 'tag_count'  => 0 ) );
+}
+
+/* Feeds */
+
+/**
+ * Send status headers for clients supporting Conditional Get
+ *
+ * The function sends the Last-Modified and ETag headers for all clients. It
+ * then checks both the If-None-Match and If-Modified-Since headers to see if
+ * the client has used them. If so, and the ETag does matches the client ETag
+ * or the last modified date sent by the client is newer or the same as the
+ * generated last modified, the function sends a 304 Not Modified and exits.
+ *
+ * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
+ * @param string $bb_last_modified Last modified time. Must be a HTTP-date
+ */
+function bb_send_304( $bb_last_modified ) {
+	$bb_etag = '"' . md5($bb_last_modified) . '"';
+	@header("Last-Modified: $bb_last_modified");
+	@header("ETag: $bb_etag");
+
+	// Support for Conditional GET
+	if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) $client_etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
+	else $client_etag = false;
+
+	$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE']);
+	// If string is empty, return 0. If not, attempt to parse into a timestamp
+	$client_modified_timestamp = $client_last_modified ? bb_gmtstrtotime($client_last_modified) : 0;
+
+	// Make a timestamp for our most recent modification...	
+	$bb_modified_timestamp = bb_gmtstrtotime($bb_last_modified);
+
+	if ( ($client_last_modified && $client_etag) ?
+		 (($client_modified_timestamp >= $bb_modified_timestamp) && ($client_etag == $bb_etag)) :
+		 (($client_modified_timestamp >= $bb_modified_timestamp) || ($client_etag == $bb_etag)) ) {
+		status_header( 304 );
+		exit;
+	}
+}
+
+/* Nonce */
+
+/**
+ * Retrieve URL with nonce added to URL query.
+ *
+ * @package bbPress
+ * @subpackage Security
+ * @since 1.0
+ *
+ * @param string $actionurl URL to add nonce action
+ * @param string $action Optional. Nonce action name
+ * @return string URL with nonce action added.
+ */
+function bb_nonce_url( $actionurl, $action = -1 ) {
+	$actionurl = str_replace( '&amp;', '&', $actionurl );
+	$nonce = bb_create_nonce( $action );
+	return esc_html( add_query_arg( '_wpnonce', $nonce, $actionurl ) );
+}
+
+/**
+ * Retrieve or display nonce hidden field for forms.
+ *
+ * The nonce field is used to validate that the contents of the form came from
+ * the location on the current site and not somewhere else. The nonce does not
+ * offer absolute protection, but should protect against most cases. It is very
+ * important to use nonce field in forms.
+ *
+ * If you set $echo to true and set $referer to true, then you will need to
+ * retrieve the {@link wp_referer_field() wp referer field}. If you have the
+ * $referer set to true and are echoing the nonce field, it will also echo the
+ * referer field.
+ *
+ * The $action and $name are optional, but if you want to have better security,
+ * it is strongly suggested to set those two parameters. It is easier to just
+ * call the function without any parameters, because validation of the nonce
+ * doesn't require any parameters, but since crackers know what the default is
+ * it won't be difficult for them to find a way around your nonce and cause
+ * damage.
+ *
+ * The input name will be whatever $name value you gave. The input value will be
+ * the nonce creation value.
+ *
+ * @package bbPress
+ * @subpackage Security
+ * @since 1.0
+ *
+ * @param string $action Optional. Action name.
+ * @param string $name Optional. Nonce name.
+ * @param bool $referer Optional, default true. Whether to set the referer field for validation.
+ * @param bool $echo Optional, default true. Whether to display or return hidden form field.
+ * @return string Nonce field.
+ */
+function bb_nonce_field( $action = -1, $name = "_wpnonce", $referer = true , $echo = true ) {
+	$name = esc_attr( $name );
+	$nonce = bb_create_nonce( $action );
+	$nonce_field = '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . $nonce . '" />';
+	if ( $echo )
+		echo $nonce_field;
+
+	if ( $referer )
+		wp_referer_field( $echo, 'previous' );
+
+	return $nonce_field;
+}
+
+function bb_nonce_ays( $action )
+{
+	$title = __( 'bbPress Failure Notice' );
+	$html .= "\t<div id='message' class='updated fade'>\n\t<p>" . esc_html( bb_explain_nonce( $action ) ) . "</p>\n\t<p>";
+	if ( wp_get_referer() )
+		$html .= "<a href='" . remove_query_arg( 'updated', esc_url( wp_get_referer() ) ) . "'>" . __( 'Please try again.' ) . "</a>";
+	$html .= "</p>\n\t</div>\n";
+	$html .= "</body>\n</html>";
+	bb_die( $html, $title );
+}
+
+function bb_install_header( $title = '', $header = false, $logo = false )
+{
+	if ( empty($title) )
+		if ( function_exists('__') )
+			$title = __('bbPress');
+		else
+			$title = 'bbPress';
+		
+		$uri = false;
+		if ( function_exists('bb_get_uri') && !BB_INSTALLING ) {
+			$uri = bb_get_uri();
+			$uri_stylesheet = bb_get_uri('bb-admin/install.css', null, BB_URI_CONTEXT_LINK_STYLESHEET_HREF + BB_URI_CONTEXT_BB_INSTALLER);
+			$uri_stylesheet_rtl = bb_get_uri('bb-admin/install-rtl.css', null, BB_URI_CONTEXT_LINK_STYLESHEET_HREF + BB_URI_CONTEXT_BB_INSTALLER);
+			$uri_logo = bb_get_uri('bb-admin/images/bbpress-logo.png', null, BB_URI_CONTEXT_IMG_SRC + BB_URI_CONTEXT_BB_INSTALLER);
+		}
+		
+		if (!$uri) {
+			$uri = preg_replace('|(/bb-admin)?/[^/]+?$|', '/', $_SERVER['PHP_SELF']);
+			$uri_stylesheet = $uri . 'bb-admin/install.css';
+			$uri_stylesheet_rtl = $uri . 'bb-admin/install-rtl.css';
+			$uri_logo = $uri . 'bb-admin/images/bbpress-logo.png';
+		}
+	
+	header('Content-Type: text/html; charset=utf-8');
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"<?php if ( function_exists( 'bb_language_attributes' ) ) bb_language_attributes(); ?>>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title><?php echo $title; ?></title>
+	<meta name="robots" content="noindex, nofollow" />
+	<link rel="stylesheet" href="<?php echo $uri_stylesheet; ?>" type="text/css" />
+<?php
+	if ( function_exists( 'bb_get_option' ) && 'rtl' == bb_get_option( 'text_direction' ) ) {
+?>
+	<link rel="stylesheet" href="<?php echo $uri_stylesheet_rtl; ?>" type="text/css" />
+<?php
+	}
+?>
+</head>
+<body>
+	<div id="container">
+<?php
+	if ( $logo ) {
+?>
+		<div class="logo">
+			<img src="<?php echo $uri_logo; ?>" alt="bbPress" />
+		</div>
+<?php
+	}
+
+	if ( !empty($header) ) {
+?>
+		<h1>
+			<?php echo $header; ?>
+		</h1>
+<?php
+	}
+}
+
+function bb_install_footer() {
+?>
+	</div>
+</body>
+</html>
+<?php
+}
+
+function bb_die( $message, $title = '', $header = 0 ) {
+	global $bb_locale;
+
+	if ( $header && !headers_sent() )
+		status_header( $header );
+
+	if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
+		if ( empty( $title ) ) {
+			$error_data = $message->get_error_data();
+			if ( is_array( $error_data ) && isset( $error_data['title'] ) )
+				$title = $error_data['title'];
+		}
+		$errors = $message->get_error_messages();
+		switch ( count( $errors ) ) :
+		case 0 :
+			$message = '';
+			break;
+		case 1 :
+			$message = "<p>{$errors[0]}</p>";
+			break;
+		default :
+			$message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $errors ) . "</li>\n\t</ul>";
+			break;
+		endswitch;
+	} elseif ( is_string( $message ) ) {
+		$message = bb_autop( $message );
+	}
+
+	if ( empty($title) )
+		$title = __('bbPress &rsaquo; Error');
+	
+	bb_install_header( $title );
+?>
+	<?php echo $message; ?>
+<?php
+	if ($uri = bb_get_uri()) {
+?>
+	<p class="last"><?php printf( __('Back to <a href="%s">%s</a>.'), $uri, bb_get_option( 'name' ) ); ?></p>
+<?php
+	}
+	bb_install_footer();
+	die();
+}
+
+function bb_explain_nonce($action) {
+	if ( $action !== -1 && preg_match('/([a-z]+)-([a-z]+)(_(.+))?/', $action, $matches) ) {
+		$verb = $matches[1];
+		$noun = $matches[2];
+
+		$trans = array();
+		$trans['create']['post'] = array(__('Your attempt to submit this post has failed.'), false);
+		$trans['edit']['post'] = array(__('Your attempt to edit this post has failed.'), false);
+		$trans['delete']['post'] = array(__('Your attempt to delete this post has failed.'), false);
+
+		$trans['create']['topic'] = array(__('Your attempt to create this topic has failed.'), false);
+		$trans['resolve']['topic'] = array(__('Your attempt to change the resolution status of this topic has failed.'), false);
+		$trans['delete']['topic'] = array(__('Your attempt to delete this topic has failed.'), false);
+		$trans['close']['topic'] = array(__('Your attempt to change the status of this topic has failed.'), false);
+		$trans['stick']['topic'] = array(__('Your attempt to change the sticky status of this topic has failed.'), false);
+		$trans['move']['topic'] = array(__('Your attempt to move this topic has failed.'), false);
+
+		$trans['add']['tag'] = array(__('Your attempt to add this tag to this topic has failed.'), false);
+		$trans['rename']['tag'] = array(__('Your attempt to rename this tag has failed.'), false);
+		$trans['merge']['tag'] = array(__('Your attempt to submit these tags has failed.'), false);
+		$trans['destroy']['tag'] = array(__('Your attempt to destroy this tag has failed.'), false);
+		$trans['remove']['tag'] = array(__('Your attempt to remove this tag from this topic has failed.'), false);
+
+		$trans['toggle']['favorite'] = array(__('Your attempt to toggle your favorite status for this topic has failed.'), false);
+
+		$trans['edit']['profile'] = array(__("Your attempt to edit this user's profile has failed."), false);
+
+		$trans['add']['forum'] = array(__("Your attempt to add this forum has failed."), false);
+		$trans['update']['forums'] = array(__("Your attempt to update your forums has failed."), false);
+		$trans['delete']['forums'] = array(__("Your attempt to delete that forum has failed."), false);
+
+		$trans['do']['counts'] = array(__("Your attempt to recount these items has failed."), false);
+
+		$trans['switch']['theme'] = array(__("Your attempt to switch themes has failed."), false);
+
+		if ( isset($trans[$verb][$noun]) ) {
+			if ( !empty($trans[$verb][$noun][1]) ) {
+				$lookup = $trans[$verb][$noun][1];
+				$object = $matches[4];
+				if ( 'use_id' != $lookup )
+					$object = call_user_func($lookup, $object);
+				return sprintf($trans[$verb][$noun][0], esc_html( $object ));
+			} else {
+				return $trans[$verb][$noun][0];
+			}
+		}
+	}
+
+	return apply_filters( 'bb_explain_nonce_' . $verb . '-' . $noun, __('Your attempt to do this has failed.'), $matches[4] );
+}
+
+/* DB Helpers */
+
+function bb_count_last_query( $query = '' ) {
+	global $bbdb, $bb_last_countable_query;
+
+	if ( $query )
+		$q = $query;
+	elseif ( $bb_last_countable_query )
+		$q = $bb_last_countable_query;
+	else
+		$q = $bbdb->last_query;
+
+	if ( false === strpos($q, 'SELECT') )
+		return false;
+
+	if ( false !== strpos($q, 'SQL_CALC_FOUND_ROWS') )
+		return (int) $bbdb->get_var( "SELECT FOUND_ROWS()" );
+
+	$q_original = $q;
+
+	$q = preg_replace(
+		array('/SELECT.*?\s+FROM/', '/LIMIT [0-9]+(\s*,\s*[0-9]+)?/', '/ORDER BY\s+.*$/', '/DESC/', '/ASC/'),
+		array('SELECT COUNT(*) FROM', ''),
+		$q
+	);
+
+	if ( preg_match( '/GROUP BY\s+(\S+)/', $q, $matches ) )
+		$q = str_replace( array( 'COUNT(*)', $matches[0] ), array( "COUNT(DISTINCT $matches[1])", '' ), $q );
+
+	if ( !$query )
+		$bb_last_countable_query = '';
+
+	$q = apply_filters( 'bb_count_last_query', $q, $q_original );
+
+	return (int) $bbdb->get_var($q);
+}
+
+function bb_no_where( $where ) {
+	return;
+}
+
+/* Plugins/Themes utility */
+
+function bb_basename( $file, $directories )
+{
+	if ( strpos( $file, '#' ) !== false ) {
+		return $file; // It's already a basename
+	}
+
+	foreach ( $directories as $type => $directory ) {
+		if ( strpos( $file, $directory ) !== false ) {
+			break; // Keep the $file and $directory set and use them below, nifty huh?
+		}
+	}
+
+	list( $file, $directory ) = str_replace( '\\','/', array( $file, $directory ) );
+	list( $file, $directory ) = preg_replace( '|/+|','/', array( $file,$directory ) );
+	$file = preg_replace( '|^.*' . preg_quote( $directory, '|' ) . '|', $type . '#', $file );
+
+	return $file;
+}
+
+/* Plugins */
+
+function bb_plugin_basename( $file )
+{
+	global $bb;
+	$directories = array();
+	foreach ( $bb->plugin_locations as $_name => $_data ) {
+		$directories[$_name] = $_data['dir'];
+	}
+	return bb_basename( $file, $directories );
+}
+
+function bb_register_plugin_activation_hook( $file, $function )
+{
+	$file = bb_plugin_basename( $file );
+	add_action( 'bb_activate_plugin_' . $file, $function );
+}
+
+function bb_register_plugin_deactivation_hook( $file, $function )
+{
+	$file = bb_plugin_basename( $file );
+	add_action( 'bb_deactivate_plugin_' . $file, $function );
+}
+
+function bb_get_plugin_uri( $plugin = false )
+{
+	global $bb;
+	if ( preg_match( '/^([a-z0-9_-]+)#((?:[a-z0-9\/\\_-]+.)+)(php)$/i', $plugin, $_matches ) ) {
+		$plugin_uri = $bb->plugin_locations[$_matches[1]]['url'] . $_matches[2] . $_matches[3];
+		$plugin_uri = dirname( $plugin_uri ) . '/';
+	} else {
+		$plugin_uri = $bb->plugin_locations['core']['url'];
+	}
+	return apply_filters( 'bb_get_plugin_uri', $plugin_uri, $plugin );
+}
+
+function bb_get_plugin_directory( $plugin = false, $path = false )
+{
+	global $bb;
+	if ( preg_match( '/^([a-z0-9_-]+)#((?:[a-z0-9\/\\_-]+.)+)(php)$/i', $plugin, $_matches ) ) {
+		$plugin_directory = $bb->plugin_locations[$_matches[1]]['dir'] . $_matches[2] . $_matches[3];
+		if ( !$path ) {
+			$plugin_directory = dirname( $plugin_directory ) . '/';
+		}
+	} else {
+		$plugin_directory = $bb->plugin_locations['core']['dir'];
+	}
+	return apply_filters( 'bb_get_plugin_directory', $plugin_directory, $plugin, $path );
+}
+
+function bb_get_plugin_path( $plugin = false )
+{
+	$plugin_path = bb_get_plugin_directory( $plugin, true );
+	return apply_filters( 'bb_get_plugin_path', $plugin_path, $plugin );
+}
+
+/* Themes / Templates */
+
+function bb_get_active_theme_directory()
+{
+	return apply_filters( 'bb_get_active_theme_directory', bb_get_theme_directory() );
+}
+
+function bb_get_theme_directory( $theme = false )
+{
+	global $bb;
+	if ( !$theme ) {
+		$theme = bb_get_option( 'bb_active_theme' );
+	}
+	if ( preg_match( '/^([a-z0-9_-]+)#([\.a-z0-9_-]+)$/i', $theme, $_matches ) ) {
+		$theme_directory = $bb->theme_locations[$_matches[1]]['dir'] . $_matches[2] . '/';
+	} else {
+		$theme_directory = BB_DEFAULT_THEME_DIR;
+	}
+	return $theme_directory;
+}
+
+function bb_get_themes()
+{
+	$r = array();
+	global $bb;
+	foreach ( $bb->theme_locations as $_name => $_data ) {
+		if ( $themes_dir = @dir( $_data['dir'] ) ) {
+			while( ( $theme_dir = $themes_dir->read() ) !== false ) {
+				if ( is_file( $_data['dir'] . $theme_dir . '/style.css' ) && is_readable( $_data['dir'] . $theme_dir . '/style.css' ) && '.' != $theme_dir{0} ) {
+					$r[$_name . '#' . $theme_dir] = $_name . '#' . $theme_dir;
+				}
+			}
+		}
+	}
+	ksort( $r );
+	return $r;
+}
+
+function bb_theme_basename( $file )
+{
+	global $bb;
+	$directories = array();
+	foreach ( $bb->theme_locations as $_name => $_data ) {
+		$directories[$_name] = $_data['dir'];
+	}
+	$file = bb_basename( $file, $directories );
+	$file = preg_replace( '|/+.*|', '', $file );
+	return $file;
+}
+
+function bb_register_theme_activation_hook( $file, $function )
+{
+	$file = bb_theme_basename( $file );
+	add_action( 'bb_activate_theme_' . $file, $function );
+}
+
+function bb_register_theme_deactivation_hook( $file, $function )
+{
+	$file = bb_theme_basename( $file );
+	add_action( 'bb_deactivate_theme_' . $file, $function );
+}
+
+/* Search Functions */
+// NOT bbdb::prepared
+function bb_user_search( $args = '' ) {
+	global $bbdb, $bb_last_countable_query;
+
+	if ( $args && is_string( $args ) && false === strpos( $args, '=' ) ) {
+		$args = array( 'query' => $args );
+	}
+
+	$defaults = array(
+		'query' => '',
+		'append_meta' => true,
+		'user_login' => true,
+		'display_name' => true,
+		'user_nicename' => false,
+		'user_url' => true,
+		'user_email' => false,
+		'user_meta' => false,
+		'users_per_page' => false,
+		'page' => false,
+		'roles' => false
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$query = trim( $query );
+	if ( $query && strlen( preg_replace( '/[^a-z0-9]/i', '', $query ) ) < 3 ) {
+		return new WP_Error( 'invalid-query', __('Your search term was too short') );
+	}
+	$query = $bbdb->escape( $query );
+
+	if ( !$page ) {
+		$page = $GLOBALS['page'];
+	}
+	$page = (int) $page;
+
+	$limit = 0 < (int) $users_per_page ? (int) $users_per_page : bb_get_option( 'page_topics' );
+	if ( 1 < $page ) {
+		$limit = ($limit * ($page - 1)) . ", $limit";
+	}
+
+	$likeit = preg_replace( '/\s+/', '%', like_escape( $query ) );
+
+	$fields = array();
+	foreach ( array( 'user_login', 'display_name', 'user_nicename', 'user_url', 'user_email' ) as $field ) {
+		if ( $$field ) {
+			$fields[] = $field;
+		}
+	}
+
+	if ( $roles ) {
+		$roles = (array) $roles;
+	}
+
+	if ( $roles && !empty( $roles ) && false === $role_user_ids = apply_filters( 'bb_user_search_role_user_ids', false, $roles, $args ) ) {
+		$role_meta_key = $bbdb->escape( $bbdb->prefix . 'capabilities' );
+		$role_sql_terms = array();
+		foreach ( $roles as $role ) {
+			$role_sql_terms[] = "`meta_value` LIKE '%" . $bbdb->escape( like_escape( $role ) ) . "%'";
+		}
+		$role_sql_terms = join( ' OR ', $role_sql_terms );
+		$role_sql = "SELECT `user_id` FROM `$bbdb->usermeta` WHERE `meta_key` = '$role_meta_key' AND ($role_sql_terms);";
+		$role_user_ids = $bbdb->get_col( $role_sql, 0 );
+		if ( is_wp_error( $role_user_ids ) ) {
+			return false;
+		}
+	}
+
+	if ( is_array( $role_user_ids ) && empty( $role_user_ids ) ) {
+		return false;
+	}
+
+	if ( $query && $user_meta && false === $meta_user_ids = apply_filters( 'bb_user_search_meta_user_ids', false, $args ) ) {
+		$meta_sql = "SELECT `user_id` FROM `$bbdb->usermeta` WHERE `meta_value` LIKE ('%$likeit%')";
+		if ( empty( $fields ) ) {
+			$meta_sql .= " LIMIT $limit";
+		}
+		$meta_user_ids = $bbdb->get_col( $meta_sql, 0 );
+		if ( is_wp_error( $meta_user_ids ) ) {
+			$meta_user_ids = false;
+		}
+	}
+
+	$user_ids = array();
+	if ( $role_user_ids && $meta_user_ids ) {
+		$user_ids = array_intersect( (array) $role_user_ids, (array) $meta_user_ids );
+	} elseif ( $role_user_ids ) {
+		$user_ids = (array) $role_user_ids;
+	} elseif ( $meta_user_ids ) {
+		$user_ids = (array) $meta_user_ids;
+	}
+
+	$sql = "SELECT * FROM $bbdb->users";
+
+	$sql_terms = array();
+	if ( $query && count( $fields ) ) {
+		foreach ( $fields as $field ) {
+			$sql_terms[] = "$field LIKE ('%$likeit%')";
+		}
+	}
+
+	$user_ids_sql = '';
+	if ( $user_ids ) {
+		$user_ids_sql = "AND ID IN (". join(',', $user_ids) . ")";
+	}
+
+	if ( $query && empty( $sql_terms ) ) {
+		return new WP_Error( 'invalid-query', __( 'Your query parameters are invalid' ) );
+	}
+
+	if ( count( $sql_terms ) || count( $user_ids ) ) {
+		$sql .= ' WHERE ';
+	}
+
+	if ( count( $sql_terms ) ) {
+		$sql .= '(' . implode( ' OR ', $sql_terms ) . ')';
+	}
+
+	if ( count( $sql_terms ) && count( $user_ids ) ) {
+		$sql .= ' AND ';
+	}
+
+	if ( count( $user_ids ) ) {
+		$sql .= '`ID` IN (' . join( ',', $user_ids ) . ')';
+	}
+
+	$sql .= " ORDER BY user_login LIMIT $limit";
+
+	$bb_last_countable_query = $sql;
+
+	do_action( 'bb_user_search', $sql, $args );
+
+	if ( ( $users = $bbdb->get_results( $sql ) ) && $append_meta ) {
+		return bb_append_meta( $users, 'user' );
+	}
+
+	return $users ? $users : false;
+}
+
+function bb_tag_search( $args = '' ) {
+	global $page, $wp_taxonomy_object;
+
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'search' => $args );
+
+	$defaults = array( 'search' => '', 'number' => false );
+
+	$args = wp_parse_args( $args );
+	if ( isset( $args['query'] ) )
+		$args['search'] = $args['query'];
+	if ( isset( $args['tags_per_page'] ) )
+		$args['number'] = $args['tags_per_page'];
+	unset($args['query'], $args['tags_per_page']);
+	$args = wp_parse_args( $args, $defaults );
+
+	extract( $args, EXTR_SKIP );
+
+	$number = (int) $number;
+	$search = trim( $search );
+	if ( strlen( $search ) < 3 )
+		return new WP_Error( 'invalid-query', __('Your search term was too short') );
+
+	$number = 0 < $number ? $number : bb_get_option( 'page_topics' );
+	if ( 1 < $page )
+		$offset = ( intval($page) - 1 ) * $number;
+
+	$args = array_merge( $args, compact( 'number', 'offset', 'search' ) );
+
+	$terms = $wp_taxonomy_object->get_terms( 'bb_topic_tag', $args );
+	if ( is_wp_error( $terms ) )
+		return false;
+
+	for ( $i = 0; isset($terms[$i]); $i++ )
+		_bb_make_tag_compat( $terms[$i] );
+
+	return $terms;
+}
+
+
+
+/* Slugs */
+
+function bb_slug_increment( $slug, $existing_slug, $slug_length = 255 ) {
+	if ( preg_match('/^.*-([0-9]+)$/', $existing_slug, $m) )
+		$number = (int) $m[1] + 1;
+	else
+		$number = 1;
+
+	$r = bb_encoded_utf8_cut( $slug, $slug_length - 1 - strlen($number) );
+	return apply_filters( 'bb_slug_increment', "$r-$number", $slug, $existing_slug, $slug_length );
+}
+
+function bb_get_id_from_slug( $table, $slug, $slug_length = 255 ) {
+	global $bbdb;
+	$tablename = $table . 's';
+
+	list($_slug, $sql) = bb_get_sql_from_slug( $table, $slug, $slug_length );
+
+	if ( !$_slug || !$sql )
+		return 0;
+
+	return (int) $bbdb->get_var( "SELECT ${table}_id FROM {$bbdb->$tablename} WHERE $sql" );
+}
+
+function bb_get_sql_from_slug( $table, $slug, $slug_length = 255 ) {
+	global $bbdb;
+
+	// Look for new style equiv of old style slug
+	$_slug = bb_slug_sanitize( (string) $slug );
+	if ( strlen( $_slug ) < 1 )
+		return '';
+
+	if ( strlen($_slug) > $slug_length && preg_match('/^.*-([0-9]+)$/', $_slug, $m) ) {
+		$_slug = bb_encoded_utf8_cut( $_slug, $slug_length - 1 - strlen($number) );
+		$number = (int) $m[1];
+		$_slug =  "$_slug-$number";
+	}
+
+	return array( $_slug, $bbdb->prepare( "${table}_slug = %s", $_slug ) );
+}	
+
+
+
+/* Utility */
+
+function bb_flatten_array( $array, $cut_branch = 0, $keep_child_array_keys = true ) {
+	if ( !is_array($array) )
+		return $array;
+	
+	if ( empty($array) )
+		return null;
+	
+	$temp = array();
+	foreach ( $array as $k => $v ) {
+		if ( $cut_branch && $k == $cut_branch )
+			continue;
+		if ( is_array($v) ) {
+			if ( $keep_child_array_keys ) {
+				$temp[$k] = true;
+			}
+			$temp += bb_flatten_array($v, $cut_branch, $keep_child_array_keys);
+		} else {
+			$temp[$k] = $v;
+		}
+	}
+	return $temp;
+}
+
+function bb_get_common_parts($string1 = false, $string2 = false, $delimiter = '', $reverse = false) {
+	if (!$string1 || !$string2) {
+		return false;
+	}
+	
+	if ($string1 === $string2) {
+		return $string1;
+	}
+	
+	$string1_parts = explode( $delimiter, (string) $string1 );
+	$string2_parts = explode( $delimiter, (string) $string2 );
+	
+	if ($reverse) {
+		$string1_parts = array_reverse( $string1_parts );
+		$string2_parts = array_reverse( $string2_parts );
+		ksort( $string1_parts );
+		ksort( $string2_parts );
+	}
+	
+	$common_parts = array();
+	foreach ( $string1_parts as $index => $part ) {
+		if ( isset( $string2_parts[$index] ) && $string2_parts[$index] == $part ) {
+			$common_parts[] = $part;
+		} else {
+			break;
+		}
+	}
+	
+	if (!count($common_parts)) {
+		return false;
+	}
+	
+	if ($reverse) {
+		$common_parts = array_reverse( $common_parts );
+	}
+	
+	return join( $delimiter, $common_parts );
+}
+
+function bb_get_common_domains($domain1 = false, $domain2 = false) {
+	if (!$domain1 || !$domain2) {
+		return false;
+	}
+	
+	$domain1 = strtolower( preg_replace( '@^https?://([^/]+).*$@i', '$1', $domain1 ) );
+	$domain2 = strtolower( preg_replace( '@^https?://([^/]+).*$@i', '$1', $domain2 ) );
+	
+	return bb_get_common_parts( $domain1, $domain2, '.', true );
+}
+
+function bb_get_common_paths($path1 = false, $path2 = false) {
+	if (!$path1 || !$path2) {
+		return false;
+	}
+	
+	$path1 = preg_replace('@^https?://[^/]+(.*)$@i', '$1', $path1);
+	$path2 = preg_replace('@^https?://[^/]+(.*)$@i', '$1', $path2);
+	
+	if ($path1 === $path2) {
+		return $path1;
+	}
+	
+	$path1 = trim( $path1, '/' );
+	$path2 = trim( $path2, '/' );
+	
+	$common_path = bb_get_common_parts( $path1, $path2, '/' );
+	
+	if ($common_path) {
+		return '/' . $common_path . '/';
+	} else {
+		return '/';
+	}
+}
+
+function bb_match_domains($domain1 = false, $domain2 = false) {
+	if (!$domain1 || !$domain2) {
+		return false;
+	}
+	
+	$domain1 = strtolower( preg_replace( '@^https?://([^/]+).*$@i', '$1', $domain1 ) );
+	$domain2 = strtolower( preg_replace( '@^https?://([^/]+).*$@i', '$1', $domain2 ) );
+	
+	if ( (string) $domain1 === (string) $domain2 ) {
+		return true;
+	}
+	
+	return false;
+}
+
+function bb_glob($pattern) {
+	// On fail return an empty array so that loops don't explode
+	
+	if (!$pattern)
+		return array();
+	
+	// May break if pattern contains forward slashes
+	$directory = dirname( $pattern );
+	
+	if (!$directory)
+		return array();
+	
+	if (!file_exists($directory))
+		return array();
+	
+	if (!is_dir($directory))
+		return array();
+	
+	if (!function_exists('glob'))
+		return array();
+	
+	if (!is_callable('glob'))
+		return array();
+	
+	$glob = glob($pattern);
+	
+	if (!is_array($glob))
+		$glob = array();
+	
+	return $glob;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-deprecated.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-deprecated.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-deprecated.php	(revision 5057)
@@ -0,0 +1,1193 @@
+<?php
+/**
+ * Deprecated functions from past bbPress versions. You shouldn't use these
+ * globals and functions and look for the alternatives instead. The functions
+ * and globals will be removed in a later version.
+ *
+ * @package bbPress
+ * @subpackage Deprecated
+ */
+
+
+
+function bb_specialchars( $text, $quotes = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'esc_html');
+	return esc_html( $text, $quotes );
+}
+
+function bb_make_clickable( $ret ) {
+	bb_log_deprecated('function', __FUNCTION__, 'make_clickable');
+	return make_clickable( $ret );
+}
+
+function bb_apply_filters($tag, $string, $filter = true) {
+	bb_log_deprecated('function', __FUNCTION__, 'apply_filters');
+	$args = func_get_args();
+	return call_user_func_array('apply_filters', $args);
+}
+
+function bb_add_filter($tag, $function_to_add, $priority = 10) {
+	bb_log_deprecated('function', __FUNCTION__, 'add_filter');
+	$args = func_get_args();
+	return call_user_func_array('add_filter', $args);
+}
+
+function bb_remove_filter($tag, $function_to_remove, $priority = 10) {
+	bb_log_deprecated('function', __FUNCTION__, 'remove_filter');
+	$args = func_get_args();
+	return call_user_func_array('remove_filter', $args);
+}
+
+function bb_do_action($tag) {
+	bb_log_deprecated('function', __FUNCTION__, 'do_action');
+	$args = func_get_args();
+	return call_user_func_array('do_action', $args);
+}
+
+function bb_add_action($tag, $function_to_add, $priority = 10) {
+	bb_log_deprecated('function', __FUNCTION__, 'add_action');
+	$args = func_get_args();
+	return call_user_func_array('add_action', $args);
+}
+
+function bb_remove_action($tag, $function_to_remove, $priority = 10) {
+	bb_log_deprecated('function', __FUNCTION__, 'remove_action');
+	$args = func_get_args();
+	return call_user_func_array('remove_action', $args);
+}
+
+function bb_add_query_arg() {
+	bb_log_deprecated('function', __FUNCTION__, 'add_query_arg');
+	$args = func_get_args();
+	return call_user_func_array('add_query_arg', $args);
+}
+
+function bb_remove_query_arg($key, $query = '') {
+	bb_log_deprecated('function', __FUNCTION__, 'remove_query_arg');
+	return remove_query_arg($key, $query);
+}
+
+if ( !function_exists('language_attributes') ) :
+function language_attributes( $xhtml = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_language_attributes');
+	bb_language_attributes( $xhtml );
+}
+endif;
+
+function cast_meta_value( $data ) {
+	bb_log_deprecated('function', __FUNCTION__, 'maybe_unserialize');
+	return maybe_unserialize( $data );
+}
+
+function option( $option ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_option');
+	return bb_option( $option );
+}
+
+// Use topic_time
+function topic_date( $format = '', $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmdate_i18n( $format, get_topic_timestamp( $id ) )');
+	echo bb_gmdate_i18n( $format, get_topic_timestamp( $id ) );
+}
+function get_topic_date( $format = '', $id = 0 ){
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmdate_i18n( $format, get_topic_timestamp( $id ) )');
+	return bb_gmdate_i18n( $format, get_topic_timestamp( $id ) );
+}
+function get_topic_timestamp( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmtstrtotime( $topic->topic_time )');
+	global $topic;
+	if ( $id )
+		$topic = get_topic( $id );
+	return bb_gmtstrtotime( $topic->topic_time );
+}
+
+// Use topic_start_time
+function topic_start_date( $format = '', $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmdate_i18n( $format, get_topic_start_timestamp( $id ) )');
+	echo bb_gmdate_i18n( $format, get_topic_start_timestamp( $id ) );
+}
+function get_topic_start_timestamp( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmtstrtotime( $topic->topic_start_time )');
+	global $topic;
+	if ( $id )
+		$topic = get_topic( $id );
+	return bb_gmtstrtotime( $topic->topic_start_time );
+}
+
+// Use bb_post_time
+function post_date( $format ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmdate_i18n( $format, bb_gmtstrtotime( $bb_post->post_time ) )');
+	global $bb_post;
+	echo bb_gmdate_i18n( $format, bb_gmtstrtotime( $bb_post->post_time ) );
+}
+function get_post_timestamp() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_gmtstrtotime( $bb_post->post_time )');
+	global $bb_post;
+	return bb_gmtstrtotime( $bb_post->post_time );
+}
+
+function get_inception() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_inception( \'timestamp\' )');
+	return bb_get_inception( 'timestamp' );
+}
+
+function forum_dropdown( $c = false, $a = false ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_forum_dropdown');
+	bb_forum_dropdown( $c, $a );
+}
+
+function get_ids_by_role( $role = 'moderator', $sort = 0, $limit_str = '' ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_ids_by_role');
+	return bb_get_ids_by_role( $role , $sort , $limit_str);
+}
+
+function get_deleted_posts( $page = 1, $limit = false, $status = 1, $topic_status = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+}
+
+function bozo_posts( $where ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_posts');
+	return bb_bozo_posts( $where );
+}
+
+function bozo_topics( $where ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_topics');
+	return bb_bozo_topics( $where );
+}
+
+function get_bozos( $page = 1 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_bozos');
+	return bb_get_bozos($page);
+}
+
+function current_user_is_bozo( $topic_id = false ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_current_user_is_bozo');
+	return bb_current_user_is_bozo( $topic_id );
+}
+
+function bozo_pre_permalink() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_pre_permalink');
+	return bb_bozo_pre_permalink();
+}
+
+function bozo_latest_filter() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_latest_filter');
+	return bb_bozo_latest_filter();
+}
+
+function bozo_topic_db_filter() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_topic_db_filter');
+	return bb_bozo_topic_db_filter();
+}
+
+function bozo_profile_db_filter() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_profile_db_filter');
+	return bb_bozo_profile_db_filter();
+}
+
+function bozo_recount_topics() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_recount_topics');
+	return bb_bozo_recount_topics();
+}
+
+function bozo_recount_users() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_recount_users');
+	return bb_bozo_recount_users();
+}
+
+function bozo_post_del_class( $status ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_post_del_class');
+	return bb_bozo_post_del_class( $status );
+}
+
+function bozo_add_recount_list() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_add_recount_list');
+	return bb_bozo_add_recount_list() ;
+}
+
+function bozo_topic_pages_add( $add ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_topic_pages_add');
+	return bb_bozo_topic_pages_add( $add );
+}
+
+function bozo_get_topic_posts( $topic_posts ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_get_topic_posts');
+	return bb_bozo_get_topic_posts( $topic_posts ) ;
+}
+
+function bozo_new_post( $post_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_new_post');
+	return bb_bozo_new_post( $post_id );
+}
+
+function bozo_pre_post_status( $status, $post_id, $topic_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_pre_post_status');
+	return bb_bozo_pre_post_status( $status, $post_id, $topic_id ) ;
+}
+
+function bozo_delete_post( $post_id, $new_status, $old_status ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_delete_post');
+	return bb_bozo_delete_post( $post_id, $new_status, $old_status ) ;
+}
+
+function bozon( $user_id, $topic_id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozon');
+	return bb_bozon( $user_id, $topic_id ) ;
+}
+
+function fermion( $user_id, $topic_id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_fermion');
+	return bb_fermion( $user_id, $topic_id ) ;
+}
+
+function bozo_profile_admin_keys( $a ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_profile_admin_keys');
+	return bb_bozo_profile_admin_keys( $a ) ;
+}
+function bozo_add_admin_page() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_add_admin_page');
+	return bb_bozo_add_admin_page() ;
+}
+
+function bozo_admin_page() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_bozo_admin_page');
+	return bb_bozo_admin_page() ;
+}
+
+function encodeit( $matches ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_encodeit');
+	return bb_encodeit( $matches ) ;
+}
+
+function decodeit( $matches ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_decodeit');
+	return bb_decodeit( $matches ) ;
+}
+
+function code_trick( $text ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_code_trick');
+	return bb_code_trick( $text ) ;
+}
+
+function code_trick_reverse( $text ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_code_trick_reverse');
+	return bb_code_trick_reverse( $text ) ;
+}
+
+function encode_bad( $text ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_encode_bad');
+	return bb_encode_bad( $text ) ;
+}
+
+function user_sanitize( $text, $strict = false ) {
+	bb_log_deprecated('function', __FUNCTION__, 'sanitize_user');
+	return sanitize_user( $text, $strict );
+}
+
+function bb_user_sanitize( $text, $strict = false ) {
+	bb_log_deprecated('function', __FUNCTION__, 'sanitize_user');
+	return sanitize_user( $text, $strict );
+}
+
+function utf8_cut( $utf8_string, $length ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_utf8_cut');
+	return bb_utf8_cut( $utf8_string, $length ) ;
+}
+
+function tag_sanitize( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_sanitize');
+	return bb_tag_sanitize( $tag ) ;
+}
+
+function sanitize_with_dashes( $text, $length = 200 ) { // Multibyte aware
+	bb_log_deprecated('function', __FUNCTION__, 'bb_sanitize_with_dashes');
+	return bb_sanitize_with_dashes( $text, $length ) ;
+}
+
+function bb_make_feed( $link ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no aternative');
+	return trim( $link );
+}
+
+function show_context( $term, $text ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_show_context');
+	return bb_show_context( $term, $text );
+}
+
+function closed_title( $title ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_closed_label');
+	return bb_closed_label( $title );
+}
+
+// Closed label now applied using bb_topic_labels filters
+function bb_closed_title( $title ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_closed_label');
+	return bb_closed_label( $title );
+}
+
+function make_link_view_all( $link ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_make_link_view_all');
+	return bb_make_link_view_all( $link );
+}
+
+function remove_topic_tag( $tag_id, $user_id, $topic_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_remove_topic_tag');
+	return bb_remove_topic_tag( $tag_id, $user_id, $topic_id );
+}
+
+function add_topic_tag( $topic_id, $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_add_topic_tag');
+	return bb_add_topic_tag( $topic_id, $tag );
+}
+
+function add_topic_tags( $topic_id, $tags ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_add_topic_tags');
+	return bb_add_topic_tags( $topic_id, $tags );
+}
+
+function create_tag( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_create_tag');
+	return bb_create_tag( $tag );
+}
+
+function destroy_tag( $tag_id, $recount_topics = true ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_destroy_tag');
+	return bb_destroy_tag( $tag_id, $recount_topics );
+}
+
+if ( !function_exists( 'get_tag_id' ) ) :
+function get_tag_id( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_id');
+	return bb_get_tag_id( $tag );
+}
+endif;
+
+if ( !function_exists( 'get_tag' ) ) :
+function get_tag( $tag_id, $user_id = 0, $topic_id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag');
+	return bb_get_tag( $tag_id, $user_id, $topic_id );
+}
+endif;
+
+if ( !function_exists( 'get_tag_by_name' ) ) :
+function get_tag_by_name( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_by_name');
+	return bb_get_tag( $tag );
+}
+endif;
+
+function get_topic_tags( $topic_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_topic_tags');
+	return bb_get_topic_tags( $topic_id );
+}
+
+function get_user_tags( $topic_id, $user_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_user_tags');
+	return bb_get_user_tags( $topic_id, $user_id );
+}
+
+function get_other_tags( $topic_id, $user_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_other_tags');
+	return bb_get_other_tags( $topic_id, $user_id );
+}
+
+function get_public_tags( $topic_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_public_tags');
+	return bb_get_public_tags( $topic_id );
+}
+
+function get_tagged_topic_ids( $tag_id ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tagged_topic_ids');
+	return bb_get_tagged_topic_ids( $tag_id );
+}
+
+if ( !function_exists( 'get_top_tags' ) ) :
+function get_top_tags( $recent = true, $limit = 40 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_top_tags');
+	return bb_get_top_tags( array( 'number' => $limit ) );
+}
+endif;
+
+if ( !function_exists( 'get_tag_name' ) ) :
+function get_tag_name( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_name');
+	return bb_get_tag_name( $id );
+}
+endif;
+
+if ( !function_exists( 'tag_name' ) ) :
+function tag_name( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_name');
+	bb_tag_name( $id );
+}
+endif;
+
+if ( !function_exists( 'get_tag_rss_link' ) ) :
+function get_tag_rss_link( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_posts_rss_link');
+	return bb_get_tag_posts_rss_link( $id );
+}
+endif;
+
+if ( !function_exists( 'tag_rss_link' ) ) :
+function tag_rss_link( $id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_posts_rss_link');
+	bb_tag_posts_rss_link( $id );
+}
+endif;
+
+if ( !function_exists( 'get_tag_page_link' ) ) :
+function get_tag_page_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_page_link');
+	bb_get_tag_page_link();
+}
+endif;
+
+if ( !function_exists( 'tag_page_link' ) ) :
+function tag_page_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_page_link');
+	bb_tag_page_link();
+}
+endif;
+
+if ( !function_exists( 'get_tag_remove_link' ) ) :
+function get_tag_remove_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_remove_link');
+	bb_get_tag_remove_link();
+}
+endif;
+
+if ( !function_exists( 'tag_remove_link' ) ) :
+function tag_remove_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_remove_link');
+	bb_tag_remove_link();
+}
+endif;
+
+if ( !function_exists( 'tag_heat_map' ) ) :
+function tag_heat_map( $args = '' ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_heat_map (with variations to arguments)');
+	$defaults = array( 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'limit' => 45, 'format' => 'flat' );
+	$args = wp_parse_args( $args, $defaults );
+
+	if ( 1 < $fn = func_num_args() ) : // For back compat
+		$args['smallest'] = func_get_arg(0);
+		$args['largest']  = func_get_arg(1);
+		$args['unit']     = 2 < $fn ? func_get_arg(2) : $unit;
+		$args['limit']    = 3 < $fn ? func_get_arg(3) : $limit;
+	endif;
+
+	bb_tag_heat_map( $args );
+}
+endif;
+
+function get_bb_location() {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	$r = bb_get_location();
+	if ( !$r )
+		$r = apply_filters( 'get_bb_location', '' ); // Deprecated filter
+	return $r;
+}
+
+function bb_parse_args( $args, $defaults = '' ) {
+	bb_log_deprecated('function', __FUNCTION__, 'wp_parse_args');
+	return wp_parse_args( $args, $defaults );
+}
+
+if ( !function_exists( 'is_tag' ) ) :
+function is_tag() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_is_tag');
+	return bb_is_tag();
+}
+endif;
+
+if ( !function_exists( 'is_tags' ) ) :
+function is_tags() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_is_tags');
+	return bb_is_tags();
+}
+endif;
+
+if ( !function_exists( 'tag_link' ) ) :
+function tag_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_link');
+	bb_tag_link();
+}
+endif;
+
+if ( !function_exists( 'tag_link_base' ) ) :
+function tag_link_base() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_tag_link_base');
+	bb_tag_link_base();
+}
+endif;
+
+if ( !function_exists( 'get_tag_link' ) ) :
+function get_tag_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_link');
+	bb_get_tag_link();
+}
+endif;
+
+if ( !function_exists( 'get_tag_link_base' ) ) :
+function get_tag_link_base() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag_link_base');
+	bb_get_tag_link_base();
+}
+endif;
+
+// It's not omnipotent
+function bb_path_to_url( $path ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return apply_filters( 'bb_path_to_url', bb_convert_path_base( $path, BB_PATH, bb_get_uri(null, null, BB_URI_CONTEXT_TEXT) ), $path );
+}
+
+// Neither is this one
+function bb_url_to_path( $url ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return apply_filters( 'bb_url_to_path', bb_convert_path_base( $url, bb_get_uri(null, null, BB_URI_CONTEXT_TEXT), BB_PATH ), $url );
+}
+
+function bb_convert_path_base( $path, $from_base, $to_base ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	$last_char = $path{strlen($path)-1};
+	if ( '/' != $last_char && '\\' != $last_char )
+		$last_char = '';
+
+	list($from_base, $to_base) = bb_trim_common_path_right($from_base, $to_base);
+
+	if ( 0 === strpos( $path, $from_base ) )
+		$r = $to_base . substr($path, strlen($from_base)) . $last_char;
+	else
+		return false;
+
+	$r = str_replace(array('//', '\\\\'), array('/', '\\'), $r);
+	$r = preg_replace('|:/([^/])|', '://$1', $r);
+
+	return $r;
+}
+
+function bb_trim_common_path_right( $one, $two ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	$root_one = false;
+	$root_two = false;
+
+	while ( false === $root_one ) {
+		$base_one = basename($one);
+		$base_two = basename($two);
+		if ( !$base_one || !$base_two )
+			break;		
+		if ( $base_one == $base_two ) {
+			$one = dirname($one);
+			$two = dirname($two);
+		} else {
+			$root_one = $one;
+			$root_two = $two;
+		}
+	}
+
+	return array($root_one, $root_two);
+}
+
+function deleted_topics( $where ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return preg_replace( '/(\w+\.)?topic_status = ["\']?0["\']?/', "\\1topic_status = 1", $where);
+}
+
+function no_replies( $where ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return $where . ' AND topic_posts = 1 ';
+}
+
+function untagged( $where ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return $where . ' AND tag_count = 0 ';
+}
+
+function get_views() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_views');
+	return bb_get_views();
+}
+
+if ( !function_exists( 'balanceTags' ) ) :
+function balanceTags( $text ) {
+	bb_log_deprecated('function', __FUNCTION__, 'force_balance_tags');
+	return force_balance_tags( $text );
+}
+endif;
+
+// With no extra arguments, converts array of objects into object of arrays
+// With extra arguments corresponding to name of object properties, returns array of arrays:
+//     list($a, $b) = bb_pull_cols( $obj_array, 'a', 'b' );
+function bb_pull_cols( $obj_array ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	$r = new stdClass;
+	foreach ( array_keys($obj_array) as $o )
+		foreach ( get_object_vars( $obj_array[$o] ) as $k => $v )
+			$r->{$k}[] = $v;
+
+	if ( 1 == func_num_args() )
+		return $r;
+
+	$args = func_get_args();
+	$args = array_splice($args, 1);
+
+	$a = array();
+	foreach ( $args as $arg )
+		$a[] = $r->$arg;
+	return $a;
+}
+
+// $length parameter is deprecated
+function bb_random_pass( $length ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_generate_password');
+	if ( 12 < (int) $length ) {
+		$length = 12;
+	}
+	return bb_generate_password( $length );
+}
+
+// Old RSS related functions
+function get_recent_rss_link() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_posts_rss_link');
+	return bb_get_posts_rss_link();
+}
+
+function forum_rss_link( $forum_id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_forum_posts_rss_link');
+	bb_forum_posts_rss_link( $forum_id );
+}
+
+function get_forum_rss_link( $forum_id = 0 ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_forum_posts_rss_link');
+	return bb_get_forum_posts_rss_link( $forum_id );
+}
+
+function bb_register_activation_hook($file, $function) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_register_plugin_activation_hook');
+	bb_register_plugin_activation_hook($file, $function);
+}
+
+function bb_register_deactivation_hook($file, $function) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_register_plugin_deactivation_hook');
+	bb_register_plugin_deactivation_hook($file, $function);
+}
+
+function bb_enqueue_script( $handle, $src = false, $deps = array(), $ver = false ) {
+	bb_log_deprecated('function', __FUNCTION__, 'wp_enqueue_script');
+	wp_enqueue_script( $handle, $src, $deps, $ver );
+}
+
+function bb_get_user_by_name( $name ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_user');
+	return bb_get_user( $name, array( 'by' => 'login' ) );
+}
+
+function bb_user_exists( $user ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_user');
+	return bb_get_user( $user );
+}
+
+function bb_maybe_serialize( $string ) {
+	bb_log_deprecated('function', __FUNCTION__, 'maybe_serialize');
+	return maybe_serialize( $string );
+}
+
+function bb_maybe_unserialize( $string ) {
+	bb_log_deprecated('function', __FUNCTION__, 'maybe_unserialize');
+	return maybe_unserialize( $string );
+}
+
+function bb_get_active_theme_folder() {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_active_theme_directory');
+	return apply_filters( 'bb_get_active_theme_folder', bb_get_active_theme_directory() );
+}
+
+function bb_tag_sanitize( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_pre_term_slug');
+	return bb_pre_term_slug( $tag );
+}
+
+function bb_get_tag_by_name( $tag ) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_get_tag');
+	return bb_get_tag( $tag );
+}
+
+function bb_dbDelta($queries, $execute = true) {
+	bb_log_deprecated('function', __FUNCTION__, 'bb_sql_delta');
+	return bb_sql_delta($queries, $execute);
+}
+
+function bb_make_db_current() {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return false;
+}
+
+function bb_maybe_add_column( $table_name, $column_name, $create_ddl ) {
+	bb_log_deprecated('function', __FUNCTION__, 'no alternative');
+	return false;
+}
+
+class BB_Cache {
+	var $use_cache = false;
+	var $flush_freq = 100;
+	var $flush_time = 172800; // 2 days
+
+	function get_user( $user_id, $use_cache = true ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_get_user');
+		return bb_get_user( $user_id );
+	}
+
+	function append_current_user_meta( $user ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_append_meta');
+		return bb_append_meta( $user, 'user' );
+	}
+
+	function append_user_meta( $user ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_append_meta');
+		return bb_append_meta( $user, 'user' );
+	}
+
+	// NOT bbdb::prepared
+	function cache_users( $ids, $use_cache = true ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_cache_users');
+		return bb_cache_users( $ids );
+	}
+
+	// NOT bbdb::prepared
+	function get_topic( $topic_id, $use_cache = true ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'get_topic');
+		return get_topic( $topic_id, $use_cache );
+	}
+
+	// NOT bbdb::prepared
+	function get_thread( $topic_id, $page = 1, $reverse = 0 ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'get_thread');
+		return get_thread( $topic_id, $page, $reverse );
+	}
+
+	// NOT bbdb::prepared
+	function cache_posts( $query ) { // soft cache
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_cache_posts');
+		return bb_cache_posts( $query );
+	}
+
+	// NOT bbdb::prepared
+	function get_forums() {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_get_forums');
+		return bb_get_forums();
+	}
+
+	function get_forum( $forum_id ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'bb_get_forum');
+		return bb_get_forum( $forum_id );
+	}
+
+	function read_cache($file) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return false;
+	}
+
+	function write_cache($file, $data) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return false;
+	}
+
+	function flush_one( $type, $id = false, $page = 0 ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return true;
+	}
+
+	function flush_many( $type, $id, $start = 0 ) {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return true;
+	}
+
+	function flush_old() {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return true;
+	}
+
+	function flush_all() {
+		bb_log_deprecated('class::function', __CLASS__ . '::' . __FUNCTION__, 'no alternative');
+		return true;
+	}
+
+}
+
+function new_topic( $args = null ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_new_topic_link' );
+	bb_new_topic_link( $args );
+}
+
+function bb_upgrade_1060() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'no alternative' );
+}
+
+if ( !function_exists( 'paginate_links' ) ) : // Deprecated in bbPress not WordPress
+function paginate_links( $args = '' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_paginate_links' );
+	return bb_paginate_links( $args );
+}
+endif;
+
+if ( !function_exists('wp_clear_auth_cookie') ) : // Deprecated in bbPress not WordPress
+function wp_clear_auth_cookie() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_clear_auth_cookie' );
+	bb_clear_auth_cookie();
+}
+endif;
+
+if ( !function_exists( 'wp_validate_auth_cookie' ) ) : // Deprecated in bbPress not WordPress
+function wp_validate_auth_cookie( $cookie = '', $scheme = 'auth' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_validate_auth_cookie' );
+	return bb_validate_auth_cookie( $cookie, $scheme );
+}
+endif;
+
+if ( !function_exists( 'wp_set_auth_cookie' ) ) : // Deprecated in bbPress not WordPress
+function wp_set_auth_cookie( $user_id, $remember = false, $secure = '' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_set_auth_cookie' );
+	bb_set_auth_cookie( $user_id, $remember, $secure );
+}
+endif;
+
+if ( !function_exists( 'wp_salt' ) ) : // Deprecated in bbPress not WordPress
+function wp_salt( $scheme = 'auth' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_salt' );
+	return bb_salt( $scheme );
+}
+endif;
+
+if ( !function_exists( 'wp_hash' ) ) : // Deprecated in bbPress not WordPress
+function wp_hash( $data, $scheme = 'auth' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_hash' );
+	return bb_hash( $data, $scheme );
+}
+endif;
+
+if ( !function_exists( 'wp_hash_password' ) ) : // Deprecated in bbPress not WordPress
+function wp_hash_password( $password ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_hash_password' );
+	return bb_hash_password( $password );
+}
+endif;
+
+if ( !function_exists( 'wp_check_password') ) : // Deprecated in bbPress not WordPress
+function wp_check_password( $password, $hash, $user_id = '' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_check_password' );
+	return bb_check_password( $password, $hash, $user_id );
+}
+endif;
+
+if ( !function_exists( 'wp_generate_password' ) ) : // Deprecated in bbPress not WordPress
+function wp_generate_password( $length = 12, $special_chars = true ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_generate_password' );
+	return bb_generate_password( $length, $special_chars );
+}
+endif;
+
+if ( !class_exists( 'WP_User' ) ) : // Deprecated in BackPress not WordPress
+class WP_User extends BP_User {
+	function WP_User( $id, $name = '' ) {
+		return parent::BP_User( $id, $name );
+	}
+}
+endif;
+
+function bb_sql_get_column_definition( $column_data ) {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	bb_log_deprecated( 'function', __FUNCTION__, 'BP_SQL_Schema_Parser::get_column_definition' );
+	return BP_SQL_Schema_Parser::get_column_definition( $column_data );
+}
+
+function bb_sql_get_index_definition( $index_data ) {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	bb_log_deprecated( 'function', __FUNCTION__, 'BP_SQL_Schema_Parser::get_index_definition' );
+	return BP_SQL_Schema_Parser::get_index_definition( $index_data );
+}
+
+function bb_sql_describe_table( $query ) {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	bb_log_deprecated( 'function', __FUNCTION__, 'BP_SQL_Schema_Parser::describe_table' );
+	return BP_SQL_Schema_Parser::describe_table( $query );
+}
+
+function bb_sql_parse( $sql ) {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	bb_log_deprecated( 'function', __FUNCTION__, 'BP_SQL_Schema_Parser::parse' );
+	return BP_SQL_Schema_Parser::parse( $sql );
+}
+
+function bb_sql_delta( $queries, $execute = true ) {
+	require_once( BACKPRESS_PATH . 'class.bp-sql-schema-parser.php' );
+	bb_log_deprecated( 'function', __FUNCTION__, 'BP_SQL_Schema_Parser::delta' );
+	global $bbdb;
+	return BP_SQL_Schema_Parser::delta( $bbdb, $queries, false, $execute );
+}
+
+function is_front() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_front' );
+	return bb_is_front();
+}
+
+function is_forum() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_forum' );
+	return bb_is_forum();
+}
+
+function is_bb_tags() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_tags' );
+	return bb_is_tags();
+}
+
+function is_bb_tag() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_tag' );
+	return bb_is_tag();
+}
+
+function is_topic_edit() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_topic_edit' );
+	return bb_is_topic_edit();
+}
+
+function is_topic() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_topic' );
+	return bb_is_topic();
+}
+
+function is_bb_feed() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_feed' );
+	return bb_is_feed();
+}
+
+function is_bb_search() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_search' );
+	return bb_is_search();
+}
+
+function is_bb_profile() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_profile' );
+	return bb_is_profile();
+}
+
+function is_bb_favorites() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_favorites' );
+	return bb_is_favorites();
+}
+
+function is_view() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_view' );
+	return bb_is_view();
+}
+
+function is_bb_stats() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_statistics' );
+	return bb_is_statistics();
+}
+
+function is_bb_admin() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_is_admin' );
+	return bb_is_admin();
+}
+
+function bb_verify_email( $email, $check_dns = false )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'is_email' );
+	return is_email( $email, $check_dns );
+}
+
+function bb_tag_rss_link( $tag_id = 0, $context = 0 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_tag_posts_rss_link' );
+	return bb_tag_posts_rss_link( $tag_id, $context );
+}
+
+function bb_get_tag_rss_link( $tag_id = 0, $context = 0 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_tag_posts_rss_link' );
+	return bb_get_tag_posts_rss_link( $tag_id, $context );
+}
+
+function rename_tag( $tag_id, $tag_name )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_rename_tag' );
+	return bb_rename_tag( $tag_id, $tag_name );
+}
+
+function merge_tags( $old_id, $new_id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_merge_tags' );
+	return bb_merge_tags( $old_id, $new_id );
+}
+
+function bb_related_tags( $_tag = false, $number = 0 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'no alternative' );
+	return false;
+}
+
+function bb_related_tags_heat_map( $args = '' )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'no alternative' );
+	return;
+}
+
+function bb_cache_javascript_headers()
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'cache_javascript_headers' );
+	cache_javascript_headers();
+}
+
+function bb_is_ssl()
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'is_ssl' );
+	return is_ssl();
+}
+
+function bb_force_ssl_user_forms( $force = '' )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'force_ssl_login' );
+	return force_ssl_login( $force );
+}
+
+function bb_force_ssl_admin( $force = '' )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'force_ssl_admin' );
+	return force_ssl_admin( $force );
+}
+
+function get_forums( $args = null )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_forums' );
+	return bb_get_forums( $args );
+}
+
+function get_forum( $id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_forum' );
+	return bb_get_forum( $id );
+}
+
+function get_latest_posts( $limit = 0, $page = 1 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_latest_posts' );
+	return bb_get_latest_posts( $limit, $page );
+}
+
+function get_latest_forum_posts( $forum_id, $limit = 0, $page = 1 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_latest_forum_posts' );
+	return bb_get_latest_forum_posts( $forum_id, $limit, $page );
+}
+
+function update_post_positions( $topic_id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_update_post_positions' );
+	return bb_update_post_positions( $topic_id );
+}
+
+function topics_replied_on_undelete_post( $post_id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_topics_replied_on_undelete_post' );
+	return bb_topics_replied_on_undelete_post( $post_id );
+}
+
+function post_author_cache( $posts )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_post_author_cache' );
+	return bb_post_author_cache( $posts );
+}
+
+function get_recent_user_replies( $user_id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_recent_user_replies' );
+	return bb_get_recent_user_replies( $user_id );
+}
+
+function no_where( $where )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_no_where' );
+	return bb_no_where( $where );
+}
+
+function get_path( $level = 1, $base = false, $request = false )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_path' );
+	return bb_get_path( $level, $base, $request );
+}
+
+function add_profile_tab( $tab_title, $users_cap, $others_cap, $file, $arg = false )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_add_profile_tab' );
+	return bb_add_profile_tab( $tab_title, $users_cap, $others_cap, $file, $arg );
+}
+
+function can_access_tab( $profile_tab, $viewer_id, $owner_id )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_can_access_tab' );
+	return bb_can_access_tab( $profile_tab, $viewer_id, $owner_id );
+}
+
+function get_profile_info_keys( $context = null )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_profile_info_keys' );
+	return bb_get_profile_info_keys( $context );
+}
+
+function get_profile_admin_keys( $context = null )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_profile_admin_keys' );
+	return bb_get_profile_admin_keys( $context );
+}
+
+function get_assignable_caps()
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_assignable_caps' );
+	return bb_get_assignable_caps();
+}
+
+function get_page_number( $item, $per_page = 0 )
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_page_number' );
+	return bb_get_page_number( $item, $per_page );
+}
+
+function get_recent_registrants( $num = 10 )
+{
+	bb_log_deprecated('function', __FUNCTION__, 'no aternative');
+	return;
+}
+
+if ( !function_exists( 'get_total_users' ) ) {
+	function get_total_users()
+	{
+		bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_total_users' );
+		return bb_get_total_users();
+	}
+}
+
+function total_users()
+{
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_total_users' );
+	bb_total_users();
+}
+
+if ( !function_exists( 'update_user_status' ) ) {
+	function update_user_status( $user_id, $user_status = 0 )
+	{
+		bb_log_deprecated( 'function', __FUNCTION__, 'bb_update_user_status' );
+		return bb_update_user_status( $user_id, $user_status );
+	}
+}
+
+function bb_get_current_commenter() {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_get_current_poster' );
+	extract( bb_get_current_poster() );
+	return array( 'comment_author' => $post_author, 'comment_email' => $post_author_email, 'comment_author_url' => $post_author_url );
+}
+
+function bb_check_comment_flood( $ip = '', $email = '', $date = '' ) {
+	bb_log_deprecated( 'function', __FUNCTION__, 'bb_check_post_flood' );
+	bb_check_post_flood();
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-formatting.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-formatting.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-formatting.php	(revision 5057)
@@ -0,0 +1,405 @@
+<?php
+
+function bb_autop($pee, $br = 1) { // Reduced to be faster
+	$pee = $pee . "\n"; // just to make things a little easier, pad the end
+	$pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
+	// Space things out a little
+	$pee = preg_replace('!(<(?:ul|ol|li|blockquote|pre|p)[^>]*>)!', "\n$1", $pee); 
+	$pee = preg_replace('!(</(?:ul|ol|li|blockquote|pre|p)>)!', "$1\n", $pee);
+	$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines 
+	$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
+	$pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $pee); // make paragraphs, including one at the end 
+	$pee = preg_replace('|<p>\s*?</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace 
+	$pee = preg_replace('!<p>\s*(</?(?:ul|ol|li|blockquote|p)[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
+	$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
+	$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
+	$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
+	$pee = preg_replace('!<p>\s*(</?(?:ul|ol|li|blockquote|p)[^>]*>)!', "$1", $pee);
+	$pee = preg_replace('!(</?(?:ul|ol|li|blockquote|p)[^>]*>)\s*</p>!', "$1", $pee); 
+	if ($br) $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
+	$pee = preg_replace('!(</?(?:ul|ol|li|blockquote|p)[^>]*>)\s*<br />!', "$1", $pee);
+	$pee = preg_replace('!<br />(\s*</?(?:p|li|ul|ol)>)!', '$1', $pee);
+	if ( false !== strpos( $pee, '<pre' ) )
+		$pee = preg_replace_callback('!(<pre.*?>)(.*?)</pre>!is', 'clean_pre', $pee );
+	return $pee; 
+}
+
+function bb_encodeit( $matches ) {
+	$text = trim($matches[2]);
+	$text = htmlspecialchars($text, ENT_QUOTES);
+	$text = str_replace(array("\r\n", "\r"), "\n", $text);
+	$text = preg_replace("|\n\n\n+|", "\n\n", $text);
+	$text = str_replace('&amp;amp;', '&amp;', $text);
+	$text = str_replace('&amp;lt;', '&lt;', $text);
+	$text = str_replace('&amp;gt;', '&gt;', $text);
+	$text = "<code>$text</code>";
+	if ( "`" != $matches[1] )
+		$text = "<pre>$text</pre>";
+	return $text;
+}
+
+function bb_decodeit( $matches ) {
+	$text = $matches[2];
+	$trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
+	$text = strtr($text, $trans_table);
+	$text = str_replace('<br />', '<coded_br />', $text);
+	$text = str_replace('<p>', '<coded_p>', $text);
+	$text = str_replace('</p>', '</coded_p>', $text);
+	$text = str_replace(array('&#38;','&amp;'), '&', $text);
+	$text = str_replace('&#39;', "'", $text);
+	if ( '<pre><code>' == $matches[1] )
+		$text = "\n$text\n";
+	return "`$text`";
+}
+
+function bb_code_trick( $text ) {
+	$text = str_replace(array("\r\n", "\r"), "\n", $text);
+	$text = preg_replace_callback("|(`)(.*?)`|", 'bb_encodeit', $text);
+	$text = preg_replace_callback("!(^|\n)`(.*?)`!s", 'bb_encodeit', $text);
+	return $text;
+}
+
+function bb_code_trick_reverse( $text ) {
+	$text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", 'bb_decodeit', $text);
+	$text = str_replace(array('<p>', '<br />'), '', $text);
+	$text = str_replace('</p>', "\n", $text);
+	$text = str_replace('<coded_br />', '<br />', $text);
+	$text = str_replace('<coded_p>', '<p>', $text);
+	$text = str_replace('</coded_p>', '</p>', $text);
+	return $text;
+}
+
+function _bb_encode_bad_empty(&$text, $key, $preg) {
+	if (strpos($text, '`') !== 0)
+		$text = preg_replace("|&lt;($preg)\s*?/*?&gt;|i", '<$1 />', $text);
+}
+
+function _bb_encode_bad_normal(&$text, $key, $preg) {
+	if (strpos($text, '`') !== 0)
+		$text = preg_replace("|&lt;(/?$preg)&gt;|i", '<$1>', $text);
+}
+
+function bb_encode_bad( $text ) {
+	$text = wp_specialchars( $text, ENT_NOQUOTES );
+
+	$text = preg_split('@(`[^`]*`)@m', $text, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE);
+
+	$allowed = bb_allowed_tags();
+	$empty = array( 'br' => true, 'hr' => true, 'img' => true, 'input' => true, 'param' => true, 'area' => true, 'col' => true, 'embed' => true );
+
+	foreach ( $allowed as $tag => $args ) {
+		$preg = $args ? "$tag(?:\s.*?)?" : $tag;
+
+		if ( isset( $empty[$tag] ) )
+			array_walk($text, '_bb_encode_bad_empty', $preg);
+		else
+			array_walk($text, '_bb_encode_bad_normal', $preg);
+	}
+
+	return join('', $text);
+}
+
+function bb_filter_kses($data) {
+	$allowedtags = bb_allowed_tags();
+	return wp_kses($data, $allowedtags);
+}
+
+function bb_allowed_tags() {
+	$tags = array(
+		'a' => array(
+			'href' => array(),
+			'title' => array(),
+			'rel' => array()),
+		'blockquote' => array('cite' => array()),
+		'br' => array(),
+		'code' => array(),
+		'pre' => array(),
+		'em' => array(),
+		'strong' => array(),
+		'ul' => array(),
+		'ol' => array(),
+		'li' => array()
+	);
+	return apply_filters( 'bb_allowed_tags', $tags );
+}
+
+function bb_rel_nofollow( $text ) {
+	return preg_replace_callback('|<a (.+?)>|i', 'bb_rel_nofollow_callback', $text);
+}
+
+function bb_rel_nofollow_callback( $matches ) {
+	$text = $matches[1];
+	$text = str_replace(array(' rel="nofollow"', " rel='nofollow'"), '', $text);
+	return "<a $text rel=\"nofollow\">";
+}
+
+// Should be able to take both escaped and unescaped data
+function bb_trim_for_db( $string, $length ) {
+	$_string = $string;
+	if ( seems_utf8( $string ) ) {
+		$string = bb_utf8_cut( $string, $length );
+		// if we have slashes at the end, make sure we have a reasonable number of them
+		if ( preg_match( '#[^\\\\](\\\\+)$#', $string, $matches ) ) {
+			$end = stripslashes($matches[1]);
+			$end = addslashes($end);
+			$string = trim( $string, '\\' ) . $end;
+		}
+	}
+	return apply_filters( 'bb_trim_for_db', $string, $_string, $length );
+}
+
+// Reduce utf8 string to $length in single byte character equivalents without breaking multibyte characters
+function bb_utf8_cut( $utf8_string, $length = 0 ) {
+	if ( $length < 1 )
+		return $utf8_string;
+
+	$unicode = '';
+	$chars = array();
+	$num_octets = 1;
+
+	for ($i = 0; $i < strlen( $utf8_string ); $i++ ) {
+
+		$value = ord( $utf8_string[ $i ] );
+
+		if ( 128 > $value ) {
+			if ( strlen($unicode) + 1 > $length )
+				break; 
+			$unicode .= $utf8_string[ $i ];
+		} else {
+			if ( count( $chars ) == 0 )
+				$num_octets = ( 224 > $value ) ? 2 : 3;
+
+			$chars[] = $utf8_string[ $i ];
+			if ( strlen($unicode) + $num_octets > $length )
+				break;
+			if ( count( $chars ) == $num_octets ) {
+				$unicode .= join('', $chars);
+				$chars = array();
+				$num_octets = 1;
+			}
+		}
+	}
+
+	return $unicode;
+}
+
+function bb_encoded_utf8_cut( $encoded, $length = 0 ) {
+	if ( $length < 1 )
+		return $encoded;
+
+	$r = '';
+	$values = preg_split( '/(%[0-9a-f]{2})/i', $encoded, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );;
+
+	for ($i = 0; $i < count( $values ); $i += $num_octets ) {
+		$num_octets = 1;
+		if ( '%' != $values[$i][0] ) {
+			$r .= $values[$i];
+			if ( $length && strlen($r) > $length )
+				return substr($r, 0, $length);
+		} else {
+			$value = hexdec(substr($values[$i], 1));
+
+			if ( 1 == $num_octets )
+				$num_octets = ( 224 > $value ) ? 2 : 3;
+
+			if ( $length && ( strlen($r) + $num_octets * 3 ) > $length )
+				return $r;
+
+			$r .= $values[$i] . $values[$i + 1];
+			if ( 3 == $num_octets )
+				$r .= $values[$i + 2];
+		}
+	}
+
+	return $r;
+}
+
+function bb_pre_term_slug( $slug, $taxonomy = '', $term_id = 0 ) {
+	return bb_sanitize_with_dashes( $slug, 200 );
+}
+
+function bb_trim_for_db_55( $string ) {
+	return bb_trim_for_db( $string, 55 );
+}
+
+function bb_trim_for_db_150( $string ) {
+	return bb_trim_for_db( $string, 150 );
+}
+
+function bb_slug_sanitize( $slug, $length = 255 ) {
+	$_slug = $slug;
+	return apply_filters( 'bb_slug_sanitize', bb_sanitize_with_dashes( $slug, $length ), $_slug, $length );
+}
+
+function bb_user_nicename_sanitize( $user_nicename, $length = 50 ) {
+	$_user_nicename = $user_nicename;
+	return apply_filters( 'bb_user_nicename_sanitize', bb_sanitize_with_dashes( $user_nicename, $length ), $_user_nicename, $length );
+}
+
+function bb_sanitize_with_dashes( $text, $length = 0 ) { // Multibyte aware
+	$_text = $text;
+	$text = trim($text);
+	$text = strip_tags($text);
+	// Preserve escaped octets.
+	$text = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $text);
+	// Remove percent signs that are not part of an octet.
+	$text = str_replace('%', '', $text);
+	// Restore octets.
+	$text = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $text);
+
+	$text = apply_filters( 'pre_sanitize_with_dashes', $text, $_text, $length );
+
+	$text = strtolower($text);
+	$text = preg_replace('/&(^\x80-\xff)+?;/', '', $text); // kill entities
+	$text = preg_replace('/[^%a-z0-9\x80-\xff _-]/', '', $text);
+	$text = trim($text);
+	$text = preg_replace('/\s+/', '-', $text);
+	$text = preg_replace(array('|-+|', '|_+|'), array('-', '_'), $text); // Kill the repeats
+	
+	return $text;
+}
+
+function bb_pre_sanitize_with_dashes_utf8( $text, $_text = '', $length = 0 ) {
+	$text = remove_accents($text);
+
+	if ( seems_utf8( $text ) ) {
+		if ( function_exists('mb_strtolower') )
+			$text = mb_strtolower($text, 'UTF-8');
+		$text = utf8_uri_encode( $text, $length );
+	}
+
+	return $text;
+}
+
+function bb_show_topic_context( $term, $text ) {
+	$text = strip_tags( $text );
+	$term = preg_quote( $term );
+	$text = preg_replace( "|.*?(.{0,80})$term(.{0,80}).*|is", "$1<strong>$term</strong>$2", $text, 1 );
+	$text = substr( $text, 0, 210 );
+	return $text;
+}
+
+function bb_post_text_context( $post_text ) {
+	return bb_show_context( $GLOBALS['q'], $post_text );
+}
+
+function bb_show_context( $term, $text ) {
+	$text = strip_shortcodes( $text );
+	$text = strip_tags( $text );
+	$term = preg_quote( $term );
+	$text = preg_replace( "|.*?(.{0,80})$term(.{0,80}).*|is", "... $1<strong>$term</strong>$2 ...", $text, 1 );
+	$text = substr( $text, 0, 210 );
+	return $text;
+}
+
+function bb_fix_link( $link ) {
+	if ( false === strpos($link, '.') ) // these are usually random words
+		return '';
+	$link = wp_kses_no_null( $link );
+	return esc_url( $link );
+}
+
+function bb_sticky_label( $label ) {
+	global $topic;
+	if (bb_is_front()) {
+		if ( '2' === $topic->topic_sticky ) {
+			return sprintf(__('[sticky] %s'), $label);
+		}
+	} else {
+		if ( '1' === $topic->topic_sticky || '2' === $topic->topic_sticky ) {
+			return sprintf(__('[sticky] %s'), $label);
+		}
+	}
+	return $label;
+}
+
+function bb_closed_label( $label ) {
+	global $topic;
+	if ( '0' === $topic->topic_open )
+		return sprintf(__('[closed] %s'), $label);
+	return $label;
+}
+
+function bb_make_link_view_all( $link ) {
+	return esc_html( add_query_arg( 'view', 'all', $link ) );
+}
+
+function bb_gmtstrtotime( $string ) {
+	if ( is_numeric($string) )
+		return $string;
+	if ( !is_string($string) )
+		return -1;
+
+	if ( stristr($string, 'utc') || stristr($string, 'gmt') || stristr($string, '+0000') )
+		return strtotime($string);
+
+	if ( -1 == $time = strtotime($string . ' +0000') )
+		return strtotime($string);
+
+	return $time;
+}
+
+/**
+ * Converts a number of characters from a string.
+ *
+ * Metadata tags <<title>> and <<category>> are removed, <<br>> and <<hr>> are
+ * converted into correct XHTML and Unicode characters are converted to the
+ * valid range.
+ *
+ * @param string $content String of characters to be converted.
+ * 
+ * @return string Converted string.
+ */
+function bb_convert_chars( $content ) {
+	// Translation of invalid Unicode references range to valid range
+	$wp_htmltranswinuni = array(
+	'&#128;' => '&#8364;', // the Euro sign
+	'&#129;' => '',
+	'&#130;' => '&#8218;', // these are Windows CP1252 specific characters
+	'&#131;' => '&#402;',  // they would look weird on non-Windows browsers
+	'&#132;' => '&#8222;',
+	'&#133;' => '&#8230;',
+	'&#134;' => '&#8224;',
+	'&#135;' => '&#8225;',
+	'&#136;' => '&#710;',
+	'&#137;' => '&#8240;',
+	'&#138;' => '&#352;',
+	'&#139;' => '&#8249;',
+	'&#140;' => '&#338;',
+	'&#141;' => '',
+	'&#142;' => '&#382;',
+	'&#143;' => '',
+	'&#144;' => '',
+	'&#145;' => '&#8216;',
+	'&#146;' => '&#8217;',
+	'&#147;' => '&#8220;',
+	'&#148;' => '&#8221;',
+	'&#149;' => '&#8226;',
+	'&#150;' => '&#8211;',
+	'&#151;' => '&#8212;',
+	'&#152;' => '&#732;',
+	'&#153;' => '&#8482;',
+	'&#154;' => '&#353;',
+	'&#155;' => '&#8250;',
+	'&#156;' => '&#339;',
+	'&#157;' => '',
+	'&#158;' => '',
+	'&#159;' => '&#376;'
+	);
+
+	// Remove metadata tags
+	$content = preg_replace( '/<title>(.+?)<\/title>/', '', $content );
+	$content = preg_replace( '/<category>(.+?)<\/category>/', '', $content );
+
+	// Converts lone & characters into &#38; (a.k.a. &amp;)
+	$content = preg_replace( '/&([^#])(?![a-z1-4]{1,8};)/i', '&#038;$1', $content );
+
+	// Fix Word pasting
+	$content = strtr( $content, $wp_htmltranswinuni );
+
+	// Just a little XHTML help
+	$content = str_replace( '<br>', '<br />', $content );
+	$content = str_replace( '<hr>', '<hr />', $content );
+
+	return $content;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-forums.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-forums.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-forums.php	(revision 5057)
@@ -0,0 +1,157 @@
+<?php
+
+/* Forums */
+
+function bb_get_forums_hierarchical( $root = 0, $depth = 0, $leaves = false, $_recursed = false ) {
+	static $_leaves = false;
+
+	if (!$_recursed)
+		$_leaves = false;
+
+	$root = (int) $root;
+
+	if ( false === $_leaves )
+		$_leaves = $leaves ? $leaves : bb_get_forums();
+
+	if ( !$_leaves )
+		return false;
+
+	$branch = array();
+
+	reset($_leaves);
+
+	while ( list($l, $leaf) = each($_leaves) ) {
+		if ( $root == $leaf->forum_parent ) {
+			$new_root = (int) $leaf->forum_id;
+			unset($_leaves[$l]);
+			$branch[$new_root] = 1 == $depth ? true : bb_get_forums_hierarchical( $new_root, $depth - 1, false, true );
+			reset($_leaves);
+		}
+	}
+
+	if ( !$_recursed ) {
+		if ( !$root )
+			foreach ( $_leaves as $leaf ) // Attach orphans to root
+				$branch[$leaf->forum_id] = true;
+		$_leaves = false;
+		return ( empty($branch) ? false : $branch );
+	}
+
+	return $branch ? $branch : true;
+}
+
+function _bb_get_cached_data( $keys, $group, $callback ) {
+	$return = array();
+	foreach ( $keys as $key ) {
+		// should use wp_cache_get_multi if available
+		if ( false === $value = wp_cache_get( $key, $group ) ) {
+			if ( !$value = call_user_func( $callback, $key ) ) {
+				continue;
+			}
+		}
+		$return[$key] = $value;
+	}
+	return $return;
+}
+
+// 'where' arg provided for backward compatibility only
+function bb_get_forums( $args = null ) {
+	global $bbdb;
+
+	if ( is_numeric($args) ) {
+		$args = array( 'child_of' => $args, 'hierarchical' => 1, 'depth' => 0 );
+	} elseif ( is_callable($args) ) {
+		$args = array( 'callback' => $args );
+		if ( 1 < func_num_args() )
+			$args['callback_args'] = func_get_arg(1);
+	}
+
+	$defaults = array( 'callback' => false, 'callback_args' => false, 'child_of' => 0, 'hierarchical' => 0, 'depth' => 0, 'cut_branch' => 0, 'where' => '', 'order_by' => 'forum_order' );
+	$args = wp_parse_args( $args, $defaults );
+
+	extract($args, EXTR_SKIP);
+	$child_of = (int) $child_of;
+	$hierarchical = 'false' === $hierarchical ? false : (bool) $hierarchical;
+	$depth = (int) $depth;
+
+	$where = apply_filters( 'get_forums_where', $where );
+	$key = md5( serialize( $where . '|' . $order_by ) ); // The keys that change the SQL query
+	if ( false !== $forum_ids = wp_cache_get( $key, 'bb_forums' ) ) {
+		$forums = _bb_get_cached_data( $forum_ids, 'bb_forum', 'bb_get_forum' );
+	} else {
+		$forum_ids = array();
+		$forums = array();
+		$_forums = (array) $bbdb->get_results("SELECT * FROM $bbdb->forums $where ORDER BY `$order_by`;");
+		$_forums = bb_append_meta( $_forums, 'forum' );
+		foreach ( $_forums as $f ) {
+			$forums[(int) $f->forum_id] = $f;
+			$forum_ids[] = (int) $f->forum_id;
+			wp_cache_add( $f->forum_id, $f, 'bb_forum' );
+			wp_cache_add( $f->forum_slug, $f->forum_id, 'bb_forum_slug' );
+		}
+		wp_cache_set( $key, $forum_ids, 'bb_forums' );
+	}
+
+	$forums = (array) apply_filters( 'get_forums', $forums );
+
+	if ( $child_of || $hierarchical || $depth ) {
+
+		$_forums = bb_get_forums_hierarchical( $child_of, $depth, $forums );
+
+		if ( !is_array( $_forums ) )
+			return false;
+
+		$_forums = (array) bb_flatten_array( $_forums, $cut_branch );
+
+		foreach ( array_keys($_forums) as $_id )
+			$_forums[$_id] = $forums[$_id];
+
+		$forums = $_forums;
+	}
+
+	if ( !is_callable($callback) )
+		return $forums;
+
+	if ( !is_array($callback_args) )
+		$callback_args = array();
+
+	foreach ( array_keys($forums) as $f ) :
+		$_callback_args = $callback_args;
+		array_push( $_callback_args, $forums[$f]->forum_id );
+		if ( false == call_user_func_array( $callback, $_callback_args ) ) // $forum_id will be last arg;
+			unset($forums[$f]);
+	endforeach;
+	return $forums;
+}
+
+function bb_get_forum( $id ) {
+	global $bbdb;
+
+	if ( !is_numeric($id) ) {
+		list($slug, $sql) = bb_get_sql_from_slug( 'forum', $id );
+		$id = wp_cache_get( $slug, 'bb_forum_slug' );
+	}
+
+	// not else
+	if ( is_numeric($id) )
+		$sql = $bbdb->prepare( "forum_id = %d", $id );
+
+	if ( 0 === $id || empty( $sql ) )
+		return false;
+
+	// $where is NOT bbdb:prepared
+	if ( $where = apply_filters( 'get_forum_where', '' ) ) {
+		$forum = $bbdb->get_row( "SELECT * FROM $bbdb->forums WHERE $sql $where" );
+		return bb_append_meta( $forum, 'forum' );
+	}
+
+	if ( is_numeric($id) && false !== $forum = wp_cache_get( $id, 'bb_forum' ) )
+		return $forum;
+
+	$forum = $bbdb->get_row( "SELECT * FROM $bbdb->forums WHERE $sql" );
+	$forum = bb_append_meta( $forum, 'forum' );
+	wp_cache_set( $forum->forum_id, $forum, 'bb_forum' );
+	wp_cache_add( $forum->forum_slug, $forum->forum_id, 'bb_forum_slug' );
+
+	return $forum;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-l10n.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-l10n.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-l10n.php	(revision 5057)
@@ -0,0 +1,490 @@
+<?php
+/**
+ * bbPress Translation API
+ *
+ * @package bbPress
+ * @subpackage i18n
+ */
+
+/**
+ * Gets the current locale.
+ *
+ * If the locale is set, then it will filter the locale in the 'locale' filter
+ * hook and return the value.
+ *
+ * If the locale is not set already, then the BB_LANG constant is used if it is
+ * defined. Then it is filtered through the 'locale' filter hook and the value
+ * for the locale global set and the locale is returned.
+ *
+ * The process to get the locale should only be done once but the locale will
+ * always be filtered using the 'locale' hook.
+ *
+ * @since 0.7.2
+ * @uses apply_filters() Calls 'locale' hook on locale value.
+ * @uses $locale Gets the locale stored in the global.
+ *
+ * @return string The locale of the blog or from the 'locale' hook.
+ */
+function bb_get_locale() {
+	global $locale;
+
+	if (isset($locale))
+		return apply_filters( 'locale', $locale );
+
+	// BB_LANG is defined in bb-config.php
+	if (defined('BB_LANG'))
+		$locale = BB_LANG;
+
+	if (empty($locale))
+		$locale = 'en_US';
+
+	return apply_filters('locale', $locale);
+}
+
+if ( !function_exists( 'get_locale' ) ) :
+function get_locale() {
+	return bb_get_locale();
+}
+endif;
+
+if ( !function_exists( 'translate' ) ) :
+/**
+ * Retrieves the translation of $text. If there is no translation, or
+ * the domain isn't loaded the original text is returned.
+ *
+ * @see __() Don't use translate() directly, use __()
+ * @since 1.0
+ * @uses apply_filters() Calls 'gettext' on domain translated text
+ *		with the untranslated text as second parameter.
+ *
+ * @param string $text Text to translate.
+ * @param string $domain Domain to retrieve the translated text.
+ * @return string Translated text
+ */
+function translate( $text, $domain = 'default' ) {
+	$translations = &get_translations_for_domain( $domain );
+	return apply_filters('gettext', $translations->translate($text), $text, $domain);
+}
+endif;
+
+if ( !function_exists( 'before_last_bar' ) ) :
+/**
+ * @since 1.0
+ */
+function before_last_bar( $string ) {
+	$last_bar = strrpos( $string, '|' );
+	if ( false == $last_bar )
+		return $string;
+	else
+		return substr( $string, 0, $last_bar );
+}
+endif;
+
+if ( !function_exists( 'translate_with_context' ) ) :
+/**
+ * Translate $text like translate(), but assumes that the text
+ * contains a context after its last vertical bar.
+ *
+ * @since 1.0
+ * @uses translate()
+ *
+ * @param string $text Text to translate
+ * @param string $domain Domain to retrieve the translated text
+ * @return string Translated text
+ */
+function translate_with_context( $text, $domain = 'default' ) {
+	return before_last_bar( translate( $text, $domain ) );
+
+}
+endif;
+
+if ( !function_exists( 'translate_with_gettext_context' ) ) :
+/**
+ * @since 1.0
+ */
+function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
+	$translations = &get_translations_for_domain( $domain );
+	return apply_filters( 'gettext_with_context', $translations->translate( $text, $context ), $text, $context, $domain);
+}
+endif;
+
+if ( !function_exists( '__' ) ) :
+/**
+ * Retrieves the translation of $text. If there is no translation, or
+ * the domain isn't loaded the original text is returned.
+ *
+ * @see translate() An alias of translate()
+ * @since 0.7.2
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ * @return string Translated text
+ */
+function __( $text, $domain = 'default' ) {
+	return translate( $text, $domain );
+}
+endif;
+
+if ( !function_exists( 'esc_attr__' ) ) :
+/**
+ * Retrieves the translation of $text and escapes it for safe use in an attribute.
+ * If there is no translation, or the domain isn't loaded the original text is returned.
+ *
+ * @see translate() An alias of translate()
+ * @see esc_attr()
+ * @since 1.0
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ * @return string Translated text
+ */
+function esc_attr__( $text, $domain = 'default' ) {
+	return esc_attr( translate( $text, $domain ) );
+}
+endif;
+
+if ( !function_exists( 'esc_html__' ) ) :
+/**
+ * Retrieves the translation of $text and escapes it for safe use in HTML output.
+ * If there is no translation, or the domain isn't loaded the original text is returned.
+ *
+ * @see translate() An alias of translate()
+ * @see esc_html()
+ * @since 1.0
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ * @return string Translated text
+ */
+function esc_html__( $text, $domain = 'default' ) {
+	return esc_html( translate( $text, $domain ) );
+}
+endif;
+
+if ( !function_exists( '_e' ) ) :
+/**
+ * Displays the returned translated text from translate().
+ *
+ * @see translate() Echos returned translate() string
+ * @since 0.7.2
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ */
+function _e( $text, $domain = 'default' ) {
+	echo translate( $text, $domain );
+}
+endif;
+
+if ( !function_exists( 'esc_attr_e' ) ) :
+/**
+ * Displays translated text that has been escaped for safe use in an attribute.
+ *
+ * @see translate() Echoes returned translate() string
+ * @see esc_attr()
+ * @since 1.0
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ */
+function esc_attr_e( $text, $domain = 'default' ) {
+	echo esc_attr( translate( $text, $domain ) );
+}
+endif;
+
+if ( !function_exists( 'esc_html_e' ) ) :
+/**
+ * Displays translated text that has been escaped for safe use in HTML output.
+ *
+ * @see translate() Echoes returned translate() string
+ * @see esc_html()
+ * @since 1.0
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ */
+function esc_html_e( $text, $domain = 'default' ) {
+	echo esc_html( translate( $text, $domain ) );
+}
+endif;
+
+if ( !function_exists( '_c' ) ) :
+/**
+ * Retrieve translated string with vertical bar context
+ *
+ * Quite a few times, there will be collisions with similar translatable text
+ * found in more than two places but with different translated context.
+ *
+ * In order to use the separate contexts, the _c() function is used and the
+ * translatable string uses a pipe ('|') which has the context the string is in.
+ *
+ * When the translated string is returned, it is everything before the pipe, not
+ * including the pipe character. If there is no pipe in the translated text then
+ * everything is returned.
+ *
+ * @since 1.0
+ *
+ * @param string $text Text to translate
+ * @param string $domain Optional. Domain to retrieve the translated text
+ * @return string Translated context string without pipe
+ */
+function _c($text, $domain = 'default') {
+	return translate_with_context($text, $domain);
+}
+endif;
+
+if ( !function_exists( '_x' ) ) :
+/**
+ * @since 1.0
+ */
+function _x( $single, $context, $domain = 'default' ) {
+	return translate_with_gettext_context( $single, $context, $domain );
+}
+endif;
+
+if ( !function_exists( 'esc_attr_x' ) ) :
+function esc_attr_x( $single, $context, $domain = 'default' ) {
+	return esc_attr( translate_with_gettext_context( $single, $context, $domain ) );
+}
+endif;
+
+if ( !function_exists( '__ngettext' ) ) :
+/**
+ * @deprecated Use _n()
+ */
+function __ngettext() {
+	//_deprecated_function( __FUNCTION__, '2.8', '_n()' );
+	$args = func_get_args();
+	return call_user_func_array('_n', $args);
+}
+endif;
+
+if ( !function_exists( '_n' ) ) :
+/**
+ * Retrieve the plural or single form based on the amount.
+ *
+ * If the domain is not set in the $l10n list, then a comparsion will be made
+ * and either $plural or $single parameters returned.
+ *
+ * If the domain does exist, then the parameters $single, $plural, and $number
+ * will first be passed to the domain's ngettext method. Then it will be passed
+ * to the 'ngettext' filter hook along with the same parameters. The expected
+ * type will be a string.
+ *
+ * @since 1.0
+ * @uses $l10n Gets list of domain translated string (gettext_reader) objects
+ * @uses apply_filters() Calls 'ngettext' hook on domains text returned,
+ *		along with $single, $plural, and $number parameters. Expected to return string.
+ *
+ * @param string $single The text that will be used if $number is 1
+ * @param string $plural The text that will be used if $number is not 1
+ * @param int $number The number to compare against to use either $single or $plural
+ * @param string $domain Optional. The domain identifier the text should be retrieved in
+ * @return string Either $single or $plural translated text
+ */
+function _n($single, $plural, $number, $domain = 'default') {
+	$translations = &get_translations_for_domain( $domain );
+	$translation = $translations->translate_plural( $single, $plural, $number );
+	return apply_filters( 'ngettext', $translation, $single, $plural, $number );
+}
+endif;
+
+if ( !function_exists( '_nc' ) ) :
+/**
+ * @see _n() A version of _n(), which supports contexts --
+ * strips everything from the translation after the last bar
+ * @since 1.0
+ */
+function _nc( $single, $plural, $number, $domain = 'default' ) {
+	return before_last_bar( _n( $single, $plural, $number, $domain ) );
+}
+endif;
+
+if ( !function_exists( '_nx' ) ) :
+/**
+ * @since 1.0
+ */
+function _nx($single, $plural, $number, $context, $domain = 'default') {
+	$translations = &get_translations_for_domain( $domain );
+	$translation = $translations->translate_plural( $single, $plural, $number, $context );
+	return apply_filters( 'ngettext_with_context ', $translation, $single, $plural, $number, $context );
+}
+endif;
+
+if ( !function_exists( '__ngettext_noop' ) ) :
+/**
+ * @deprecated Use _n_noop()
+ */
+function __ngettext_noop() {
+	//_deprecated_function( __FUNCTION__, '2.8', '_n_noop()' );
+	$args = func_get_args();
+	return call_user_func_array('_n_noop', $args);
+}
+endif;
+
+if ( !function_exists( '_n_noop' ) ) :
+/**
+ * Register plural strings in POT file, but don't translate them.
+ *
+ * Used when you want do keep structures with translatable plural strings and
+ * use them later.
+ *
+ * Example:
+ *  $messages = array(
+ *  	'post' => _n_noop('%s post', '%s posts'),
+ *  	'page' => _n_noop('%s pages', '%s pages')
+ *  );
+ *  ...
+ *  $message = $messages[$type];
+ *  $usable_text = sprintf(_n($message[0], $message[1], $count), $count);
+ *
+ * @since 1.0
+ * @param $single Single form to be i18ned
+ * @param $plural Plural form to be i18ned
+ * @return array array($single, $plural)
+ */
+function _n_noop( $single, $plural ) {
+	return array( $single, $plural );
+}
+endif;
+
+if ( !function_exists( '_nx_noop' ) ) :
+/**
+ * Register plural strings with context in POT file, but don't translate them.
+ *
+ * @see _n_noop()
+ */
+function _nx_noop( $single, $plural, $context ) {
+	return array( $single, $plural, $context );
+}
+endif;
+
+if ( !function_exists( 'load_textdomain' ) ) :
+/**
+ * Loads MO file into the list of domains.
+ *
+ * If the domain already exists, the inclusion will fail. If the MO file is not
+ * readable, the inclusion will fail.
+ *
+ * On success, the mofile will be placed in the $l10n global by $domain and will
+ * be an gettext_reader object.
+ *
+ * @since 0.7.2
+ * @uses $l10n Gets list of domain translated string (gettext_reader) objects
+ * @uses CacheFileReader Reads the MO file
+ * @uses gettext_reader Allows for retrieving translated strings
+ *
+ * @param string $domain Unique identifier for retrieving translated strings
+ * @param string $mofile Path to the .mo file
+ * @return null On failure returns null and also on success returns nothing.
+ */
+function load_textdomain($domain, $mofile) {
+	global $l10n;
+
+	if ( !is_readable($mofile)) return;
+	
+	$mo = new MO();
+	$mo->import_from_file( $mofile );
+
+	if (isset($l10n[$domain]))
+		$mo->merge_with( $l10n[$domain] );
+		
+	$l10n[$domain] = &$mo;
+}
+endif;
+
+/**
+ * Loads default translated strings based on locale.
+ *
+ * Loads the .mo file in WP_LANG_DIR constant path from WordPress root. The
+ * translated (.mo) file is named based off of the locale.
+ *
+ * @since 1.5.0
+ */
+function bb_load_default_textdomain() {
+	$locale = bb_get_locale();
+
+	$mofile = BB_LANG_DIR . "/$locale.mo";
+
+	load_textdomain('default', $mofile);
+}
+
+if ( !function_exists( 'load_default_textdomain' ) ) :
+function load_default_textdomain() {
+	bb_load_default_textdomain();
+}
+endif;
+
+/**
+ * Loads the plugin's translated strings.
+ *
+ * If the path is not given then it will be the root of the plugin directory.
+ * The .mo file should be named based on the domain with a dash followed by a
+ * dash, and then the locale exactly.
+ *
+ * @since 0.7.2
+ *
+ * @param string $domain Unique identifier for retrieving translated strings
+ * @param string $path Optional. Absolute path to folder where the .mo file resides
+ */
+function bb_load_plugin_textdomain($domain, $path = false) {
+	$locale = bb_get_locale();
+
+	if ( false === $path ) {
+		global $bb;
+		$path = $bb->plugin_locations['core']['dir'];
+	}
+
+	$mofile = rtrim( trim( $path ), " \t\n\r\0\x0B/" ) . '/'. $domain . '-' . $locale . '.mo';
+	load_textdomain($domain, $mofile);
+}
+
+if ( !function_exists( 'load_plugin_textdomain' ) ) :
+function load_plugin_textdomain( $domain, $path = false ) {
+	bb_load_plugin_textdomain( $domain, $path );
+}
+endif;
+
+/**
+ * Loads the theme's translated strings.
+ *
+ * If the current locale exists as a .mo file in the theme's root directory, it
+ * will be included in the translated strings by the $domain.
+ *
+ * The .mo files must be named based on the locale exactly.
+ *
+ * @since 0.7.2
+ *
+ * @param string $domain Unique identifier for retrieving translated strings
+ */
+function bb_load_theme_textdomain($domain, $path = false) {
+	$locale = bb_get_locale();
+
+	$mofile = ( empty( $path ) ) ? bb_get_template( $locale . '.mo' ) : "$path/$locale.mo";
+	
+	load_textdomain($domain, $mofile);
+}
+
+if ( !function_exists( 'load_theme_textdomain' ) ) :
+function load_theme_textdomain( $domain, $path = false ) {
+	bb_load_theme_textdomain( $domain, $path );
+}
+endif;
+
+if ( !function_exists( 'get_translations_for_domain' ) ) :
+/**
+ * Returns the Translations instance for a domain. If there isn't one,
+ * returns empty Translations instance.
+ *
+ * @param string $domain
+ * @return object A Translation instance
+ */
+function &get_translations_for_domain( $domain ) {
+	global $l10n;
+	$empty =& new Translations;
+	if ( isset($l10n[$domain]) )
+		return $l10n[$domain];
+	else
+		return $empty;
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-meta.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-meta.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-meta.php	(revision 5057)
@@ -0,0 +1,940 @@
+<?php
+
+/* Options/Meta */
+
+/* Internal */
+
+function bb_sanitize_meta_key( $key )
+{
+	return preg_replace( '|[^a-z0-9_]|i', '', $key );
+}
+
+/**
+ * Adds and updates meta data in the database
+ *
+ * @internal
+ */
+function bb_update_meta( $object_id = 0, $meta_key, $meta_value, $type, $global = false )
+{
+	global $bbdb;
+	if ( !is_numeric( $object_id ) || empty( $object_id ) && !$global ) {
+		return false;
+	}
+	$cache_object_id = $object_id = (int) $object_id;
+	switch ( $type ) {
+		case 'option':
+			$object_type = 'bb_option';
+			break;
+		case 'user' :
+			global $wp_users_object;
+			$id = $object_id;
+			$return = $wp_users_object->update_meta( compact( 'id', 'meta_key', 'meta_value' ) );
+			if ( is_wp_error( $return ) ) {
+				return false;
+			}
+			return $return;
+			break;
+		case 'forum' :
+			$object_type = 'bb_forum';
+			break;
+		case 'topic' :
+			$object_type = 'bb_topic';
+			break;
+		case 'post' :
+			$object_type = 'bb_post';
+			break;
+		default :
+			$object_type = $type;
+			break;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+
+	$meta_tuple = compact( 'object_type', 'object_id', 'meta_key', 'meta_value', 'type' );
+	$meta_tuple = apply_filters( 'bb_update_meta', $meta_tuple );
+	extract( $meta_tuple, EXTR_OVERWRITE );
+
+	$meta_value = $_meta_value = maybe_serialize( $meta_value );
+	$meta_value = maybe_unserialize( $meta_value );
+
+	$cur = $bbdb->get_row( $bbdb->prepare( "SELECT * FROM `$bbdb->meta` WHERE `object_type` = %s AND `object_id` = %d AND `meta_key` = %s", $object_type, $object_id, $meta_key ) );
+	if ( !$cur ) {
+		$bbdb->query( $bbdb->prepare(
+			"INSERT INTO `$bbdb->meta` ( `object_type`, `object_id`, `meta_key`, `meta_value` ) VALUES( %s, %d, %s, %s )
+			ON DUPLICATE KEY UPDATE `meta_value` = VALUES( `meta_value` )",
+			$object_type, $object_id, $meta_key, $_meta_value
+		) );
+	} elseif ( $cur->meta_value != $meta_value ) {
+		$bbdb->update( $bbdb->meta, array( 'meta_value' => $_meta_value), array( 'object_type' => $object_type, 'object_id' => $object_id, 'meta_key' => $meta_key ) );
+	}
+
+	if ( $object_type === 'bb_option' ) {
+		$cache_object_id = $meta_key;
+		wp_cache_delete( $cache_object_id, 'bb_option_not_set' );
+	}
+	wp_cache_delete( $cache_object_id, $object_type );
+
+	if ( !$cur ) {
+		return true;
+	}
+}
+
+/**
+ * Deletes meta data from the database
+ *
+ * @internal
+ */
+function bb_delete_meta( $object_id = 0, $meta_key, $meta_value, $type, $global = false )
+{
+	global $bbdb;
+	if ( !is_numeric( $object_id ) || empty( $object_id ) && !$global ) {
+		return false;
+	}
+	$cache_object_id = $object_id = (int) $object_id;
+	switch ( $type ) {
+		case 'option':
+			$object_type = 'bb_option';
+			break;
+		case 'user':
+			global $wp_users_object;
+			$id = $object_id;
+			return $wp_users_object->delete_meta( compact( 'id', 'meta_key', 'meta_value' ) );
+			break;
+		case 'forum':
+			$object_type = 'bb_forum';
+			break;
+		case 'topic':
+			$object_type = 'bb_topic';
+			break;
+		case 'post':
+			$object_type = 'bb_post';
+			break;
+		default:
+			$object_type = $type;
+			break;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+
+	$meta_tuple = compact( 'object_type', 'object_id', 'meta_key', 'meta_value', 'type' );
+	$meta_tuple = apply_filters( 'bb_delete_meta', $meta_tuple );
+	extract( $meta_tuple, EXTR_OVERWRITE );
+
+	$meta_value = maybe_serialize( $meta_value );
+
+	if ( empty( $meta_value ) ) {
+		$meta_sql = $bbdb->prepare( "SELECT `meta_id` FROM `$bbdb->meta` WHERE `object_type` = %s AND `object_id` = %d AND `meta_key` = %s", $object_type, $object_id, $meta_key );
+	} else {
+		$meta_sql = $bbdb->prepare( "SELECT `meta_id` FROM `$bbdb->meta` WHERE `object_type` = %s AND `object_id` = %d AND `meta_key` = %s AND `meta_value` = %s", $object_type, $object_id, $meta_key, $meta_value );
+	}
+
+	if ( !$meta_id = $bbdb->get_var( $meta_sql ) ) {
+		return false;
+	}
+
+	$bbdb->query( $bbdb->prepare( "DELETE FROM `$bbdb->meta` WHERE `meta_id` = %d", $meta_id ) );
+
+	if ( $object_type == 'bb_option' ) {
+		$cache_object_id = $meta_key;
+		wp_cache_delete( $cache_object_id, 'bb_option_not_set' );
+	}
+	wp_cache_delete( $cache_object_id, $object_type );
+	return true;
+}
+
+/**
+ * Adds an objects meta data to the object
+ *
+ * This is the only function that should add to user / topic - NOT bbdb::prepared
+ *
+ * @internal
+ */
+function bb_append_meta( $object, $type )
+{
+	global $bbdb;
+	switch ( $type ) {
+		case 'user':
+			global $wp_users_object;
+			return $wp_users_object->append_meta( $object );
+			break;
+		case 'forum':
+			$object_id_column = 'forum_id';
+			$object_type = 'bb_forum';
+			$slug = 'forum_slug';
+			break;
+		case 'topic':
+			$object_id_column = 'topic_id';
+			$object_type = 'bb_topic';
+			$slug = 'topic_slug';
+			break;
+		case 'post':
+			$object_id_column = 'post_id';
+			$object_type = 'bb_post';
+			$slug = false;
+			break;
+	}
+
+	if ( is_array( $object ) && count( $object ) ) {
+		$trans = array();
+		foreach ( array_keys( $object ) as $i ) {
+			$trans[$object[$i]->$object_id_column] =& $object[$i];
+		}
+		$ids = array_map( 'intval', array_keys( $trans ) );
+		$query_ids = array();
+		$cached_objects = array();
+		$position = 0;
+		foreach ( $ids as $_id ) {
+			if ( false !== $_cached_object = wp_cache_get( $_id, $object_type ) ) {
+				$cached_objects[$position] = $_cached_object;
+			} else {
+				$query_ids[$position] = $_id;
+			}
+			$position++;
+		}
+		if ( !count( $query_ids ) ) {
+			return $cached_objects;
+		}
+
+		$_query_ids = $query_ids;
+		sort( $_query_ids );
+		$_query_ids = join( ',', $_query_ids );
+
+		if ( $metas = $bbdb->get_results( "SELECT `object_id`, `meta_key`, `meta_value` FROM `$bbdb->meta` WHERE `object_type` = '$object_type' AND `object_id` IN ($_query_ids) /* bb_append_meta */" ) ) {
+			usort( $metas, '_bb_append_meta_sort' );
+			foreach ( $metas as $meta ) {
+				$trans[$meta->object_id]->{$meta->meta_key} = maybe_unserialize( $meta->meta_value );
+				if ( strpos($meta->meta_key, $bbdb->prefix) === 0 ) {
+					$trans[$meta->object_id]->{substr($meta->meta_key, strlen($bbdb->prefix))} = maybe_unserialize( $meta->meta_value );
+				}
+			}
+		}
+		foreach ( $query_ids as $position => $i ) {
+			$cached_objects[$position] = $trans[$i];
+			wp_cache_add( $i, $trans[$i], $object_type );
+			if ( $slug ) {
+				wp_cache_add( $trans[$i]->$slug, $i, 'bb_' . $slug );
+			}
+		}
+		if ( !count( $cached_objects ) ) {
+			return $object;
+		}
+		ksort( $cached_objects );
+
+		return $cached_objects;
+	} elseif ( $object ) {
+		if ( false !== $cached_object = wp_cache_get( $object->$object_id_column, $object_type ) ) {
+			return $cached_object;
+		}
+		if ( $metas = $bbdb->get_results( $bbdb->prepare( "SELECT `object_id`, `meta_key`, `meta_value` FROM `$bbdb->meta` WHERE `object_type` = '$object_type' AND `object_id` = %d /* bb_append_meta */", $object->$object_id_column ) ) ) {
+			usort( $metas, '_bb_append_meta_sort' );
+			foreach ( $metas as $meta ) {
+				$object->{$meta->meta_key} = maybe_unserialize( $meta->meta_value );
+				if ( strpos( $meta->meta_key, $bbdb->prefix ) === 0 ) {
+					$object->{substr( $meta->meta_key, strlen( $bbdb->prefix ) )} = $object->{$meta->meta_key};
+				}
+			}
+		}
+		if ( $object->$object_id_column ) {
+			wp_cache_add( $object->$object_id_column, $object, $object_type );
+			if ( $slug ) {
+				wp_cache_add( $object->$slug, $object->$object_id_column, 'bb_' . $slug );
+			}
+		}
+		return $object;
+	}
+}
+
+/**
+ * Sorts meta keys by length to ensure $appended_object->{$bbdb->prefix} key overwrites $appended_object->key as desired
+ *
+ * @internal
+ */
+function _bb_append_meta_sort( $a, $b )
+{
+	return strlen( $a->meta_key ) - strlen( $b->meta_key );
+}
+
+
+
+/* Options */
+
+/**
+ * Echoes the requested bbPress option by calling bb_get_option()
+ *
+ * @param string The option to be echoed
+ * @return void
+ */
+function bb_option( $option )
+{
+	echo apply_filters( 'bb_option_' . $option, bb_get_option( $option ) );
+}
+
+/**
+ * Returns the requested bbPress option from the meta table or the $bb object
+ *
+ * @param string The option to be echoed
+ * @return mixed The value of the option
+ */
+function bb_get_option( $option )
+{
+	// Allow plugins to short-circuit options.
+	if ( false !== $r = apply_filters( 'bb_pre_get_option_' . $option, false, $option ) ) {
+		return $r;
+	}
+
+	global $bb;
+
+	switch ( $option ) {
+		case 'site_id':
+			if ( isset( $bb->site_id ) && is_numeric( $bb->site_id ) ) {
+				$r = (int) $bb->site_id;
+			} else {
+				$r = 1;
+			}
+			break;
+		case 'language':
+			$r = str_replace( '_', '-', bb_get_locale() );
+			break;
+		case 'text_direction':
+			global $bb_locale;
+			$r = $bb_locale->text_direction;
+			break;
+		case 'version':
+			return '1.1-alpha-2855'; // Don't filter
+			break;
+		case 'bb_db_version' :
+			return '2471'; // Don't filter
+			break;
+		case 'html_type':
+			$r = 'text/html';
+			break;
+		case 'charset':
+			$r = 'UTF-8';
+			break;
+		case 'bb_table_prefix':
+		case 'table_prefix':
+			global $bbdb;
+			return $bbdb->prefix; // Don't filter;
+			break;
+		case 'url':
+			$option = 'uri';
+		default:
+			if ( isset( $bb->$option ) ) {
+				$r = $bb->$option;
+				if ( $option === 'mod_rewrite' ) {
+					if ( is_bool( $r ) ) {
+						$r = (int) $r;
+					}
+				}
+				break;
+			}
+
+			$r = bb_get_option_from_db( $option );
+
+			if ( !$r ) {
+				switch ( $option ) {
+					case 'name':
+						$r = __( 'Please give me a name!' );
+						break;
+					case 'wp_table_prefix' :
+						global $wp_table_prefix; // This global is deprecated
+						return $wp_table_prefix; // Don't filter;
+						break;
+					case 'mod_rewrite':
+						$r = 0;
+						break;
+					case 'page_topics':
+						$r = 30;
+						break;
+					case 'edit_lock':
+						$r = 60;
+						break;
+					case 'gmt_offset':
+						$r = 0;
+						break;
+					case 'uri_ssl':
+						$r = preg_replace( '|^http://|i', 'https://', bb_get_option( 'uri' ) );
+						break;
+					case 'throttle_time':
+						$r = 30;
+						break;
+					case 'email_login':
+						$r = false;
+						break;
+				}
+			}
+			break;
+	}
+
+	return apply_filters( 'bb_get_option_' . $option, $r, $option );
+}
+
+/**
+ * Retrieves and returns the requested bbPress option from the meta table
+ *
+ * @param string The option to be echoed
+ * @return void
+ */
+function bb_get_option_from_db( $option )
+{
+	global $bbdb;
+	$option = bb_sanitize_meta_key( $option );
+
+	if ( wp_cache_get( $option, 'bb_option_not_set' ) ) {
+		$r = null;
+	} elseif ( false !== $_r = wp_cache_get( $option, 'bb_option' ) ) {
+		$r = $_r;
+	} else {
+		if ( BB_INSTALLING ) {
+			$bbdb->suppress_errors();
+		}
+		$row = $bbdb->get_row( $bbdb->prepare( "SELECT `meta_value` FROM `$bbdb->meta` WHERE `object_type` = 'bb_option' AND `meta_key` = %s", $option ) );
+		if ( BB_INSTALLING ) {
+			$bbdb->suppress_errors( false );
+		}
+
+		if ( is_object( $row ) ) {
+			$r = maybe_unserialize( $row->meta_value );
+		} else {
+			$r = null;
+		}
+	}
+
+	if ( $r === null ) {
+		wp_cache_set( $option, true, 'bb_option_not_set' );
+	} else {
+		wp_cache_set( $option, $r, 'bb_option' );
+	}
+
+	return apply_filters( 'bb_get_option_from_db_' . $option, $r, $option );
+}
+
+function bb_form_option( $option )
+{
+	echo bb_get_form_option( $option );
+}
+
+function bb_get_form_option( $option )
+{
+	return esc_attr( bb_get_option( $option ) );
+}
+
+// Don't use the return value; use the API. Only returns options stored in DB.
+function bb_cache_all_options()
+{
+	$base_options = array(
+		'site_id',
+		'bb_db_version',
+		'name',
+		'description',
+		'uri_ssl',
+		'from_email',
+		'bb_auth_salt',
+		'bb_secure_auth_salt',
+		'bb_logged_in_salt',
+		'bb_nonce_salt',
+		'page_topics',
+		'edit_lock',
+		'bb_active_theme',
+		'active_plugins',
+		'mod_rewrite',
+		'datetime_format',
+		'date_format',
+		'avatars_show',
+		'avatars_default',
+		'avatars_rating',
+		'wp_table_prefix',
+		'user_bbdb_name',
+		'user_bbdb_user',
+		'user_bbdb_password',
+		'user_bbdb_host',
+		'user_bbdb_charset',
+		'user_bbdb_collate',
+		'custom_user_table',
+		'custom_user_meta_table',
+		'wp_siteurl',
+		'wp_home',
+		'cookiedomain',
+		'usercookie',
+		'passcookie',
+		'authcookie',
+		'cookiepath',
+		'sitecookiepath',
+		'secure_auth_cookie',
+		'logged_in_cookie',
+		'admin_cookie_path',
+		'core_plugins_cookie_path',
+		'user_plugins_cookie_path',
+		'wp_admin_cookie_path',
+		'wp_plugins_cookie_path',
+		'wordpress_mu_primary_blog_id',
+		'enable_loginless',
+		'enable_subscriptions',
+		'enable_xmlrpc',
+		'enable_pingback',
+		'throttle_time',
+		'bb_xmlrpc_allow_user_switching',
+		'bp_bbpress_cron',
+		'email_login',
+		'static_title',
+		'plugin_cookie_paths',
+		'wp_roles_map',
+		'gmt_offset',
+		'timezone_string',
+		'name_link_profile',
+		'bp_bbpress_cron_check',
+	);
+
+	// Check that these aren't already in the cache
+	$query_options = array();
+	foreach ( $base_options as $base_option ) {
+		if ( isset( $bb->$base_option ) ) {
+			continue;
+		}
+		if ( wp_cache_get( $base_option, 'bb_option_not_set' ) ) {
+			continue;
+		}
+		if ( false === wp_cache_get( $base_option, 'bb_option' ) ) {
+			$query_options[] = $base_option;
+			wp_cache_set( $base_option, true, 'bb_option_not_set' );
+		}
+	}
+	if ( !count( $query_options ) ) {
+		// It's all in cache
+		return true;
+	}
+
+	$query_keys = "('" . join( "','", $query_options ) . "')";
+
+	global $bbdb;
+	$results = $bbdb->get_results( "SELECT `meta_key`, `meta_value` FROM `$bbdb->meta` WHERE `object_type` = 'bb_option' AND `meta_key` IN $query_keys;" );
+
+	if ( count( $base_options ) === count( $query_options ) && ( !$results || !is_array( $results ) || !count( $results ) ) ) {
+		// Let's assume that the options haven't been populated from the old topicmeta table
+		if ( !BB_INSTALLING && ( !defined( 'BB_DO_NOT_UPGRADE_TOPICMETA' ) || !BB_DO_NOT_UPGRADE_TOPICMETA ) ) {
+			$topicmeta_exists = $bbdb->query( "SELECT * FROM $bbdb->topicmeta LIMIT 1" );
+			if ($topicmeta_exists) {
+				require_once( BB_PATH . 'bb-admin/includes/defaults.bb-schema.php' );
+				// Create the meta table
+				$bbdb->query( $bb_queries['meta'] );
+				// Copy options
+				$bbdb->query( "INSERT INTO `$bbdb->meta` (`meta_key`, `meta_value`) SELECT `meta_key`, `meta_value` FROM `$bbdb->topicmeta` WHERE `topic_id` = 0;" );
+				// Copy topic meta
+				$bbdb->query( "INSERT INTO `$bbdb->meta` (`object_id`, `meta_key`, `meta_value`) SELECT `topic_id`, `meta_key`, `meta_value` FROM `$bbdb->topicmeta` WHERE `topic_id` != 0;" );
+				// Entries with an object_id are topic meta at this stage
+				$bbdb->query( "UPDATE `$bbdb->meta` SET `object_type` = 'bb_topic' WHERE `object_id` != 0" );
+			}
+			unset( $topicmeta_exists );
+
+			return bb_cache_all_options();
+		}
+
+		return false;
+	} else {
+		foreach ( $results as $options ) {
+			wp_cache_delete( $options->meta_key, 'bb_option_not_set' );
+			wp_cache_set( $options->meta_key, maybe_unserialize( $options->meta_value ), 'bb_option' );
+		}
+	}
+
+	return true;
+}
+
+// Can store anything but NULL.
+function bb_update_option( $option, $value )
+{
+	return bb_update_meta( 0, $option, $value, 'option', true );
+}
+
+function bb_delete_option( $option, $value = '' )
+{
+	return bb_delete_meta( 0, $option, $value, 'option', true );
+}
+
+/**
+ * Delete a transient
+ *
+ * @since 1.0
+ * @package bbPress
+ * @subpackage Transient
+ *
+ * @param string $transient Transient name. Expected to not be SQL-escaped
+ * @return bool true if successful, false otherwise
+ */
+function bb_delete_transient( $transient )
+{
+	global $_bb_using_ext_object_cache, $bbdb;
+
+	if ( $_bb_using_ext_object_cache ) {
+		return wp_cache_delete( $transient, 'transient' );
+	} else {
+		$transient = '_transient_' . $bbdb->escape( $transient );
+		return bb_delete_option( $transient );
+	}
+}
+
+/**
+ * Get the value of a transient
+ *
+ * If the transient does not exist or does not have a value, then the return value
+ * will be false.
+ * 
+ * @since 1.0
+ * @package bbPress
+ * @subpackage Transient
+ *
+ * @param string $transient Transient name. Expected to not be SQL-escaped
+ * @return mixed Value of transient
+ */
+function bb_get_transient( $transient )
+{
+	global $_bb_using_ext_object_cache, $bbdb;
+
+	if ( $_bb_using_ext_object_cache ) {
+		$value = wp_cache_get( $transient, 'transient' );
+	} else {
+		$transient_option = '_transient_' . $bbdb->escape( $transient );
+		$transient_timeout = '_transient_timeout_' . $bbdb->escape( $transient );
+		$timeout = bb_get_option( $transient_timeout );
+		if ( $timeout && $timeout < time() ) {
+			bb_delete_option( $transient_option );
+			bb_delete_option( $transient_timeout );
+			return false;
+		}
+
+		$value = bb_get_option( $transient_option );
+	}
+
+	return apply_filters( 'transient_' . $transient, $value );
+}
+
+/**
+ * Set/update the value of a transient
+ *
+ * You do not need to serialize values, if the value needs to be serialize, then
+ * it will be serialized before it is set.
+ *
+ * @since 1.0
+ * @package bbPress
+ * @subpackage Transient
+ *
+ * @param string $transient Transient name. Expected to not be SQL-escaped
+ * @param mixed $value Transient value.
+ * @param int $expiration Time until expiration in seconds, default 0
+ * @return bool False if value was not set and true if value was set.
+ */
+function bb_set_transient( $transient, $value, $expiration = 0 )
+{
+	global $_bb_using_ext_object_cache, $bbdb;
+
+	if ( $_bb_using_ext_object_cache ) {
+		return wp_cache_set( $transient, $value, 'transient', $expiration );
+	} else {
+		$transient_timeout = '_transient_timeout_' . $bbdb->escape( $transient );
+		$transient = '_transient_' . $bbdb->escape( $transient );
+		if ( 0 != $expiration ) {
+			bb_update_option( $transient_timeout, time() + $expiration );
+		}
+		return bb_update_option( $transient, $value );
+	}
+}
+
+
+
+/* User meta */
+
+function bb_get_usermeta( $user_id, $meta_key )
+{
+	if ( !$user = bb_get_user( $user_id ) ) {
+		return;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+	if ( !isset( $user->$meta_key ) ) {
+		return;
+	}
+	return $user->$meta_key;
+}
+
+function bb_update_usermeta( $user_id, $meta_key, $meta_value )
+{
+	return bb_update_meta( $user_id, $meta_key, $meta_value, 'user' );
+}
+
+// $meta_value defaults to null to conform to BackPress' WP_User::delete_meta()
+function bb_delete_usermeta( $user_id, $meta_key, $meta_value = null )
+{
+	return bb_delete_meta( $user_id, $meta_key, $meta_value, 'user' );
+}
+
+/**
+ * Saves and restores user interface settings stored in a cookie.
+ *
+ * Checks if the current user-settings cookie is updated and stores it. When no
+ * cookie exists (different browser used), adds the last saved cookie restoring
+ * the settings.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ */
+function bb_user_settings()
+{
+	if ( !defined( 'BB_IS_ADMIN' ) || !BB_IS_ADMIN ) {
+		return;
+	}
+
+	if ( defined( 'DOING_AJAX' ) ) {
+		return;
+	}
+
+	if ( !$user = bb_get_current_user() ) {
+		return;
+	}
+
+	$settings = bb_get_usermeta( $user->ID, 'bb_user_settings' );
+
+	if ( isset( $_COOKIE['bb-user-settings-' . $user->ID] ) ) {
+		$cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['bb-user-settings-' . $user->ID] );
+
+		if ( !empty( $cookie ) && strpos( $cookie, '=' ) ) {
+			if ( $cookie == $settings ) {
+				return;
+			}
+
+			$last_time = (int) bb_get_usermeta( $user->ID, 'bb_user_settings_time' );
+			$saved = isset( $_COOKIE['bb-user-settings-time-' . $user->ID] ) ? preg_replace( '/[^0-9]/', '', $_COOKIE['bb-user-settings-time-' . $user->ID] ) : 0;
+
+			if ( $saved > $last_time ) {
+				bb_update_usermeta( $user->ID, 'bb_user_settings', $cookie );
+				bb_update_usermeta( $user->ID, 'bb_user_settings_time', time() - 5 );
+				return;
+			}
+		}
+	}
+
+	setcookie( 'bb-user-settings-' . $user->ID, $settings, time() + 31536000, $bb->cookiepath );
+	setcookie( 'bb-user-settings-time-' . $user->ID, time(), time() + 31536000, $bb->cookiepath );
+}
+
+/**
+ * Retrieve user interface setting value based on setting name.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ *
+ * @param string $name The name of the setting.
+ * @param string $default Optional default value to return when $name is not set.
+ * @return mixed the last saved user setting or the default value/false if it doesn't exist.
+ */
+function bb_get_user_setting( $name, $default = false )
+{
+	$arr = bb_get_all_user_settings();
+
+	return isset( $arr[$name] ) ? $arr[$name] : $default;
+}
+
+/**
+ * Adds or updates a user interface setting value based on setting name.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ *
+ * @param string $name The name of the setting.
+ * @param string $value The value of the setting.
+ */
+function bb_update_user_setting( $name, $value )
+{
+	if ( is_null( $value ) || $value === '' ) {
+		return;
+	}
+
+	if ( !$user = bb_get_current_user() ) {
+		return;
+	}
+
+	$name = (string) $name;
+	$name = preg_replace( '/[^A-Za-z0-9_]/', '', $name );
+
+	$value = (string) $value;
+	$value = preg_replace( '/[^A-Za-z0-9_]/', '', $value );
+
+	$arr = bb_get_all_user_settings();
+	$arr[$name] = $value;
+
+	foreach ( $arr as $k => $v ) {
+		$settings .= $k . '=' . $v . '&';
+	}
+	$settings = rtrim( $settings, '&' );
+
+	bb_update_usermeta( $user->ID, 'bb_user_settings', $settings );
+	setcookie( 'bb-user-settings-' . $user->ID, $settings, time() + 31536000, $bb->cookiepath );
+}
+
+/**
+ * Delete user interface settings.
+ *
+ * Deleting settings would reset them to the defaults.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ *
+ * @param mixed $names The name or array of names of the setting to be deleted.
+ */
+function bb_delete_user_setting( $names )
+{
+	$arr = bb_get_all_user_settings();
+	$names = (array) $names;
+
+	if ( !$user = bb_get_current_user() ) {
+		return;
+	}
+
+	foreach ( $names as $name ) {
+		if ( isset( $arr[$name] ) ) {
+			unset( $arr[$name] );
+			$settings = '';
+		}
+	}
+
+	if ( isset( $settings ) ) {
+		foreach ( $arr as $k => $v ) {
+			$settings .= $k . '=' . $v . '&';
+		}
+		$settings = rtrim( $settings, '&' );
+
+		bb_update_usermeta( $user->ID, 'bb_user_settings', $settings );
+		setcookie( 'bb-user-settings-' . $user->ID, $settings, time() + 31536000, $bb->cookiepath );
+	}
+}
+
+/**
+ * Retrieve all user interface settings.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ *
+ * @return array the last saved user settings or empty array.
+ */
+function bb_get_all_user_settings()
+{
+	if ( ! $user = bb_get_current_user() ) {
+		return array();
+	}
+
+	$arr = array();
+	if ( isset( $_COOKIE['bb-user-settings-' . $user->ID] ) ) {
+		$cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['bb-user-settings-' . $user->ID] );
+
+		if ( $cookie && strpos( $cookie, '=' ) ) { // the '=' cannot be 1st char
+			parse_str( $cookie, $arr );
+		}
+	} elseif ( isset( $user->bb_user_settings ) && is_string( $user->bb_user_settings ) ) {
+		parse_str( $user->bb_user_settings, $arr );
+	}
+
+	return $arr;
+}
+
+/**
+ * Delete the user settings of the current user.
+ *
+ * @package bbPress
+ * @subpackage Meta
+ * @since 1.0
+ */
+function bb_delete_all_user_settings()
+{
+	if ( !$user = bb_get_current_user() ) {
+		return;
+	}
+
+	bb_delete_usermeta( $user->ID, 'bb_user_settings' );
+	setcookie( 'bb-user-settings-' . $user->ID, ' ', time() - 31536000, $bb->cookiepath );
+}
+
+
+
+
+/* Forum meta */
+
+function bb_get_forummeta( $forum_id, $meta_key )
+{
+	if ( !$forum = bb_get_forum( $forum_id ) ) {
+		return;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+	if ( !isset( $forum->$meta_key ) ) {
+		return;
+	}
+	return $forum->$meta_key;
+}
+
+function bb_update_forummeta( $forum_id, $meta_key, $meta_value )
+{
+	return bb_update_meta( $forum_id, $meta_key, $meta_value, 'forum' );
+}
+
+function bb_delete_forummeta( $forum_id, $meta_key, $meta_value = '' )
+{
+	return bb_delete_meta( $forum_id, $meta_key, $meta_value, 'forum' );
+}
+
+
+
+/* Topic meta */
+
+function bb_get_topicmeta( $topic_id, $meta_key )
+{
+	if ( !$topic = get_topic( $topic_id ) ) {
+		return;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+	if ( !isset($topic->$meta_key) ) {
+		return;
+	}
+	return $topic->$meta_key;
+}
+
+function bb_update_topicmeta( $topic_id, $meta_key, $meta_value )
+{
+	return bb_update_meta( $topic_id, $meta_key, $meta_value, 'topic' );
+}
+
+function bb_delete_topicmeta( $topic_id, $meta_key, $meta_value = '' )
+{
+	return bb_delete_meta( $topic_id, $meta_key, $meta_value, 'topic' );
+}
+
+
+
+/* Post meta */
+
+function bb_get_postmeta( $post_id, $meta_key )
+{
+	if ( !$post = bb_get_post( $post_id ) ) {
+		return;
+	}
+
+	$meta_key = bb_sanitize_meta_key( $meta_key );
+	if ( !isset( $post->$meta_key ) ) {
+		return;
+	}
+	return $post->$meta_key;
+}
+
+function bb_update_postmeta( $post_id, $meta_key, $meta_value )
+{
+	return bb_update_meta( $post_id, $meta_key, $meta_value, 'post' );
+}
+
+function bb_delete_postmeta( $post_id, $meta_key, $meta_value = '' )
+{
+	return bb_delete_meta( $post_id, $meta_key, $meta_value, 'post' );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-pluggable.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-pluggable.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-pluggable.php	(revision 5057)
@@ -0,0 +1,937 @@
+<?php
+
+if ( !function_exists( 'bb_auth' ) ) :
+function bb_auth( $scheme = 'auth' ) { // Checks if a user has a valid cookie, if not redirects them to the main page
+	if ( !bb_validate_auth_cookie( '', $scheme ) ) {
+		nocache_headers();
+		if ( 'auth' === $scheme && !bb_is_user_logged_in() ) {
+			$protocol = 'http://';
+			if ( is_ssl() ) {
+				$protocol = 'https://';
+			}
+			wp_redirect( bb_get_uri( 'bb-login.php', array( 'redirect_to' => $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ), BB_URI_CONTEXT_HEADER + BB_URI_CONTEXT_BB_USER_FORMS ) );
+		} else {
+			wp_redirect( bb_get_uri( null, null, BB_URI_CONTEXT_HEADER ) );
+		}
+		exit;
+	}
+}
+endif;
+
+// $already_md5 variable is deprecated
+if ( !function_exists('bb_check_login') ) :
+function bb_check_login($user, $pass, $already_md5 = false) {
+	global $wp_users_object;
+
+	if ( !bb_get_option( 'email_login' ) || false === strpos( $user, '@' ) ) { // user_login
+		$user = $wp_users_object->get_user( $user, array( 'by' => 'login' ) );
+	} else { // maybe an email
+		$email_user = $wp_users_object->get_user( $user, array( 'by' => 'email' ) );
+		$user = $wp_users_object->get_user( $user, array( 'by' => 'login' ) );
+		// 9 cases.  each can be FALSE, USER, or WP_ERROR
+		if (
+			( !$email_user && $user ) // FALSE && USER, FALSE && WP_ERROR
+		||
+			( is_wp_error( $email_user ) && $user && !is_wp_error( $user ) ) // WP_ERROR && USER
+		) {
+			// nope: it really was a user_login
+			// [sic]: use $user
+		} elseif (
+			( $email_user && !$user ) // USER && FALSE, WP_ERROR && FALSE
+		||
+			( $email_user && !is_wp_error( $email_user ) && is_wp_error( $user ) ) // USER && WP_ERROR
+		) {
+			// yup: it was an email
+			$user =& $email_user;
+		} elseif ( !$email_user && !$user ) { // FALSE && FALSE
+			// Doesn't matter what it was: neither worked
+			return false;
+		} elseif ( is_wp_error( $email_user ) && is_wp_error( $user ) ) { // WP_ERROR && WP_ERROR
+			// This can't happen.  If it does, let's use the email error.  It's probably "multiple matches", so maybe logging in with a username will work
+			$user =& $email_user;
+		} elseif ( $email_user && $user ) { // USER && USER
+			// both are user objects
+			if ( $email_user->ID == $user->ID ); // [sic]: they are the same, use $user
+			elseif ( bb_check_password($pass, $user->user_pass, $user->ID) ); // [sic]: use $user
+			elseif ( bb_check_password($pass, $email_user->user_pass, $email_user->ID) )
+				$user =& $email_user;
+		} else { // This can't happen, that's all 9 cases.
+			// [sic]: use $user
+		}
+	}
+
+	if ( !$user )
+		return false;
+
+	if ( is_wp_error($user) )
+		return $user;
+	
+	if ( !bb_check_password($pass, $user->user_pass, $user->ID) )
+		return false;
+
+	// User is logging in for the first time, update their user_status to normal
+	if ( 1 == $user->user_status )
+		bb_update_user_status( $user->ID, 0 );
+	
+	return $user;
+}
+endif;
+
+if ( !function_exists('bb_get_current_user') ) :
+function bb_get_current_user() {
+	global $wp_auth_object;
+	return $wp_auth_object->get_current_user();
+}
+endif;
+
+if ( !function_exists('bb_set_current_user') ) :
+function bb_set_current_user( $id ) {
+	global $wp_auth_object;
+	$current_user = $wp_auth_object->set_current_user( $id );
+	
+	do_action('bb_set_current_user', isset($current_user->ID) ? $current_user->ID : 0 );
+	
+	return $current_user;
+}
+endif;
+
+if ( !function_exists('bb_current_user') ) :
+//This is only used at initialization.  Use bb_get_current_user_info() (or $bb_current_user global if really needed) to grab user info.
+function bb_current_user() {
+	if (BB_INSTALLING)
+		return false;
+
+	return bb_get_current_user();
+}
+endif;
+
+if ( !function_exists('bb_is_user_authorized') ) :
+function bb_is_user_authorized() {
+	return bb_is_user_logged_in();
+}
+endif;
+
+if ( !function_exists('bb_is_user_logged_in') ) :
+function bb_is_user_logged_in() {
+	$current_user = bb_get_current_user();
+
+	if ( empty($current_user) )
+		return false;
+	
+	return true;
+}
+endif;
+
+if ( !function_exists('bb_login') ) :
+function bb_login( $login, $password, $remember = false ) {
+	$user = bb_check_login( $login, $password );
+	if ( $user && !is_wp_error( $user ) ) {
+		bb_set_auth_cookie( $user->ID, $remember );
+		do_action('bb_user_login', (int) $user->ID );
+	}
+	
+	return $user;
+}
+endif;
+
+if ( !function_exists('bb_logout') ) :
+function bb_logout() {
+	bb_clear_auth_cookie();
+	
+	do_action('bb_user_logout');
+}
+endif;
+
+if ( !function_exists( 'bb_validate_auth_cookie' ) ) :
+function bb_validate_auth_cookie( $cookie = '', $scheme = 'auth' ) {
+	global $wp_auth_object;
+	if ( empty($cookie) && $scheme == 'auth' ) {
+		if ( is_ssl() ) {
+			$scheme = 'secure_auth';
+		} else {
+			$scheme = 'auth';
+		}
+	}
+	return $wp_auth_object->validate_auth_cookie( $cookie, $scheme );
+}
+endif;
+
+if ( !function_exists( 'bb_set_auth_cookie' ) ) :
+function bb_set_auth_cookie( $user_id, $remember = false, $schemes = false ) {
+	global $wp_auth_object;
+
+	if ( $remember ) {
+		$expiration = $expire = time() + 1209600;
+	} else {
+		$expiration = time() + 172800;
+		$expire = 0;
+	}
+
+	if ( true === $schemes ) {
+		$schemes = array( 'secure_auth', 'logged_in' );
+	} elseif ( !is_array( $schemes ) ) {
+		$schemes = array();
+		if ( force_ssl_login() || force_ssl_admin() ) {
+			$schemes[] = 'secure_auth';
+		}
+		if ( !( force_ssl_login() && force_ssl_admin() ) ) {
+			$schemes[] = 'auth';
+		}
+		$schemes[] = 'logged_in';
+	}
+	$schemes = array_unique( $schemes );
+
+	foreach ( $schemes as $scheme ) {
+		$wp_auth_object->set_auth_cookie( $user_id, $expiration, $expire, $scheme );
+	}
+}
+endif;
+
+if ( !function_exists('bb_clear_auth_cookie') ) :
+function bb_clear_auth_cookie() {
+	global $bb, $wp_auth_object;
+	
+	$wp_auth_object->clear_auth_cookie();
+	
+	// Old cookies
+	setcookie($bb->authcookie, ' ', time() - 31536000, $bb->cookiepath, $bb->cookiedomain);
+	setcookie($bb->authcookie, ' ', time() - 31536000, $bb->sitecookiepath, $bb->cookiedomain);
+	
+	// Even older cookies
+	setcookie($bb->usercookie, ' ', time() - 31536000, $bb->cookiepath, $bb->cookiedomain);
+	setcookie($bb->usercookie, ' ', time() - 31536000, $bb->sitecookiepath, $bb->cookiedomain);
+	setcookie($bb->passcookie, ' ', time() - 31536000, $bb->cookiepath, $bb->cookiedomain);
+	setcookie($bb->passcookie, ' ', time() - 31536000, $bb->sitecookiepath, $bb->cookiedomain);
+}
+endif;
+
+if ( !function_exists('wp_redirect') ) : // [WP11537]
+/**
+ * Redirects to another page, with a workaround for the IIS Set-Cookie bug.
+ *
+ * @link http://support.microsoft.com/kb/q176113/
+ * @since 1.5.1
+ * @uses apply_filters() Calls 'wp_redirect' hook on $location and $status.
+ *
+ * @param string $location The path to redirect to
+ * @param int $status Status code to use
+ * @return bool False if $location is not set
+ */
+function wp_redirect($location, $status = 302) {
+	global $is_IIS;
+
+	$location = apply_filters('wp_redirect', $location, $status);
+	$status = apply_filters('wp_redirect_status', $status, $location);
+
+	if ( !$location ) // allows the wp_redirect filter to cancel a redirect
+		return false;
+
+	$location = wp_sanitize_redirect($location);
+
+	if ( $is_IIS ) {
+		header("Refresh: 0;url=$location");
+	} else {
+		if ( php_sapi_name() != 'cgi-fcgi' )
+			status_header($status); // This causes problems on IIS and some FastCGI setups
+		header("Location: $location");
+	}
+}
+endif;
+
+if ( !function_exists('wp_sanitize_redirect') ) : // [WP11537]
+/**
+ * Sanitizes a URL for use in a redirect.
+ *
+ * @since 2.3
+ *
+ * @return string redirect-sanitized URL
+ **/
+function wp_sanitize_redirect($location) {
+	$location = preg_replace('|[^a-z0-9-~+_.?#=&;,/:%!]|i', '', $location);
+	$location = wp_kses_no_null($location);
+
+	// remove %0d and %0a from location
+	$strip = array('%0d', '%0a');
+	$found = true;
+	while($found) {
+		$found = false;
+		foreach( (array) $strip as $val ) {
+			while(strpos($location, $val) !== false) {
+				$found = true;
+				$location = str_replace($val, '', $location);
+			}
+		}
+	}
+	return $location;
+}
+endif;
+
+if ( !function_exists('bb_safe_redirect') ) : // based on [WP6145] (home is different)
+/**
+ * Performs a safe (local) redirect, using wp_redirect().
+ *
+ * Checks whether the $location is using an allowed host, if it has an absolute
+ * path. A plugin can therefore set or remove allowed host(s) to or from the
+ * list.
+ *
+ * If the host is not allowed, then the redirect is to the site url
+ * instead. This prevents malicious redirects which redirect to another host,
+ * but only used in a few places.
+ *
+ * @uses apply_filters() Calls 'allowed_redirect_hosts' on an array containing
+ *		bbPress host string and $location host string.
+ *
+ * @return void Does not return anything
+ **/
+function bb_safe_redirect( $location, $status = 302 ) {
+
+	// Need to look at the URL the way it will end up in wp_redirect()
+	$location = wp_sanitize_redirect($location);
+
+	// browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'
+	if ( substr($location, 0, 2) == '//' )
+		$location = 'http:' . $location;
+
+	// In php 5 parse_url may fail if the URL query part contains http://, bug #38143
+	$test = ( $cut = strpos($location, '?') ) ? substr( $location, 0, $cut ) : $location;
+
+	$lp = parse_url($test);
+	$bp = parse_url(bb_get_uri());
+
+	$allowed_hosts = (array) apply_filters('allowed_redirect_hosts', array($bp['host']), isset($lp['host']) ? $lp['host'] : '');
+
+	if ( isset($lp['host']) && ( !in_array($lp['host'], $allowed_hosts) && $lp['host'] != strtolower($bp['host'])) )
+		$location = bb_get_uri(null, null, BB_URI_CONTEXT_HEADER);
+
+	return wp_redirect($location, $status);
+}
+endif;
+
+if ( !function_exists('bb_nonce_tick') ) :
+/**
+ * Get the time-dependent variable for nonce creation.
+ *
+ * A nonce has a lifespan of two ticks. Nonces in their second tick may be
+ * updated, e.g. by autosave.
+ *
+ * @since 1.0
+ *
+ * @return int
+ */
+function bb_nonce_tick() {
+	$nonce_life = apply_filters('bb_nonce_life', 86400);
+
+	return ceil(time() / ( $nonce_life / 2 ));
+}
+endif;
+
+if ( !function_exists('bb_verify_nonce') ) :
+/**
+ * Verify that correct nonce was used with time limit.
+ *
+ * The user is given an amount of time to use the token, so therefore, since the
+ * UID and $action remain the same, the independent variable is the time.
+ *
+ * @param string $nonce Nonce that was used in the form to verify
+ * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
+ * @return bool Whether the nonce check passed or failed.
+ */
+function bb_verify_nonce($nonce, $action = -1) {
+	$user = bb_get_current_user();
+	$uid = (int) $user->ID;
+
+	$i = bb_nonce_tick();
+
+	// Nonce generated 0-12 hours ago
+	if ( substr(bb_hash($i . $action . $uid, 'nonce'), -12, 10) == $nonce )
+		return 1;
+	// Nonce generated 12-24 hours ago
+	if ( substr(bb_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) == $nonce )
+		return 2;
+	// Invalid nonce
+	return false;
+}
+endif;
+
+if ( !function_exists('bb_create_nonce') ) :
+/**
+ * Creates a random, one time use token.
+ *
+ * @since 2.0.4
+ *
+ * @param string|int $action Scalar value to add context to the nonce.
+ * @return string The one use form token
+ */
+function bb_create_nonce($action = -1) {
+	$user = bb_get_current_user();
+	$uid = (int) $user->ID;
+
+	$i = bb_nonce_tick();
+	
+	return substr(bb_hash($i . $action . $uid, 'nonce'), -12, 10);
+}
+endif;
+
+function _bb_get_key( $key, $default_key = false )
+{
+	global $bb_default_secret_key;
+
+	if ( defined( $key ) && '' != constant( $key ) && $bb_default_secret_key != constant( $key ) ) {
+		return constant( $key );
+	}
+
+	return '';
+}
+
+function _bb_get_salt( $constants, $option = false )
+{
+	if ( !is_array( $constants ) ) {
+		$constants = array( $constants );
+	}
+
+	foreach ($constants as $constant ) {
+		if ( defined( $constant ) ) {
+			return constant( $constant );
+		}
+	}
+
+	if ( !defined( 'BB_INSTALLING' ) || !BB_INSTALLING ) {
+		if ( !$option ) {
+			$option = strtolower( $constants[0] );
+		}
+		$salt = bb_get_option( $option );
+		if ( empty( $salt ) ) {
+			$salt = bb_generate_password( 64 );
+			bb_update_option( $option, $salt );
+		}
+		return $salt;
+	}
+
+	return '';
+}
+
+// Not verbatim WP, constants have different names, uses helper functions.
+if ( !function_exists( 'bb_salt' ) ) :
+/**
+ * Get salt to add to hashes to help prevent attacks.
+ *
+ * @since 0.9
+ * @link http://api.wordpress.org/secret-key/1.1/bbpress/ Create a set of keys for bb-config.php
+ * @uses _bb_get_key()
+ * @uses _bb_get_salt()
+ *
+ * @return string Salt value for the given scheme
+ */
+function bb_salt( $scheme = 'auth' )
+{
+	// Deprecated
+	$secret_key = _bb_get_key( 'BB_SECRET_KEY' );
+
+	switch ($scheme) {
+		case 'auth':
+			$secret_key = _bb_get_key( 'BB_AUTH_KEY' );
+			$salt = _bb_get_salt( array( 'BB_AUTH_SALT', 'BB_SECRET_SALT' ) );
+			break;
+
+		case 'secure_auth':
+			$secret_key = _bb_get_key( 'BB_SECURE_AUTH_KEY' );
+			$salt = _bb_get_salt( 'BB_SECURE_AUTH_SALT' );
+			break;
+
+		case 'logged_in':
+			$secret_key = _bb_get_key( 'BB_LOGGED_IN_KEY' );
+			$salt = _bb_get_salt( 'BB_LOGGED_IN_SALT' );
+			break;
+
+		case 'nonce':
+			$secret_key = _bb_get_key( 'BB_NONCE_KEY' );
+			$salt = _bb_get_salt( 'BB_NONCE_SALT' );
+			break;
+
+		default:
+			// ensure each auth scheme has its own unique salt
+			$salt = hash_hmac( 'md5', $scheme, $secret_key );
+			break;
+	}
+
+	return apply_filters( 'salt', $secret_key . $salt, $scheme );
+}
+endif;
+
+if ( !function_exists( 'bb_hash' ) ) :
+function bb_hash( $data, $scheme = 'auth' ) { 
+	$salt = bb_salt( $scheme );
+
+	return hash_hmac( 'md5', $data, $salt );
+}
+endif;
+
+if ( !function_exists( 'bb_hash_password' ) ) :
+function bb_hash_password( $password ) {
+	return WP_Pass::hash_password( $password );
+}
+endif;
+
+if ( !function_exists( 'bb_check_password') ) :
+function bb_check_password( $password, $hash, $user_id = '' ) {
+	return WP_Pass::check_password( $password, $hash, $user_id );
+}
+endif;
+
+if ( !function_exists( 'bb_generate_password' ) ) :
+/**
+ * Generates a random password drawn from the defined set of characters
+ * @return string the password
+ */
+function bb_generate_password( $length = 12, $special_chars = true ) {
+	return WP_Pass::generate_password( $length, $special_chars );
+}
+endif;
+
+if ( !function_exists('bb_check_admin_referer') ) :
+function bb_check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) {
+	$nonce = '';
+	if ( isset( $_POST[$query_arg] ) && $_POST[$query_arg] ) {
+		$nonce = $_POST[$query_arg];
+	} elseif ( isset( $_GET[$query_arg] ) && $_GET[$query_arg] ) {
+		$nonce = $_GET[$query_arg];
+	}
+	if ( !bb_verify_nonce($nonce, $action) ) {
+		bb_nonce_ays($action);
+		die();
+	}
+	do_action('bb_check_admin_referer', $action);
+}
+endif;
+
+if ( !function_exists('bb_check_ajax_referer') ) :
+function bb_check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
+	$requests = array();
+	if ( $query_arg ) {
+		$requests[] = $query_arg;
+	}
+	$requests[] = '_ajax_nonce';
+	$requests[] = '_wpnonce';
+
+	$nonce = '';
+	foreach ( $requests as $request ) {
+		if ( isset( $_POST[$request] ) && $_POST[$request] ) {
+			$nonce = $_POST[$request];
+			break;
+		} elseif ( isset( $_GET[$request] ) && $_GET[$request] ) {
+			$nonce = $_GET[$request];
+			break;
+		}
+	}
+
+	$result = bb_verify_nonce( $nonce, $action );
+
+	if ( $die && false == $result )
+		die('-1');
+
+	do_action('bb_check_ajax_referer', $action, $result);
+	return $result;
+}
+endif;
+
+if ( !function_exists('bb_break_password') ) :
+function bb_break_password( $user_id ) {
+	global $bbdb;
+	$user_id = (int) $user_id;
+	if ( !$user = bb_get_user( $user_id ) )
+		return false;
+	$secret = substr(bb_hash( 'bb_break_password' ), 0, 13);
+	if ( false === strpos( $user->user_pass, '---' ) )
+		return $bbdb->query( $bbdb->prepare(
+			"UPDATE $bbdb->users SET user_pass = CONCAT(user_pass, '---', %s) WHERE ID = %d",
+			$secret, $user_id
+		) );
+	else
+		return true;
+}
+endif;
+
+if ( !function_exists('bb_fix_password') ) :
+function bb_fix_password( $user_id ) {
+	global $bbdb;
+	$user_id = (int) $user_id;
+	if ( !$user = bb_get_user( $user_id ) )
+		return false;
+	if ( false === strpos( $user->user_pass, '---' ) )
+		return true;
+	else
+		return $bbdb->query( $bbdb->prepare(
+			"UPDATE $bbdb->users SET user_pass = SUBSTRING_INDEX(user_pass, '---', 1) WHERE ID = %d",
+			$user_id
+		) );
+}
+endif;
+
+if ( !function_exists('bb_has_broken_pass') ) :
+function bb_has_broken_pass( $user_id = 0 ) {
+	global $bb_current_user;
+	if ( !$user_id )
+		$user =& $bb_current_user->data;
+	else
+		$user = bb_get_user( $user_id );
+
+	return ( false !== strpos($user->user_pass, '---' ) );
+}
+endif;
+
+if ( !function_exists('bb_new_user') ) :
+function bb_new_user( $user_login, $user_email, $user_url, $user_status = 1 ) {
+	global $wp_users_object, $bbdb;
+
+	// is_email check + dns
+	if ( !$user_email = is_email( $user_email ) )
+		return new WP_Error( 'user_email', __( 'Invalid email address' ), $user_email );
+
+	if ( !$user_login = sanitize_user( $user_login, true ) )
+		return new WP_Error( 'user_login', __( 'Invalid username' ), $user_login );
+	
+	// user_status = 1 means the user has not yet been verified
+	$user_status = is_numeric($user_status) ? (int) $user_status : 1;
+	if ( defined( 'BB_INSTALLING' ) )
+		$user_status = 0;
+	
+	$user_nicename = $_user_nicename = bb_user_nicename_sanitize( $user_login );
+	if ( strlen( $_user_nicename ) < 1 )
+		return new WP_Error( 'user_login', __( 'Invalid username' ), $user_login );
+
+	while ( is_numeric($user_nicename) || $existing_user = bb_get_user_by_nicename( $user_nicename ) )
+		$user_nicename = bb_slug_increment($_user_nicename, $existing_user->user_nicename, 50);
+	
+	$user_url = $user_url ? bb_fix_link( $user_url ) : '';
+
+	$user_pass = bb_generate_password();
+
+	$user = $wp_users_object->new_user( compact( 'user_login', 'user_email', 'user_url', 'user_nicename', 'user_status', 'user_pass' ) );
+	if ( is_wp_error($user) ) {
+		if ( 'user_nicename' == $user->get_error_code() )
+			return new WP_Error( 'user_login', $user->get_error_message() );
+		return $user;
+	}
+
+	if (BB_INSTALLING) {
+		bb_update_usermeta( $user['ID'], $bbdb->prefix . 'capabilities', array('keymaster' => true) );
+	} else {
+		bb_update_usermeta( $user['ID'], $bbdb->prefix . 'capabilities', array('member' => true) );
+		bb_send_pass( $user['ID'], $user['plain_pass'] );
+	}
+
+	do_action('bb_new_user', $user['ID'], $user['plain_pass']);
+	return $user['ID'];
+}
+endif;
+
+if ( !function_exists( 'bb_mail' ) ) :
+/**
+ * Send mail, similar to PHP's mail
+ *
+ * A true return value does not automatically mean that the user received the
+ * email successfully. It just only means that the method used was able to
+ * process the request without any errors.
+ *
+ * Using the two 'bb_mail_from' and 'bb_mail_from_name' hooks allow from
+ * creating a from address like 'Name <email@address.com>' when both are set. If
+ * just 'bb_mail_from' is set, then just the email address will be used with no
+ * name.
+ *
+ * The default content type is 'text/plain' which does not allow using HTML.
+ * However, you can set the content type of the email by using the
+ * 'bb_mail_content_type' filter.
+ *
+ * The default charset is based on the charset used on the blog. The charset can
+ * be set using the 'bb_mail_charset' filter.
+ *
+ * @uses apply_filters() Calls 'bb_mail' hook on an array of all of the parameters.
+ * @uses apply_filters() Calls 'bb_mail_from' hook to get the from email address.
+ * @uses apply_filters() Calls 'bb_mail_from_name' hook to get the from address name.
+ * @uses apply_filters() Calls 'bb_mail_content_type' hook to get the email content type.
+ * @uses apply_filters() Calls 'bb_mail_charset' hook to get the email charset
+ * @uses do_action_ref_array() Calls 'bb_phpmailer_init' hook on the reference to
+ *		phpmailer object.
+ * @uses PHPMailer
+ *
+ * @param string $to Email address to send message
+ * @param string $subject Email subject
+ * @param string $message Message contents
+ * @param string|array $headers Optional. Additional headers.
+ * @param string|array $attachments Optional. Files to attach.
+ * @return bool Whether the email contents were sent successfully.
+ */
+function bb_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
+	// Compact the input, apply the filters, and extract them back out
+	extract( apply_filters( 'bb_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ) );
+
+	if ( !is_array($attachments) )
+		$attachments = explode( "\n", $attachments );
+
+	global $bb_phpmailer;
+
+	// (Re)create it, if it's gone missing
+	if ( !is_object( $bb_phpmailer ) || !is_a( $bb_phpmailer, 'PHPMailer' ) ) {
+		require_once BACKPRESS_PATH . 'class.mailer.php';
+		require_once BACKPRESS_PATH . 'class.mailer-smtp.php';
+		$bb_phpmailer = new PHPMailer();
+	}
+
+	// Headers
+	if ( empty( $headers ) ) {
+		$headers = array();
+	} else {
+		if ( !is_array( $headers ) ) {
+			// Explode the headers out, so this function can take both
+			// string headers and an array of headers.
+			$tempheaders = (array) explode( "\n", $headers );
+		} else {
+			$tempheaders = $headers;
+		}
+		$headers = array();
+
+		// If it's actually got contents
+		if ( !empty( $tempheaders ) ) {
+			// Iterate through the raw headers
+			foreach ( (array) $tempheaders as $header ) {
+				if ( strpos($header, ':') === false ) {
+					if ( false !== stripos( $header, 'boundary=' ) ) {
+						$parts = preg_split('/boundary=/i', trim( $header ) );
+						$boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
+					}
+					continue;
+				}
+				// Explode them out
+				list( $name, $content ) = explode( ':', trim( $header ), 2 );
+
+				// Cleanup crew
+				$name = trim( $name );
+				$content = trim( $content );
+
+				// Mainly for legacy -- process a From: header if it's there
+				if ( 'from' == strtolower($name) ) {
+					if ( strpos($content, '<' ) !== false ) {
+						// So... making my life hard again?
+						$from_name = substr( $content, 0, strpos( $content, '<' ) - 1 );
+						$from_name = str_replace( '"', '', $from_name );
+						$from_name = trim( $from_name );
+
+						$from_email = substr( $content, strpos( $content, '<' ) + 1 );
+						$from_email = str_replace( '>', '', $from_email );
+						$from_email = trim( $from_email );
+					} else {
+						$from_email = trim( $content );
+					}
+				} elseif ( 'content-type' == strtolower($name) ) {
+					if ( strpos( $content,';' ) !== false ) {
+						list( $type, $charset ) = explode( ';', $content );
+						$content_type = trim( $type );
+						if ( false !== stripos( $charset, 'charset=' ) ) {
+							$charset = trim( str_replace( array( 'charset=', '"' ), '', $charset ) );
+						} elseif ( false !== stripos( $charset, 'boundary=' ) ) {
+							$boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset ) );
+							$charset = '';
+						}
+					} else {
+						$content_type = trim( $content );
+					}
+				} elseif ( 'cc' == strtolower($name) ) {
+					$cc = explode(",", $content);
+				} elseif ( 'bcc' == strtolower($name) ) {
+					$bcc = explode(",", $content);
+				} else {
+					// Add it to our grand headers array
+					$headers[trim( $name )] = trim( $content );
+				}
+			}
+		}
+	}
+
+	// Empty out the values that may be set
+	$bb_phpmailer->ClearAddresses();
+	$bb_phpmailer->ClearAllRecipients();
+	$bb_phpmailer->ClearAttachments();
+	$bb_phpmailer->ClearBCCs();
+	$bb_phpmailer->ClearCCs();
+	$bb_phpmailer->ClearCustomHeaders();
+	$bb_phpmailer->ClearReplyTos();
+
+	// From email and name
+	// If we don't have a name from the input headers
+	if ( !isset( $from_name ) ) {
+		$from_name = bb_get_option('name');
+	}
+
+	// If we don't have an email from the input headers
+	if ( !isset( $from_email ) ) {
+		$from_email = bb_get_option('from_email');
+	}
+
+	// If there is still no email address
+	if ( !$from_email ) {
+		// Get the site domain and get rid of www.
+		$sitename = strtolower( $_SERVER['SERVER_NAME'] );
+		if ( substr( $sitename, 0, 4 ) == 'www.' ) {
+			$sitename = substr( $sitename, 4 );
+		}
+
+		$from_email = 'bbpress@' . $sitename;
+	}
+
+	// Plugin authors can override the potentially troublesome default
+	$bb_phpmailer->From = apply_filters( 'bb_mail_from', $from_email );
+	$bb_phpmailer->FromName = apply_filters( 'bb_mail_from_name', $from_name );
+
+	// Set destination address
+	$bb_phpmailer->AddAddress( $to );
+
+	// Set mail's subject and body
+	$bb_phpmailer->Subject = $subject;
+	$bb_phpmailer->Body = $message;
+
+	// Add any CC and BCC recipients
+	if ( !empty($cc) ) {
+		foreach ( (array) $cc as $recipient ) {
+			$bb_phpmailer->AddCc( trim($recipient) );
+		}
+	}
+	if ( !empty($bcc) ) {
+		foreach ( (array) $bcc as $recipient) {
+			$bb_phpmailer->AddBcc( trim($recipient) );
+		}
+	}
+
+	// Set to use PHP's mail()
+	$bb_phpmailer->IsMail();
+
+	// Set Content-Type and charset
+	// If we don't have a content-type from the input headers
+	if ( !isset( $content_type ) ) {
+		$content_type = 'text/plain';
+	}
+
+	$content_type = apply_filters( 'bb_mail_content_type', $content_type );
+
+	$bb_phpmailer->ContentType = $content_type;
+
+	// Set whether it's plaintext or not, depending on $content_type
+	if ( $content_type == 'text/html' ) {
+		$bb_phpmailer->IsHTML( true );
+	}
+
+	// If we don't have a charset from the input headers
+	if ( !isset( $charset ) ) {
+		$charset = bb_get_option( 'charset' );
+	}
+
+	// Set the content-type and charset
+	$bb_phpmailer->CharSet = apply_filters( 'bb_mail_charset', $charset );
+
+	// Set custom headers
+	if ( !empty( $headers ) ) {
+		foreach( (array) $headers as $name => $content ) {
+			$bb_phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
+		}
+		if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) {
+			$bb_phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
+		}
+	}
+
+	if ( !empty( $attachments ) ) {
+		foreach ( $attachments as $attachment ) {
+			$bb_phpmailer->AddAttachment($attachment);
+		}
+	}
+
+	do_action_ref_array( 'bb_phpmailer_init', array( &$bb_phpmailer ) );
+
+	// Send!
+	$result = @$bb_phpmailer->Send();
+
+	return $result;
+}
+endif;
+
+if ( !function_exists( 'bb_get_avatar' ) ) :
+/**
+ * Retrieve the avatar for a user provided a user ID or email address
+ *
+ * @since 0.9
+ * @param int|string $id_or_email A user ID or email address
+ * @param int $size Size of the avatar image
+ * @param string $default URL to a default image to use if no avatar is available
+ * @param string $alt Alternate text to use in image tag. Defaults to blank
+ * @return string <img> tag for the user's avatar
+*/
+function bb_get_avatar( $id_or_email, $size = 80, $default = '', $alt = false ) {
+	if ( !bb_get_option('avatars_show') )
+		return false;
+
+	if ( false === $alt)
+		$safe_alt = '';
+	else
+		$safe_alt = esc_attr( $alt );
+
+	if ( !is_numeric($size) )
+		$size = 80;
+
+	if ( $email = bb_get_user_email($id_or_email) ) {
+		$class = 'photo ';
+	} else {
+		$class = '';
+		$email = $id_or_email;
+	}
+
+	if ( !$email )
+		$email = '';
+
+	if ( empty($default) )
+		$default = bb_get_option('avatars_default');
+
+ 	if ( is_ssl() )
+		$host = 'https://secure.gravatar.com';
+	else
+		$host = 'http://www.gravatar.com';
+
+	switch ($default) {
+		case 'logo':
+			$default = '';
+			break;
+		case 'blank':
+			$default = bb_get_uri( 'bb-admin/images/blank.gif', null, BB_URI_CONTEXT_IMG_SRC );
+			break;
+		case 'monsterid':
+		case 'wavatar':
+		case 'identicon':
+		case 'retro':
+			break;
+		case 'default':
+		default:
+			$default = $host . '/avatar/ad516503a11cd5ca435acc9bb6523536?s=' . $size;
+			// ad516503a11cd5ca435acc9bb6523536 == md5('unknown@gravatar.com')
+			break;
+	}
+
+	$src = $host . '/avatar/';
+	$class .= 'avatar avatar-' . $size;
+
+	if ( !empty($email) ) {
+		$src .= md5( strtolower( $email ) );
+	} else {
+		$src .= 'd41d8cd98f00b204e9800998ecf8427e';
+		// d41d8cd98f00b204e9800998ecf8427e == md5('')
+		$class .= ' avatar-noemail';
+	}
+
+	$src .= '?s=' . $size;
+	$src .= '&amp;d=' . urlencode( $default );
+
+	$rating = bb_get_option('avatars_rating');
+	if ( !empty( $rating ) )
+		$src .= '&amp;r=' . $rating;
+
+	$avatar = '<img alt="' . $safe_alt . '" src="' . $src . '" class="' . $class . '" style="height:' . $size . 'px; width:' . $size . 'px;" />';
+
+	return apply_filters('bb_get_avatar', $avatar, $id_or_email, $size, $default, $alt);
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-posts.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-posts.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-posts.php	(revision 5057)
@@ -0,0 +1,739 @@
+<?php
+
+/* Posts */
+
+/**
+ * Check to make sure that a user is not making too many posts in a short amount of time.
+ */
+function bb_check_post_flood() {
+	global $bbdb;
+	$user_id = (int) $user_id;
+	$throttle_time = bb_get_option( 'throttle_time' );
+
+	if ( bb_current_user_can( 'manage_options' ) || empty( $throttle_time ) )
+		return;
+
+	if ( bb_is_user_logged_in() ) {
+		$bb_current_user = bb_get_current_user();
+		
+		if ( isset($bb_current_user->data->last_posted) && time() < $bb_current_user->data->last_posted + $throttle_time && ! bb_current_user_can( 'throttle' ) )
+			if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
+				die( __( 'Slow down; you move too fast.' ) );
+			else
+				bb_die( __( 'Slow down; you move too fast.' ) );
+	} else {
+		if ( ( $last_posted = bb_get_transient($_SERVER['REMOTE_ADDR'] . '_last_posted') ) && time() < $last_posted + $throttle_time )
+			if ( defined('DOING_AJAX') && DOING_AJAX )
+				die( __( 'Slow down; you move too fast.' ) );
+			else
+				bb_die( __( 'Slow down; you move too fast.' ) );
+	}
+}
+
+/**
+ * Get the current, non-logged-in poster data.
+ * @return array The associative array of author, email, and url data.
+ */
+function bb_get_current_poster() {
+	// Cookies should already be sanitized.
+	$post_author = '';
+	if ( isset( $_COOKIE['post_author_' . BB_HASH] ) )
+		$post_author = $_COOKIE['post_author_' . BB_HASH];
+
+	$post_author_email = '';
+	if ( isset( $_COOKIE['post_author_email_' . BB_HASH] ) )
+		$post_author_email = $_COOKIE['post_author_email_' . BB_HASH];
+
+	$post_author_url = '';
+	if ( isset( $_COOKIE['post_author_url_' . BB_HASH] ) )
+		$post_author_url = $_COOKIE['post_author_url_' . BB_HASH];
+
+	return compact( 'post_author', 'post_author_email', 'post_author_url' );
+}
+
+function bb_get_post( $post_id ) {
+	global $bbdb;
+	$post_id = (int) $post_id;
+	if ( false === $post = wp_cache_get( $post_id, 'bb_post' ) ) {
+		$post = $bbdb->get_row( $bbdb->prepare( "SELECT * FROM $bbdb->posts WHERE post_id = %d", $post_id ) );
+		$post = bb_append_meta( $post, 'post' );
+		wp_cache_set( $post_id, $post, 'bb_post' );
+	}
+	return $post;
+}
+
+// NOT bbdb::prepared
+function bb_is_first( $post_id ) { // First post in thread
+	global $bbdb;
+	if ( !$bb_post = bb_get_post( $post_id ) )
+		return false;
+	$post_id = (int) $bb_post->post_id;
+	$topic_id = (int) $bb_post->topic_id;
+
+	static $first_post;
+	if ( !isset( $first_post ) ) {
+		$where = apply_filters('bb_is_first_where', 'AND post_status = 0');
+		$first_post = (int) $bbdb->get_var("SELECT post_id FROM $bbdb->posts WHERE topic_id = $topic_id $where ORDER BY post_id ASC LIMIT 1");
+	}
+
+	return $post_id == $first_post;
+}
+
+// Globalizes the result.
+function bb_get_first_post( $_topic = false, $author_cache = true ) {
+	global $topic, $bb_first_post_cache, $bb_post;
+	if ( !$_topic )
+		$topic_id = (int) $topic->topic_id;
+	else if ( is_object($_topic) )
+		$topic_id = (int) $_topic->topic_id;
+	else if ( is_numeric($_topic) )
+		$topic_id = (int) $_topic;
+
+	if ( !$topic_id )
+		return false;
+
+	if ( isset($bb_first_post_cache[$topic_id]) ) {
+		$post = bb_get_post( $bb_first_post_cache[$topic_id] );
+	} else {
+		$first_posts = bb_cache_first_posts( array($topic_id), $author_cache );
+		if ( isset($first_posts[$topic_id]) )
+			$post = $first_posts[$topic_id];
+	}
+
+	if ( $post ) {
+		$bb_post = $post;
+		return $bb_post;
+	}
+
+	return false;
+}
+
+// Ignore the return value.  Cache first posts with this function and use bb_get_first_post to grab each.
+// NOT bbdb::prepared
+function bb_cache_first_posts( $_topics = false, $author_cache = true ) {
+	global $topics, $bb_first_post_cache, $bbdb;
+	if ( !$_topics )
+		$_topics =& $topics;
+	if ( !is_array($_topics) )
+		return false;
+
+	$topic_ids = array();
+	foreach ( $_topics as $topic )
+		if ( is_object($topic) )
+			$topic_ids[] = (int) $topic->topic_id;
+		else if ( is_numeric($topic) )
+			$topic_ids[] = (int) $topic;
+
+	$_topic_ids = join(',', $topic_ids);
+
+	$posts = (array) bb_cache_posts( "SELECT post_id FROM $bbdb->posts WHERE topic_id IN ($_topic_ids) AND post_position = 1", true );
+
+	$first_posts = array();
+	foreach ( $posts as $post ) {
+		$bb_first_post_cache[(int) $post->topic_id] = (int) $post->post_id;
+		$first_posts[(int) $post->topic_id] = $post;
+	}
+
+	if ( $author_cache )
+		bb_post_author_cache( $posts );
+
+	return $first_posts;
+}
+
+function bb_cache_posts( $query, $post_id_query = false ) {
+	global $bbdb;
+
+	$_query = '';
+	$_query_post_ids = array();
+	$_query_posts = array();
+	$_cached_posts = array();
+	$ordered_post_ids = array();
+
+	if ( $post_id_query && is_string( $query ) ) {
+		// The query is a SQL query to retrieve post_ids only
+		$key = md5( $query );
+		if ( false === $post_ids = wp_cache_get( $key, 'bb_cache_posts_post_ids' ) ) {
+			if ( !$post_ids = (array) $bbdb->get_col( $query, 0 ) ) {
+				return array();
+			}
+			wp_cache_add( $key, $post_ids, 'bb_cache_posts_post_ids' );
+		}
+		$query = $post_ids;
+	}
+
+	if ( is_array( $query ) ) {
+		$get_order_from_query = false;
+
+		foreach ( $query as $_post_id ) {
+			$ordered_post_ids[] = $_post_id;
+			if ( false === $_post = wp_cache_get( $_post_id, 'bb_post' ) ) {
+				$_query_post_ids[] = $_post_id;
+			} else {
+				$_cached_posts[$_post->post_id] = $_post;
+			}
+		}
+
+		if ( count( $_query_post_ids ) ) {
+			// Escape the ids for the SQL query
+			$_query_post_ids = $bbdb->escape_deep( $_query_post_ids );
+
+			// Sort the ids so the MySQL will more consistently cache the query
+			sort( $_query_post_ids );
+
+			$_query = "SELECT * FROM $bbdb->posts WHERE post_id IN ('" . join( "','", $_query_post_ids ) . "')";
+		}
+	} else {
+		// The query is a full SQL query which needs to be executed
+		$get_order_from_query = true;
+		$_query = $query;
+	}
+
+	if ( $_query_posts = (array) $bbdb->get_results( $_query ) ) {
+		$_appendable_posts = array();
+		foreach ( $_query_posts as $_query_post ) {
+			if ( $get_order_from_query ) {
+				$ordered_post_ids[] = $_query_post->post_id;
+			}
+			if ( false === $_post = wp_cache_get( $_query_post->post_id, 'bb_post' ) ) {
+				$_appendable_posts[] = $_query_post;
+			} else {
+				$_cached_posts[$_query_post->post_id] = $_post;
+			}
+		}
+		if ( count( $_appendable_posts ) ) {
+			$_query_posts = bb_append_meta( $_appendable_posts, 'post' );
+			foreach( $_query_posts as $_query_post ) {
+				wp_cache_add( $_query_post->post_id, $_query_post, 'bb_post' );
+			}
+		} else {
+			$_query_posts = array();
+		}
+	} else {
+		$_query_posts = array();
+	}
+
+	foreach ( array_merge( $_cached_posts, $_query_posts ) as $_post ) {
+		$keyed_posts[$_post->post_id] = $_post;
+	}
+
+	$the_posts = array();
+	foreach ( $ordered_post_ids as $ordered_post_id ) {
+		$the_posts[] = $keyed_posts[$ordered_post_id];
+	}
+
+	return $the_posts;
+}
+
+// Globalizes the result
+function bb_get_last_post( $_topic = false, $author_cache = true ) {
+	global $topic, $bb_post;
+	if ( !$_topic )
+		$topic_id = (int) $topic->topic_id;
+	else if ( is_object($_topic) )
+		$topic_id = (int) $_topic->topic_id;
+	else if ( is_numeric($_topic) )
+		$topic_id = (int) $_topic;
+
+	if ( !$topic_id )
+		return false;
+
+	$_topic = get_topic( $topic_id );
+
+	if ( $post = bb_get_post( $_topic->topic_last_post_id ) ) {
+		if ( $author_cache )
+			bb_post_author_cache( array($post) );
+		$bb_post = $post;
+	}
+
+	return $post;
+}
+
+// No return value. Cache last posts with this function and use bb_get_last_post to grab each.
+// NOT bbdb::prepared
+function bb_cache_last_posts( $_topics = false, $author_cache = true ) {
+	global $topics, $bbdb;
+	if ( !$_topics )
+		$_topics =& $topics;
+	if ( !is_array($_topics) )
+		return false;
+
+	$last_post_ids = array();
+	$topic_ids = array();
+	foreach ( $_topics as $topic )
+		if ( is_object($topic) )
+			$last_post_ids[] = (int) $topic->topic_last_post_id;
+		else if ( is_numeric($topic) && false !== $cached_topic = wp_cache_get( $topic, 'bb_topic' ) )
+			$last_post_ids[] = (int) $cached_topic->topic_last_post_id;
+		else if ( is_numeric($topic) )
+			$topic_ids[] = (int) $topic;
+
+	if ( !empty($last_post_ids) ) {
+		$_last_post_ids = join(',', $last_post_ids);
+		$posts = (array) bb_cache_posts( "SELECT post_id FROM $bbdb->posts WHERE post_id IN ($_last_post_ids) AND post_status = 0", true );
+		if ( $author_cache )
+			bb_post_author_cache( $posts );
+	}
+
+	if ( !empty($topic_ids) ) {	
+		$_topic_ids = join(',', $topic_ids);
+		$posts = (array) bb_cache_posts( "SELECT p.post_id FROM $bbdb->topics AS t LEFT JOIN $bbdb->posts AS p ON ( t.topic_last_post_id = p.post_id ) WHERE t.topic_id IN ($_topic_ids) AND p.post_status = 0", true );
+		if ( $author_cache )
+			bb_post_author_cache( $posts );
+	}
+}
+
+// NOT bbdb::prepared
+function bb_cache_post_topics( $posts ) {
+	global $bbdb;
+
+	if ( !$posts )
+		return;
+
+	$topic_ids = array();
+	foreach ( $posts as $post )
+		if ( false === wp_cache_get( $post->topic_id, 'bb_topic' ) )
+			$topic_ids[] = (int) $post->topic_id;
+
+	if ( !$topic_ids )
+		return;
+
+	sort( $topic_ids );
+	$topic_ids = join(',', $topic_ids);
+
+	if ( $topics = $bbdb->get_results( "SELECT * FROM $bbdb->topics WHERE topic_id IN($topic_ids)" ) )
+		bb_append_meta( $topics, 'topic' );
+}
+
+function bb_get_latest_posts( $limit = 0, $page = 1 ) {
+	$limit = (int) $limit;
+	$post_query = new BB_Query( 'post', array( 'page' => $page, 'per_page' => $limit ), 'get_latest_posts' );
+	return $post_query->results;
+}
+
+function bb_get_latest_forum_posts( $forum_id, $limit = 0, $page = 1 ) {
+	$forum_id = (int) $forum_id;
+	$limit    = (int) $limit;
+	$post_query = new BB_Query( 'post', array( 'forum_id' => $forum_id, 'page' => $page, 'per_page' => $limit ), 'get_latest_forum_posts' );
+	return $post_query->results;
+}
+
+function bb_insert_post( $args = null ) {
+	global $bbdb, $bb_current_user, $bb;
+
+	if ( !$args = wp_parse_args( $args ) )
+		return false;
+
+	$fields = array_keys( $args );
+
+	if ( isset($args['post_id']) && false !== $args['post_id'] ) {
+		$update = true;
+		if ( !$post_id = (int) get_post_id( $args['post_id'] ) )
+			return false;
+		// Get from db, not cache.  Good idea?
+		$post = $bbdb->get_row( $bbdb->prepare( "SELECT * FROM $bbdb->posts WHERE post_id = %d", $post_id ) );
+		$defaults = get_object_vars( $post );
+		unset( $defaults['post_id'] );
+
+		// Only update the args we passed
+		$fields = array_intersect( $fields, array_keys($defaults) );
+		if ( in_array( 'topic_id', $fields ) )
+			$fields[] = 'forum_id';
+
+		// No need to run filters if these aren't changing
+		// bb_new_post() and bb_update_post() will always run filters
+		$run_filters = (bool) array_intersect( array( 'post_status', 'post_text' ), $fields );
+	} else {
+		$post_id = false;
+		$update = false;
+		$now = bb_current_time( 'mysql' );
+		$current_user_id = bb_get_current_user_info( 'id' );
+		$ip_address = $_SERVER['REMOTE_ADDR'];
+
+		$defaults = array(
+			'topic_id' => 0,
+			'post_text' => '',
+			'post_time' => $now,
+			'poster_id' => $current_user_id, // accepts ids or names
+			'poster_ip' => $ip_address,
+			'post_status' => 0, // use bb_delete_post() instead
+			'post_position' => false
+		);
+
+		// Insert all args
+		$fields = array_keys($defaults);
+		$fields[] = 'forum_id';
+
+		$run_filters = true;
+	}
+
+	$defaults['throttle'] = true;
+	extract( wp_parse_args( $args, $defaults ) );
+	
+	// If the user is not logged in and loginless posting is ON, then this function expects $post_author, $post_email and $post_url to be sanitized (check bb-post.php for example)
+
+	if ( !$topic = get_topic( $topic_id ) )
+		return false;
+
+	if ( bb_is_login_required() && ! $user = bb_get_user( $poster_id ) )
+		return false;
+
+	$topic_id = (int) $topic->topic_id;
+	$forum_id = (int) $topic->forum_id;
+
+	if ( $run_filters && !$post_text = apply_filters('pre_post', $post_text, $post_id, $topic_id) )
+		return false;
+
+	if ( $update ) // Don't change post_status with this function.  Use bb_delete_post().
+		$post_status = $post->post_status;
+
+	if ( $run_filters )
+		$post_status = (int) apply_filters('pre_post_status', $post_status, $post_id, $topic_id);
+
+	if ( false === $post_position )
+		$post_position = $topic_posts = intval( ( 0 == $post_status ) ? $topic->topic_posts + 1 : $topic->topic_posts );
+
+	unset($defaults['throttle']);
+
+	if ( $update ) {
+		$bbdb->update( $bbdb->posts, compact( $fields ), compact( 'post_id' ) );
+		wp_cache_delete( $post_id, 'bb_post' );
+	} else {
+		$bbdb->insert( $bbdb->posts, compact( $fields ) );
+		$post_id = $topic_last_post_id = (int) $bbdb->insert_id;
+
+		if ( 0 == $post_status ) {
+			$topic_time = $post_time;
+			$topic_last_poster = ( ! bb_is_user_logged_in() && ! bb_is_login_required() ) ? -1 : $poster_id;
+			$topic_last_poster_name = ( ! bb_is_user_logged_in() && ! bb_is_login_required() ) ? $post_author : $user->user_login;
+
+			$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET posts = posts + 1 WHERE forum_id = %d;", $topic->forum_id ) );
+			$bbdb->update(
+				$bbdb->topics,
+				compact( 'topic_time', 'topic_last_poster', 'topic_last_poster_name', 'topic_last_post_id', 'topic_posts' ),
+				compact ( 'topic_id' )
+			);
+
+			$query = new BB_Query( 'post', array( 'post_author_id' => $poster_id, 'topic_id' => $topic_id, 'post_id' => "-$post_id" ) );
+			if ( !$query->results ) {
+				$topics_replied_key = $bbdb->prefix . 'topics_replied';
+				bb_update_usermeta( $poster_id, $topics_replied_key, $user->$topics_replied_key + 1 );
+			}
+
+		} else {
+			bb_update_topicmeta( $topic->topic_id, 'deleted_posts', isset($topic->deleted_posts) ? $topic->deleted_posts + 1 : 1 );
+		}
+	}
+	bb_update_topic_voices( $topic_id );
+
+	// if user not logged in, save user data as meta data
+	if ( !$user ) {
+		bb_update_meta($post_id, 'post_author', $post_author, 'post');
+		bb_update_meta($post_id, 'post_email', $post_email, 'post');
+		bb_update_meta($post_id, 'post_url', $post_url, 'post');
+	}
+	
+	if ( $throttle && !bb_current_user_can( 'throttle' ) ) {
+		if ( $user )
+			bb_update_usermeta( $poster_id, 'last_posted', time() );
+		else
+			bb_set_transient( $_SERVER['REMOTE_ADDR'] . '_last_posted', time() );
+	}
+	
+	if ( !bb_is_login_required() && !$user = bb_get_user( $poster_id ) ) {
+		$post_cookie_lifetime = apply_filters( 'bb_post_cookie_lifetime', 30000000 );
+		setcookie( 'post_author_' . BB_HASH, $post_author, time() + $post_cookie_lifetime, $bb->cookiepath, $bb->cookiedomain );
+		setcookie( 'post_author_email_' . BB_HASH, $post_email, time() + $post_cookie_lifetime, $bb->cookiepath, $bb->cookiedomain );
+		setcookie( 'post_author_url_' . BB_HASH, $post_url, time() + $post_cookie_lifetime, $bb->cookiepath, $bb->cookiedomain );
+	}
+
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	wp_cache_delete( $topic_id, 'bb_thread' );
+	wp_cache_delete( $forum_id, 'bb_forum' );
+	wp_cache_flush( 'bb_forums' );
+	wp_cache_flush( 'bb_query' );
+	wp_cache_flush( 'bb_cache_posts_post_ids' );
+
+	if ( $update ) // fire actions after cache is flushed
+		do_action( 'bb_update_post', $post_id );
+	else
+		do_action( 'bb_new_post', $post_id );
+
+	do_action( 'bb_insert_post', $post_id, $args, compact( array_keys($args) ) ); // post_id, what was passed, what was used
+
+	if (bb_get_option('enable_pingback')) {
+		bb_update_postmeta($post_id, 'pingback_queued', '');
+		wp_schedule_single_event(time(), 'do_pingbacks');
+	}
+
+	return $post_id;
+}
+
+// Deprecated: expects $post_text to be pre-escaped
+function bb_new_post( $topic_id, $post_text ) {
+	$post_text = stripslashes( $post_text );
+	return bb_insert_post( compact( 'topic_id', 'post_text' ) );
+}
+
+// Deprecated: expects $post_text to be pre-escaped
+function bb_update_post( $post_text, $post_id, $topic_id ) {
+	$post_text = stripslashes( $post_text );
+	return bb_insert_post( compact( 'post_text', 'post_id', 'topic_id' ) );
+}
+
+function bb_update_post_positions( $topic_id ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	$posts = get_thread( $topic_id, array( 'per_page' => '-1' ) );
+	if ( $posts ) {
+		foreach ( $posts as $i => $post ) {
+			$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->posts SET post_position = %d WHERE post_id = %d", $i + 1, $post->post_id ) );
+			wp_cache_delete( (int) $post->post_id, 'bb_post' );
+		}
+		wp_cache_delete( $topic_id, 'bb_thread' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+		return true;
+	} else {
+		return false;
+	}
+}
+
+function bb_delete_post( $post_id, $new_status = 0 ) {
+	global $bbdb, $topic, $bb_post;
+	$post_id = (int) $post_id;
+	$bb_post    = bb_get_post ( $post_id );
+	$new_status = (int) $new_status;
+	$old_status = (int) $bb_post->post_status;
+	add_filter( 'get_topic_where', 'bb_no_where' );
+	$topic   = get_topic( $bb_post->topic_id );
+	$topic_id = (int) $topic->topic_id;
+
+	if ( $bb_post ) {
+		$uid = (int) $bb_post->poster_id;
+		if ( $new_status == $old_status )
+			return;
+		_bb_delete_post( $post_id, $new_status );
+		if ( 0 == $old_status ) {
+			bb_update_topicmeta( $topic_id, 'deleted_posts', $topic->deleted_posts + 1 );
+			$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET posts = posts - 1 WHERE forum_id = %d", $topic->forum_id ) );
+		} else if ( 0 == $new_status ) {
+			bb_update_topicmeta( $topic_id, 'deleted_posts', $topic->deleted_posts - 1 );
+			$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET posts = posts + 1 WHERE forum_id = %d", $topic->forum_id ) );
+		}
+		$posts = (int) $bbdb->get_var( $bbdb->prepare( "SELECT COUNT(*) FROM $bbdb->posts WHERE topic_id = %d AND post_status = 0", $topic_id ) );
+		$bbdb->update( $bbdb->topics, array( 'topic_posts' => $posts ), compact( 'topic_id' ) );
+
+		if ( 0 == $posts ) {
+			if ( 0 == $topic->topic_status || 1 == $new_status )
+				bb_delete_topic( $topic_id, $new_status );
+		} else {
+			if ( 0 != $topic->topic_status ) {
+				$bbdb->update( $bbdb->topics, array( 'topic_status' => 0 ), compact( 'topic_id' ) );
+				$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET topics = topics + 1 WHERE forum_id = %d", $topic->forum_id ) );
+			}
+			bb_topic_set_last_post( $topic_id );
+			bb_update_post_positions( $topic_id );
+			bb_update_topic_voices( $topic_id );
+		}
+
+		$user = bb_get_user( $uid );
+
+		$user_posts = new BB_Query( 'post', array( 'post_author_id' => $user->ID, 'topic_id' => $topic_id ) );
+		if ( $new_status && !$user_posts->results ) {
+			$topics_replied_key = $bbdb->prefix . 'topics_replied';
+			bb_update_usermeta( $user->ID, $topics_replied_key, $user->$topics_replied_key - 1 );
+		}
+		wp_cache_delete( $topic_id, 'bb_topic' );
+		wp_cache_delete( $topic_id, 'bb_thread' );
+		wp_cache_flush( 'bb_forums' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+		do_action( 'bb_delete_post', $post_id, $new_status, $old_status );
+		return $post_id;
+	} else {
+		return false;
+	}
+}
+
+function _bb_delete_post( $post_id, $post_status ) {
+	global $bbdb;
+	$post_id = (int) $post_id;
+	$post_status = (int) $post_status;
+	$bbdb->update( $bbdb->posts, compact( 'post_status' ), compact( 'post_id' ) );
+	wp_cache_delete( $post_id, 'bb_post' );
+	do_action( '_bb_delete_post', $post_id, $post_status );
+}
+
+function bb_topics_replied_on_undelete_post( $post_id ) {
+	global $bbdb;
+	$bb_post = bb_get_post( $post_id );
+	$topic = get_topic( $bb_post->topic_id );
+
+	$user_posts = new BB_Query( 'post', array( 'post_author_id' => $bb_post->poster_id, 'topic_id' => $topic->topic_id ) );
+
+	if ( 1 == count($user_posts) && $user = bb_get_user( $bb_post->poster_id ) ) {
+		$topics_replied_key = $bbdb->prefix . 'topics_replied';
+		bb_update_usermeta( $user->ID, $topics_replied_key, $user->$topics_replied_key + 1 );
+	}
+}
+
+function bb_post_author_cache($posts) {
+	if ( !$posts )
+		return;
+
+	$ids = array();
+	foreach ($posts as $bb_post)
+		$ids[] = $bb_post->poster_id;
+
+	if ( $ids )
+		bb_cache_users(array_unique($ids));
+}
+
+// These two filters are lame.  It'd be nice if we could do this in the query parameters
+function bb_get_recent_user_replies_fields( $fields ) {
+	return 'MAX( p.post_id ) AS post_id';
+}
+
+function bb_get_recent_user_replies_group_by() {
+	return 'p.topic_id';
+}
+
+function bb_get_recent_user_replies( $user_id ) {
+	global $bbdb;
+	$user_id = (int) $user_id;
+
+	$post_query = new BB_Query(
+		'post',
+		array(
+			'post_author_id' => $user_id,
+			'order_by' => 'post_id',
+			'post_id_only' => true,
+		),
+		'get_recent_user_replies'
+	);
+
+	return $post_query->results;
+}
+
+/**
+ * Sends notification emails for new posts.
+ *
+ * Gets new post's ID and check if there are subscribed
+ * user to that topic, and if there are, send notifications
+ *
+ * @since 1.1
+ *
+ * @param int $post_id ID of new post
+ */
+function bb_notify_subscribers( $post_id ) {
+	global $bbdb, $bb_ksd_pre_post_status;
+
+	if ( !empty( $bb_ksd_pre_post_status ) )
+		return false;
+
+	if ( !$post = bb_get_post( $post_id ) )
+		return false;
+
+	if ( !$topic = get_topic( $post->topic_id ) )
+		return false;
+	
+	$post_id = $post->post_id;
+	$topic_id = $topic->topic_id;
+
+	if ( !$poster_name = get_post_author( $post_id ) )
+		return false;
+	
+	do_action( 'bb_pre_notify_subscribers', $post_id, $topic_id );
+
+	if ( !$user_ids = $bbdb->get_col( $bbdb->prepare( "SELECT `$bbdb->term_relationships`.`object_id`
+		FROM $bbdb->term_relationships, $bbdb->term_taxonomy, $bbdb->terms
+		WHERE `$bbdb->term_relationships`.`term_taxonomy_id` = `$bbdb->term_taxonomy`.`term_taxonomy_id`
+		AND `$bbdb->term_taxonomy`.`term_id` = `$bbdb->terms`.`term_id`
+		AND `$bbdb->term_taxonomy`.`taxonomy` = 'bb_subscribe'
+		AND `$bbdb->terms`.`slug` = 'topic-%d'",
+		$topic_id ) ) )
+		return false;
+
+	foreach ( (array) $user_ids as $user_id ) {
+		if ( $user_id == $post->poster_id )
+			continue; // don't send notifications to the person who made the post
+		
+		$user = bb_get_user( $user_id );
+		
+		if ( !$message = apply_filters( 'bb_subscription_mail_message', __( "%1\$s wrote:\n\n%2\$s\n\nRead this post on the forums: %3\$s\n\nYou're getting this email because you subscribed to '%4\$s.'\nPlease click the link above, login, and click 'Unsubscribe' at the top of the page to stop receiving emails from this topic." ), $post_id, $topic_id ) )
+			continue; /* For plugins */
+		
+		bb_mail(
+			$user->user_email,
+			apply_filters( 'bb_subscription_mail_title', '[' . bb_get_option( 'name' ) . '] ' . $topic->topic_title, $post_id, $topic_id ),
+			sprintf( $message, $poster_name, strip_tags( $post->post_text ), get_post_link( $post_id ), strip_tags( $topic->topic_title ) )
+		);
+	}
+	
+	do_action( 'bb_post_notify_subscribers', $post_id, $topic_id );
+}
+
+/**
+ * Updates user's subscription status in database.
+ *
+ * Gets user's new subscription status for topic and
+ * adds new status to database.
+ *
+ * @since 1.1
+ *
+ * @param int $topic_id ID of topic for subscription
+ * @param string $new_status New subscription status
+ * @param int $user_id Optional. ID of user for subscription
+ */
+function bb_subscription_management( $topic_id, $new_status, $user_id = '' ) {
+	global $bbdb, $wp_taxonomy_object;
+	
+	$topic = get_topic( $topic_id );
+	if (!$user_id) {
+		$user_id = bb_get_current_user_info( 'id' );
+	}
+	
+	do_action( 'bb_subscripton_management', $topic_id, $new_status, $user_id );
+	
+	switch ( $new_status ) {
+		case 'add':
+			$tt_ids = $wp_taxonomy_object->set_object_terms( $user_id, 'topic-' . $topic->topic_id, 'bb_subscribe', array( 'append' => true, 'user_id' => $user_id ) );
+			break;
+		case 'remove':
+			// I hate this with the passion of a thousand suns
+			$term_id = $bbdb->get_var( "SELECT term_id FROM $bbdb->terms WHERE slug = 'topic-$topic->topic_id'" );
+			$term_taxonomy_id = $bbdb->get_var( "SELECT term_taxonomy_id FROM $bbdb->term_taxonomy WHERE term_id = $term_id AND taxonomy = 'bb_subscribe'" );
+			$bbdb->query( "DELETE FROM $bbdb->term_relationships WHERE object_id = $user_id AND term_taxonomy_id = $term_taxonomy_id" );
+			$bbdb->query( "DELETE FROM $bbdb->term_taxonomy WHERE term_id = $term_id AND taxonomy = 'bb_subscribe'" );
+			break;
+	}
+	
+}
+
+/**
+ * Process subscription checkbox submission.
+ *
+ * Get ID of and new subscription status and pass values to
+ * bb_user_subscribe_checkbox_update function
+ *
+ * @since 1.1
+ *
+ * @param int $post_id ID of new/edited post
+ */
+function bb_user_subscribe_checkbox_update( $post_id ) {
+	if ( !bb_is_user_logged_in() )
+		return false;
+	
+	$post		= bb_get_post( $post_id );
+	$topic_id	= (int) $post->topic_id;
+	$subscribed	= bb_is_user_subscribed( array( 'topic_id' => $topic_id, 'user_id' => $post->poster_id ) ) ? true : false;
+	$check		= $_REQUEST['subscription_checkbox'];
+	
+	do_action( 'bb_user_subscribe_checkbox_update', $post_id, $topic_id, $subscribe, $check );
+	
+	if ( 'subscribe' == $check && !$subscribed )
+		bb_subscription_management( $topic_id, 'add' );
+	elseif ( !$check && $subscribed )
+		bb_subscription_management( $topic_id, 'remove' );
+	
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-script-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-script-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-script-loader.php	(revision 5057)
@@ -0,0 +1,100 @@
+<?php
+
+function bb_default_scripts( &$scripts ) {
+	$scripts->base_url = bb_get_uri( BB_INC, null, BB_URI_CONTEXT_SCRIPT_SRC );
+	$scripts->base_url_admin = bb_get_uri( 'bb-admin/', null, BB_URI_CONTEXT_SCRIPT_SRC + BB_URI_CONTEXT_BB_ADMIN );
+	$scripts->content_url = ''; // May not work - might need to specify plugin and theme urls
+	$scripts->default_version = bb_get_option( 'version' );
+	$scripts->default_dirs = array( '/bb-admin/js/', '/bb-includes/js/' );
+
+	// These are our enqueued scripts
+	$scripts->add( 'topic', $scripts->base_url . 'js/topic.js', array('wp-lists'), '20090602' );
+	$scripts->add( 'profile-edit', $scripts->base_url . 'js/profile-edit.js', array('password-strength-meter'), '20080721' );
+	$scripts->add( 'admin-forums', $scripts->base_url_admin . 'js/admin-forums.js', array('wp-lists', 'interface'), '20090320' );
+	$scripts->add( 'utils', $scripts->base_url_admin . 'js/utils.js', false, '20090102' );
+	$scripts->add( 'common', $scripts->base_url_admin . 'js/common.js', array('jquery', 'hoverIntent', 'utils'), '20090517' );
+	$scripts->add_data( 'common', 'group', 1 );
+	$scripts->localize( 'common', 'commonL10n', array(
+		'warnDelete' => __( "You are about to delete the selected items.\n  'Cancel' to stop, 'OK' to delete." ),
+		'l10n_print_after' => 'try{convertEntities(commonL10n);}catch(e){};'
+	) );
+	$scripts->localize( 'admin-forums', 'bbSortForumsL10n', array(
+		'handleText' => __('drag'),
+		'saveText'   => __('Save Forum Order'),
+		'editText'   => __('Edit Forum Order')
+	));
+
+	// These are non-3rd-party libraries
+	$scripts->add( 'wp-lists', $scripts->base_url . 'js/wp-lists.js', array('wp-ajax-response','jquery-color'), '20080826' );
+	$scripts->localize( 'wp-lists', 'wpListL10n', array(
+		'url' => $scripts->base_url_admin . 'admin-ajax.php'
+	) );
+	$scripts->add( 'wp-ajax-response', $scripts->base_url . 'js/wp-ajax-response.js', array('jquery'), '20080316' );
+	$scripts->localize( 'wp-ajax-response', 'wpAjax', array(
+		'noPerm' => __('You do not have permission to do that.'),
+		'broken' => __('An unidentified error has occurred.')
+	) );
+
+	// jQuery and friends
+	$scripts->add( 'jquery', $scripts->base_url . 'js/jquery/jquery.js', false, '1.4.2' );
+	$scripts->add( 'jquery-color', $scripts->base_url . 'js/jquery/jquery.color.js', array('jquery'), '2.0-4561' );
+	$scripts->add( 'interface', $scripts->base_url . 'js/jquery/interface.js', array('jquery'), '1.2.3' );
+	$scripts->add( 'password-strength-meter', $scripts->base_url . 'js/jquery/password-strength-meter.js', array('jquery'), '20070405' );
+	$scripts->localize( 'password-strength-meter', 'pwsL10n', array(
+		'short' => __('Too short'),
+		'bad' => __('Bad'),
+		'good' => __('Good'),
+		'strong' => __('Strong')
+	));
+	$scripts->add( 'hoverIntent', $scripts->base_url . 'js/jquery/hoverIntent.js', array('jquery'), '20090102' );
+	$scripts->add_data( 'hoverIntent', 'group', 1 );
+}
+
+/**
+ * Reorder JavaScript scripts array to place prototype before jQuery.
+ *
+ * @param array $js_array JavaScript scripst array
+ * @return array Reordered array, if needed.
+ */
+function bb_prototype_before_jquery( $js_array ) {
+	if ( false === $jquery = array_search( 'jquery', $js_array, true ) )
+		return $js_array;
+
+	if ( false === $prototype = array_search( 'prototype', $js_array, true ) )
+		return $js_array;
+
+	if ( $prototype < $jquery )
+		return $js_array;
+
+	unset($js_array[$prototype]);
+
+	array_splice( $js_array, $jquery, 0, 'prototype' );
+
+	return $js_array;
+}
+
+/**
+ * Load localized script just in time for MCE.
+ *
+ * These localizations require information that may not be loaded even by init.
+ */
+function bb_just_in_time_script_localization() {
+	wp_localize_script( 'topic', 'bbTopicJS', array(
+		'currentUserId' => bb_get_current_user_info( 'id' ),
+		'topicId' => get_topic_id(),
+		'favoritesLink' => get_favorites_link(),
+		'isFav' => (int) is_user_favorite( bb_get_current_user_info( 'id' ) ),
+		'confirmPostDelete' => __("Are you sure you want to delete this post?"),
+		'confirmPostUnDelete' => __("Are you sure you want to undelete this post?"),
+		'favLinkYes' => __( 'favorites' ),
+		'favLinkNo' => __( '?' ),
+		'favYes' => __( 'This topic is one of your %favLinkYes% [%favDel%]' ),
+		'favNo' => __( '%favAdd% (%favLinkNo%)' ),
+		'favDel' => __( '&times;' ),
+		'favAdd' => __( 'Add this topic to your favorites' )
+	));
+}
+
+add_action( 'wp_default_scripts', 'bb_default_scripts' );
+add_filter( 'wp_print_scripts', 'bb_just_in_time_script_localization' );
+add_filter( 'print_scripts_array', 'bb_prototype_before_jquery' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-statistics.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-statistics.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-statistics.php	(revision 5057)
@@ -0,0 +1,250 @@
+<?php
+/**
+ * bbPress Forum Content Statistics Functions
+ *
+ * @package bbPress
+ */
+
+
+
+/**
+ * Get the total number of forums
+ *
+ * @since 1.0
+ * @uses $bbdb Database Object
+ * @uses $bb_total_forums Cache of result generated by previous run
+ *
+ * @return int
+ */
+function get_total_forums() {
+	global $bbdb, $bb_total_forums;
+	if ( isset($bb_total_forums) )
+		return $bb_total_forums;
+	$bb_total_forums = $bbdb->get_var("SELECT COUNT(*) FROM $bbdb->forums USE INDEX (PRIMARY)");
+	return $bb_total_forums;
+}
+
+/**
+ * Output the number of forums
+ *
+ * @since 1.0
+ */
+function total_forums() {
+	echo apply_filters('total_forums', get_total_forums() );
+}
+
+/**
+ * Get the total number of users
+ *
+ * @since 1.0
+ * @uses $bbdb Database Object
+ * @uses $bb_total_users Cache of result generated by previous run
+ *
+ * @return int
+ */
+function bb_get_total_users()
+{
+	global $bbdb, $bb_total_users;
+	if ( isset( $bb_total_users ) ) {
+		return $bb_total_users;
+	}
+	if ( false === $bb_total_users = apply_filters( 'bb_get_total_users', false ) ) {
+		$bb_total_users = $bbdb->get_var( "SELECT COUNT(*) FROM $bbdb->users USE INDEX (PRIMARY);" );
+	}
+	return $bb_total_users;
+}
+
+/**
+ * Output the number of users
+ *
+ * @since 1.0
+ */
+function bb_total_users()
+{
+	echo apply_filters( 'total_users', bb_get_total_users() );
+}
+
+/**
+ * Get the total number of posts
+ *
+ * @since 0.7.2
+ * @uses $bbdb Database Object
+ * @uses $bb_total_posts Cache of result generated by previous run
+ *
+ * @return int
+ */
+function get_total_posts() {
+	global $bbdb, $bb_total_posts;
+	if ( isset($bb_total_posts) )
+		return $bb_total_posts;
+	$bb_total_posts = $bbdb->get_var("SELECT SUM(posts) FROM $bbdb->forums");
+	return $bb_total_posts;
+}
+
+/**
+ * Output the number of posts
+ *
+ * @since 0.7.2
+ */
+function total_posts() {
+	echo apply_filters('total_posts', get_total_posts() );
+}
+
+/**
+ * Get the total number of topics
+ *
+ * @since 0.7.2
+ * @uses $bbdb Database Object
+ * @uses $bb_total_topics Cache of result generated by previous run
+ *
+ * @return int
+ */
+function get_total_topics() {
+	global $bbdb, $bb_total_topics;
+	if ( isset($bb_total_topics) )
+		return $bb_total_topics;
+	$bb_total_topics = $bbdb->get_var("SELECT SUM(topics) FROM $bbdb->forums");
+	return $bb_total_topics;
+}
+
+/**
+ * Output the number of topics
+ *
+ * @since 0.7.2
+ */
+function total_topics() {
+	echo apply_filters('total_topics', get_total_topics());
+}
+
+/**
+ * Get the popular topics
+ *
+ * @since 0.7.2
+ *
+ * @param int $num Number of topics to return
+ * @return array
+ */
+function get_popular_topics( $num = 10 ) {
+	$query = new BB_Query( 'topic', array('per_page' => $num, 'order_by' => 'topic_posts', 'append_meta' => 0) );
+	return $query->results;
+}
+
+/**
+ * Output the date when current installation was created
+ *
+ * @since 0.8
+ *
+ * @param string|array $args Arguments to pass through to bb_get_inception()
+ */
+function bb_inception( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+	$time = apply_filters( 'bb_inception', bb_get_inception( array('format' => 'mysql') + $args), $args );
+	echo _bb_time_function_return( $time, $args );
+}
+
+/**
+ * Get the date when current installation was created
+ *
+ * @since 0.8
+ * @uses $bbdb Database Object
+ * @uses $bb_inception Result cache
+ *
+ * @param string|array $args Formatting options for the timestamp.
+ * @return int
+ */
+function bb_get_inception( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+
+	global $bbdb, $bb_inception;
+	if ( !isset($bb_inception) )
+		$bb_inception = $bbdb->get_var("SELECT topic_start_time FROM $bbdb->topics ORDER BY topic_start_time LIMIT 1");
+
+	return apply_filters( 'bb_get_inception', _bb_time_function_return( $bb_inception, $args ) );
+}
+
+/**
+ * Get the average number of registrations per day
+ *
+ * @since 0.7.2
+ *
+ * @return int|float
+ */
+function get_registrations_per_day()
+{
+	return bb_get_total_users() / ceil( ( time() - bb_get_inception( 'timestamp' ) ) / 3600 / 24 );
+}
+
+/**
+ * Output the average number of registrations per day
+ *
+ * @since 0.7.2
+ */
+function registrations_per_day() {
+	echo apply_filters('registrations_per_day', bb_number_format_i18n(get_registrations_per_day(),3));
+}
+
+/**
+ * Get the average number of posts per day
+ *
+ * @since 0.7.2
+ *
+ * @return int|float
+ */
+function get_posts_per_day() {
+	return get_total_posts() / ceil( ( time() - bb_get_inception( 'timestamp' ) ) / 3600 / 24 );
+}
+
+/**
+ * Output the average number of posts per day
+ *
+ * @since 0.7.2
+ */
+function posts_per_day() {
+	echo apply_filters('posts_per_day', bb_number_format_i18n(get_posts_per_day(),3));
+}
+
+/**
+ * Get the average number of topics per day
+ *
+ * @since 0.7.2
+ *
+ * @return int|float
+ */
+function get_topics_per_day() {
+	return get_total_topics() / ceil( ( time() - bb_get_inception( 'timestamp' ) ) / 3600 / 24 );
+}
+
+/**
+ * Output the average number of topics per day
+ *
+ * @since 0.7.2
+ */
+function topics_per_day() {
+	echo apply_filters('topics_per_day', bb_number_format_i18n(get_topics_per_day(),3));
+}
+
+function bb_get_total_topic_tags()
+{
+	global $bb_total_topic_tags;
+	if ( isset($bb_total_topic_tags) ) {
+		return $bb_total_topic_tags;
+	}
+	global $wp_taxonomy_object;
+	$bb_total_topic_tags = $wp_taxonomy_object->count_terms( 'bb_topic_tag' );
+	return $bb_total_topic_tags;
+}
+
+function bb_total_topic_tags()
+{
+	echo apply_filters( 'bb_total_topic_tags', bb_get_total_topic_tags() );
+}
+
+function bb_get_topic_tags_per_day()
+{
+	return bb_get_total_topic_tags() / ceil( ( time() - bb_get_inception( 'timestamp' ) ) / 3600 / 24 );
+}
+
+function bb_topic_tags_per_day()
+{
+	echo apply_filters('bb_topic_tags_per_day', bb_number_format_i18n( bb_get_topic_tags_per_day(), 3 ) );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-template.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-template.php	(revision 5057)
@@ -0,0 +1,3763 @@
+<?php
+
+function bb_load_template( $files, $globals = false, $action_arg = null )
+{
+	global $bb, $bbdb, $bb_current_user, $page, $bb_cache,
+		$posts, $bb_post, $post_id, $topics, $topic, $topic_id,
+		$forums, $forum, $forum_id, $tags, $tag, $tag_name, $user, $user_id, $view,
+		$del_class, $bb_alt;
+
+	if ( $globals ) {
+		foreach ( $globals as $global => $v ) {
+			if ( !is_numeric($global) ) {
+				$$global = $v;
+			} else {
+				global $$v;
+			}
+		}
+	}
+
+	$files = (array) $files;
+	$template = false;
+	$default_template = false;
+	$file_used = false;
+	$default_file_used = false;
+
+	foreach ( $files as $file ) {
+		do_action( 'bb_' . $file, $action_arg );
+		if ( false !== $template = bb_get_template( $file, false ) ) {
+			$file_used = $file;
+			break;
+		}
+		if ( !$default_template ) {
+			if ( false !== $default_template = bb_get_default_template( $file ) ) {
+				$default_file_used = $file;
+			}
+		}
+	}
+
+	if ( !$template && $default_template ) {
+		$template = $default_template;
+		$file_used = $default_file_used;
+	}
+
+	$template = apply_filters( 'bb_template', $template, $file_used );
+	include( $template );
+
+	do_action( 'bb_after_' . $file_used, $action_arg );
+}
+
+function bb_get_template( $file, $default = true )
+{
+	global $bb;
+	// Skip theme loading in "safe" mode
+	if ( !isset( $bb->safemode ) || $bb->safemode !== true ) {
+		if ( file_exists( bb_get_active_theme_directory() .  $file ) ) {
+			return bb_get_active_theme_directory() .  $file;
+		}
+	}
+
+	if ( !$default ) {
+		return false;
+	}
+
+	return bb_get_default_template( $file );
+}
+
+function bb_get_default_template( $file )
+{
+	if ( file_exists( BB_DEFAULT_THEME_DIR . $file ) ) {
+		return BB_DEFAULT_THEME_DIR . $file;
+	}
+}
+
+function bb_get_header()
+{
+	bb_load_template( 'header.php' );
+}
+
+function bb_language_attributes( $xhtml = 0 )
+{
+	$output = '';
+	if ( $dir = bb_get_option( 'text_direction' ) ) {
+		$output = 'dir="' . $dir . '" ';
+	}
+	if ( $lang = bb_get_option( 'language' ) ) {
+		$output .= 'xml:lang="' . $lang . '" ';
+		if ( $xhtml < '1.1' ) {
+			$output .= 'lang="' . $lang . '"';
+		}
+	}
+
+	echo ' ' . rtrim( $output );
+}
+
+function bb_generator( $type = 'xhtml' )
+{
+	if ( !$type ) {
+		$type = 'xhtml';
+	}
+	echo apply_filters( 'bb_generator', bb_get_generator( $type ) . "\n", $type );
+}
+
+function bb_get_generator( $type = 'xhtml' )
+{
+	if ( !$type ) {
+		$type = 'xhtml';
+	}
+	switch ( $type ) {
+		case 'html':
+			$gen = '<meta name="generator" content="bbPress ' . bb_get_option( 'version' ) . '">';
+			break;
+		case 'xhtml':
+			$gen = '<meta name="generator" content="bbPress ' . bb_get_option( 'version' ) . '" />';
+			break;
+		case 'atom':
+			$gen = '<generator uri="http://bbpress.org/" version="' . bb_get_option( 'version' ) . '">bbPress</generator>';
+			break;
+		case 'rss2':
+			$gen = '<generator>http://bbpress.org/?v=' . bb_get_option( 'version' ) . '</generator>';
+			break;
+		case 'rdf':
+			$gen = '<admin:generatorAgent rdf:resource="http://bbpress.org/?v=' . bb_get_option( 'version' ) . '" />';
+			break;
+		case 'comment':
+			$gen = '<!-- generator="bbPress/' . bb_get_option( 'version' ) . '" -->';
+			break;
+		case 'export':
+			$gen = '<!-- generator="bbPress/' . bb_get_option( 'version' ) . '" created="'. date( 'Y-m-d H:i' ) . '"-->';
+			break;
+	}
+	return apply_filters( 'bb_get_generator', $gen, $type );
+}
+
+function bb_stylesheet_uri( $stylesheet = '' )
+{
+	echo esc_html( bb_get_stylesheet_uri( $stylesheet ) );
+}
+
+function bb_get_stylesheet_uri( $stylesheet = '' )
+{
+	if ( 'rtl' == $stylesheet ) {
+		$css_file = 'style-rtl.css';
+	} else {
+		$css_file = 'style.css';
+	}
+
+	$active_theme = bb_get_active_theme_directory();
+
+	if ( file_exists( $active_theme . $css_file ) ) {
+		$r = bb_get_active_theme_uri() . $css_file;
+	} else {
+		$r = BB_DEFAULT_THEME_URL . $css_file;
+	}
+
+	return apply_filters( 'bb_get_stylesheet_uri', $r, $stylesheet );
+}
+
+function bb_active_theme_uri()
+{
+	echo bb_get_active_theme_uri();
+}
+
+function bb_get_active_theme_uri()
+{
+	global $bb;
+	// Skip theme loading in "safe" mode
+	if ( isset( $bb->safemode ) && $bb->safemode === true ) {
+		$active_theme_uri = BB_DEFAULT_THEME_URL;
+	} elseif ( !$active_theme = bb_get_option( 'bb_active_theme' ) ) {
+		$active_theme_uri = BB_DEFAULT_THEME_URL;
+	} else {
+		$active_theme_uri = bb_get_theme_uri( $active_theme );
+	}
+	return apply_filters( 'bb_get_active_theme_uri', $active_theme_uri );
+}
+
+function bb_get_theme_uri( $theme = false )
+{
+	global $bb;
+	if ( preg_match( '/^([a-z0-9_-]+)#([a-z0-9_-]+)$/i', $theme, $_matches ) ) {
+		$theme_uri = $bb->theme_locations[$_matches[1]]['url'] . $_matches[2] . '/';
+	} else {
+		$theme_uri = $bb->theme_locations['core']['url'];
+	}
+	return apply_filters( 'bb_get_theme_uri', $theme_uri, $theme );
+}
+
+function bb_get_footer()
+{
+	bb_load_template( 'footer.php' );
+}
+
+function bb_head()
+{
+	do_action('bb_head');
+}
+
+/**
+ * Display the link to the Really Simple Discovery service endpoint.
+ *
+ * @link http://archipelago.phrasewise.com/rsd
+ * @since 1.0
+ */
+function bb_rsd_link() {
+	if (bb_get_option('enable_xmlrpc'))
+		echo '<link rel="EditURI" type="application/rsd+xml" title="RSD" href="' . bb_get_uri('xmlrpc.php', 'rsd', BB_URI_CONTEXT_LINK_OTHER + BB_URI_CONTEXT_BB_XMLRPC) . '" />' . "\n";
+}
+
+/**
+ * Display the link to the pingback service endpoint.
+ *
+ * @since 1.0
+ */
+function bb_pingback_link() {
+	if (bb_get_option('enable_pingback'))
+		echo '<link rel="pingback" href="' . bb_get_uri('xmlrpc.php', null, BB_URI_CONTEXT_LINK_OTHER + BB_URI_CONTEXT_BB_XMLRPC) . '" />' . "\n";
+}
+
+function profile_menu() {
+	global $user_id, $profile_menu, $self, $profile_page_title;
+	$list  = "<ul id='profile-menu'>";
+	$list .= "\n\t<li" . ( ( $self ) ? '' : ' class="current"' ) . '><a href="' . esc_attr( get_user_profile_link( $user_id ) ) . '">' . __('Profile') . '</a></li>';
+	$id = bb_get_current_user_info( 'id' );
+	foreach ($profile_menu as $item) {
+		// 0 = name, 1 = users cap, 2 = others cap, 3 = file
+		$class = '';
+		if ( $item[3] == $self ) {
+			$class = ' class="current"';
+			$profile_page_title = $item[0];
+		}
+		if ( bb_can_access_tab( $item, $id, $user_id ) )
+			if ( file_exists($item[3]) || is_callable($item[3]) )
+				$list .= "\n\t<li$class><a href='" . esc_attr( get_profile_tab_link($user_id, $item[4]) ) . "'>{$item[0]}</a></li>";
+	}
+	$list .= "\n</ul>";
+	echo $list;
+}
+
+function login_form() {
+	if ( bb_is_user_logged_in() )
+		bb_load_template( 'logged-in.php' );
+	else
+		bb_load_template( 'login-form.php', array('user_login', 'remember_checked', 'redirect_to', 're') );
+}
+
+function search_form( $q = '' ) {
+	bb_load_template( 'search-form.php', array('q' => $q) );
+}
+
+function bb_post_template() {
+	bb_load_template( 'post.php' );
+}
+
+function post_form( $args = array() ) {
+	global $page, $topic, $forum;
+
+	$defaults = array(
+		'h2' => '',
+		'last_page_only' => true
+	);
+	if ( is_string( $args ) ) {
+		$defaults['h2'] = $args;
+	}
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( isset( $forum->forum_is_category ) && $forum->forum_is_category ) {
+		return;
+	}
+
+	$add = topic_pages_add();
+	if ( empty( $h2 ) && false !== $h2 ) {
+		if ( bb_is_topic() ) {
+			$h2 = __( 'Reply' );
+		} elseif ( bb_is_forum() ) {
+			$h2 = __( 'New Topic in this Forum' );
+		} elseif ( bb_is_tag() || bb_is_front() ) {
+			$h2 = __( 'Add New Topic' );
+		}
+	}
+
+	$last_page = bb_get_page_number( ( isset( $topic->topic_posts ) ? $topic->topic_posts : 0 ) + $add );
+
+	if ( !empty( $h2 ) ) {
+		if ( bb_is_topic() && ( $page != $last_page && $last_page_only ) ) {
+			$h2 = '<a href="' . esc_attr( get_topic_link( 0, $last_page ) . '#postform' ) . '">' . $h2 . ' &raquo;</a>';
+		}
+		echo '<h2 class="post-form">' . $h2 . '</h2>' . "\n";
+	}
+
+	do_action( 'pre_post_form' );
+
+	if (
+		( false === bb_is_login_required() ) ||
+		( bb_is_topic() && bb_current_user_can( 'write_post', $topic->topic_id ) && ( $page == $last_page || !$last_page_only ) ) ||
+		( !bb_is_topic() && bb_current_user_can( 'write_topic', isset( $forum->forum_id ) ? $forum->forum_id : 0 ) )
+	) {
+		echo '<form class="postform post-form" id="postform" method="post" action="' . bb_get_uri( 'bb-post.php', null, BB_URI_CONTEXT_FORM_ACTION ) . '">' . "\n";
+		echo '<fieldset>' . "\n";
+		bb_load_template( 'post-form.php', array('h2' => $h2) );
+		bb_nonce_field( bb_is_topic() ? 'create-post_' . $topic->topic_id : 'create-topic' );
+		if ( bb_is_forum() ) {
+			echo '<input type="hidden" name="forum_id" value="' . $forum->forum_id . '" />' . "\n";
+		} elseif ( bb_is_topic() ) {
+			echo '<input type="hidden" name="topic_id" value="' . $topic->topic_id . '" />' . "\n";
+		}
+		do_action( 'post_form' );
+		echo "\n</fieldset>\n</form>\n";
+	} elseif ( !bb_is_user_logged_in() ) {
+		echo '<p>';
+		printf(
+			__('You must <a href="%s">log in</a> to post.'),
+			esc_attr( bb_get_uri( 'bb-login.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_USER_FORMS ) )
+		);
+		echo '</p>';
+	}
+
+	do_action( 'post_post_form' );
+}
+
+function edit_form() {
+	global $bb_post;
+	do_action('pre_edit_form');
+	echo '<form class="postform edit-form" method="post" action="' . bb_get_uri('bb-edit.php', null, BB_URI_CONTEXT_FORM_ACTION)  . '">' . "\n";
+	echo '<fieldset>' . "\n";
+	bb_load_template( 'edit-form.php', array('topic_title') );
+	bb_nonce_field( 'edit-post_' . $bb_post->post_id );
+	do_action('edit_form');
+	if ($_REQUEST['view'] === 'all')
+		echo "\n" . '<input type="hidden" name="view" value="all" />';
+	echo "\n" . '</fieldset>' . "\n" . '</form>' . "\n";
+	do_action('post_edit_form');
+}
+
+function bb_anonymous_post_form() {
+	if ( !bb_is_user_logged_in() && !bb_is_login_required() )
+		bb_load_template( 'post-form-anonymous.php' );
+}
+
+function alt_class( $key, $others = '' ) {
+	echo get_alt_class( $key, $others );
+}
+
+function get_alt_class( $key, $others = '' ) {
+	global $bb_alt;
+	$class = '';
+	if ( !isset( $bb_alt[$key] ) ) $bb_alt[$key] = -1;
+	++$bb_alt[$key];
+	$others = trim($others);
+	if ( $others xor $bb_alt[$key] % 2 )
+		$class = ' class="' . ( ($others) ? $others : 'alt' ) . '"';
+	elseif ( $others && $bb_alt[$key] % 2 )
+		$class = ' class="' . $others . ' alt"';
+	return $class;
+}
+
+function bb_location() {
+	echo apply_filters( 'bb_location', bb_get_location() );
+}
+
+function bb_get_location() { // Not for display.  Do not internationalize.
+	static $file;
+	static $filename;
+
+	if ( !isset( $file ) ) {
+		$path = '';
+		foreach ( array( $_SERVER['SCRIPT_NAME'], $_SERVER['SCRIPT_FILENAME'], $_SERVER['PHP_SELF'] ) as $_path ) {
+			if ( false !== strpos( $_path, '.php' ) ) {
+				$path = $_path;
+				break;
+			}
+		}
+
+		$filename = bb_find_filename( $path );
+		// Make $file relative to bbPress root directory
+		$file = str_replace( bb_get_option( 'path' ), '', $path );
+	}
+
+	switch ( $filename ) {
+		case 'index.php':
+		case 'page.php':
+			$location = 'front-page';
+			break;
+		case 'forum.php':
+			$location = 'forum-page';
+			break;
+		case 'tags.php':
+			$location = 'tag-page';
+			break;
+		case 'edit.php':
+			$location = 'topic-edit-page';
+			break;
+		case 'topic.php':
+			$location = 'topic-page';
+			break;
+		case 'rss.php':
+			$location = 'feed-page';
+			break;
+		case 'search.php':
+			$location = 'search-page';
+			break;
+		case 'profile.php':
+			$location = 'profile-page';
+			break;
+		case 'favorites.php':
+			$location = 'favorites-page';
+			break;
+		case 'view.php':
+			$location = 'view-page';
+			break;
+		case 'statistics.php':
+			$location = 'stats-page';
+			break;
+		case 'bb-login.php':
+			$location = 'login-page';
+			break;
+		case 'register.php':
+			$location = 'register-page';
+			break;
+		default:
+			$location = apply_filters( 'bb_get_location', '', $file );
+			break;
+	}
+
+	return $location;
+}
+
+function bb_is_front() {
+	return 'front-page' == bb_get_location();
+}
+
+function bb_is_forum() {
+	return 'forum-page' == bb_get_location();
+}
+
+/**
+ * Whether a user is required to log in in order to create posts and forums.
+ * @return bool Whether a user must be logged in.
+ */
+function bb_is_login_required() {
+	return ! (bool) bb_get_option('enable_loginless');
+}
+
+function bb_is_tags() {
+	return 'tag-page' == bb_get_location();
+}
+
+function bb_is_tag() {
+	global $tag, $tag_name;
+	return $tag && $tag_name && bb_is_tags();
+}
+
+function bb_is_topic_edit() {
+	return 'topic-edit-page' == bb_get_location();
+}
+
+function bb_is_topic() {
+	return 'topic-page' == bb_get_location();
+}
+
+function bb_is_feed() {
+	return 'feed-page' == bb_get_location();
+}
+
+function bb_is_search() {
+	return 'search-page' == bb_get_location();
+}
+
+function bb_is_profile() {
+	return 'profile-page' == bb_get_location();
+}
+
+function bb_is_favorites() {
+	return 'favorites-page' == bb_get_location();
+}
+
+function bb_is_view() {
+	return 'view-page' == bb_get_location();
+}
+
+function bb_is_statistics() {
+	return 'stats-page' == bb_get_location();
+}
+
+function bb_is_admin() {
+	if ( defined('BB_IS_ADMIN') )
+		return BB_IS_ADMIN;
+	return false;
+}
+
+function bb_title( $args = '' ) {
+	echo apply_filters( 'bb_title', bb_get_title( $args ), $args );
+}
+
+function bb_get_title( $args = '' ) {
+	$defaults = array(
+		'separator' => ' &laquo; ',
+		'order' => 'normal',
+		'front' => ''
+	);
+	$args = wp_parse_args( $args, $defaults );
+	$title = array();
+	
+	switch ( bb_get_location() ) {
+		case 'search-page':
+			if ( !$q = trim( @$_GET['search'] ) )
+				if ( !$q = trim( @$_GET['q'] ) )
+					break;
+			$title[] = sprintf( __( 'Search for %s' ), esc_html( $q ) );
+			break;
+		case 'front-page':
+			if ( !empty( $args['front'] ) )
+				$title[] = $args['front'];
+			break;
+		
+		case 'topic-edit-page':
+		case 'topic-page':
+			$title[] = get_topic_title();
+			break;
+		
+		case 'forum-page':
+			$title[] = get_forum_name();
+			break;
+		
+		case 'tag-page':
+			if ( bb_is_tag() )
+				$title[] = esc_html( bb_get_tag_name() );
+			
+			$title[] = __( 'Tags' );
+			break;
+		
+		case 'profile-page':
+			$title[] = get_user_display_name();
+			break;
+		
+		case 'view-page':
+			$title[] = get_view_name();
+			break;
+	}
+	
+	if ( $st = bb_get_option( 'static_title' ) )
+		$title = array( $st );
+	
+	$title[] = bb_get_option( 'name' );
+	
+	if ( 'reversed' == $args['order'] )
+		$title = array_reverse( $title );
+	
+	return apply_filters( 'bb_get_title', implode( $args['separator'], $title ), $args, $title );
+}
+
+function bb_feed_head() {
+	
+	$feeds = array();
+	
+	switch (bb_get_location()) {
+		case 'profile-page':
+			if ( $tab = isset($_GET['tab']) ? $_GET['tab'] : bb_get_path(2) )
+				if ($tab != 'favorites')
+					break;
+			
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; User Favorites: %2$s'), bb_get_option( 'name' ), get_user_name()),
+				'href'  => get_favorites_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			break;
+		
+		case 'topic-page':
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; Topic: %2$s'), bb_get_option( 'name' ), get_topic_title()),
+				'href'  => get_topic_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			break;
+		
+		case 'tag-page':
+			if (bb_is_tag()) {
+				$feeds[] = array(
+					'title' => sprintf(__('%1$s &raquo; Tag: %2$s - Recent Posts'), bb_get_option( 'name' ), bb_get_tag_name()),
+					'href'  => bb_get_tag_posts_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+				);
+				$feeds[] = array(
+					'title' => sprintf(__('%1$s &raquo; Tag: %2$s - Recent Topics'), bb_get_option( 'name' ), bb_get_tag_name()),
+					'href'  => bb_get_tag_topics_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+				);
+			}
+			break;
+		
+		case 'forum-page':
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; Forum: %2$s - Recent Posts'), bb_get_option( 'name' ), get_forum_name()),
+				'href'  => bb_get_forum_posts_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; Forum: %2$s - Recent Topics'), bb_get_option( 'name' ), get_forum_name()),
+				'href'  => bb_get_forum_topics_rss_link(0, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			break;
+		
+		case 'front-page':
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; Recent Posts'), bb_get_option( 'name' )),
+				'href'  => bb_get_posts_rss_link(BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			$feeds[] = array(
+				'title' => sprintf(__('%1$s &raquo; Recent Topics'), bb_get_option( 'name' )),
+				'href'  => bb_get_topics_rss_link(BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+			);
+			break;
+		
+		case 'view-page':
+			global $bb_views, $view;
+			if ($bb_views[$view]['feed']) {
+				$feeds[] = array(
+					'title' => sprintf(__('%1$s &raquo; View: %2$s'), bb_get_option( 'name' ), get_view_name()),
+					'href'  => bb_get_view_rss_link(null, BB_URI_CONTEXT_LINK_ALTERNATE_HREF + BB_URI_CONTEXT_BB_FEED)
+				);
+			}
+			break;
+	}
+	
+	if (count($feeds)) {
+		$feed_links = array();
+		foreach ($feeds as $feed) {
+			$link = '<link rel="alternate" type="application/rss+xml" ';
+			$link .= 'title="' . esc_attr($feed['title']) . '" ';
+			$link .= 'href="' . esc_attr($feed['href']) . '" />';
+			$feed_links[] = $link;
+		}
+		$feed_links = join("\n", $feed_links);
+	} else {
+		$feed_links = '';
+	}
+	
+	echo apply_filters('bb_feed_head', $feed_links);
+}
+
+function bb_get_posts_rss_link($context = 0) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	if ( bb_get_option( 'mod_rewrite' ) )
+		$link = bb_get_uri('rss/', null, $context);
+	else
+		$link = bb_get_uri('rss.php', null, $context);
+	return apply_filters( 'bb_get_posts_rss_link', $link, $context );
+}
+
+function bb_get_topics_rss_link($context = 0) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	if ( bb_get_option( 'mod_rewrite' ) )
+		$link = bb_get_uri('rss/topics', null, $context);
+	else
+		$link = bb_get_uri('rss.php', array('topics' => 1), $context);
+	return apply_filters( 'bb_get_topics_rss_link', $link, $context );
+}
+
+function bb_view_rss_link($view = null, $context = 0) {
+	echo apply_filters( 'bb_view_rss_link', bb_get_view_rss_link($view, $context), $context);
+}
+
+function bb_get_view_rss_link($view = null, $context = 0) {
+	if (!$view) {
+		global $view;
+	}
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	if ( bb_get_option( 'mod_rewrite' ) )
+		$link = bb_get_uri('rss/view/' . $view, null, $context);
+	else
+		$link = bb_get_uri('rss.php', array('view' => $view), $context);
+	return apply_filters( 'bb_get_view_rss_link', $link, $context );
+}
+
+function bb_latest_topics_pages( $args = null )
+{
+	$defaults = array( 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page;
+	static $bb_latest_topics_count;
+	if ( !$bb_latest_topics_count) {
+		global $bbdb;
+		$bb_latest_topics_count = $bbdb->get_var('SELECT COUNT(`topic_id`) FROM `' . $bbdb->topics . '` WHERE `topic_status` = 0 AND `topic_sticky` != 2;');
+	}
+	if ( $pages = apply_filters( 'bb_latest_topics_pages', get_page_number_links( $page, $bb_latest_topics_count ), $bb_latest_topics_count ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+// FORUMS
+
+function forum_id( $forum_id = 0 ) {
+	echo apply_filters( 'forum_id', get_forum_id( $forum_id ) );
+}
+
+function get_forum_id( $forum_id = 0 ) {
+	global $forum;
+	$forum_id = (int) $forum_id;
+	if ( $forum_id )
+		$_forum = bb_get_forum( $forum_id );
+	else
+		$_forum =& $forum;
+	return $_forum->forum_id;
+}
+
+function forum_link( $forum_id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+	echo apply_filters('forum_link', get_forum_link( $forum_id, $page, $context ), $forum_id, $context );
+}
+
+function get_forum_link( $forum_id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+	
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'forum_slug';
+		} else {
+			$column = 'forum_id';
+		}
+		$page = (1 < $page) ? '/page/' . $page : '';
+		$link = bb_get_uri('forum/' . $forum->$column . $page, null, $context);
+	} else {
+		$query = array(
+			'id' => $forum->forum_id,
+			'page' => (1 < $page) ? $page : false
+		);
+		$link = bb_get_uri('forum.php', $query, $context);
+	}
+
+	return apply_filters( 'get_forum_link', $link, $forum->forum_id, $context );
+}
+
+function forum_name( $forum_id = 0 ) {
+	echo apply_filters( 'forum_name', get_forum_name( $forum_id ), $forum_id );
+}
+
+function get_forum_name( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_name', $forum->forum_name, $forum->forum_id );
+}
+
+function forum_description( $args = null ) {
+	if ( is_numeric($args) )
+		$args = array( 'id' => $args );
+	elseif ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'before' => $args );
+	$defaults = array( 'id' => 0, 'before' => ' &#8211; ', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	if ( $desc = apply_filters( 'forum_description', get_forum_description( $args['id'] ), $args['id'], $args ) )
+		echo $args['before'] . $desc . $args['after'];
+}
+
+function get_forum_description( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_description', $forum->forum_desc, $forum->forum_id );
+}
+
+function get_forum_parent( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_parent', $forum->forum_parent, $forum->forum_id );
+}
+
+function get_forum_position( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_position', $forum->forum_order, $forum->forum_id );
+}
+
+function bb_get_forum_is_category( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'bb_get_forum_is_category', isset($forum->forum_is_category) ? $forum->forum_is_category : false, $forum->forum_id );
+}
+
+function forum_topics( $forum_id = 0 ) {
+	echo apply_filters( 'forum_topics', get_forum_topics( $forum_id ), $forum_id );
+}
+
+function get_forum_topics( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_topics', $forum->topics, $forum->forum_id );
+}
+
+function forum_posts( $forum_id = 0 ) {
+	echo apply_filters( 'forum_posts', get_forum_posts( $forum_id ), $forum_id );
+}
+
+function get_forum_posts( $forum_id = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	return apply_filters( 'get_forum_posts', $forum->posts, $forum->forum_id );
+}
+
+function forum_pages( $args = null )
+{
+	// Compatibility
+	if ( $args && is_numeric( $args ) ) {
+		$args = array( 'id' => $args );
+	}
+	$defaults = array( 'id' => 0, 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page;
+	$forum = bb_get_forum( get_forum_id( $args['id'] ) );
+	if ( $pages = apply_filters( 'forum_pages', get_page_number_links( $page, $forum->topics ), $forum->topics ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+function bb_forum_posts_rss_link( $forum_id = 0, $context = 0 ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	echo apply_filters('bb_forum_posts_rss_link', bb_get_forum_posts_rss_link( $forum_id, $context ), $context );
+}
+
+function bb_get_forum_posts_rss_link( $forum_id = 0, $context = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'forum_slug';
+		} else {
+			$column = 'forum_id';
+		}
+		$link = bb_get_uri('rss/forum/' . $forum->$column, null, $context);
+	} else {
+		$link = bb_get_uri('rss.php', array('forum' => $forum->forum_id), $context);
+	}
+	return apply_filters( 'bb_get_forum_posts_rss_link', $link, $forum->forum_id, $context );
+}
+
+function bb_forum_topics_rss_link( $forum_id = 0, $context = 0 ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	echo apply_filters('bb_forum_topics_rss_link', bb_get_forum_topics_rss_link( $forum_id, $context ), $context );
+}
+
+function bb_get_forum_topics_rss_link( $forum_id = 0, $context = 0 ) {
+	$forum = bb_get_forum( get_forum_id( $forum_id ) );
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'forum_slug';
+		} else {
+			$column = 'forum_id';
+		}
+		$link = bb_get_uri('rss/forum/' . $forum->$column . '/topics', null, $context);
+	} else {
+		$link = bb_get_uri('rss.php', array('forum' => $forum->forum_id, 'topics' => 1), $context);
+	}
+	return apply_filters( 'bb_get_forum_topics_rss_link', $link, $forum->forum_id, $context );
+}
+
+function bb_get_forum_bread_crumb($args = '') {
+	$defaults = array(
+		'forum_id' => 0,
+		'separator' => ' &raquo; ',
+		'class' => null
+	);
+	$args = wp_parse_args($args, $defaults);
+	extract($args, EXTR_SKIP);
+
+	$trail = '';
+	$trail_forum = bb_get_forum(get_forum_id($forum_id));
+	if ($class) {
+		$class = ' class="' . $class . '"';
+	}
+	$current_trail_forum_id = $trail_forum->forum_id;
+	while ( $trail_forum && $trail_forum->forum_id > 0 ) {
+		$crumb = $separator;
+		if ($current_trail_forum_id != $trail_forum->forum_id || !bb_is_forum()) {
+			$crumb .= '<a' . $class . ' href="' . get_forum_link($trail_forum->forum_id) . '">';
+		} elseif ($class) {
+			$crumb .= '<span' . $class . '>';
+		}
+		$crumb .= get_forum_name($trail_forum->forum_id);
+		if ($current_trail_forum_id != $trail_forum->forum_id || !bb_is_forum()) {
+			$crumb .= '</a>';
+		} elseif ($class) {
+			$crumb .= '</span>';
+		}
+		$trail = $crumb . $trail;
+		$trail_forum = bb_get_forum($trail_forum->forum_parent);
+	}
+
+	return apply_filters('bb_get_forum_bread_crumb', $trail, $forum_id );
+}
+
+function bb_forum_bread_crumb( $args = '' ) {
+	echo apply_filters('bb_forum_bread_crumb', bb_get_forum_bread_crumb( $args ) );
+}
+
+// Forum Loop //
+
+function &bb_forums( $args = '' ) {
+	global $bb_forums_loop;
+
+	$default_type = 'flat';
+
+	if ( is_numeric($args) ) {
+		$args = array( 'child_of' => $args );
+	} elseif ( func_num_args() > 1 ) { // bb_forums( 'ul', $args ); Deprecated
+		$default_type = $args;
+		$args = func_get_arg(1);
+	} elseif ( $args && is_string($args) && false === strpos($args, '=') ) {
+		$args = array( 'type' => $args );
+	}
+
+	// hierarchical not used here.  Sent to bb_get_forums for proper ordering.
+	$args = wp_parse_args( $args, array('hierarchical' => true, 'type' => $default_type, 'walker' => 'BB_Walker_Blank') );
+
+	$levels = array( '', '' );
+
+	if ( in_array($args['type'], array('list', 'ul')) )
+		$levels = array( '<ul>', '</ul>' );
+
+	$forums = bb_get_forums( $args );
+
+	if ( !class_exists($args['walker']) )
+		$args['walker'] = 'BB_Walker_Blank';
+
+	if ( $bb_forums_loop = BB_Loop::start( $forums, $args['walker'] ) ) {
+		$bb_forums_loop->preserve( array('forum', 'forum_id') );
+		$bb_forums_loop->walker->db_fields = array( 'id' => 'forum_id', 'parent' => 'forum_parent' );
+		list($bb_forums_loop->walker->start_lvl, $bb_forums_loop->walker->end_lvl) = $levels;
+		return $bb_forums_loop->elements;
+	}
+	$false = false;
+	return $false;
+}
+
+function bb_forum() { // Returns current depth
+	global $bb_forums_loop;
+	if ( !is_object($bb_forums_loop) || !is_a($bb_forums_loop, 'BB_Loop') )
+		return false;
+	if ( !is_array($bb_forums_loop->elements) )
+		return false;
+
+	if ( $bb_forums_loop->step() ) {
+		$GLOBALS['forum'] =& $bb_forums_loop->elements[key($bb_forums_loop->elements)]; // Globalize the current forum object
+	} else {
+		$bb_forums_loop->reinstate();
+		return $bb_forums_loop = null; // All done?  Kill the object and exit the loop.
+	}
+
+	return $bb_forums_loop->walker->depth;
+}
+
+function bb_forum_pad( $pad, $offset = 0 ) {
+	global $bb_forums_loop;
+	if ( !is_object($bb_forums_loop) || !is_a($bb_forums_loop, 'BB_Loop') )
+		return false;
+
+	echo $bb_forums_loop->pad( $pad, $offset );
+}
+
+function bb_forum_class( $args = null ) {
+	echo apply_filters( 'bb_forum_class', get_alt_class( 'forum', bb_get_forum_class_names( $args ) ), $args );
+}
+
+function bb_get_forum_class_names( $args = null ) {
+	if ( is_numeric( $args ) ) { // Not used
+		$args = array( 'id' => $args );
+	} elseif ( $args && is_string( $args ) && false === strpos( $args, '=' ) ) {
+		$args = array( 'class' => $args );
+	}
+	$defaults = array( 'id' => 0, 'key' => 'forum', 'class' => '', 'output' => 'string' );
+	$args = wp_parse_args( $args, $defaults );
+
+	$classes = array();
+	if ( $args['class'] ) {
+		$classes[] = $args['class'];
+	}
+
+	global $bb_forums_loop;
+	if ( is_object( $bb_forums_loop ) && is_a( $bb_forums_loop, 'BB_Loop' ) ) {
+		$classes = array_merge( $classes, $bb_forums_loop->classes( 'array' ) );
+	}
+
+	if ( $args['output'] === 'string' ) {
+		$classes = join( ' ', $classes );
+	}
+
+	return apply_filters( 'bb_get_forum_class', $classes, $args );
+}
+
+// TOPICS
+function topic_id( $id = 0 ) {
+	echo apply_filters( 'topic_id', get_topic_id( $id ) );
+}
+
+function get_topic_id( $id = 0 ) {
+	global $topic;
+	$id = (int) $id;
+	if ( $id )
+		$_topic = get_topic( $id );
+	else
+		$_topic =& $topic;
+
+	if ( empty($_topic->topic_id) )
+		return 0;
+
+	return (int) $_topic->topic_id;
+}
+
+function topic_link( $id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	echo apply_filters( 'topic_link', get_topic_link( $id, $page, $context ), $id, $page, $context );
+}
+
+function get_topic_link( $id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+
+	$args = array();
+
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'topic_slug';
+		} else {
+			$column = 'topic_id';
+		}
+		$page = (1 < $page) ? '/page/' . $page : '';
+		$link = bb_get_uri('topic/' . $topic->$column . $page, null, $context);
+	} else {
+		$page = (1 < $page) ? $page : false;
+		$link = bb_get_uri('topic.php', array('id' => $topic->topic_id, 'page' => $page), $context);
+	}
+
+	return apply_filters( 'get_topic_link', $link, $topic->topic_id, $context );
+}
+
+function topic_rss_link( $id = 0, $context = 0 ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	echo apply_filters('topic_rss_link', get_topic_rss_link($id, $context), $id, $context );
+}
+
+function get_topic_rss_link( $id = 0, $context = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'topic_slug';
+		} else {
+			$column = 'topic_id';
+		}
+		$link = bb_get_uri('rss/topic/' . $topic->$column, null, $context);
+	} else {
+		$link = bb_get_uri('rss.php', array('topic' => $topic->topic_id), $context);
+	}
+	return apply_filters( 'get_topic_rss_link', $link, $topic->topic_id, $context );
+}
+
+function bb_topic_labels() {
+	echo apply_filters( 'bb_topic_labels', null );
+}
+
+function topic_title( $id = 0 ) {
+	echo apply_filters( 'topic_title', get_topic_title( $id ), get_topic_id( $id ) );
+}
+
+function get_topic_title( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	return apply_filters( 'get_topic_title', $topic->topic_title, $topic->topic_id );
+}
+
+function topic_page_links( $id = 0, $args = null ) {
+	echo apply_filters( 'topic_page_links', get_topic_page_links( $id, $args ), get_topic_id( $id ) );
+}
+
+function get_topic_page_links( $id = 0, $args = null ) {
+
+	$defaults = array(
+		'show_all' => false,
+		'end_size' => 3,
+		'before' => ' - ',
+		'after' => null
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+
+	$topic = get_topic( get_topic_id( $id ) );
+
+	$uri = get_topic_link();
+	if ( bb_get_option('mod_rewrite') ) {
+		if ( false === $pos = strpos( $uri, '?' ) ) {
+			$uri = $uri . '%_%';
+		} else {
+			$uri = substr_replace( $uri, '%_%', $pos, 0 );
+		}
+	} else {
+		$uri = add_query_arg( 'page', '%_%', $uri );
+	}
+
+	$posts = $topic->topic_posts + topic_pages_add( $topic->topic_id );
+
+	$per_page = apply_filters( 'get_topic_page_links_per_page', bb_get_option('page_topics') );
+
+	$_links = bb_paginate_links(
+		array(
+			'base' => $uri,
+			'format' => bb_get_option('mod_rewrite') ? '/page/%#%' : '%#%',
+			'total' => ceil($posts/$per_page),
+			'current' => 0,
+			'show_all' => $args['show_all'],
+			'end_size' => $args['end_size'],
+			'type' => 'array'
+		)
+	);
+
+	$links = $_links;
+
+	$r = '';
+
+	if ( $links ) {
+		if ( !$show_first ) {
+			unset( $links[0] );
+		}
+
+		if ( $args['before'] ) {
+			$r .= $args['before'];
+		}
+		$r .= join('', $links);
+		if ( $args['after'] ) {
+			$r .= $args['after'];
+		}
+	}
+
+	return apply_filters( 'get_topic_page_links', $r, $_links, $topic->topic_id );
+}
+
+function bb_topic_voices( $id = 0 ) {
+	echo apply_filters( 'bb_topic_voices', bb_get_topic_voices( $id ), get_topic_id( $id ) );
+}
+
+function bb_get_topic_voices( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if ( empty( $topic->voices_count ) ) {
+		global $bbdb;
+
+		if ( $voices = $bbdb->get_col( $bbdb->prepare( "SELECT DISTINCT poster_id FROM $bbdb->posts WHERE topic_id = %s AND post_status = '0';", $topic->topic_id ) ) ) {
+			$voices = count( $voices );
+			bb_update_topicmeta( $topic->topic_id, 'voices_count', $voices );
+		}
+	} else {
+		$voices = $topic->voices_count;
+	}
+
+	return apply_filters( 'bb_get_topic_voices', $voices, $topic->topic_id );
+}
+
+function topic_posts( $id = 0 ) {
+	echo apply_filters( 'topic_posts', get_topic_posts( $id ), get_topic_id( $id ) );
+}
+
+function get_topic_posts( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	return apply_filters( 'get_topic_posts', $topic->topic_posts, $topic->topic_id );
+}
+
+function get_topic_deleted_posts( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	return apply_filters( 'get_topic_deleted_posts', isset($topic->deleted_posts) ? $topic->deleted_posts : 0, $topic->topic_id );
+}
+
+function topic_noreply( $title ) {
+	if ( 1 == get_topic_posts() && ( bb_is_front() || bb_is_forum() ) )
+		$title = "<strong>$title</strong>";
+	return $title;
+}
+
+function topic_last_poster( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	echo apply_filters( 'topic_last_poster', get_topic_last_poster( $id ), $topic->topic_last_poster, $topic->topic_id ); // $topic->topic_last_poster = user ID
+}
+
+function get_topic_last_poster( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	$user_display_name = get_post_author( $topic->topic_last_post_id );
+	return apply_filters( 'get_topic_last_poster', $user_display_name, $topic->topic_last_poster, $topic->topic_id ); // $topic->topic_last_poster = user ID
+}
+
+function topic_author( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	echo apply_filters( 'topic_author', get_topic_author( $id ), $topic->topic_poster, $topic->topic_id ); // $topic->topic_poster = user ID
+}
+
+function get_topic_author( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	$first_post = bb_get_first_post( $topic );
+	$user_display_name = get_post_author( $first_post->post_id );
+	return apply_filters( 'get_topic_author', $user_display_name, $topic->topic_poster, $topic->topic_id ); // $topic->topic_poster = user ID
+}
+
+// Filters expect the format to by mysql on both topic_time and get_topic_time
+function topic_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+	$time = apply_filters( 'topic_time', get_topic_time( array('format' => 'mysql') + $args), $args );
+	echo _bb_time_function_return( $time, $args );
+}
+
+function get_topic_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+
+	$topic = get_topic( get_topic_id( $args['id'] ) );
+
+	$time = apply_filters( 'get_topic_time', $topic->topic_time, $args );
+
+	return _bb_time_function_return( $time, $args );
+}
+
+function topic_start_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+	$time = apply_filters( 'topic_start_time', get_topic_start_time( array('format' => 'mysql') + $args), $args );
+	echo _bb_time_function_return( $time, $args );
+}
+
+function get_topic_start_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+
+	$topic = get_topic( get_topic_id( $args['id'] ) );
+
+	$time = apply_filters( 'get_topic_start_time', $topic->topic_start_time, $args, $topic->topic_id );
+
+	return _bb_time_function_return( $time, $args );
+}
+
+function topic_last_post_link( $id = 0 ) {
+	echo apply_filters( 'topic_last_post_link', get_topic_last_post_link( $id ), $id);
+}
+
+function get_topic_last_post_link( $id = 0 ){
+	$topic = get_topic( get_topic_id( $id ) );
+	$page = bb_get_page_number( $topic->topic_posts );
+	return apply_filters( 'get_post_link', get_topic_link( $topic->topic_id, $page ) . "#post-$topic->topic_last_post_id", $topic->topic_last_post_id, $topic->topic_id );
+}
+
+function topic_pages( $args = null )
+{
+	// Compatibility
+	if ( $args && is_numeric( $args ) ) {
+		$args = array( 'id' => $args );
+	}
+	$defaults = array( 'id' => 0, 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page;
+	$topic = get_topic( get_topic_id( $args['id'] ) );
+	$add = topic_pages_add( $topic->topic_id );
+	if ( $pages = apply_filters( 'topic_pages', get_page_number_links( $page, $topic->topic_posts + $add ), $topic->topic_id ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+function topic_pages_add( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	if ( isset($_GET['view']) && 'all' == $_GET['view'] && bb_current_user_can('browse_deleted') && isset( $topic->deleted_posts ) )
+		$add = $topic->deleted_posts;
+	else
+		$add = 0;
+	return apply_filters( 'topic_pages_add', $add, isset($topic->topic_id) ? $topic->topic_id : 0 );
+}
+
+function get_page_number_links( $args ) {
+	if ( 1 < func_num_args() ) {
+		$_args = func_get_args();
+		$args = array(
+			'page' => $_args[0],
+			'total' => $_args[1],
+			'per_page' => isset( $_args[2] ) ? $_args[2] : '',
+			'mod_rewrite' => isset( $_args[3] ) ? $_args[3] : 'use_option'
+		);
+	}
+	$defaults = array(
+		'page' => 1,
+		'total' => false,
+		'per_page' => '',
+		'mod_rewrite' => 'use_option',
+		'prev_text' => __( '&laquo; Previous' ),
+		'next_text' => __( 'Next &raquo;' )
+	);
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$add_args = array();
+	$uri = rtrim( $_SERVER['REQUEST_URI'], '?&' );
+
+	if ( $mod_rewrite === 'use_option' ) {
+		$mod_rewrite = bb_get_option( 'mod_rewrite' );
+	}
+
+	if ( $mod_rewrite ) {
+		$format = '/page/%#%';
+		if ( 1 == $page ) {
+			if ( false === $pos = strpos($uri, '?') )
+				$uri = $uri . '%_%';
+			else
+				$uri = substr_replace($uri, '%_%', $pos, 0);
+		} else {
+			$uri = preg_replace('|/page/[0-9]+|', '%_%', $uri);
+		}
+		$uri = str_replace( '/%_%', '%_%', $uri );
+	} else {
+		if ( 1 == $page ) {
+			if ( false === $pos = strpos($uri, '?') ) {
+				$uri = $uri . '%_%';
+				$format = '?page=%#%';
+			} else {
+				$uri = substr_replace($uri, '?%_%', $pos, 1);
+				$format = 'page=%#%&';
+			}
+		} else {
+			if ( false === strpos($uri, '?page=') ) {
+				$uri = preg_replace('!&page=[0-9]+!', '%_%', $uri );
+				$uri = str_replace( '&page=', '', $uri );
+				$format = '&page=%#%';
+			} else {
+				$uri = preg_replace('!\?page=[0-9]+!', '%_%', $uri );
+				$uri = str_replace( '?page=', '', $uri );
+				$format = '?page=%#%';
+			}
+		}
+	}
+
+	if ( isset($_GET['view']) && in_array($_GET['view'], bb_get_views()) )
+		$add_args['view'] = $_GET['view'];
+
+	if ( empty( $per_page ) ) {
+		$per_page = bb_get_option( 'page_topics' );
+	}
+
+	$links = bb_paginate_links( array(
+		'base' => $uri,
+		'format' => $format,
+		'total' => ceil( $total/$per_page ),
+		'current' => $page,
+		'add_args' => $add_args,
+		'type' => 'array',
+		'mid_size' => 1,
+		'prev_text' => $prev_text,
+		'next_text' => $next_text
+	) );
+
+	if ($links) {
+		$links = join('', $links);
+	}
+	return $links;
+}
+
+function bb_topic_admin( $args = '' ) {
+	$parts = array(
+		'delete' => bb_get_topic_delete_link( $args ),
+		'close'  => bb_get_topic_close_link( $args ),
+		'sticky' => bb_get_topic_sticky_link( $args ),
+		'move'   => bb_get_topic_move_dropdown( $args )
+	);
+
+	echo join( "\n", apply_filters( 'bb_topic_admin', $parts, $args ) );
+}
+
+function topic_delete_link( $args = '' ) {
+	echo bb_get_topic_delete_link( $args );
+}
+
+function bb_get_topic_delete_link( $args = '' ) {
+	$defaults = array( 'id' => 0, 'before' => '[', 'after' => ']', 'delete_text' => false, 'undelete_text' => false, 'redirect' => true );
+	extract(wp_parse_args( $args, $defaults ), EXTR_SKIP);
+	$id = (int) $id;
+
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if ( !$topic || !bb_current_user_can( 'delete_topic', $topic->topic_id ) )
+		return;
+
+	if ( 0 == $topic->topic_status ) {
+		if ( true === $redirect )
+			$redirect = add_query_arg( bb_is_admin() ? array() : array( 'view' => 'all'  ) );
+
+		$query   = array( 'id' => $topic->topic_id, '_wp_http_referer' => $redirect ? rawurlencode( $redirect ) : false );
+		$confirm = __('Are you sure you want to delete that?');
+		$display = esc_html( $delete_text ? $delete_text : __('Delete entire topic') );
+	} else {
+		if ( true === $redirect )
+			$redirect = remove_query_arg( bb_is_admin() ? array() : 'view' );
+
+		$query   = array('id' => $topic->topic_id, 'view' => 'all', '_wp_http_referer' => $redirect ? rawurlencode( $redirect ) : false );
+		$confirm = __('Are you sure you want to undelete that?');
+		$display = esc_html( $undelete_text ? $undelete_text : __('Undelete entire topic') );
+	}
+	$uri = bb_get_uri('bb-admin/delete-topic.php', $query, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri = esc_url( bb_nonce_url( $uri, 'delete-topic_' . $topic->topic_id ) );
+	
+	return $before . '<a href="' . $uri . '" onclick="return confirm(\'' . esc_js( $confirm ) . '\');">' . $display . '</a>' . $after;
+}
+
+function topic_close_link( $args = '' ) {
+	echo bb_get_topic_close_link( $args );
+}
+
+function bb_get_topic_close_link( $args = '' ) {
+	$defaults = array( 'id' => 0, 'before' => '[', 'after' => ']', 'close_text' => false, 'open_text' => false, 'redirect' => true );
+	extract(wp_parse_args( $args, $defaults ), EXTR_SKIP);
+	$id = (int) $id;
+
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if ( !$topic || !bb_current_user_can( 'close_topic', $topic->topic_id ) )
+		return;
+
+	if ( topic_is_open( $topic->topic_id ) )
+		$display = esc_html( $close_text ? $close_text : __( 'Close topic' ) );
+	else
+		$display = esc_html( $open_text ? $open_text : __( 'Open topic' ) );
+
+	if ( true === $redirect )
+		$redirect = $_SERVER['REQUEST_URI'];
+
+	$uri = bb_get_uri('bb-admin/topic-toggle.php', array( 'id' => $topic->topic_id, '_wp_http_referer' => $redirect ? rawurlencode( $redirect ) : false ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri = esc_url( bb_nonce_url( $uri, 'close-topic_' . $topic->topic_id ) );
+	
+	return $before . '<a href="' . $uri . '">' . $display . '</a>' . $after;
+}
+
+function topic_sticky_link( $args = '' ) {
+	echo bb_get_topic_sticky_link( $args );
+}
+
+function bb_get_topic_sticky_link( $args = '' ) {
+	$defaults = array( 'id' => 0, 'before' => '[', 'after' => ']' );
+	extract(wp_parse_args( $args, $defaults ), EXTR_SKIP);
+	$id = (int) $id;
+
+	$topic = get_topic( get_topic_id( $id ) );
+
+	if ( !$topic || !bb_current_user_can( 'stick_topic', $topic->topic_id ) )
+		return;
+
+	$uri_stick = bb_get_uri('bb-admin/sticky.php', array('id' => $topic->topic_id), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri_stick = esc_url( bb_nonce_url( $uri_stick, 'stick-topic_' . $topic->topic_id ) );
+
+	$uri_super = bb_get_uri('bb-admin/sticky.php', array('id' => $topic->topic_id, 'super' => 1), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri_super = esc_url( bb_nonce_url( $uri_super, 'stick-topic_' . $topic->topic_id ) );
+
+	if ( topic_is_sticky( $topic->topic_id ) )
+		return "$before<a href='" . $uri_stick . "'>". __('Unstick topic') ."</a>$after";
+	else
+		return "$before<a href='" . $uri_stick . "'>". __('Stick topic') . "</a> (<a href='" . $uri_super . "'>" . __('to front') . "</a>)$after";
+}
+
+function topic_show_all_link( $id = 0 ) {
+	if ( !bb_current_user_can( 'browse_deleted' ) )
+		return;
+	if ( 'all' == @$_GET['view'] )
+		echo "<a href='" . esc_attr( get_topic_link( $id ) ) . "'>". __('View normal posts') ."</a>";
+	else
+		echo "<a href='" . esc_attr( add_query_arg( 'view', 'all', get_topic_link( $id ) ) ) . "'>". __('View all posts') ."</a>";
+}
+
+function topic_posts_link( $id = 0 ) {
+	echo get_topic_posts_link( $id );
+}
+
+function get_topic_posts_link( $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	$post_num = get_topic_posts( $id );
+	$posts = sprintf(__ngettext( '%s post', '%s posts', $post_num ), $post_num);
+	$r = '';
+
+	if ( ( 'all' == @$_GET['view'] || bb_is_admin() ) && bb_current_user_can('browse_deleted') )
+		$r .= "<a href='" . esc_attr( get_topic_link( $id ) ) . "'>$posts</a>";
+	else
+		$r .= $posts;
+
+	if ( bb_current_user_can( 'browse_deleted' ) ) {
+		$user_id = bb_get_current_user_info( 'id' );
+		if ( isset($topic->bozos[$user_id]) && 'all' != @$_GET['view'] )
+			add_filter('get_topic_deleted_posts', create_function('$a', "\$a -= {$topic->bozos[$user_id]}; return \$a;") );
+		if ( $deleted = get_topic_deleted_posts( $id ) ) {
+			$extra = sprintf(__('+%d more'), $deleted);
+			if ( 'all' == @$_GET['view'] )
+				$r .= " $extra";
+			else
+				$r .= " <a href='" . esc_attr( add_query_arg( 'view', 'all', get_topic_link( $id ) ) ) . "'>$extra</a>";
+		}
+	}
+
+	return $r;
+}
+
+function topic_move_dropdown( $args = '' )
+{
+	echo bb_get_topic_move_dropdown( $args );
+}
+
+function bb_get_topic_move_dropdown( $args = '' )
+{
+	if ( $args && is_numeric( $args ) ) {
+		$args = array( 'id' => (integer) $args );
+	}
+
+	$defaults = array( 'id' => 0, 'before' => '[', 'after' => ']' );
+	extract(wp_parse_args( $args, $defaults ), EXTR_SKIP);
+	$id = (int) $id;
+
+	$topic = get_topic( get_topic_id( $id ) );
+	if ( !bb_current_user_can( 'move_topic', $topic->topic_id ) )
+		return;
+
+	$dropdown = bb_get_forum_dropdown( array(
+		'callback' => 'bb_current_user_can',
+		'callback_args' => array('move_topic', $topic->topic_id),
+		'selected' => $topic->forum_id,
+		'tab' => false
+	) );
+
+	if ( !$dropdown )
+		return;
+
+	$r = $before . '<form id="topic-move" method="post" action="' . bb_get_uri( 'bb-admin/topic-move.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ) . '"><fieldset><div>' . "\n";
+	$r .= '<input type="hidden" name="topic_id" value="' . $topic->topic_id . '" />' . "\n";
+	$r .= '<label for="forum-id">'. __( 'Move to' ) . '</label>' . "\n";
+	$r .= $dropdown . "\n";
+	$r .= bb_nonce_field( 'move-topic_' . $topic->topic_id, '_wpnonce', true , false );
+	$r .= '<input type="submit" name="Submit" value="' . __( 'Move' ) . '" />' . "\n";
+	$r .= '</div></fieldset></form>' . $after;
+
+	return $r;
+}
+
+function topic_class( $class = '', $key = 'topic', $id = 0 ) {
+	$topic = get_topic( get_topic_id( $id ) );
+	$class = $class ? explode(' ', $class ) : array();
+	if ( '1' === $topic->topic_status && bb_current_user_can( 'browse_deleted' ) )
+		$class[] = 'deleted';
+	elseif ( 1 < $topic->topic_status && bb_current_user_can( 'browse_deleted' ) )
+		$class[] = 'bozo';
+	if ( '0' === $topic->topic_open )
+		$class[] = 'closed';
+	if ( 1 == $topic->topic_sticky && ( bb_is_forum() || bb_is_view() ) )
+		$class[] = 'sticky';
+	elseif ( 2 == $topic->topic_sticky && ( bb_is_front() || bb_is_forum() || bb_is_view() ) )
+		$class[] = 'sticky super-sticky';
+	$class = apply_filters( 'topic_class', $class, $topic->topic_id );
+	$class = join(' ', $class);
+	alt_class( $key, $class );
+}
+
+/**
+ * bb_get_new_topic_link() - Get the link to the form for a new topic
+ *
+ * @since 1.0
+ * @param mixed The arguments for this function.
+ * @return string The link to the new topic form
+ */
+function bb_get_new_topic_link( $args = null ) {
+	$defaults = array( 'text' => __('Add New &raquo;'), 'forum' => 0, 'tag' => '' );
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'text' => $args );
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( $forum && $forum = bb_get_forum( $forum ) )
+		$url = get_forum_link( $forum->forum_id ) . '#postform';
+	elseif ( $tag && $tag = bb_get_tag( $tag ) )
+		$url = bb_get_tag_link( $tag->tag ) . '#postform';
+	elseif ( bb_is_forum() ) {
+		global $forum;
+		$url = get_forum_link( $forum->forum_id ) . '#postform';
+	} elseif ( bb_is_tag() ) {
+		global $tag;
+		$url = bb_get_tag_link( $tag ) . '#postform';
+	} elseif ( bb_is_topic() )
+		$url = get_forum_link() . '#postform';
+	elseif ( bb_is_front() )
+		$url = bb_get_uri(null, array('new' => 1));
+
+	if ( !bb_is_user_logged_in() && bb_is_login_required() )
+		$url = bb_get_uri('bb-login.php', array('redirect_to' => $url), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_USER_FORMS);
+	elseif ( bb_is_forum() || bb_is_topic() ) {
+		if ( !bb_current_user_can( 'write_topic', get_forum_id() ) )
+			return;
+	} else {
+		if ( !bb_current_user_can( 'write_topics' ) )
+			return;
+	}
+
+	if ( $url = esc_attr( apply_filters( 'new_topic_url', $url, $args ) ) )
+		return '<a href="' . $url . '" class="new-topic">' . $text . '</a>' . "\n";
+}
+
+function bb_new_topic_link( $args = null ) {
+	echo bb_get_new_topic_link($args);
+}
+
+function bb_new_topic_forum_dropdown( $args = '' ) {
+	if ( !is_array( $args ) ) {
+		$args = array(
+			'callback' => 'bb_current_user_can',
+			'callback_args' => array( 'write_topic' )
+		);
+	}
+	if ( !isset( $args['callback'] ) && !isset( $args['callback_args'] ) ) {
+		$args['callback']      = 'bb_current_user_can';
+		$args['callback_args'] = array( 'write_topic' );
+	}
+	bb_forum_dropdown( $args );
+}
+
+function bb_topic_search_form( $args = null, $query_obj = null ) {
+	global $bb_query_form;
+
+	if ( $query_obj && is_a($query_obj, 'BB_Query_Form') ); // [sic]
+	else
+		$query_obj =& $bb_query_form;
+
+	$query_obj->form( $args );
+}
+
+function bb_search_pages( $args = null ) {
+	global $page, $search_count, $per_page;
+	
+	$defaults = array( 'before' => '', 'after' => '' );
+ 	$args = wp_parse_args( $args, $defaults );
+	
+	if ( $pages = apply_filters( 'bb_search_pages', get_page_number_links( array( 'page' => $page, 'total' => $search_count, 'per_page' => $per_page, 'mod_rewrite' => false ) ) ) )
+		echo $args['before'] . $pages . $args['after'];
+}
+
+/**
+ * bb_topic_pagecount() - Print the total page count for a topic
+ *
+ * @since 0.9
+ * @param int $topic_id The topic id of the topic being queried
+ * @return void
+ */
+function bb_topic_pagecount( $topic_id = 0 ) {
+	echo bb_get_topic_pagecount( $topic_id );
+}
+
+/**
+ * bb_get_topic_pagecount() - Get the total page count for a topic
+ *
+ * @since 0.9
+ * @param int $topic_id The topic id of the topic being queried
+ * @return int The total number of pages in the topic
+ */
+function bb_get_topic_pagecount( $topic_id = 0 ) {
+	$topic = get_topic( get_topic_id( $topic_id ) );
+	return bb_get_page_number( $topic->topic_posts + topic_pages_add() );
+}
+
+/**
+ * bb_is_topic_lastpage() - Report whether the current page is the last page of a given topic
+ *
+ * @since 0.9
+ * @param int $topic_id The topic id of the topic being queried
+ * @return boolean True if called on the last page of a topic, otherwise false
+ */
+function bb_is_topic_lastpage( $topic_id = 0 ) {
+	global $page;
+	return ( $page == bb_get_topic_pagecount( $topic_id ) );
+}
+
+// POSTS
+
+function post_id( $post_id = 0 ) {
+	echo get_post_id( $post_id );
+}
+
+function get_post_id( $post_id = 0 ) {
+	global $bb_post;
+	$post_id = (int) $post_id;
+	if ( $post_id )
+		$post = bb_get_post( $post_id );
+	else
+		$post =& $bb_post;
+	return $post->post_id;
+}
+
+function post_link( $post_id = 0 ) {
+	echo apply_filters( 'post_link', get_post_link( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_link( $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	$page = bb_get_page_number( $bb_post->post_position );
+	return apply_filters( 'get_post_link', get_topic_link( $bb_post->topic_id, $page ) . "#post-$bb_post->post_id", $bb_post->post_id );
+}
+
+function post_anchor_link( $force_full = false ) {
+	if ( defined('DOING_AJAX') || $force_full )
+		post_link();
+	else
+		echo '#post-' . get_post_id();
+}
+
+function post_position( $post_id = 0 ) {
+	echo apply_filters( 'post_position', get_post_position( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_position( $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	return apply_filters( 'get_post_position', $bb_post->post_position, $bb_post->post_id );
+}
+
+function post_position_link( $topic_id = 0, $position = 1 ) {
+	echo apply_filters( 'post_position_link', get_post_position_link( $topic_id, $position ), get_topic_id( $topic_id ), (integer) $position );
+}
+
+function get_post_position_link( $topic_id = 0, $position = 1 ) {
+	$position = (integer) $position;
+	$bb_topic = get_topic( get_topic_id( $topic_id ) );
+	if ( $bb_topic->topic_posts < $position ) {
+		return;
+	}
+	$page = bb_get_page_number( $position );
+	return apply_filters( 'get_post_position_link', get_topic_link( $bb_post->topic_id, $page ) . "#position-$position", $bb_topic->topic_id, $position );
+}
+
+function bb_post_meta( $key, $post_id = 0 ) {
+	echo bb_get_post_meta( $key, $post_id );
+}
+
+function bb_get_post_meta( $key, $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	if ( isset($bb_post->$key) )
+		return $bb_post->$key;
+}
+
+
+function post_author( $post_id = 0 ) {
+	echo apply_filters('post_author', get_post_author( $post_id ), $post_id );
+}
+
+function get_post_author( $post_id = 0 ) {
+	if ( $user = bb_get_user( get_post_author_id( $post_id ) ) )
+		return apply_filters( 'get_post_author', $user->display_name, $user->ID, $post_id );
+	elseif ( $title = bb_get_post_meta( 'pingback_title', $post_id ) )
+		return apply_filters( 'bb_get_pingback_title', $title, $post_id );
+	elseif ( $title = bb_get_post_meta( 'post_author', $post_id ) )
+		return apply_filters( 'get_post_author', $title, 0, $post_id );
+	else
+		return apply_filters( 'get_post_author', __('Anonymous'), 0, $post_id );
+}
+
+function post_author_link( $post_id = 0 ) {
+	if ( $link = ( bb_get_option( 'name_link_profile' ) ? get_user_profile_link( get_post_author_id( $post_id ) ) : get_user_link( get_post_author_id( $post_id ) ) ) ) {
+		echo '<a href="' . esc_attr( $link ) . '">' . get_post_author( $post_id ) . '</a>';
+	} elseif ( $link = bb_get_post_meta( 'pingback_uri' )) {
+		echo '<a href="' . esc_attr( $link ) . '">' . get_post_author( $post_id ) . '</a>';
+	} elseif ( $link = bb_get_post_meta( 'post_url' ) ) {
+		echo '<a href="' . esc_attr( $link ) . '">' . get_post_author( $post_id ) . '</a>';
+	} else {
+		post_author( $post_id );
+	}
+}
+
+function post_author_avatar( $size = '48', $default = '', $post_id = 0 ) {
+	if ( ! bb_get_option('avatars_show') )
+		return false;
+	
+	$author_id = get_post_author_id( $post_id );
+	echo bb_get_avatar( $author_id, $size, $default );
+}
+
+function post_author_avatar_link( $size = '48', $default = '', $post_id = 0 ) {
+	if ( ! bb_get_option('avatars_show') )
+		return false;
+	
+	$author_id = get_post_author_id( $post_id );
+	if ( $link = get_user_link( $author_id ) ) {
+		echo '<a href="' . esc_attr( $link ) . '">' . bb_get_avatar( $author_id, $size, $default ) . '</a>';
+	} else {
+		echo bb_get_avatar( $author_id, $size, $default );
+	}
+}
+
+function post_text( $post_id = 0 ) {
+	echo apply_filters( 'post_text', get_post_text( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_text( $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	return apply_filters( 'get_post_text', $bb_post->post_text, $bb_post->post_id );
+}
+
+function bb_post_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+	$time = apply_filters( 'bb_post_time', bb_get_post_time( array('format' => 'mysql') + $args ), $args );
+	echo _bb_time_function_return( $time, $args );
+}
+
+function bb_get_post_time( $args = '' ) {
+	$args = _bb_parse_time_function_args( $args );
+
+	$bb_post = bb_get_post( get_post_id( $args['id'] ) );
+
+	$time = apply_filters( 'bb_get_post_time', $bb_post->post_time, $args );
+
+	return _bb_time_function_return( $time, $args );
+}
+
+function post_ip( $post_id = 0 ) {
+	if ( bb_current_user_can( 'view_by_ip' ) )
+		echo apply_filters( 'post_ip', get_post_ip( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_ip( $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	return apply_filters( 'get_post_ip', $bb_post->poster_ip, $bb_post->post_id );
+}
+
+function bb_post_admin( $args = null )
+{
+	$defaults = array(
+		'post_id' => 0,
+		'before' => '',
+		'after' => '',
+		'before_each' => '',
+		'after_each' => "\n",
+		'each' => array(
+			'ip' => array(
+				'post_id' => 0
+			),
+			'edit' => array(
+				'post_id' => 0
+			),
+			'delete' => array(
+				'post_id' => 0
+			),
+			'undelete' => array(
+				'post_id' => 0
+			)
+		)
+	);
+	if ( isset( $args['each'] ) ) {
+		$each_args = $args['each'];
+		$_each_args = $defaults['each'];
+		foreach ( $each_args as $_part_name => $_part_args ) {
+			if ( !isset( $defaults['each'][$_part_name] ) ) {
+				continue;
+			}
+			$_each_args[$_part_name] = wp_parse_args( $_part_args, $defaults['each'][$_part_name] );
+		}
+	}
+	$args = wp_parse_args( $args, $defaults );
+	if ( isset( $_each_args ) ) {
+		$args['each'] = $_each_args;
+	}
+
+	$parts = array();
+	if ( is_array( $args['each'] ) && count( $args['each'] ) ) {
+		foreach ( $args['each'] as $_part_name => $_part_args ) {
+			if ( $args['post_id'] && !$_part_args['post_id'] ) {
+				$_part_args['post_id'] = $args['post_id'];
+			}
+			if ( $args['before_each'] && !$_part_args['before'] ) {
+				$_part_args['before'] = $args['before_each'];
+			}
+			if ( $args['after_each'] && !$_part_args['after'] ) {
+				$_part_args['after'] = $args['after_each'];
+			}
+			$_part_function = 'bb_get_post_' . $_part_name . '_link';
+			$parts[$_part_name] = $_part_function( $_part_args );
+		}
+
+		// For the benefit of filters, mark the final part
+		if ( !isset( $args['last_each'] ) ) {
+			$args['last_each'] = $_part_args;
+		}
+	}
+
+	$parts = apply_filters( 'bb_post_admin', $parts, $args );
+
+	if ( !count( $parts ) ) {
+		return;
+	}
+
+	echo $args['before'] . join( '', $parts ) . $args['after'];
+}
+
+function post_ip_link( $args = null )
+{
+	echo bb_get_post_ip_link( $args );
+}
+
+function bb_get_post_ip_link( $args = null )
+{
+	if ( !bb_current_user_can( 'view_by_ip' ) ) {
+		return;
+	}
+
+	$defaults = array(
+		'post_id' => 0,
+		'before' => '',
+		'after' => '',
+		'text' => '%s'
+	);
+	if ( is_numeric( $args ) ) {
+		$args = array( 'post_id' => $args );
+	}
+	$args = wp_parse_args( $args, $defaults );
+
+	$bb_post = bb_get_post( get_post_id( $args['post_id'] ) );
+
+	$uri = bb_get_uri( 'bb-admin/posts.php', array( 'poster_ip' => get_post_ip( $bb_post->post_id ) ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN );
+
+	// Make sure that the last tag in $before gets a class (if it's there)
+	if ( preg_match( '/.*(<[^>]+>)[^<]*/', $args['before'], $_node ) ) {
+		if ( preg_match( '/class=(\'|")(.*)\1/U', $_node[1], $_class ) ) {
+			$args['before'] = str_replace( $_class[0], 'class=' . $_class[1] . 'before-post-ip-link ' . $_class[2] . $_class[1], $args['before'] );
+		} else {
+			$args['before'] = preg_replace( '/(.*)<([a-z0-9_-]+)(\s?)([^>]*)>([^<]*)/i', '$1<$2 class="before-post-ip-link"$3$4>$5', $args['before'], 1 );
+		}
+	}
+
+	$link = $args['before'] . '<a class="post-ip-link" href="' . esc_attr( $uri ) . '">' . esc_html( sprintf( $args['text'], get_post_ip( $bb_post->post_id ) ) ) . '</a>' . $args['after'];
+	return apply_filters( 'post_ip_link', $link, $bb_post->post_id, $args );
+}
+
+function post_edit_link( $args = null )
+{
+	echo bb_get_post_edit_link( $args );
+}
+
+function bb_get_post_edit_link( $args = null )
+{
+	$defaults = array(
+		'post_id' => 0,
+		'before' => '',
+		'after' => '',
+		'text' => __( 'Edit' )
+	);
+	if ( is_numeric( $args ) ) {
+		$args = array( 'post_id' => $args );
+	}
+	$args = wp_parse_args( $args, $defaults );
+
+	$bb_post = bb_get_post( get_post_id( $args['post_id'] ) );
+
+	if ( bb_current_user_can( 'edit_post', $bb_post->post_id ) ) {
+		$uri = bb_get_uri( 'edit.php', array( 'id' => $bb_post->post_id ) );
+
+		// Make sure that the last tag in $before gets a class (if it's there)
+		if ( preg_match( '/.*(<[^>]+>)[^<]*/', $args['before'], $_node ) ) {
+			if ( preg_match( '/class=(\'|")(.*)\1/U', $_node[1], $_class ) ) {
+				$args['before'] = str_replace( $_class[0], 'class=' . $_class[1] . 'before-post-edit-link ' . $_class[2] . $_class[1], $args['before'] );
+			} else {
+				$args['before'] = preg_replace( '/(.*)<([a-z0-9_-]+)(\s?)([^>]*)>([^<]*)/i', '$1<$2 class="before-post-edit-link"$3$4>$5', $args['before'], 1 );
+			}
+		}
+
+		$r = $args['before'] . '<a class="post-edit-link" href="' . esc_attr( apply_filters( 'post_edit_uri', $uri, $bb_post->post_id, $args ) ) . '">' . esc_html( $args['text'] ) . '</a>' . $args['after'];
+		return apply_filters( 'bb_get_post_edit_link', $r, $bb_post->post_id, $args );
+	}
+}
+
+function post_del_class( $post_id = 0 )
+{
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	$classes = array();
+	if ( bb_get_post_meta( 'pingback_uri', $post_id ) ) {
+		$classes[] = 'pingback';
+	}
+	if ( $bb_post->post_status == 1 ) {
+		$classes[] = 'deleted';
+	} elseif ( $bb_post->post_status != 0 ) {
+		$classes[] = 'post-status-' . $bb_post->post_status;
+	}
+	if ( count( $classes ) ) {
+		$classes = join( ' ', $classes );
+	} else {
+		$classes = '';
+	}
+	return apply_filters( 'post_del_class', $classes, $bb_post->post_id, $bb_post );
+}
+
+function post_delete_link( $args = null )
+{
+	echo bb_get_post_delete_link( $args );
+}
+
+function bb_get_post_delete_link( $args = null )
+{
+	$defaults = array(
+		'post_id' => 0,
+		'before' => '',
+		'after' => '',
+		'text' => __( 'Delete' ),
+		'redirect' => true
+	);
+	if ( is_numeric( $args ) || is_object( $args ) ) {
+		$args = array( 'post_id' => $args );
+	}
+	if ( isset( $args['delete_text'] ) && ( !isset( $args['text'] ) || !$args['text'] ) ) {
+		$args['text'] = $args['delete_text'];
+	}
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	//if ( bb_is_first( $bb_post->post_id ) ) {
+	//	$topic = get_topic( $bb_post->topic_id );
+	//	if ( 2 > $topic->topic_posts ) {
+			// Should delete the whole topic
+	//		return;
+	//	}
+	//}
+	
+	if ( !bb_current_user_can( 'delete_post', $bb_post->post_id ) ) {
+		return;
+	}
+
+	if ( true === $redirect ) {
+		$redirect = $_SERVER['REQUEST_URI'];
+	}
+
+	$uri = bb_get_uri('bb-admin/delete-post.php', array(
+		'id' => $bb_post->post_id,
+		'status' => 1,
+		'_wp_http_referer' => $redirect ? rawurlencode( $redirect ) : false
+	), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri = esc_url( bb_nonce_url( $uri, 'delete-post_' . $bb_post->post_id ) );
+
+	if ( ( bb_is_admin() || isset( $_GET['view'] ) && 'all' == $_GET['view'] ) ) {
+		$ajax_class = 'dim:thread:post-' . $bb_post->post_id . ':deleted:FF3333:FFFF33:action=delete-post&amp;status=1';
+	} else {
+		$ajax_class = 'delete:thread:post-' . $bb_post->post_id . '::status=1';
+	}
+
+	$text = esc_html( $text );
+
+	// Make sure that the last tag in $before gets a class (if it's there)
+	if ( preg_match( '/.*(<[^>]+>)[^<]*/', $before, $_node ) ) {
+		if ( preg_match( '/class=(\'|")(.*)\1/U', $_node[1], $_class ) ) {
+			$before = str_replace( $_class[0], 'class=' . $_class[1] . 'before-post-delete-link ' . $_class[2] . $_class[1], $before );
+		} else {
+			$before = preg_replace( '/(.*)<([a-z0-9_-]+)(\s?)([^>]*)>([^<]*)/i', '$1<$2 class="before-post-delete-link"$3$4>$5', $before, 1 );
+		}
+	}
+
+	$r = $before . '<a href="' . $uri . '" class="' . $ajax_class . ' post-delete-link">' . $text . '</a>' . $after;
+	$r = apply_filters( 'post_delete_link', $r, $bb_post->post_status, $bb_post->post_id, $args );
+	return $r;
+}
+
+function bb_post_undelete_link( $args = null )
+{
+	echo bb_get_post_undelete_link( $args );
+}
+
+function bb_get_post_undelete_link( $args = null )
+{
+	$defaults = array(
+		'post_id' => 0,
+		'before' => '',
+		'after' => '',
+		'text' => __( 'Undelete' ),
+		'redirect' => true
+	);
+	if ( is_numeric( $args ) || is_object( $args ) ) {
+		$args = array( 'post_id' => $args );
+	}
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+
+	if ( !bb_current_user_can( 'delete_post', $bb_post->post_id ) ) {
+		return;
+	}
+
+	if ( true === $redirect ) {
+		$redirect = $_SERVER['REQUEST_URI'];
+	}
+
+	$uri = bb_get_uri('bb-admin/delete-post.php', array(
+		'id' => $bb_post->post_id,
+		'status' => 0,
+		'view' => 'all',
+		'_wp_http_referer' => $redirect ? rawurlencode( $redirect ) : false
+	), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN);
+	$uri = esc_url( bb_nonce_url( $uri, 'delete-post_' . $bb_post->post_id ) );
+
+	$ajax_class = 'dim:thread:post-' . $bb_post->post_id . ':deleted:FF3333:FFFF33:action=delete-post&amp;status=0';
+
+	$text = esc_html( $text );
+
+	// Make sure that the last tag in $before gets a class (if it's there)
+	if ( preg_match( '/.*(<[^>]+>)[^<]*/', $before, $_node ) ) {
+		if ( preg_match( '/class=(\'|")(.*)\1/U', $_node[1], $_class ) ) {
+			$before = str_replace( $_class[0], 'class=' . $_class[1] . 'before-post-undelete-link ' . $_class[2] . $_class[1], $before );
+		} else {
+			$before = preg_replace( '/(.*)<([a-z0-9_-]+)(\s?)([^>]*)>([^<]*)/i', '$1<$2 class="before-post-undelete-link"$3$4>$5', $before, 1 );
+		}
+	}
+
+	$r = $before . '<a href="' . $uri . '" class="' . $ajax_class . ' post-undelete-link">' . $text . '</a>' . $after;
+	$r = apply_filters( 'post_undelete_link', $r, $bb_post->post_status, $bb_post->post_id, $args );
+	return $r;
+}
+
+function post_author_id( $post_id = 0 ) {
+	echo apply_filters( 'post_author_id', get_post_author_id( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_author_id( $post_id = 0 ) {
+	$bb_post = bb_get_post( get_post_id( $post_id ) );
+	return apply_filters( 'get_post_author_id', (int) $bb_post->poster_id, get_post_id( $post_id ) );
+}
+
+function post_author_title( $post_id = 0 ) {
+	echo apply_filters( 'post_author_title', get_post_author_title( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_author_title( $post_id = 0 ) {
+	return get_user_title( get_post_author_id( $post_id ) );
+}
+
+function post_author_title_link( $post_id = 0 ) {
+	echo apply_filters( 'post_author_title_link', get_post_author_title_link( $post_id ), get_post_id( $post_id ) );
+}
+
+function get_post_author_title_link( $post_id = 0 ) {
+	$title = get_post_author_title( $post_id );
+	if ( false === $title ) {
+		if ( bb_get_post_meta( 'pingback_uri', $post_id ) )
+			$r = __('PingBack');
+		else
+			$r = __('Unregistered'); // This should never happen
+	} else {
+		if ( $link = bb_get_option( 'name_link_profile' ) ? get_user_link( get_post_author_id( $post_id ) ) : get_user_profile_link( get_post_author_id( $post_id ) ) )
+			$r = '<a href="' . esc_attr( $link ) . '">' . $title . '</a>';
+		else
+			$r = $title;
+	}
+
+	return apply_filters( 'get_post_author_title_link', $r, get_post_id( $post_id ) );
+}
+
+function post_author_type( $post_id = 0 ) {
+	$id = get_post_author_id( $post_id );
+	$type = get_user_type( $id );
+	if ( false === $type ) {
+		if ( bb_get_post_meta( 'pingback_uri', $post_id ) )
+			$r = __('PingBack');
+		else
+			$r = __('Unregistered'); // This should never happen
+	} else
+		$r = '<a href="' . esc_attr( get_user_profile_link( $id ) ) . '">' . $type . '</a>';
+
+	echo apply_filters( 'post_author_type', $r, $post_id );
+}
+
+function allowed_markup( $args = '' ) {
+	echo apply_filters( 'allowed_markup', get_allowed_markup( $args ) );
+}
+
+// format=list or array( 'format' => 'list' )
+function get_allowed_markup( $args = '' ) {
+	$args = wp_parse_args( $args, array('format' => 'flat') );
+	extract($args, EXTR_SKIP);
+
+	$tags = bb_allowed_tags();
+	unset($tags['pre'], $tags['br']);
+	$tags = array_keys($tags);
+
+	switch ( $format ) :
+	case 'array' :
+		$r = $tags;
+		break;
+	case 'list' :
+		$r = "<ul class='allowed-markup'>\n\t<li>";
+		$r .= join("</li>\n\t<li>", $tags);
+		$r .= "</li>\n</ul>\n";
+		break;
+	default :
+		$r = join(' ', $tags);
+		break;
+	endswitch;
+	return apply_filters( 'get_allowed_markup', $r, $format );
+}
+
+// USERS
+function bb_get_user_id( $id = 0 ) {
+	global $user;
+	if ( is_object($id) && isset($id->ID) )
+		return (int) $id->ID;
+	elseif ( !$id )
+		return $user->ID;
+
+	$_user = bb_get_user( (int) $id );
+	return isset($_user->ID) ? $_user->ID : 0;
+}
+
+function user_profile_link( $id = 0 , $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+	echo apply_filters( 'user_profile_link', get_user_profile_link( $id ), bb_get_user_id( $id ), $context );
+}
+
+function get_user_profile_link( $id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+	
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'user_nicename';
+		} else {
+			$column = 'ID';
+		}
+		$page = (1 < $page) ? '/page/' . $page : '';
+		$r = bb_get_uri('profile/' . $user->$column . $page, null, $context);
+	} else {
+		$query = array(
+			'id' => $user->ID,
+			'page' => (1 < $page) ? $page : false
+		);
+		$r = bb_get_uri('profile.php', $query, $context);
+	}
+	return apply_filters( 'get_user_profile_link', $r, $user->ID, $context );
+}
+
+function user_delete_button() {
+	global $user;
+	
+	$user_obj = new BP_User( $user->ID );
+	if ( !bb_current_user_can( 'keep_gate' ) && 'keymaster' == $user_obj->roles[0] )
+		return;
+	
+	if ( bb_current_user_can( 'edit_users' ) && bb_get_current_user_info( 'id' ) != (int) $user->ID )
+		echo apply_filters( 'user_delete_button', get_user_delete_button() );
+}
+
+function get_user_delete_button() {
+	$r  = '<input type="submit" class="delete" name="delete-user" value="' . __('Delete User &raquo;') . '" ';
+	$r .= 'onclick="return confirm(\'' . esc_js(__('Are you sure you want to delete this user?')) . '\')" />';
+	return apply_filters( 'get_user_delete_button', $r);
+}
+
+function profile_tab_link( $id = 0, $tab, $page = 1 ) {
+	echo apply_filters( 'profile_tab_link', get_profile_tab_link( $id, $tab ) );
+}
+
+function get_profile_tab_link( $id = 0, $tab, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+
+	$tab = bb_sanitize_with_dashes($tab);
+
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+
+	if ( $tab === 'edit' && !( $context & BB_URI_CONTEXT_BB_USER_FORMS ) ) {
+		$context += BB_URI_CONTEXT_BB_USER_FORMS;
+	}
+
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'user_nicename';
+		} else {
+			$column = 'ID';
+		}
+		$page = (1 < $page) ? '/page/' . $page : '';
+		$r = bb_get_uri('profile/' . $user->$column . '/' . $tab . $page, null, $context);
+	} else {
+		$query = array(
+			'id' => $user->ID,
+			'tab' => $tab,
+			'page' => (1 < $page) ? $page : false
+		);
+		$r = bb_get_uri('profile.php', $query, $context);
+	}
+	return apply_filters( 'get_profile_tab_link', $r, $user->ID, $context );
+}
+
+function user_link( $id = 0 ) {
+	echo apply_filters( 'user_link', get_user_link( $id ), $id );
+}
+
+function get_user_link( $id = 0 ) {
+	if ( $user = bb_get_user( bb_get_user_id( $id ) ) )
+		return apply_filters( 'get_user_link', $user->user_url, $user->ID );
+}
+
+function full_user_link( $id = 0 ) {
+	echo get_full_user_link( $id );
+}
+
+function get_full_user_link( $id = 0 ) {
+	if ( get_user_link( $id ) )
+		$r = '<a href="' . esc_attr( get_user_link( $id ) ) . '">' . get_user_display_name( $id ) . '</a>';
+	else
+		$r = get_user_display_name( $id );
+	return $r;
+}
+
+function user_type_label( $type ) {
+	echo apply_filters( 'user_type_label', get_user_type_label( $type ), $type );
+}
+
+function get_user_type_label( $type ) {
+	global $wp_roles;
+	if ( $wp_roles->is_role( $type ) )
+		return apply_filters( 'get_user_type_label', $wp_roles->role_names[$type], $type );
+}
+
+function user_type( $id = 0 ) {
+	echo apply_filters( 'user_type', get_user_type( $id ), $id );
+}
+
+function get_user_type( $id = 0 ) {
+	if ( $user = bb_get_user( bb_get_user_id( $id ) ) ) :
+		@$caps = array_keys($user->capabilities);
+		if ( !$caps )
+			$caps[] = 'inactive';
+
+		$type = get_user_type_label( $caps[0] ); //Just support one role for now.
+	else :
+		$type = false;
+	endif;
+	return apply_filters( 'get_user_type', $type, $user->ID );
+}
+
+function get_user_name( $id = 0 ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+	return apply_filters( 'get_user_name', $user->user_login, $user->ID );
+}
+
+function get_user_display_name( $id = 0 ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+	return apply_filters( 'get_user_display_name', $user->display_name, $user->ID );
+}
+
+function user_title( $id = 0 ) {
+	echo apply_filters( 'user_title', get_user_title( $id ), bb_get_user_id( $id ) );
+}
+
+function get_user_title( $id = 0 ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+	return empty( $user->title ) ? get_user_type( $id ) : apply_filters( 'get_user_title', $user->title, $user->ID );
+}
+
+function profile_pages( $args = null )
+{
+	$defaults = array( 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page, $user;
+	$add = apply_filters( 'profile_pages_add', $add );
+	if ( $pages = apply_filters( 'profile_pages', get_page_number_links( $page, $user->topics_replied + $add ), $user->user_id ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+function bb_profile_data( $id = 0 ) {
+	if ( !$user = bb_get_user( bb_get_user_id( $id ) ) )
+		return;
+
+	$reg_time = bb_gmtstrtotime( $user->user_registered );
+	$profile_info_keys = bb_get_profile_info_keys();
+	echo "<dl id='userinfo'>\n";
+	echo "\t<dt>" . __('Member Since') . "</dt>\n";
+	echo "\t<dd>" . bb_datetime_format_i18n($reg_time, 'date') . ' (' . bb_since($reg_time) . ")</dd>\n";
+	if ( is_array( $profile_info_keys ) ) {
+		foreach ( $profile_info_keys as $key => $label ) {
+			if ( in_array($key, array('first_name', 'last_name', 'display_name')) || !isset($user->$key) )
+				continue;
+			$val = 'user_url' == $key ? get_user_link( $user->ID ) : $user->$key;
+			if (
+				( 'user_email' != $key || ( 'user_email' == $key && bb_current_user_can( 'edit_users' ) ) )
+				&& $val
+				&& 'http://' != $val
+			) {
+				echo "\t<dt>{$label[1]}</dt>\n";
+				$val = make_clickable( $val );
+				$attributes = array();
+				if (isset($label[2]) && !empty($label[2]))
+					if (preg_match("#^<a#i", $val))
+						$val = preg_replace("#^<a#i", '<a class="' . esc_attr($label[2]) . '"', $val);
+					else
+						$val = '<span class="' . esc_attr($label[2]) . '">' . $val . '</span>';
+				
+				echo "\t<dd>" . $val . "</dd>\n";
+			}
+		}
+	}
+	echo "</dl>\n";
+}
+
+function bb_profile_base_content() {
+	global $self;
+	if ( !is_callable( $self ) )
+		return; // should never happen
+	call_user_func( $self );
+}
+
+function bb_profile_data_form( $id = 0 ) {
+	global $errors;
+	if ( !$user = bb_get_user( bb_get_user_id( $id ) ) )
+		return;
+
+	if ( !bb_current_user_can( 'edit_user', $user->ID ) )
+		return;
+
+	$error_codes = $errors->get_error_codes();
+	$profile_info_keys = bb_get_profile_info_keys();
+	$required = false;
+	if ( in_array( 'delete', $error_codes ) )
+		echo '<div class="form-invalid error">' . $errors->get_error_message( 'delete' ) . '</div>';
+?>
+<table id="userinfo">
+<?php
+	if ( is_array($profile_info_keys) ) :
+		$bb_current_id = bb_get_current_user_info( 'id' );
+		foreach ( $profile_info_keys as $key => $label ) :
+			if ( $label[0] ) {
+				$class = 'form-field form-required required';
+				$required = true;
+			} else {
+				$class = 'form-field';
+			}
+			$title = esc_attr( $label[1] );
+
+			$name = esc_attr( $key );
+			$type = isset($label[2]) ? esc_attr( $label[2] ) : 'text';
+			if ( !in_array( $type, array( 'checkbox', 'file', 'hidden', 'image', 'password', 'radio', 'text' ) ) ) {
+				$type = 'text';
+			}
+
+			$checked = false;
+			if ( in_array( $key, $error_codes ) ) {
+				$class .= ' form-invalid error';
+				$data = $errors->get_error_data( $key );
+				if ( 'checkbox' == $type ) {
+					if ( isset($data['data']) )
+						$checked = $data['data'];
+					else
+						$checked = $_POST[$key];
+					$value = $label[3];
+					$checked = $checked == $value;
+				} else {
+					if ( isset($data['data']) )
+						$value = $data['data'];
+					else
+						$value = $_POST[$key];
+				}
+
+				$message = esc_html( $errors->get_error_message( $key ) );
+				$message = "<em>$message</em>";
+			} else {
+				if ( 'checkbox' == $type ) {
+					$checked = $user->$key == $label[3] || $label[4] == $label[3];
+					$value = $label[3];
+				} else {
+					$value = isset($user->$key) ? $user->$key : '';
+				}
+				$message = '';
+			}
+
+			$checked = $checked ? ' checked="checked"' : '';
+			$value = esc_attr( $value );
+
+?>
+
+<tr class="<?php echo $class; ?>">
+	<th scope="row">
+		<label for="<?php echo $name; ?>"><?php echo $title; ?></label>
+		<?php echo $message; ?>
+	</th>
+	<td>
+<?php
+			if ($key == 'display_name') {
+?>
+		<select name="display_name" id="display_name">
+<?php
+				$public_display = array();
+				$public_display['display_displayname'] = $user->display_name;
+				//$public_display['display_nickname'] = $user->nickname;
+				$public_display['display_username'] = $user->user_login;
+				if ( isset($user->first_name) ) {
+					$public_display['display_firstname'] = $user->first_name;
+					if ( isset($user->last_name) ) {
+						$public_display['display_firstlast'] = $user->first_name.' '.$user->last_name;
+						$public_display['display_lastfirst'] = $user->last_name.' '.$user->first_name;
+					}
+				}
+				if ( isset($user->last_name) )
+					$public_display['display_lastname'] = $user->last_name;
+				
+				$public_display = array_unique(array_filter(array_map('trim', $public_display)));
+				
+				foreach($public_display as $id => $item) {
+?>
+			<option id="<?php echo esc_attr( $id ); ?>" value="<?php echo esc_attr( $item ); ?>"><?php echo esc_html( $item ); ?></option>
+<?php
+				}
+?>
+		</select>
+<?php
+			} else {
+?>
+		<?php if ( 'checkbox' == $type && isset($label[5]) ) echo '<label for="' . $name . '">'; ?>
+		<input name="<?php echo $name; ?>" id="<?php echo $name; ?>" type="<?php echo $type; ?>"<?php echo $checked; ?> value="<?php echo $value; ?>" />
+		<?php if ( 'checkbox' == $type && isset($label[5]) ) echo esc_html( $label[5] ) . '</label>'; ?>
+<?php
+			}
+?>
+	</td>
+</tr>
+
+<?php endforeach; endif; // $profile_info_keys; $profile_info_keys ?>
+
+</table>
+
+<?php bb_nonce_field( 'edit-profile_' . $user->ID ); if ( $required ) : ?>
+
+<p class="required-message"><?php _e('These items are <span class="required">required</span>.') ?></p>
+
+<?php
+	endif;
+	do_action( 'extra_profile_info', $user->ID );
+}
+
+function bb_profile_admin_form( $id = 0 ) {
+	global $wp_roles, $errors;
+	if ( !$user = bb_get_user( bb_get_user_id( $id ) ) )
+		return;
+
+	if ( !bb_current_user_can( 'edit_user', $user->ID ) )
+		return;
+
+	$error_codes = $errors->get_error_codes();
+	$bb_current_id = bb_get_current_user_info( 'id' );
+
+	$profile_admin_keys = bb_get_profile_admin_keys();
+	$assignable_caps = bb_get_assignable_caps();
+	$required = false;
+
+	$roles = $wp_roles->role_names;
+	$can_keep_gate = bb_current_user_can( 'keep_gate' );
+
+	// Keymasters can't demote themselves
+	if ( ( $bb_current_id == $user->ID && $can_keep_gate ) || ( isset( $user->capabilities ) && is_array( $user->capabilities ) && array_key_exists( 'keymaster', $user->capabilities ) && !$can_keep_gate ) ) {
+		$roles = array( 'keymaster' => $roles['keymaster'] );
+	} elseif ( !$can_keep_gate ) { // only keymasters can promote others to keymaster status
+		unset($roles['keymaster']);
+	}
+
+	$selected = array( 'inactive' => ' selected="selected"' );
+?>
+<table id="admininfo">
+<tr class='form-field<?php if ( in_array( 'role', $error_codes ) ) echo ' form-invalid error'; ?>'>
+	<th scope="row">
+		<label for="admininfo_role"><?php _e('User Type'); ?></label>
+		<?php if ( in_array( 'role', $error_codes ) ) echo '<em>' . $errors->get_error_message( 'role' ) . '</em>'; ?>
+	</th>
+	<td>
+		<select id="admininfo_role" name="role">
+<?php
+	foreach( $roles as $r => $n ) {
+		if ( isset( $user->capabilities ) && is_array( $user->capabilities ) && array_key_exists( $r, $user->capabilities ) ) {
+			$selected['inactive'] = '';
+			$selected[$r] = ' selected="selected"';
+		} elseif ( $r !== 'inactive' ) {
+			$selected[$r] = '';
+		}
+?>
+			<option value="<?php echo $r; ?>"<?php echo $selected[$r]; ?>><?php echo $n; ?></option>
+<?php
+	}
+?>
+		</select>
+	</td>
+</tr>
+<?php
+	if (count($assignable_caps)) :
+?>
+<tr class="extra-caps-row">
+	<th scope="row"><?php _e('Allow this user to'); ?></th>
+	<td>
+<?php
+	foreach( $assignable_caps as $cap => $label ) :
+		$name = esc_attr( $cap );
+		$checked = '';
+		if ( isset( $user->capabilities ) && is_array( $user->capabilities ) && array_key_exists( $cap, $user->capabilities ) ) {
+			$checked = ' checked="checked"';
+		}
+		$label = esc_html( $label );
+?>
+
+		<label><input name="<?php echo $name; ?>" value="1" type="checkbox"<?php echo $checked; ?> /> <?php echo $label; ?></label><br />
+
+<?php endforeach; ?>
+
+	</td>
+</tr>
+
+<?php
+	endif;
+	
+	if ( is_array($profile_admin_keys) ) :
+		foreach ( $profile_admin_keys as $key => $label ) :
+			if ( $label[0] ) {
+				$class = 'form-field form-required required';
+				$required = true;
+			} else {
+				$class = 'form-field';
+			}
+			$title = esc_attr( $label[1] );
+
+			$name = esc_attr( $key );
+			$type = isset($label[2]) ? esc_attr( $label[2] ) : 'text';
+
+			$checked = false;
+			if ( in_array( $key, $error_codes ) ) {
+				$class .= ' form-invalid error';
+				$data = $errors->get_error_data( $key );
+				if ( 'checkbox' == $type ) {
+					if ( isset($data['data']) )
+						$checked = $data['data'];
+					else
+						$checked = $_POST[$key];
+					$value = $label[3];
+					$checked = $checked == $value;
+				} else {
+					if ( isset($data['data']) )
+						$value = $data['data'];
+					else
+						$value = $_POST[$key];
+				}
+
+				$message = esc_html( $errors->get_error_message( $key ) );
+				$message = "<em>$message</em>";
+			} else {
+				if ( 'checkbox' == $type ) {
+					$checked = $user->$key == $label[3] || $label[4] == $label[3];
+					$value = $label[3];
+				} else {
+					$value = isset($user->$key) ? $user->$key : '';
+				}
+				$message = '';
+			}
+
+			$checked = $checked ? ' checked="checked"' : '';
+			$value = esc_attr( $value );
+
+?>
+
+<tr class="<?php echo $class; ?>">
+	<th scope="row">
+		<label for="<?php echo $name; ?>"><?php echo $title ?></label>
+		<?php echo $message; ?>
+	</th>
+	<td>
+		<?php if ( 'checkbox' == $type && isset($label[5]) ) echo "<label for='$name'>"; ?>
+		<input name="<?php echo $name; ?>" id="<?php echo $name; ?>" type="<?php echo $type; ?>"<?php echo $checked; ?> value="<?php echo $value; ?>" />
+		<?php if ( 'checkbox' == $type && isset($label[5]) ) echo esc_html( $label[5] ) . "</label>"; ?>
+	</td>
+</tr>
+
+<?php endforeach; endif; // $profile_admin_keys; $profile_admin_keys ?>
+
+</table>
+
+<?php if ( $required ) : ?>
+<p class="required-message"><?php _e('These items are <span class="required">required</span>.') ?></p>
+
+<?php endif; ?>
+<p><?php _e('Inactive users can login and look around but not do anything. Blocked users just see a simple error message when they visit the site.'); ?></p>
+<p><?php _e('<strong>Note</strong>: Blocking a user does <em>not</em> block any IP addresses.'); ?></p>
+<?php
+}
+
+function bb_profile_password_form( $id = 0 ) {
+	global $errors;
+	if ( !$user = bb_get_user( bb_get_user_id( $id ) ) )
+		return;
+
+	if ( !bb_current_user_can( 'change_user_password', $user->ID ) )
+		return;
+
+	$class = 'form-field';
+
+	if ( $message = $errors->get_error_message( 'pass' ) ) {
+		$class .= ' form-invalid error';
+		$message = '<em>' . esc_html( $message ) . '</em>';
+	}
+?>
+
+<table>
+<tr class="<?php echo $class; ?>">
+	<th scope="row" rowspan="2">
+		<label for="pass1"><?php _e('New password'); ?></label>
+		<?php echo $message; ?>
+	</th>
+	<td>
+		<input name="pass1" type="password" id="pass1" autocomplete="off" />
+	</td>
+</tr>
+<tr class="<?php echo $class; ?>">
+	<td>
+		<input name="pass2" type="password" id="pass2" autocomplete="off" />
+	</td>
+</tr>
+<tr class="pass-strength">
+	<th scope="row"><?php _e('Password Strength'); ?></th>
+	<td>
+		<input type="hidden" name="user_login" id="user_login" value="<?php echo $user->user_login; ?>" />
+		<noscript>
+			<?php _e('Disabled (requires JavaScript)'); ?>
+		</noscript>
+		<script type="text/javascript" charset="utf-8">
+			if (typeof jQuery != 'undefined') {
+				document.writeln('<div id="pass-strength-result">' + pwsL10n.short + '</div>');
+			} else {
+				document.writeln('<?php echo str_replace("'", "\'", __('Disabled.')); ?>')
+			}
+		</script>
+	</td>
+</tr>
+</table>
+
+<p><?php _e('Hint: Use upper and lower case characters, numbers and symbols like !"?$%^&amp;( in your password.'); ?></p>
+
+<?php
+
+}
+
+function bb_logout_link( $args = '' ) {
+	echo apply_filters( 'bb_logout_link', bb_get_logout_link( $args ), $args );
+}
+
+function bb_get_logout_link( $args = '' ) {
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'text' => $args );
+
+	$defaults = array('text' => __('Log Out'), 'before' => '', 'after' => '', 'redirect' => '');
+	$args = wp_parse_args( $args, $defaults );
+	extract($args, EXTR_SKIP);
+
+	$query = array( 'action' => 'logout' );
+	if ( $redirect ) {
+		$query['redirect_to'] = $redirect;
+	}
+
+	$uri = esc_attr( bb_get_uri('bb-login.php', $query, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_USER_FORMS) );
+
+	return apply_filters( 'bb_get_logout_link', $before . '<a href="' . $uri . '">' . $text . '</a>' . $after, $args );
+}
+
+function bb_admin_link( $args = '' ) {
+	if ( !bb_current_user_can( 'moderate' ) )
+		return;
+	echo apply_filters( 'bb_admin_link', bb_get_admin_link( $args ), $args );
+}
+
+function bb_get_admin_link( $args = '' ) {
+	if ( !bb_current_user_can( 'moderate' ) )
+		return;
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'text' => $args );
+
+	$defaults = array('text' => __('Admin'), 'before' => '', 'after' => '');
+	$args = wp_parse_args( $args, $defaults );
+	extract($args, EXTR_SKIP);
+
+	$uri = esc_attr( bb_get_uri('bb-admin/', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN) );
+
+	return apply_filters( 'bb_get_admin_link', $before . '<a href="' . $uri . '">' . $text . '</a>' . $after, $args );
+}
+
+function bb_get_user_admin_link( $user_id = null ) { 
+	if( !$user_id || !bb_current_user_can( 'edit_user', $user_id ) )
+		return;
+
+	if( !bb_get_user_id( $user_id ) )
+		return;
+
+	$uri = bb_get_uri( 'bb-admin/users.php', array( 'action' => 'edit', 'user_id' => $user_id ) );
+
+	return apply_filters( 'bb_get_user_admin_link', $uri, $user_id );
+}
+
+function bb_profile_link( $args = '' ) {
+	echo apply_filters( 'bb_profile_link', bb_get_profile_link( $args ), $args );
+}
+
+function bb_get_profile_link( $args = '' ) {
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'text' => $args );
+	elseif ( is_numeric($args) )
+		$args = array( 'id' => $args );
+
+	$defaults = array( 'text' => __('View your profile'), 'before' => '', 'after' => '', 'id' => false );
+	$args = wp_parse_args( $args, $defaults );
+	extract($args, EXTR_SKIP);
+
+	$id = (int) $id;
+	if ( !$id )
+		$id = bb_get_current_user_info( 'id' );
+
+	return apply_filters( 'bb_get_profile_link', "$before<a href='" . esc_attr( get_user_profile_link( $id ) ) . "'>$text</a>$after", $args );
+}
+
+function bb_current_user_info( $key = '' ) {
+	if ( !$key )
+		return;
+	echo apply_filters( 'bb_current_user_info', bb_get_current_user_info( $key ), $key );
+}
+	
+
+function bb_get_current_user_info( $key = '' ) {
+	if ( !is_string($key) )
+		return;
+	if ( !$user = bb_get_current_user() ) // Not globalized
+		return false;
+
+	switch ( $key ) :
+	case '' :
+		return $user;
+		break;
+	case 'id' :
+	case 'ID' :
+		return (int) $user->ID;
+		break;
+	case 'name' :
+		return get_user_display_name( $user->ID );
+		break;
+	case 'login' :
+	case 'user_login' :
+		return get_user_name( $user->ID );
+		break;
+	case 'email' :
+	case 'user_email' :
+		return bb_get_user_email( $user->ID );
+		break;
+	case 'url' :
+	case 'uri' :
+	case 'user_url' :
+		return get_user_link( $user->ID );
+		break;
+	endswitch;
+}
+
+function bb_get_user_email( $id ) {
+	if ( !$user = bb_get_user( bb_get_user_id( $id ) ) )
+		return false;
+
+	return apply_filters( 'bb_get_user_email', $user->user_email, $id );
+}
+
+//TAGS
+function topic_tags()
+{
+	global $tags, $tag, $topic_tag_cache, $user_tags, $other_tags, $topic;
+	if ( is_array( $tags ) || bb_current_user_can( 'edit_tag_by_on', bb_get_current_user_info( 'id' ), $topic->topic_id ) ) {
+		bb_load_template( 'topic-tags.php', array('user_tags', 'other_tags', 'public_tags') );
+	}
+}
+
+function bb_tag_page_link()
+{
+	echo bb_get_tag_page_link();
+}
+
+function bb_get_tag_page_link( $context = BB_URI_CONTEXT_A_HREF )
+{
+	if ( bb_get_option( 'mod_rewrite' ) ) {
+		$r = bb_get_uri( 'tags/', null, $context );
+	} else {
+		$r = bb_get_uri( 'tags.php', null, $context );
+	}
+	return apply_filters( 'bb_get_tag_page_link', $r, $context );
+}
+
+function bb_tag_link( $tag_id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF )
+{
+	echo apply_filters( 'bb_tag_link', bb_get_tag_link( $tag_id, $page, $context ), $tag_id, $page, $context );
+}
+
+function bb_get_tag_link( $tag_id = 0, $page = 1, $context = BB_URI_CONTEXT_A_HREF )
+{
+	global $tag;
+
+	if ( $tag_id ) {
+		if ( is_object( $tag_id ) ) {
+			$_tag = $tag_id;
+		} else {
+			$_tag = bb_get_tag( $tag_id );
+		}
+	} else {
+		$_tag =& $tag;
+	}
+
+	if ( !is_object( $_tag ) ) {
+		return '';
+	}
+
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+
+	if ( bb_get_option( 'mod_rewrite' ) ) {
+		$page = (1 < $page) ? '/page/' . $page : '';
+		$r = bb_get_uri( 'tags/' . $_tag->tag . $page, null, $context );
+	} else {
+		$query = array(
+			'tag' => $_tag->tag,
+			'page' => ( 1 < $page ) ? $page : false
+		);
+		$r = bb_get_uri( 'tags.php', $query, $context );
+	}
+
+	return apply_filters( 'bb_get_tag_link', $r, $_tag->tag, $page, $context );
+}
+
+function bb_tag_link_base()
+{
+	echo bb_get_tag_link_base();
+}
+
+function bb_get_tag_link_base()
+{
+	return bb_get_tag_page_link() . ( bb_get_option( 'mod_rewrite' ) ? '' : '?tag=' );
+}
+
+function bb_tag_name( $tag_id = 0 )
+{
+	echo esc_html( bb_get_tag_name( $tag_id ) );
+}
+
+function bb_get_tag_name( $tag_id = 0 ) {
+	global $tag;
+
+	if ( $tag_id ) {
+		if ( is_object( $tag_id ) ) {
+			$_tag = $tag_id;
+		} else {
+			$_tag = bb_get_tag( $tag_id );
+		}
+	} else {
+		$_tag =& $tag;
+	}
+
+	if ( !is_object( $_tag ) ) {
+		return '';
+	}
+
+	return $_tag->raw_tag;
+}
+
+function bb_tag_posts_rss_link( $tag_id = 0, $context = 0 )
+{
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+
+	echo apply_filters( 'tag_posts_rss_link', bb_get_tag_posts_rss_link( $tagid, $context ), $tag_id, $context );
+}
+
+function bb_get_tag_posts_rss_link( $tag_id = 0, $context = 0 )
+{
+	global $tag;
+
+	if ( $tag_id ) {
+		if ( is_object( $tag_id ) ) {
+			$_tag = $tag_id;
+		} else {
+			$_tag = bb_get_tag( $tag_id );
+		}
+	} else {
+		$_tag =& $tag;
+	}
+
+	if ( !is_object( $_tag ) ) {
+		return '';
+	}
+
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+
+	if ( bb_get_option( 'mod_rewrite' ) ) {
+		$link = bb_get_uri( 'rss/tags/' . $_tag->tag, null, $context );
+	} else {
+		$link = bb_get_uri( 'rss.php', array( 'tag' => $_tag->tag ), $context );
+	}
+
+	return apply_filters( 'get_tag_posts_rss_link', $link, $tag_id, $context );
+}
+
+function bb_tag_topics_rss_link( $tag_id = 0, $context = 0 )
+{
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+
+	echo apply_filters( 'tag_topics_rss_link', bb_get_tag_topics_rss_link( $tag_id, $context ), $tag_id, $context );
+}
+
+function bb_get_tag_topics_rss_link( $tag_id = 0, $context = 0 )
+{
+	global $tag;
+
+	if ( $tag_id ) {
+		if ( is_object( $tag_id ) ) {
+			$_tag = $tag_id;
+		} else {
+			$_tag = bb_get_tag( $tag_id );
+		}
+	} else {
+		$_tag =& $tag;
+	}
+
+	if ( !is_object( $_tag ) ) {
+		return '';
+	}
+
+	if ( !$context || !is_integer( $context ) ) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+
+	if ( bb_get_option( 'mod_rewrite' ) ) {
+		$link = bb_get_uri( 'rss/tags/' . $_tag->tag . '/topics', null, $context );
+	} else {
+		$link = bb_get_uri( 'rss.php', array('tag' => $_tag->tag, 'topics' => 1 ), $context );
+	}
+
+	return apply_filters( 'get_tag_topics_rss_link', $link, $tag_id, $context );
+}
+
+function bb_list_tags( $args = null )
+{
+	$defaults = array(
+		'tags' => false,
+		'format' => 'list',
+		'topic' => 0,
+		'list_id' => 'tags-list'
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( !$topic = get_topic( get_topic_id( $topic ) ) ) {
+		return false;
+	}
+
+	if ( !is_array( $tags ) ) {
+		$tags = bb_get_topic_tags( $topic->topic_id );
+	}
+
+	if ( !$tags ) {
+		return false;
+	}
+
+	$list_id = esc_attr( $list_id );
+
+	$r = '';
+	switch ( strtolower( $format ) ) {
+		case 'table' :
+			break;
+
+		case 'list' :
+		default :
+			$args['format'] = 'list';
+			$r .= '<ul id="' . $list_id . '" class="tags-list list:tag">' . "\n";
+			foreach ( $tags as $tag ) {
+				$r .= _bb_list_tag_item( $tag, $args );
+			}
+			$r .= '</ul>';
+			break;
+	}
+
+	echo $r;
+}
+
+function _bb_list_tag_item( $tag, $args )
+{
+	$url = esc_url( bb_get_tag_link( $tag ) );
+	$name = esc_html( bb_get_tag_name( $tag ) );
+	if ( 'list' == $args['format'] ) {
+		$id = 'tag-' . $tag->tag_id . '_' . $tag->user_id;
+		return "\t" . '<li id="' . $id . '"' . get_alt_class( 'topic-tags' ) . '><a href="' . $url . '" rel="tag">' . $name . '</a> ' . bb_get_tag_remove_link( array( 'tag' => $tag, 'list_id' => $args['list_id'] ) ) . '</li>' . "\n";
+	}
+}
+	
+function tag_form( $args = null )
+{
+	$defaults = array( 'topic' => 0, 'submit' => __('Add &raquo;'), 'list_id' => 'tags-list' );
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( !$topic = get_topic( get_topic_id( $topic ) ) ) {
+		return false;
+	}
+
+	if ( !bb_current_user_can( 'edit_tag_by_on', bb_get_current_user_info( 'id' ), $topic->topic_id ) ) {
+		return false;
+	}
+
+	global $page;
+?>
+
+<form id="tag-form" method="post" action="<?php bb_uri('tag-add.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN); ?>" class="add:<?php echo esc_attr( $list_id ); ?>:">
+	<p>
+		<input name="tag" type="text" id="tag" />
+		<input type="hidden" name="id" value="<?php echo $topic->topic_id; ?>" />
+		<input type="hidden" name="page" value="<?php echo $page; ?>" />
+		<?php bb_nonce_field( 'add-tag_' . $topic->topic_id ); ?>
+		<input type="submit" name="submit" id="tagformsub" value="<?php echo esc_attr( $submit ); ?>" />
+	</p>
+</form>
+
+<?php
+}
+
+function manage_tags_forms()
+{
+	global $tag;
+	if ( !bb_current_user_can( 'manage_tags' ) ) {
+		return false;
+	}
+
+	$form  = '<ul id="manage-tags">' . "\n";
+	$form .= '<li id="tag-rename">' . __('Rename tag:') . "\n\t";
+	$form .= '<form method="post" action="' . bb_get_uri( 'bb-admin/tag-rename.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ) . '"><div>' . "\n\t";
+	$form .= '<input type="text" name="tag" size="10" maxlength="30" />' . "\n\t";
+	$form .= '<input type="hidden" name="id" value="' . $tag->tag_id . '" />' . "\n\t";
+	$form .= "<input type='submit' name='Submit' value='" . __('Rename') . "' />\n\t";
+	echo $form;
+	bb_nonce_field( 'rename-tag_' . $tag->tag_id );
+	echo "\n\t</div></form>\n  </li>\n ";
+	$form  = "<li id='tag-merge'>" . __('Merge this tag into:') . "\n\t";
+	$form .= "<form method='post' action='" . bb_get_uri('bb-admin/tag-merge.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN) . "'><div>\n\t";
+	$form .= "<input type='text' name='tag' size='10' maxlength='30' />\n\t";
+	$form .= "<input type='hidden' name='id' value='$tag->tag_id' />\n\t";
+	$form .= "<input type='submit' name='Submit' value='" . __('Merge') . "' ";
+	$form .= 'onclick="return confirm(\'' . esc_js( sprintf(__('Are you sure you want to merge the "%s" tag into the tag you specified? This is permanent and cannot be undone.'), $tag->raw_tag) ) . "');\" />\n\t";
+	echo $form;
+	bb_nonce_field( 'merge-tag_' . $tag->tag_id );
+	echo "\n\t</div></form>\n  </li>\n ";
+	$form  = "<li id='tag-destroy'>" . __('Destroy tag:') . "\n\t";
+	$form .= "<form method='post' action='" . bb_get_uri('bb-admin/tag-destroy.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN) . "'><div>\n\t";
+	$form .= "<input type='hidden' name='id' value='$tag->tag_id' />\n\t";
+	$form .= "<input type='submit' name='Submit' value='" . __('Destroy') . "' ";
+	$form .= 'onclick="return confirm(\'' . esc_js( sprintf(__('Are you sure you want to destroy the "%s" tag? This is permanent and cannot be undone.'), $tag->raw_tag) ) . "');\" />\n\t";
+	echo $form;
+	bb_nonce_field( 'destroy-tag_' . $tag->tag_id );
+	echo "\n\t</div></form>\n  </li>\n</ul>";
+}
+
+function bb_tag_remove_link( $args = null ) {
+	echo bb_get_tag_remove_link( $args );
+}
+
+function bb_get_tag_remove_link( $args = null ) {
+	if ( is_scalar($args) || is_object( $args ) )
+		$args = array( 'tag' => $args );
+	$defaults = array( 'tag' => 0, 'topic' => 0, 'list_id' => 'tags-list' );
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( is_object( $tag ) && isset( $tag->tag_id ) ); // [sic]
+	elseif ( !$tag = bb_get_tag( bb_get_tag_id( $tag ) ) )
+		return false;
+	if ( !$topic = get_topic( get_topic_id( $topic ) ) )
+		return false;
+	if ( !bb_current_user_can( 'edit_tag_by_on', $tag->user_id, $topic->topic_id ) )
+		return false;
+	$url = bb_get_uri('tag-remove.php', array('tag' => $tag->tag_id, 'user' => $tag->user_id, 'topic' => $topic->topic_id) );
+	$url = esc_url( bb_nonce_url( $url, 'remove-tag_' . $tag->tag_id . '|' . $topic->topic_id) );
+	$title = esc_attr__( 'Remove this tag' );
+	$list_id = esc_attr( $list_id );
+	return "[<a href='$url' class='delete:$list_id:tag-{$tag->tag_id}_{$tag->user_id}' title='$title'>&times;</a>]";
+}
+
+function bb_tag_heat_map( $args = '' ) {
+	$defaults = array( 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'limit' => 40, 'format' => 'flat' );
+	$args = wp_parse_args( $args, $defaults );
+
+	if ( 1 < $fn = func_num_args() ) : // For back compat
+		$args['smallest'] = func_get_arg(0);
+		$args['largest']  = func_get_arg(1);
+		$args['unit']     = 2 < $fn ? func_get_arg(2) : $unit;
+		$args['limit']    = 3 < $fn ? func_get_arg(3) : $limit;
+	endif;
+
+	extract($args, EXTR_SKIP);
+
+	$tags = bb_get_top_tags( array( 'number' => $limit ) );
+
+	if ( empty($tags) )
+		return;
+
+	$r = bb_get_tag_heat_map( $tags, $args );
+	echo apply_filters( 'tag_heat_map', $r, $args );
+}
+
+function bb_get_tag_heat_map( $tags, $args = '' ) {
+	$defaults = array( 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'limit' => 45, 'format' => 'flat' );
+	$args = wp_parse_args( $args, $defaults );
+	extract($args, EXTR_SKIP);
+
+	if ( !$tags )
+		return;
+
+	foreach ( (array) $tags as $tag ) {
+		$counts{$tag->raw_tag} = $tag->tag_count;
+		$taglinks{$tag->raw_tag} = bb_get_tag_link( $tag );
+	}
+
+	$min_count = min($counts);
+	$spread = max($counts) - $min_count;
+	if ( $spread <= 0 )
+		$spread = 1;
+	$fontspread = $largest - $smallest;
+	if ( $fontspread <= 0 )
+		$fontspread = 1;
+	$fontstep = $fontspread / $spread;
+
+	do_action_ref_array( 'sort_tag_heat_map', array(&$counts) );
+
+	$a = array();
+
+	foreach ( $counts as $tag => $count ) {
+		$taglink = esc_attr($taglinks{$tag});
+		$tag = str_replace(' ', '&nbsp;', esc_html( $tag ));
+		$fontsize = round( $smallest + ( ( $count - $min_count ) * $fontstep ), 1 );
+		$a[] = "<a href='$taglink' title='" . esc_attr( sprintf( __('%d topics'), $count ) ) . "' rel='tag' style='font-size:$fontsize$unit;'>$tag</a>";
+	}
+
+	switch ( $format ) :
+	case 'array' :
+		$r =& $a;
+		break;
+	case 'list' :
+		$r = "<ul class='bb-tag-heat-map'>\n\t<li>";
+		$r .= join("</li>\n\t<li>", $a);
+		$r .= "</li>\n</ul>\n";
+		break;
+	default :
+		$r = join("\n", $a);
+		break;
+	endswitch;
+
+	return apply_filters( 'bb_get_tag_heat_map', $r, $tags, $args );
+}
+
+function bb_sort_tag_heat_map( &$tag_counts ) {
+	uksort($tag_counts, 'strnatcasecmp');
+}
+
+function tag_pages( $args = null )
+{
+	$defaults = array( 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page, $tagged_topic_count;
+	if ( $pages = apply_filters( 'tag_pages', get_page_number_links( $page, $tagged_topic_count ) ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+function bb_forum_dropdown( $args = '' ) {
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'callback' => $args );
+	if ( 1 < func_num_args() )
+		$args['callback_args'] = func_get_arg(1);
+	echo bb_get_forum_dropdown( $args );
+}
+
+function bb_get_forum_dropdown( $args = '' ) {
+	$defaults = array( 'callback' => false, 'callback_args' => false, 'id' => 'forum_id', 'none' => false, 'selected' => false, 'tab' => false, 'hierarchical' => 1, 'depth' => 0, 'child_of' => 0, 'disable_categories' => 1, 'options_only' => false );
+	if ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array( 'callback' => $args );
+	if ( 1 < func_num_args() )
+		$args['callback_args'] = func_get_arg(1);
+
+	$args = wp_parse_args( $args, $defaults );
+
+	extract($args, EXTR_SKIP);
+
+	if ( !bb_forums( $args ) )
+		return;
+
+	global $forum_id, $forum;
+	$old_global = $forum;
+
+	$name = esc_attr( $id );
+	$id = str_replace( '_', '-', $name );
+	$tab = (int) $tab;
+
+	if ( $none && 1 == $none )
+		$none = __('- None -');
+
+	$r = '';
+	if ( !$options_only ) {
+		if ( $tab ) {
+			$tab = ' tabindex="' . $tab . '"';
+		} else {
+			$tab = '';
+		}
+		$r .= '<select name="' . $name . '" id="' . $id . '"' . $tab . '>' . "\n";
+	}
+	if ( $none )
+		$r .= "\n" . '<option value="0">' . $none . '</option>' . "\n";
+
+	$no_option_selected = true;
+	$options = array();
+	while ( $depth = bb_forum() ) :
+		global $forum; // Globals + References = Pain
+		$pad_left = str_repeat( '&nbsp;&nbsp;&nbsp;', $depth - 1 );
+		if ( $disable_categories && isset($forum->forum_is_category) && $forum->forum_is_category ) {
+			$options[] = array(
+				'value' => 0,
+				'display' => $pad_left . $forum->forum_name,
+				'disabled' => true,
+				'selected' => false
+			);
+			continue;
+		}
+		$_selected = false;
+		if ( (!$selected && $forum_id == $forum->forum_id) || $selected == $forum->forum_id ) {
+			$_selected = true;
+			$no_option_selected = false;
+		}
+		$options[] = array(
+			'value' => $forum->forum_id,
+			'display' => $pad_left . $forum->forum_name,
+			'disabled' => false,
+			'selected' => $_selected
+		);
+	endwhile;
+
+	if ( 1 === count( $options ) && !$none ) {
+		foreach ( $options as $option_index => $option_value ) {
+			if ( $option_value['disabled'] ) {
+				return;
+			}
+			return '<input type="hidden" name="' . $name . '" id="' . $id . '" value="' . esc_attr( $option_value['value'] ) . '" /><span>' . esc_html( $option_value['display'] ) . '</span>';
+		}
+	}
+
+	foreach ($options as $option_index => $option_value) {
+		if (!$none && !$selected && $no_option_selected && !$option_value['disabled']) {
+			$option_value['selected'] = true;
+			$no_option_selected = false;
+		}
+		$option_disabled = $option_value['disabled'] ? ' disabled="disabled"' : '';
+		$option_selected = $option_value['selected'] ? ' selected="selected"' : '';
+		$r .= "\n" . '<option value="' . esc_attr( $option_value['value'] ) . '"' . $option_disabled . $option_selected . '>' . esc_html( $option_value['display'] ) . '</option>' . "\n";
+	}
+	
+	$forum = $old_global;
+	if ( !$options_only )
+		$r .= '</select>' . "\n";
+
+	return $r;
+}
+
+//FAVORITES
+function favorites_link( $user_id = 0 ) {
+	echo apply_filters( 'favorites_link', get_favorites_link( $user_id ) );
+}
+
+function get_favorites_link( $user_id = 0 ) {
+	if ( !$user_id )
+		$user_id = bb_get_current_user_info( 'id' );
+	return apply_filters( 'get_favorites_link', get_profile_tab_link($user_id, 'favorites'), $user_id );
+}
+
+function user_favorites_link($add = array(), $rem = array(), $user_id = 0) {
+	global $topic, $bb_current_user;
+	if ( empty($add) || !is_array($add) )
+		$add = array('mid' => __('Add this topic to your favorites'), 'post' => __(' (%?%)'));
+	if ( empty($rem) || !is_array($rem) )
+		$rem = array( 'pre' => __('This topic is one of your %favorites% ['), 'mid' => __('&times;'), 'post' => __(']'));
+	if ( $user_id ) :
+		if ( !bb_current_user_can( 'edit_favorites_of', (int) $user_id ) )
+			return false;
+		if ( !$user = bb_get_user( bb_get_user_id( $user_id ) ) ) :
+			return false;
+		endif;
+	else :
+		if ( !bb_current_user_can('edit_favorites') )
+			return false;
+		$user =& $bb_current_user->data;
+	endif;
+
+        $url = esc_url( get_favorites_link( $user_id ) );
+	if ( $is_fav = is_user_favorite( $user->ID, $topic->topic_id ) ) :
+		$rem = preg_replace('|%(.+)%|', "<a href='$url'>$1</a>", $rem);
+		$favs = array('fav' => '0', 'topic_id' => $topic->topic_id);
+		$pre  = ( is_array($rem) && isset($rem['pre'])  ) ? $rem['pre']  : '';
+		$mid  = ( is_array($rem) && isset($rem['mid'])  ) ? $rem['mid']  : ( is_string($rem) ? $rem : '' );
+		$post = ( is_array($rem) && isset($rem['post']) ) ? $rem['post'] : '';
+	elseif ( false === $is_fav ) :
+		$add = preg_replace('|%(.+)%|', "<a href='$url'>$1</a>", $add);
+		$favs = array('fav' => '1', 'topic_id' => $topic->topic_id);
+		$pre  = ( is_array($add) && isset($add['pre'])  ) ? $add['pre']  : '';
+		$mid  = ( is_array($add) && isset($add['mid'])  ) ? $add['mid']  : ( is_string($add) ? $add : '' );
+		$post = ( is_array($add) && isset($add['post']) ) ? $add['post'] : '';
+	endif;
+
+	$url = esc_url(  bb_nonce_url( add_query_arg( $favs, get_favorites_link( $user_id ) ), 'toggle-favorite_' . $topic->topic_id ) );
+
+	if (  !is_null($is_fav) )
+		echo "<span id='favorite-$topic->topic_id'>$pre<a href='$url' class='dim:favorite-toggle:favorite-$topic->topic_id:is-favorite'>$mid</a>$post</span>";
+}
+
+function favorites_rss_link( $id = 0, $context = 0 ) {
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	echo apply_filters('favorites_rss_link', get_favorites_rss_link( $id, $context ), $context, $id);
+}
+
+function get_favorites_rss_link( $id = 0, $context = 0 ) {
+	$user = bb_get_user( bb_get_user_id( $id ) );
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_FEED;
+	}
+	
+	$rewrite = bb_get_option( 'mod_rewrite' );
+	if ( $rewrite ) {
+		if ( $rewrite === 'slugs' ) {
+			$column = 'user_nicename';
+		} else {
+			$column = 'ID';
+		}
+		$link = bb_get_uri('rss/profile/' . $user->$column, null, $context);
+	} else {
+		$link = bb_get_uri('rss.php', array('profile' => $user->ID), $context);
+	}
+	return apply_filters( 'get_favorites_rss_link', $link, $user->ID, $context );
+}
+
+function favorites_pages( $args = null )
+{
+	$defaults = array( 'before' => '', 'after' => '' );
+	$args = wp_parse_args( $args, $defaults );
+
+	global $page, $user, $favorites_total;
+	if ( $pages = apply_filters( 'favorites_pages', get_page_number_links( $page, $favorites_total ), $user->user_id ) ) {
+		echo $args['before'] . $pages . $args['after'];
+	}
+}
+
+//SUBSCRIPTION
+
+/** 
+ * Checks if subscription is enabled.
+ * 
+ * @since 1.1 
+ *  
+ * @return bool is subscription enabled or not 
+ */
+function bb_is_subscriptions_active() { 
+	return (bool) bb_get_option( 'enable_subscriptions' ); 
+}
+
+/**
+ * Checks if user is subscribed to current topic.
+ *
+ * @since 1.1
+ *
+ * @return bool is user subscribed or not
+ */
+function bb_is_user_subscribed( $args = null ) {
+	global $bbdb;
+	
+	$defaults = array(
+		'user_id'  => bb_is_topic_edit() ? bb_get_user_id( get_post_author_id() ) : bb_get_current_user_info( 'id' ),
+		'topic_id' => get_topic_id()
+	);
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+	
+	$there = $bbdb->get_var( $bbdb->prepare( "SELECT `$bbdb->term_relationships`.`object_id`
+				FROM $bbdb->term_relationships, $bbdb->term_taxonomy, $bbdb->terms
+				WHERE `$bbdb->term_relationships`.`object_id` = %d
+				AND `$bbdb->term_relationships`.`term_taxonomy_id` = `$bbdb->term_taxonomy`.`term_taxonomy_id`
+				AND `$bbdb->term_taxonomy`.`term_id` = `$bbdb->terms`.`term_id`
+				AND `$bbdb->term_taxonomy`.`taxonomy` = 'bb_subscribe'
+				AND `$bbdb->terms`.`slug` = 'topic-%d'",
+				$user_id, $topic_id ) );
+	
+	$there = apply_filters( 'bb_is_user_subscribed', $there, $args );
+	
+	if ( $there )
+		return true;
+	
+	return false;
+}
+
+/**
+ * Outputs the subscribe/unsubscibe link.
+ *
+ * Checks if user is subscribed and outputs link based on status.
+ *
+ * @since 1.1
+ */
+function bb_user_subscribe_link() {
+	$topic_id = get_topic_id();
+
+	if ( !bb_is_user_logged_in() )
+		return false;
+
+	if ( bb_is_user_subscribed() )
+		echo '<li id="subscription-toggle"><a href="'. bb_nonce_url( bb_get_uri( null, array( 'doit' => 'bb-subscribe', 'topic_id' => $topic_id, 'and' => 'remove' ) ), 'toggle-subscribe_' . $topic_id ) .'">' . apply_filters( 'bb_user_subscribe_link_unsubscribe', __( 'Unsubscribe from Topic' ) ) . '</a></li>';
+	else
+		echo '<li id="subscription-toggle"><a href="'. bb_nonce_url( bb_get_uri( null, array( 'doit' => 'bb-subscribe', 'topic_id' => $topic_id, 'and' => 'add' ) ), 'toggle-subscribe_' . $topic_id ) .'">' . apply_filters( 'bb_user_subscribe_link_subscribe', __( 'Subscribe to Topic' ) ) . '</a></li>';
+
+}
+
+/**
+ * Outputs the post form subscription checkbox.
+ *
+ * Checks if user is subscribed and outputs checkbox based on status.
+ *
+ * @since 1.1
+ */
+function bb_user_subscribe_checkbox( $args = null ) {
+	
+	if ( !bb_is_user_logged_in() )
+		return false;
+
+	$is_current = false;
+	$defaults   = array( 'tab' => false );
+	$args       = wp_parse_args( $args, $defaults );
+	$tab        = $args['tab'] !== false ? ' tabindex="' . $args['tab'] . '"' : '';
+
+	// Change subscription checkbox message if current or moderating
+	if ( isset( $_GET['new'] ) || ( $is_current = ( bb_get_user_id( get_post_author_id() ) == bb_get_current_user_info( 'id' ) ) ) )
+		$text = __( 'Notify me of follow-up posts via email' );
+	else
+		$text = __( 'This user should be notified of follow-up posts via email' );
+
+	echo '
+	<label for="subscription_checkbox">
+		<input name="subscription_checkbox" id="subscription_checkbox" type="checkbox" value="subscribe" ' . checked( true, bb_is_user_subscribed(), false ) . $tab . ' />
+		' .  apply_filters( 'bb_user_subscribe_checkbox_label', $text, (bool) $is_current ) . '
+	</label>';
+
+}
+
+//VIEWS
+function view_name( $view = '' ) { // Filtration should be done at bb_register_view()
+	echo get_view_name( $view );
+}
+
+function get_view_name( $_view = '' ) {
+	global $view, $bb_views;
+	if ( $_view )
+		$v = bb_slug_sanitize($_view);
+	else
+		$v =& $view;
+
+	if ( isset($bb_views[$v]) )
+		return $bb_views[$v]['title'];
+}
+
+function view_pages() {
+	global $page, $view_count;
+	echo apply_filters( 'view_pages', get_page_number_links( $page, $view_count ) );
+}
+
+function view_link( $_view = false, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	echo get_view_link( $_view, $page, $context );
+}
+
+function get_view_link( $_view = false, $page = 1, $context = BB_URI_CONTEXT_A_HREF ) {
+	global $view, $bb_views;
+	if ( $_view )
+		$v = bb_slug_sanitize($_view);
+	else
+		$v =& $view;
+	
+	if (!$context || !is_integer($context)) {
+		$context = BB_URI_CONTEXT_A_HREF;
+	}
+	
+	if ( !array_key_exists($v, $bb_views) )
+		return bb_get_uri(null, null, $context);
+	if ( bb_get_option('mod_rewrite') ) {
+		$page = ( 1 < $page ) ? '/page/' . $page : '';
+		$link = bb_get_uri('view/' . $v . $page, null, $context);
+	} else {
+		$query = array(
+			'view' => $v,
+			'page' => ( 1 < $page ) ? $page : false,
+		);
+		$link = bb_get_uri('view.php', $query, $context);
+	}
+
+	return apply_filters( 'get_view_link', $link, $v, $page, $context );
+}
+
+function _bb_parse_time_function_args( $args ) {
+	if ( is_numeric($args) )
+		$args = array('id' => $args);
+	elseif ( $args && is_string($args) && false === strpos($args, '=') )
+		$args = array('format' => $args);
+
+	$defaults = array( 'id' => 0, 'format' => 'since', 'more' => 0, 'localize' => true );
+	return wp_parse_args( $args, $defaults );
+}
+
+function _bb_time_function_return( $time, $args ) {
+	$time = bb_gmtstrtotime( $time );
+
+	switch ( $format = $args['format'] ) :
+	case 'since' :
+		return bb_since( $time, $args['more'] );
+		break;
+	case 'timestamp' :
+		$format = 'U';
+		break;
+	case 'mysql' :
+		$format = 'Y-m-d H:i:s';
+		break;
+	case 'datetime' :
+		$format = bb_get_option( 'datetime_format' );
+		break;
+	endswitch;
+
+	return $args['localize'] ? bb_gmdate_i18n( $format, $time ) : gmdate( $format, $time );
+}
+
+function bb_template_scripts() {
+	if ( bb_is_topic() && bb_is_user_logged_in() )
+		wp_enqueue_script( 'topic' );
+	elseif ( bb_is_profile() && bb_is_user_logged_in() ) {
+		global $self;
+		if ($self == 'profile-edit.php') {
+			wp_enqueue_script( 'profile-edit' );
+		}
+	}
+}
+
+if ( !function_exists( 'checked' ) ) :
+/**
+ * Outputs the html checked attribute.
+ *
+ * Compares the first two arguments and if identical marks as checked
+ *
+ * @since 1.1
+ *
+ * @param mixed $checked One of the values to compare
+ * @param mixed $current (true) The other value to compare if not just true
+ * @param bool $echo Whether to echo or just return the string
+ * @return string html attribute or empty string
+ */
+function checked( $checked, $current = true, $echo = true ) {
+	return __checked_selected_helper( $checked, $current, $echo, 'checked' );
+}
+endif;
+
+if ( !function_exists( 'selected' ) ) :
+/**
+ * Outputs the html selected attribute.
+ *
+ * Compares the first two arguments and if identical marks as selected
+ *
+ * @since 1.1
+ *
+ * @param mixed selected One of the values to compare
+ * @param mixed $current (true) The other value to compare if not just true
+ * @param bool $echo Whether to echo or just return the string
+ * @return string html attribute or empty string
+ */
+function selected( $selected, $current = true, $echo = true ) {
+	return __checked_selected_helper( $selected, $current, $echo, 'selected' );
+}
+endif;
+
+if ( !function_exists( 'disabled' ) ) :
+/**
+ * Outputs the html disabled attribute.
+ *
+ * Compares the first two arguments and if identical marks as disabled
+ *
+ * @since 1.1
+ *
+ * @param mixed $disabled One of the values to compare
+ * @param mixed $current (true) The other value to compare if not just true
+ * @param bool $echo Whether to echo or just return the string
+ * @return string html attribute or empty string
+ */
+function disabled( $disabled, $current = true, $echo = true ) {
+	return __checked_selected_helper( $disabled, $current, $echo, 'disabled' );
+}
+endif;
+
+if ( !function_exists( '__checked_selected_helper' ) ) :
+/**
+ * Private helper function for checked, selected, and disabled.
+ *
+ * Compares the first two arguments and if identical marks as $type
+ *
+ * @since 1.1
+ * @access private
+ *
+ * @param any $helper One of the values to compare
+ * @param any $current (true) The other value to compare if not just true
+ * @param bool $echo Whether to echo or just return the string
+ * @param string $type The type of checked|selected|disabled we are doing
+ * @return string html attribute or empty string
+ */
+function __checked_selected_helper( $helper, $current, $echo, $type ) {
+	if ( (string) $helper === (string) $current )
+		$result = " $type='$type'";
+	else
+		$result = '';
+
+	if ( $echo )
+		echo $result;
+
+	return $result;
+}
+endif;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topic-tags.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topic-tags.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topic-tags.php	(revision 5057)
@@ -0,0 +1,514 @@
+<?php
+
+/* Tags */
+
+/**
+ * bb_add_topic_tag() - Adds a single tag to a topic.
+ *
+ * @param int $topic_id
+ * @param string $tag The (unsanitized) full name of the tag to be added
+ * @return int|bool The TT_ID of the new bb_topic_tag or false on failure
+ */
+function bb_add_topic_tag( $topic_id, $tag ) {
+	$tt_ids = bb_add_topic_tags( $topic_id, $tag );
+	if ( is_array( $tt_ids ) )
+		return $tt_ids[0];
+	return false;
+}
+
+/**
+ * bb_add_topic_tag() - Adds a multiple tags to a topic.
+ *
+ * @param int $topic_id
+ * @param array|string $tags The (unsanitized) full names of the tag to be added.  CSV or array.
+ * @return array|bool The TT_IDs of the new bb_topic_tags or false on failure
+ */
+function bb_add_topic_tags( $topic_id, $tags ) {
+	global $wp_taxonomy_object;
+	$topic_id = (int) $topic_id;
+	if ( !$topic = get_topic( $topic_id ) )
+		return false;
+	if ( !bb_current_user_can( 'add_tag_to', $topic_id ) )
+		return false;
+
+	$user_id = bb_get_current_user_info( 'id' );
+
+	$tags = apply_filters( 'bb_add_topic_tags', $tags, $topic_id );
+
+	if ( !is_array( $tags ) )
+		$tags = explode(',', (string) $tags);
+
+	$tt_ids = $wp_taxonomy_object->set_object_terms( $topic->topic_id, $tags, 'bb_topic_tag', array( 'append' => true, 'user_id' => $user_id ) );
+
+	if ( is_array($tt_ids) ) {
+		global $bbdb;
+		$bbdb->query( $bbdb->prepare(
+			"UPDATE $bbdb->topics SET tag_count = tag_count + %d WHERE topic_id = %d", count( $tt_ids ), $topic->topic_id
+		) );
+		wp_cache_delete( $topic->topic_id, 'bb_topic' );
+		foreach ( $tt_ids as $tt_id )
+			do_action('bb_tag_added', $tt_id, $user_id, $topic_id);
+		return $tt_ids;
+	}
+	return false;
+}
+
+/**
+ * bb_create_tag() - Creates a single bb_topic_tag.
+ *
+ * @param string $tag The (unsanitized) full name of the tag to be created
+ * @return int|bool The TT_ID of the new bb_topic_tags or false on failure
+ */
+function bb_create_tag( $tag ) {
+	global $wp_taxonomy_object;
+
+	if ( list($term_id, $tt_id) = $wp_taxonomy_object->is_term( $tag, 'bb_topic_tag' ) )
+		return $tt_id;
+
+	list($term_id, $tt_id) = $wp_taxonomy_object->insert_term( $tag, 'bb_topic_tag' );
+
+	if ( is_wp_error($term_id) || is_wp_error($tt_id) || !$tt_id )
+		return false;
+
+	return $tt_id;
+}
+
+/**
+ * bb_remove_topic_tag() - Removes a single bb_topic_tag by a user from a topic.
+ *
+ * @param int $tt_id The TT_ID of the bb_topic_tag to be removed
+ * @param int $user_id
+ * @param int $topic_id
+ * @return array|false The TT_IDs of the users bb_topic_tags on that topic or false on failure
+ */
+function bb_remove_topic_tag( $tt_id, $user_id, $topic_id ) {
+	global $wp_taxonomy_object;
+	$tt_id   = (int) $tt_id;
+	$user_id  = (int) $user_id;
+	$topic_id = (int) $topic_id;
+	if ( !$topic = get_topic( $topic_id ) )
+		return false;
+	if ( !bb_current_user_can( 'edit_tag_by_on', $user_id, $topic_id ) )
+		return false;
+
+	$_tag = bb_get_tag( $tt_id );
+
+	do_action('bb_pre_tag_removed', $tt_id, $user_id, $topic_id);
+	$currents = $wp_taxonomy_object->get_object_terms( $topic_id, 'bb_topic_tag', array( 'user_id' => $user_id, 'fields' => 'all' ) );
+	if ( !is_array( $currents ) )
+		return false;
+
+	$found_tag_to_remove = false;
+	$current_tag_term_ids = array();
+	foreach ( $currents as $current ) {
+		if ( $current->term_taxonomy_id == $tt_id ) {
+			$found_tag_to_remove = true;
+			continue;
+		}
+		$current_tag_term_ids[] = $current->term_id;
+	}
+
+	if ( !$found_tag_to_remove )
+		return false;
+
+	$current_tag_term_ids = array_map( 'intval', $current_tag_term_ids );
+
+	$tt_ids = $wp_taxonomy_object->set_object_terms( $topic_id, array_values($current_tag_term_ids), 'bb_topic_tag', array( 'user_id' => $user_id ) );
+	if ( is_array( $tt_ids ) ) {
+		global $bbdb;
+		$bbdb->query( $bbdb->prepare(
+			"UPDATE $bbdb->topics SET tag_count = %d WHERE topic_id = %d", count( $tt_ids ), $topic_id
+		) );
+		wp_cache_delete( $topic_id, 'bb_topic' );
+
+		// Count is updated at set_object_terms()
+		if ( $_tag && 2 > $_tag->tag_count ) {
+			bb_destroy_tag( $_tag->term_taxonomy_id );
+		}
+	} elseif ( is_wp_error( $tt_ids ) ) {
+		return false;
+	}
+	return $tt_ids;
+}
+
+/**
+ * bb_remove_topic_tag() - Removes all bb_topic_tags from a topic.
+ *
+ * @param int $topic_id
+ * @return bool
+ */
+function bb_remove_topic_tags( $topic_id ) {
+	global $wp_taxonomy_object;
+	$topic_id = (int) $topic_id;
+	if ( !$topic_id || !get_topic( $topic_id ) )
+		return false;
+
+	$_tags = bb_get_topic_tags( $topic_id );
+
+	do_action( 'bb_pre_remove_topic_tags', $topic_id );
+
+	$wp_taxonomy_object->delete_object_term_relationships( $topic_id, 'bb_topic_tag' );
+
+	global $bbdb;
+	$bbdb->query( $bbdb->prepare(
+		"UPDATE $bbdb->topics SET tag_count = 0 WHERE topic_id = %d", $topic_id
+	) );
+	wp_cache_delete( $topic_id, 'bb_topic' );
+
+	if ( $_tags ) {
+		foreach ( $_tags as $_tag ) {
+			// Count is updated at delete_object_term_relationships()
+			if ( 2 > $_tag->tag_count ) {
+				bb_destroy_tag( $_tag->term_taxonomy_id );
+			}
+		}
+	}
+
+	return true;
+}
+
+/**
+ * bb_destroy_tag() - Completely removes a bb_topic_tag.
+ *
+ * @param int $tt_id The TT_ID of the tag to destroy
+ * @return bool
+ */
+function bb_destroy_tag( $tt_id, $recount_topics = true ) {
+	global $wp_taxonomy_object;
+
+	$tt_id = (int) $tt_id;
+
+	if ( !$tag = bb_get_tag( $tt_id ) )
+		return false;
+
+	if ( is_wp_error($tag) )
+		return false;
+
+	$topic_ids = bb_get_tagged_topic_ids( $tag->term_id );
+
+	$return = $wp_taxonomy_object->delete_term( $tag->term_id, 'bb_topic_tag' );
+
+	if ( is_wp_error($return) )
+		return false;
+
+	if ( !is_wp_error( $topic_ids ) && is_array( $topic_ids ) ) {
+		global $bbdb;
+		$bbdb->query(
+			"UPDATE $bbdb->topics SET tag_count = tag_count - 1 WHERE topic_id IN (" . join( ',', $topic_ids ) . ")"
+		);
+		foreach ( $topic_ids as $topic_id ) {
+			wp_cache_delete( $topic_id, 'bb_topic' );
+		}
+	}
+
+	return $return;
+}
+
+/**
+ * bb_get_tag_id() - Returns the id of the specified or global tag.
+ *
+ * @param mixed $id The TT_ID, tag name of the desired tag, or 0 for the global tag
+ * @return int 
+ */
+function bb_get_tag_id( $id = 0 ) {
+	global $tag;
+	if ( $id ) {
+		$_tag = bb_get_tag( $id );
+	} else {
+		$_tag =& $tag;
+	}
+	return (int) $_tag->tag_id;
+}
+
+/**
+ * bb_get_tag() - Returns the specified tag.  If $user_id and $topic_id are passed, will check to see if that tag exists on that topic by that user.
+ *
+ * @param mixed $id The TT_ID or tag name of the desired tag
+ * @param int $user_id (optional)
+ * @param int $topic_id (optional)
+ * @return object Term object (back-compat)
+ */
+function bb_get_tag( $id, $user_id = 0, $topic_id = 0 ) {
+	global $wp_taxonomy_object;
+	$user_id  = (int) $user_id;
+	$topic_id = (int) $topic_id;
+
+	$term = false;
+	if ( is_integer( $id ) ) {
+		$tt_id = (int) $id;
+	} else {
+		if ( !$term = $wp_taxonomy_object->get_term_by( 'slug', $id, 'bb_topic_tag' ) )
+			return false;
+		$tt_id = (int) $term->term_taxonomy_id;
+	}
+
+	if ( $user_id && $topic_id ) {
+		$args = array( 'user_id' => $user_id, 'fields' => 'tt_ids' );
+		$cache_id = $topic_id . serialize( $args );
+
+		$tt_ids = wp_cache_get( $cache_id, 'bb_topic_tag_terms' );
+		if ( empty( $tt_ids ) ) {
+			$tt_ids = $wp_taxonomy_object->get_object_terms( $topic_id, 'bb_topic_tag', $args );
+			wp_cache_set( $cache_id, $tt_ids, 'bb_topic_tag_terms' );
+		}
+		if ( !in_array( $tt_id, $tt_ids ) )
+			return false;
+	}
+
+	if ( !$term )
+		$term = $wp_taxonomy_object->get_term_by( 'tt_id', $tt_id, 'bb_topic_tag' );
+
+	_bb_make_tag_compat( $term );
+
+	return $term;
+}
+
+/**
+ * bb_get_topic_tags() - Returns all of the bb_topic_tags associated with the specified topic.
+ *
+ * @param int $topic_id
+ * @param mixed $args
+ * @return array|false Term objects (back-compat), false on failure
+ */
+function bb_get_topic_tags( $topic_id = 0, $args = null ) {
+	global $wp_taxonomy_object;
+
+	if ( !$topic = get_topic( get_topic_id( $topic_id ) ) )
+		return false;
+
+	$topic_id = (int) $topic->topic_id;
+
+	$cache_id = $topic_id . serialize( $args );
+
+	$terms = wp_cache_get( $cache_id, 'bb_topic_tag_terms' );
+	if ( false === $terms ) {
+		$terms = $wp_taxonomy_object->get_object_terms( (int) $topic->topic_id, 'bb_topic_tag', $args );
+		wp_cache_set( $cache_id, $terms, 'bb_topic_tag_terms' );
+	}
+
+	if ( is_wp_error( $terms ) )
+		return false;
+
+	for ( $i = 0; isset($terms[$i]); $i++ )
+		_bb_make_tag_compat( $terms[$i] );
+
+	return $terms;
+}
+
+function bb_get_user_tags( $topic_id, $user_id ) {
+	$tags = bb_get_topic_tags( $topic_id );
+	if ( !is_array( $tags ) )
+		return;
+	$user_tags = array();
+
+	foreach ( $tags as $tag ) :
+		if ( $tag->user_id == $user_id )
+			$user_tags[] = $tag;
+	endforeach;
+	return $user_tags;
+}
+
+function bb_get_other_tags( $topic_id, $user_id ) {
+	$tags = bb_get_topic_tags( $topic_id );
+	if ( !is_array( $tags ) )
+		return;
+	$other_tags = array();
+
+	foreach ( $tags as $tag ) :
+		if ( $tag->user_id != $user_id )
+			$other_tags[] = $tag;
+	endforeach;
+	return $other_tags;
+}
+
+function bb_get_public_tags( $topic_id ) {
+	$tags = bb_get_topic_tags( $topic_id );
+	if ( !is_array( $tags ) )
+		return;
+	$used_tags   = array();
+	$public_tags = array();
+
+	foreach ( $tags as $tag ) :
+		if ( !in_array($tag->tag_id, $used_tags) ) :
+			$public_tags[] = $tag;
+			$used_tags[]   = $tag->tag_id;
+		endif;
+	endforeach;
+	return $public_tags;
+}
+
+function bb_get_tagged_topic_ids( $tag_id ) {
+	global $wp_taxonomy_object, $tagged_topic_count;
+	
+	if ( $topic_ids = (array) $wp_taxonomy_object->get_objects_in_term( $tag_id, 'bb_topic_tag', array( 'field' => 'tt_id' ) ) ) {
+		$tagged_topic_count = count($topic_ids);
+		return apply_filters('get_tagged_topic_ids', $topic_ids);
+	} else {
+		$tagged_topic_count = 0;
+		return false;
+	}
+}
+
+function get_tagged_topics( $args ) {
+	global $tagged_topic_count;
+	$defaults = array( 'tag_id' => false, 'page' => 1, 'number' => false, 'count' => true );
+	if ( is_numeric( $args ) )
+		$args = array( 'tag_id' => $args );
+	else
+		$args = wp_parse_args( $args ); // Make sure it's an array
+	if ( 1 < func_num_args() )
+		$args['page'] = func_get_arg(1);
+	if ( 2 < func_num_args() )
+		$args['number'] = func_get_arg(2);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$q = array('tag_id' => (int) $tag_id, 'page' => (int) $page, 'per_page' => (int) $number, 'count' => $count );
+
+	$query = new BB_Query( 'topic', $q, 'get_tagged_topics' );
+	$tagged_topic_count = $query->found_rows;
+
+	return $query->results;
+}
+
+function get_tagged_topic_posts( $args ) {
+	$defaults = array( 'tag_id' => false, 'page' => 1, 'number' => false );
+	if ( is_numeric( $args ) )
+		$args = array( 'tag_id' => $args );
+	else
+		$args = wp_parse_args( $args ); // Make sure it's an array
+	if ( 1 < func_num_args() )
+		$args['page'] = func_get_arg(1);
+	if ( 2 < func_num_args() )
+		$args['number'] = func_get_arg(2);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	$q = array('tag_id' => (int) $tag_id, 'page' => (int) $page, 'per_page' => (int) $number);
+
+	$query = new BB_Query( 'post', $q, 'get_tagged_topic_posts' );
+	return $query->results;
+}
+
+/**
+ * bb_get_top_tags() - Returns most popular tags.
+ *
+ * @param mixed $args
+ * @return array|false Term objects (back-compat), false on failure
+ */
+function bb_get_top_tags( $args = null ) {
+	global $wp_taxonomy_object;
+
+	$args = wp_parse_args( $args, array( 'number' => 40 ) );
+	$args['order'] = 'DESC';
+	$args['orderby'] = 'count';
+
+	$terms = $wp_taxonomy_object->get_terms( 'bb_topic_tag', $args );
+	if ( is_wp_error( $terms ) )
+		return false;
+
+	foreach ( $terms as $term )
+	       	_bb_make_tag_compat( $term );
+
+	return $terms;
+}
+
+/**
+ * Adds some back-compat properties/elements to a term.
+ *
+ * Casting $tag->term_taxonomy_id to an integer is important since
+ * we check it against is_integer() in bb_get_tag()
+ *
+ * @internal
+ */
+function _bb_make_tag_compat( &$tag ) {
+	if ( is_object($tag) && isset($tag->term_id) ) {
+		$tag->term_taxonomy_id = (int) $tag->term_taxonomy_id;
+		$tag->tag_id    =& $tag->term_taxonomy_id;
+		$tag->tag       =& $tag->slug;
+		$tag->raw_tag   =& $tag->name;
+		$tag->tag_count =& $tag->count;
+	} elseif ( is_array($tag) && isset($tag['term_id']) ) {
+		$tag['term_taxonomy_id'] = (int) $tag['term_taxonomy_id'];
+		$tag['tag_id']    =& $tag['term_taxonomy_id'];
+		$tag['tag']       =& $tag['slug'];
+		$tag['raw_tag']   =& $tag['name'];
+		$tag['tag_count'] =& $tag['count'];
+	}
+}
+
+function bb_rename_tag( $tag_id, $tag_name ) {
+	if ( !bb_current_user_can( 'manage_tags' ) ) {
+		return false;
+	}
+
+	$tag_id = (int) $tag_id;
+	$raw_tag = bb_trim_for_db( $tag_name, 50 );
+	$tag_name = tag_sanitize( $tag_name ); 
+
+	if ( empty( $tag_name ) ) {
+		return false;
+	}
+
+	if ( $existing_tag = bb_get_tag( $tag_name ) ) {
+		if ( $existing_tag->term_id !== $tag_id ) {
+			return false;
+		}
+	}
+
+	if ( !$old_tag = bb_get_tag( $tag_id ) ) {
+		return false;
+	}
+
+	global $wp_taxonomy_object;
+	$ret = $wp_taxonomy_object->update_term( $tag_id, 'bb_topic_tag', array( 'name' => $raw_tag, 'slug' => $tag_name ) );
+
+	if ( $ret && !is_wp_error( $ret ) ) {
+		do_action( 'bb_tag_renamed', $tag_id, $old_tag->raw_tag, $raw_tag );
+		return bb_get_tag( $tag_id );
+	}
+	return false;
+}
+
+// merge $old_id into $new_id.
+function bb_merge_tags( $old_id, $new_id ) {
+	if ( !bb_current_user_can( 'manage_tags' ) ) {
+		return false;
+	}
+
+	$old_id = (int) $old_id;
+	$new_id = (int) $new_id;
+
+	if ( $old_id == $new_id ) {
+		return false;
+	}
+
+	do_action( 'bb_pre_merge_tags', $old_id, $new_id );
+
+	// Get all topics tagged with old tag
+	$old_topics = bb_get_tagged_topic_ids( $old_id );
+
+	// Get all toics tagged with new tag
+	$new_topics = bb_get_tagged_topic_ids( $new_id );
+
+	// Get intersection of those topics
+	$both_topics = array_intersect( $old_topics, $new_topics );
+
+	// Discard the intersection from the old tags topics
+	$old_topics = array_diff( $old_topics, $both_topics );
+
+	// Add the remainder of the old tag topics to the new tag
+	if ( count( $old_topics ) ) {
+		$new_tag = bb_get_tag( $new_id );
+		foreach ( $old_topics as $old_topic ) {
+			bb_add_topic_tag( $old_topic, $new_tag->slug );
+		}
+	}
+	
+	// Destroy the old tag
+	$old_tag = bb_destroy_tag( $old_id );
+
+	return array( 'destroyed' => $old_tag, 'old_count' => count( $old_topics ), 'diff_count' => count( $both_topics ) );
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topics.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topics.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-topics.php	(revision 5057)
@@ -0,0 +1,460 @@
+<?php
+
+/* Topics */
+
+function get_topic( $id, $cache = true ) {
+	global $bbdb;
+
+	if ( !is_numeric($id) ) {
+		list($slug, $sql) = bb_get_sql_from_slug( 'topic', $id );
+		$id = wp_cache_get( $slug, 'bb_topic_slug' );
+	}
+
+	// not else
+	if ( is_numeric($id) ) {
+		$id = (int) $id;
+		$sql = "topic_id = $id";
+	}
+
+	if ( 0 === $id || !$sql )
+		return false;
+
+	// &= not =&
+	$cache &= 'AND topic_status = 0' == $where = apply_filters( 'get_topic_where', 'AND topic_status = 0' );
+
+	if ( ( $cache || !$where ) && is_numeric($id) && false !== $topic = wp_cache_get( $id, 'bb_topic' ) )
+		return $topic;
+
+	// $where is NOT bbdb:prepared
+	$topic = $bbdb->get_row( "SELECT * FROM $bbdb->topics WHERE $sql $where" );
+	$topic = bb_append_meta( $topic, 'topic' );
+
+	if ( $cache ) {
+		wp_cache_set( $topic->topic_id, $topic, 'bb_topic' );
+		wp_cache_add( $topic->topic_slug, $topic->topic_id, 'bb_topic_slug' );
+	}
+
+	return $topic;
+}
+
+function bb_get_topic_from_uri( $uri ) {
+	// Extract the topic id or slug of the uri
+	if ( 'topic' === bb_get_path(0, false, $uri) ) {
+		$topic_id_or_slug = bb_get_path(1, false, $uri);
+	} else {
+		if ($parsed_uri = parse_url($uri)) {
+			if (preg_match('@/topic\.php$@'  ,$parsed_uri['path'])) {
+				$args = wp_parse_args($parsed_uri['query']);
+				if (isset($args['id'])) {
+					$topic_id_or_slug = $args['id'];
+				}
+			}
+		}
+	}
+
+	if ( !$topic_id_or_slug )
+		return false;
+
+	return get_topic( $topic_id_or_slug );
+}
+
+function get_latest_topics( $args = null ) {
+	$defaults = array( 'forum' => false, 'page' => 1, 'exclude' => false, 'number' => false );
+	if ( is_numeric( $args ) )
+		$args = array( 'forum' => $args );
+	else
+		$args = wp_parse_args( $args ); // Make sure it's an array
+	if ( 1 < func_num_args() )
+		$args['page'] = func_get_arg(1);
+	if ( 2 < func_num_args() )
+		$args['exclude'] = func_get_arg(2);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( $exclude ) {
+		$exclude = '-' . str_replace(',', ',-', $exclude);
+		$exclude = str_replace('--', '-', $exclude);
+		if ( $forum )
+			$forum = (string) $forum . ",$exclude";
+		else
+			$forum = $exclude;
+	}
+
+	$q = array(
+		'forum_id' => $forum,
+		'page' => $page,
+		'per_page' => $number,
+		'index_hint' => 'USE INDEX (`forum_time`)'
+	);
+
+	if ( bb_is_front() )
+		$q['sticky'] = '-2';
+	elseif ( bb_is_forum() || bb_is_view() )
+		$q['sticky'] = 0;
+
+	// Last param makes filters back compat
+	$query = new BB_Query( 'topic', $q, 'get_latest_topics' );
+	return $query->results;
+}
+
+function get_sticky_topics( $forum = false, $display = 1 ) {
+	if ( 1 != $display ) // Why is this even here?
+		return false;
+
+	$q = array(
+		'forum_id' => $forum,
+		'sticky' => bb_is_front() ? 'super' : 'sticky'
+	);
+
+	$query = new BB_Query( 'topic', $q, 'get_sticky_topics' );
+	return $query->results;
+}
+
+function get_recent_user_threads( $user_id ) {
+	global $page;
+	$q = array( 'page' => $page, 'topic_author_id' => $user_id, 'order_by' => 't.topic_time');
+
+	$query = new BB_Query( 'topic', $q, 'get_recent_user_threads' );
+
+	return $query->results;
+}
+
+function bb_insert_topic( $args = null ) {
+	global $bbdb;
+
+	if ( !$args = wp_parse_args( $args ) )
+		return false;
+
+	$fields = array_keys( $args );
+
+	if ( isset($args['topic_id']) && false !== $args['topic_id'] ) {
+		$update = true;
+		if ( !$topic_id = (int) get_topic_id( $args['topic_id'] ) )
+			return false;
+		// Get from db, not cache.  Good idea?  Prevents trying to update meta_key names in the topic table (get_topic() returns appended topic obj)
+		$topic = $bbdb->get_row( $bbdb->prepare( "SELECT * FROM $bbdb->topics WHERE topic_id = %d", $topic_id ) );
+		$defaults = get_object_vars( $topic );
+		unset($defaults['topic_id']);
+
+		// Only update the args we passed
+		$fields = array_intersect( $fields, array_keys($defaults) );
+		if ( in_array( 'topic_poster', $fields ) )
+			$fields[] = 'topic_poster_name';
+		if ( in_array( 'topic_last_poster', $fields ) )
+			$fields[] = 'topic_last_poster_name';
+	} else {
+		$topic_id = false;
+		$update = false;
+
+		$now = bb_current_time('mysql');
+		$current_user_id = bb_get_current_user_info( 'id' );
+
+		$defaults = array(
+			'topic_title' => '',
+			'topic_slug' => '',
+			'topic_poster' => $current_user_id, // accepts ids
+			'topic_poster_name' => '', // accept names
+			'topic_last_poster' => $current_user_id, // accepts ids
+			'topic_last_poster_name' => '', // accept names
+			'topic_start_time' => $now,
+			'topic_time' => $now,
+			'topic_open' => 1,
+			'forum_id' => 0 // accepts ids or slugs
+		);
+
+		// Insert all args
+		$fields = array_keys($defaults);
+	}
+
+	$defaults['tags'] = false; // accepts array or comma delimited string
+	extract( wp_parse_args( $args, $defaults ) );
+	unset($defaults['tags']);
+
+	if ( !$forum = bb_get_forum( $forum_id ) )
+		return false;
+	$forum_id = (int) $forum->forum_id;
+
+	if ( !$user = bb_get_user( $topic_poster ) )
+		$user = bb_get_user( $topic_poster_name, array( 'by' => 'login' ) );
+
+	if ( !empty( $user ) ) {
+		$topic_poster      = $user->ID;
+		$topic_poster_name = $user->user_login;
+	}
+
+	if ( !$last_user = bb_get_user( $topic_last_poster ) )
+		$last_user = bb_get_user( $topic_last_poster_name, array( 'by' => 'login' ) );
+
+	if ( !empty( $last_user ) ) {
+		$topic_last_poster      = $last_user->ID;
+		$topic_last_poster_name = $last_user->user_login;
+	}
+
+	if ( in_array( 'topic_title', $fields ) ) {
+		$topic_title = apply_filters( 'pre_topic_title', $topic_title, $topic_id );
+		if ( strlen($topic_title) < 1 )
+			return false;
+	}
+
+	if ( in_array( 'topic_slug', $fields ) ) {
+		$slug_sql = $update ?
+			"SELECT topic_slug FROM $bbdb->topics WHERE topic_slug = %s AND topic_id != %d" :
+			"SELECT topic_slug FROM $bbdb->topics WHERE topic_slug = %s";
+
+		$topic_slug = $_topic_slug = bb_slug_sanitize( $topic_slug ? $topic_slug : wp_specialchars_decode( $topic_title, ENT_QUOTES ) );
+		if ( strlen( $_topic_slug ) < 1 )
+			$topic_slug = $_topic_slug = '0';
+
+		while ( is_numeric($topic_slug) || $existing_slug = $bbdb->get_var( $bbdb->prepare( $slug_sql, $topic_slug, $topic_id ) ) )
+			$topic_slug = bb_slug_increment( $_topic_slug, $existing_slug );
+	}
+
+	if ( $update ) {
+		$bbdb->update( $bbdb->topics, compact( $fields ), compact( 'topic_id' ) );
+		wp_cache_delete( $topic_id, 'bb_topic' );
+		if ( in_array( 'topic_slug', $fields ) )
+			wp_cache_delete( $topic->topic_slug, 'bb_topic_slug' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+		do_action( 'bb_update_topic', $topic_id );
+	} else {
+		$bbdb->insert( $bbdb->topics, compact( $fields ) );
+		$topic_id = $bbdb->insert_id;
+		$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET topics = topics + 1 WHERE forum_id = %d", $forum_id ) );
+		wp_cache_delete( $forum_id, 'bb_forum' );
+		wp_cache_flush( 'bb_forums' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+		do_action( 'bb_new_topic', $topic_id );
+	}
+
+	if ( !empty( $tags ) )
+		bb_add_topic_tags( $topic_id, $tags );
+
+	do_action( 'bb_insert_topic', $topic_id, $args, compact( array_keys($args) ) ); // topic_id, what was passed, what was used
+
+	return $topic_id;
+}
+
+// Deprecated: expects $title to be pre-escaped
+function bb_new_topic( $title, $forum, $tags = '', $args = '' ) {
+	$title = stripslashes( $title );
+	$tags  = stripslashes( $tags );
+	$forum = (int) $forum;
+	return bb_insert_topic( wp_parse_args( $args ) + array( 'topic_title' => $title, 'forum_id' => $forum, 'tags' => $tags ) );
+}
+
+// Deprecated: expects $title to be pre-escaped
+function bb_update_topic( $title, $topic_id ) {
+	$title = stripslashes( $title );
+	return bb_insert_topic( array( 'topic_title' => $title, 'topic_id' => $topic_id ) );
+}
+
+function bb_delete_topic( $topic_id, $new_status = 0 ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	add_filter( 'get_topic_where', 'bb_no_where' );
+	if ( $topic = get_topic( $topic_id ) ) {
+		$new_status = (int) $new_status;
+		$old_status = (int) $topic->topic_status;
+		if ( $new_status == $old_status )
+			return;
+
+		$thread_args = array( 'per_page' => -1, 'order' => 'DESC' );
+		if ( 0 != $old_status && 0 == $new_status )
+			$thread_args['post_status'] = 'all';
+		$poster_ids = array();
+		$posts = get_thread( $topic_id, $thread_args );
+		if ( $posts && count( $posts ) ) {
+			foreach ( $posts as $post ) {
+				_bb_delete_post( $post->post_id, $new_status );
+				$poster_ids[] = $post->poster_id;
+			}
+		}
+
+		if ( count( $poster_ids ) ) {
+			foreach ( array_unique( $poster_ids ) as $id ) {
+				if ( $user = bb_get_user( $id ) ) {
+					$topics_replied_key = $bbdb->prefix . 'topics_replied';
+					bb_update_usermeta( $user->ID, $topics_replied_key, ( $old_status ? $user->$topics_replied_key + 1 : $user->$topics_replied_key - 1 ) );
+				}
+			}
+		}
+
+		if ( $ids = $bbdb->get_col( "SELECT user_id, meta_value FROM $bbdb->usermeta WHERE meta_key = 'favorites' and FIND_IN_SET('$topic_id', meta_value) > 0" ) )
+			foreach ( $ids as $id )
+				bb_remove_user_favorite( $id, $topic_id );
+
+		switch ( $new_status ) {
+			case 0: // Undeleting
+				$bbdb->update( $bbdb->topics, array( 'topic_status' => $new_status ), compact( 'topic_id' ) );
+				$topic_posts = (int) $bbdb->get_var( $bbdb->prepare(
+					"SELECT COUNT(*) FROM $bbdb->posts WHERE topic_id = %d AND post_status = 0", $topic_id
+				) );
+				$all_posts = (int) $bbdb->get_var( $bbdb->prepare(
+					"SELECT COUNT(*) FROM $bbdb->posts WHERE topic_id = %d", $topic_id
+				) );
+				bb_update_topicmeta( $topic_id, 'deleted_posts', $all_posts - $topic_posts );
+				$bbdb->query( $bbdb->prepare(
+					"UPDATE $bbdb->forums SET topics = topics + 1, posts = posts + %d WHERE forum_id = %d", $topic_posts, $topic->forum_id
+				) );
+				$bbdb->update( $bbdb->topics, compact( 'topic_posts' ), compact( 'topic_id' ) );
+				bb_topic_set_last_post( $topic_id );
+				bb_update_post_positions( $topic_id );
+				break;
+
+			default: // Other statuses (like Delete and Bozo)
+				bb_remove_topic_tags( $topic_id );
+				$bbdb->update( $bbdb->topics, array( 'topic_status' => $new_status, 'tag_count' => 0 ), compact( 'topic_id' ) );
+				$bbdb->query( $bbdb->prepare(
+					"UPDATE $bbdb->forums SET topics = topics - 1, posts = posts - %d WHERE forum_id = %d", $topic->topic_posts, $topic->forum_id
+				) );
+				break;
+		}
+
+		do_action( 'bb_delete_topic', $topic_id, $new_status, $old_status );
+		wp_cache_delete( $topic_id, 'bb_topic' );
+		wp_cache_delete( $topic->topic_slug, 'bb_topic_slug' );
+		wp_cache_delete( $topic_id, 'bb_thread' );
+		wp_cache_delete( $topic->forum_id, 'bb_forum' );
+		wp_cache_flush( 'bb_forums' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+		return $topic_id;
+	} else {
+		return false;
+	}
+}
+
+function bb_move_topic( $topic_id, $forum_id ) {
+	global $bbdb;
+	$topic = get_topic( $topic_id );
+	$forum = bb_get_forum( $forum_id );
+	$topic_id = (int) $topic->topic_id;
+	$forum_id = (int) $forum->forum_id;
+
+	if ( $topic && $forum && $topic->forum_id != $forum_id ) {
+		$bbdb->update( $bbdb->posts, compact( 'forum_id' ), compact( 'topic_id' ) );
+		$bbdb->update( $bbdb->topics, compact( 'forum_id' ), compact( 'topic_id' ) );
+		$bbdb->query( $bbdb->prepare(
+			"UPDATE $bbdb->forums SET topics = topics + 1, posts = posts + %d WHERE forum_id = %d", $topic->topic_posts, $forum_id
+		) );
+		$bbdb->query( $bbdb->prepare( 
+			"UPDATE $bbdb->forums SET topics = topics - 1, posts = posts - %d WHERE forum_id = %d", $topic->topic_posts, $topic->forum_id
+		) );
+		wp_cache_flush( 'bb_post' );
+		wp_cache_delete( $topic_id, 'bb_topic' );
+		wp_cache_delete( $forum_id, 'bb_forum' );
+		wp_cache_flush( 'bb_forums' );
+		wp_cache_flush( 'bb_query' );
+		wp_cache_flush( 'bb_cache_posts_post_ids' );
+
+		do_action( 'bb_move_topic', $topic_id, $forum_id, $topic->forum_id );
+
+		return $forum_id;
+	}
+	return false;
+}
+
+function bb_topic_set_last_post( $topic_id ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	$old_post = $bbdb->get_row( $bbdb->prepare(
+		"SELECT post_id, poster_id, post_time FROM $bbdb->posts WHERE topic_id = %d AND post_status = 0 ORDER BY post_time DESC LIMIT 1", $topic_id
+	) );
+	$old_poster = bb_get_user( $old_post->poster_id );
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	return $bbdb->update( $bbdb->topics, array( 'topic_time' => $old_post->post_time, 'topic_last_poster' => $old_post->poster_id, 'topic_last_poster_name' => $old_poster->login_name, 'topic_last_post_id' => $old_post->post_id ), compact( 'topic_id' ) );
+}
+
+function bb_close_topic( $topic_id ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	$r = $bbdb->update( $bbdb->topics, array( 'topic_open' => 0 ), compact( 'topic_id' ) );
+	do_action('close_topic', $topic_id, $r);
+	return $r;
+}
+
+function bb_open_topic( $topic_id ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	$r = $bbdb->update( $bbdb->topics, array( 'topic_open' => 1 ), compact( 'topic_id' ) );
+	do_action('open_topic', $topic_id, $r);
+	return $r;
+}
+
+function bb_stick_topic( $topic_id, $super = 0 ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	$stick = 1 + abs((int) $super);
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	$r = $bbdb->update( $bbdb->topics, array( 'topic_sticky' => $stick ), compact( 'topic_id' ) );
+	do_action('stick_topic', $topic_id, $r);
+	return $r;
+}
+
+function bb_unstick_topic( $topic_id ) {
+	global $bbdb;
+	$topic_id = (int) $topic_id;
+	wp_cache_delete( $topic_id, 'bb_topic' );
+	$r = $bbdb->update( $bbdb->topics, array( 'topic_sticky' => 0 ), compact( 'topic_id' ) );
+	do_action('unstick_topic', $topic_id, $r);
+	return $r;
+}
+
+function topic_is_open( $topic_id = 0 ) {
+	$topic = get_topic( get_topic_id( $topic_id ) );
+	return 1 == $topic->topic_open;
+}
+
+function topic_is_sticky( $topic_id = 0 ) {
+	$topic = get_topic( get_topic_id( $topic_id ) );
+	return '0' !== $topic->topic_sticky;
+}
+
+function bb_update_topic_voices( $topic_id )
+{
+	if ( !$topic_id ) {
+		return;
+	}
+
+	$topic_id = abs( (int) $topic_id );
+
+	global $bbdb;
+	if ( $voices = $bbdb->get_col( $bbdb->prepare( "SELECT DISTINCT poster_id FROM $bbdb->posts WHERE topic_id = %s AND post_status = '0';", $topic_id ) ) ) {
+		$voices = count( $voices );
+		bb_update_topicmeta( $topic_id, 'voices_count', $voices );
+	}
+}
+
+/* Thread */
+
+// Thread, topic?  Guh-wah?
+// A topic is the container, the thread is it's contents (the posts)
+
+function get_thread( $topic_id, $args = null ) {
+	$defaults = array( 'page' => 1, 'order' => 'ASC' );
+	if ( is_numeric( $args ) )
+		$args = array( 'page' => $args );
+	if ( @func_get_arg(2) )
+		$defaults['order'] = 'DESC';
+
+	$args = wp_parse_args( $args, $defaults );
+	$args['topic_id'] = $topic_id;
+
+	$query = new BB_Query( 'post', $args, 'get_thread' );
+	return $query->results;
+}
+
+// deprecated
+function get_thread_post_ids( $topic_id ) {
+	$return = array( 'post' => array(), 'poster' => array() );
+	foreach ( get_thread( $topic_id, array( 'per_page' => -1 ) ) as $post ) {
+		$return['post'][] = $post->post_id;
+		$return['poster'][] = $post->poster_id;
+	}
+	return $return;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-users.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-users.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/functions.bb-users.php	(revision 5057)
@@ -0,0 +1,496 @@
+<?php
+
+/* Users */
+
+function bb_block_current_user() {
+	global $bbdb;
+	if ( $id = bb_get_current_user_info( 'id' ) )
+		bb_update_usermeta( $id, $bbdb->prefix . 'been_blocked', 1 ); // Just for logging.
+	bb_logout();
+	bb_die(__("You've been blocked.  If you think a mistake has been made, contact this site's administrator."));
+}
+
+function bb_get_user( $user_id, $args = null ) {
+	global $bbdb, $wp_users_object;
+
+	// Get user
+	$user = $wp_users_object->get_user( $user_id, $args );
+
+	// Return on no user or error object
+	if ( !is_object( $user ) || is_wp_error( $user ) )
+		return false;
+
+	// Re calculate the user's meta in case we're pulling from a value cached on another site
+	if ( $user_vars = get_object_vars( $user ) ) {
+		$prefix_length = strlen( $bbdb->prefix );
+		foreach ( $user_vars as $k => $v ) {
+			if ( 0 === strpos( $k, $bbdb->prefix ) ) {
+				$user->{substr( $k, $prefix_length )} = $v;
+			}
+		}
+	}
+
+	return $user;
+}
+
+function bb_cache_users( $ids ) {
+	global $wp_users_object;
+	$wp_users_object->get_user( $ids );
+}
+
+function bb_get_user_by_nicename( $nicename ) {
+	global $wp_users_object;
+	$user = $wp_users_object->get_user( $nicename, array( 'by' => 'nicename' ) );
+	if ( is_wp_error($user) )
+		return false;
+	return $user;
+}
+
+function bb_delete_user( $user_id, $reassign = 0 ) {
+	global $wp_users_object, $bbdb;
+
+	if ( !$user = bb_get_user( $user_id ) )
+		return false;
+
+	if ( $reassign ) {
+		if ( !$new_user = bb_get_user( $reassign ) )
+			return false;
+		$bbdb->update( $bbdb->posts, array( 'poster_id' => $new_user->ID ), array( 'poster_id' => $user->ID ) );
+		$bbdb->update( $bbdb->term_relationships, array( 'user_id' => $new_user->ID ), array( 'user_id' => $user->ID ) );
+		$bbdb->update( $bbdb->topics, array( 'topic_poster' => $new_user->ID, 'topic_poster_name' => $new_user->user_login), array( 'topic_poster' => $user->ID ) );
+		$bbdb->update( $bbdb->topics, array( 'topic_last_poster' => $new_user->ID, 'topic_last_poster_name' => $new_user->user_login ), array( 'topic_last_poster' => $user->ID ) );
+		bb_update_topics_replied( $new_user->ID );
+		wp_cache_flush( 'bb_post' );
+		wp_cache_flush( 'bb_thread' );
+		wp_cache_flush( 'bb_topic_tag' );
+		wp_cache_flush( 'bb_topic' );
+	}
+
+	do_action( 'bb_delete_user', $user->ID, $reassign );
+
+	$wp_users_object->delete_user( $user->ID );
+
+	return true;
+}
+
+function bb_update_topics_replied( $user_id ) {
+	global $bbdb;
+
+	$user_id = (int) $user_id;
+
+	if ( !$user = bb_get_user( $user_id ) )
+		return false;
+
+	$topics_replied = (int) $bbdb->get_var( $bbdb->prepare( "SELECT COUNT(DISTINCT topic_id) FROM $bbdb->posts WHERE post_status = '0' AND poster_id = %d", $user_id ) );
+	return bb_update_usermeta( $user_id, $bbdb->prefix . 'topics_replied', $topics_replied );
+}
+
+function bb_update_user_status( $user_id, $user_status = 0 ) {
+	global $wp_users_object;
+	$user = bb_get_user( $user_id );
+	$user_status = (int) $user_status;
+	$wp_users_object->update_user( $user->ID, compact( 'user_status' ) );
+}
+
+function bb_trusted_roles() {
+	return apply_filters( 'bb_trusted_roles', array('moderator', 'administrator', 'keymaster') );
+}
+
+function bb_is_trusted_user( $user ) { // ID, user_login, WP_User, DB user obj
+	if ( is_numeric($user) || is_string($user) )
+		$user = new BP_User( $user );
+	elseif ( is_object($user) && is_a($user, 'BP_User') ); // Intentional
+	elseif ( is_object($user) && isset($user->ID) && isset($user->user_login) ) // Make sure it's actually a user object
+		$user = new BP_User( $user->ID );
+	else
+		return;
+
+	if ( !$user->ID )
+		return;
+
+	return apply_filters( 'bb_is_trusted_user', (bool) array_intersect(bb_trusted_roles(), $user->roles), $user->ID );
+}
+
+function bb_apply_wp_role_map_to_user( $user, $reload = true ) {
+	// Expects only user ids
+	if ( !is_numeric( $user ) ) {
+		return;
+	}
+
+	$user = (int) $user;
+
+	if ( !$wordpress_table_prefix = bb_get_option('wp_table_prefix') ) {
+		return;
+	}
+
+	if ( $wordpress_mu_primary_blog_id = bb_get_option( 'wordpress_mu_primary_blog_id' ) ) {
+		$wordpress_table_prefix .= $wordpress_mu_primary_blog_id . '_';
+	}
+
+	if ( !$wordpress_roles_map = bb_get_option( 'wp_roles_map' ) ) {
+		return;
+	}
+
+	global $bbdb;
+	global $wp_roles;
+	global $bb;
+
+	static $bbpress_roles_map = false;
+
+	if ( !$bbpress_roles_map ) {
+		$bbpress_roles_map = array();
+		foreach ( $wp_roles->get_names() as $_bbpress_role => $_bbpress_rolename ) {
+			$bbpress_roles_map[$_bbpress_role] = 'subscriber';
+		}
+		unset( $_bbpress_role, $_bbpress_rolename );
+		$bbpress_roles_map = array_merge( $bbpress_roles_map, array_flip( $wordpress_roles_map ) );
+		unset( $bbpress_roles_map['inactive'], $bbpress_roles_map['blocked'] );
+	}
+
+	static $wordpress_userlevel_map = array(
+		'administrator' => 10,
+		'editor' => 7,
+		'author' => 2,
+		'contributor' => 1,
+		'subscriber' => 0
+	);
+
+	$bbpress_roles = bb_get_usermeta( $user, $bbdb->prefix . 'capabilities' );
+	$wordpress_roles = bb_get_usermeta( $user, $wordpress_table_prefix . 'capabilities' );
+
+	if ( !$bbpress_roles && is_array( $wordpress_roles ) ) {
+		$bbpress_roles_new = array();
+
+		foreach ( $wordpress_roles as $wordpress_role => $wordpress_role_value ) {
+			if ( $wordpress_roles_map[strtolower( $wordpress_role )] && $wordpress_role_value ) {
+				$bbpress_roles_new[$wordpress_roles_map[strtolower( $wordpress_role )]] = true;
+			}
+		}
+
+		if ( count( $bbpress_roles_new ) ) {
+			bb_update_usermeta( $user, $bbdb->prefix . 'capabilities', $bbpress_roles_new );
+			if ( $reload ) {
+				header( 'Location: ' . bb_get_uri( null, null, BB_URI_CONTEXT_HEADER ) );
+				exit;
+			}
+		}
+	} elseif ( !$wordpress_roles && is_array( $bbpress_roles ) ) {
+		$wordpress_roles_new = array();
+
+		foreach ( $bbpress_roles as $bbpress_role => $bbpress_role_value ) {
+			if ( $bbpress_roles_map[strtolower( $bbpress_role )] && $bbpress_role_value ) {
+				$wordpress_roles_new[$bbpress_roles_map[strtolower( $bbpress_role )]] = true;
+				$wordpress_userlevels_new[] = $wordpress_userlevel_map[$bbpress_roles_map[strtolower( $bbpress_role )]];
+			}
+		}
+
+		if ( count( $wordpress_roles_new ) ) {
+			bb_update_usermeta( $user, $wordpress_table_prefix . 'capabilities', $wordpress_roles_new );
+			bb_update_usermeta( $user, $wordpress_table_prefix . 'user_level', max( $wordpress_userlevels_new ) );
+		}
+	}
+}
+
+function bb_apply_wp_role_map_to_orphans() {
+	if ( !$wordpress_table_prefix = bb_get_option('wp_table_prefix') ) {
+		return;
+	}
+
+	if ( $wordpress_mu_primary_blog_id = bb_get_option( 'wordpress_mu_primary_blog_id' ) ) {
+		$wordpress_table_prefix .= $wordpress_mu_primary_blog_id . '_';
+	}
+
+	$role_query = <<<EOQ
+		SELECT
+			ID
+		FROM
+			`%1\$s`
+		LEFT JOIN `%2\$s` AS bbrole
+			ON ID = bbrole.user_id
+			AND bbrole.meta_key = '%3\$scapabilities'
+		LEFT JOIN `%2\$s` AS wprole
+			ON ID = wprole.user_id
+			AND wprole.meta_key = '%4\$scapabilities'
+		WHERE
+			bbrole.meta_key IS NULL OR
+			bbrole.meta_value IS NULL OR
+			wprole.meta_key IS NULL OR
+			wprole.meta_value IS NULL
+		ORDER BY
+			ID
+EOQ;
+
+	global $bbdb;
+
+	$role_query = $bbdb->prepare( $role_query, $bbdb->users, $bbdb->usermeta, $bbdb->prefix, $wordpress_table_prefix );
+
+	if ( $user_ids = $bbdb->get_col( $role_query ) ) {
+		foreach ( $user_ids as $user_id ) {
+			bb_apply_wp_role_map_to_user( $user_id, false );
+		}
+	}
+}
+
+/**
+ * Updates a user's details in the database
+ *
+ * {@internal Missing Long Description}}
+ *
+ * @since 0.7.2
+ * @global bbdb $bbdb
+ *
+ * @param int $user_id
+ * @param string $user_email
+ * @param string $user_url
+ * @return int
+ */
+function bb_update_user( $user_id, $user_email, $user_url, $display_name ) {
+	global $wp_users_object;
+
+	$user_id = (int) $user_id;
+	$user_url = bb_fix_link( $user_url );
+
+	$wp_users_object->update_user( $user_id, compact( 'user_email', 'user_url', 'display_name' ) );
+
+	do_action('bb_update_user', $user_id);
+	return $user_id;
+}
+
+/**
+ * Sends a reset password email
+ *
+ * Sends an email to the email address specified in the user's profile
+ * prompting them to change their password.
+ *
+ * @since 0.7.2
+ * @global bbdb $bbdb
+ *
+ * @param string $user_login
+ * @return bool
+ */
+function bb_reset_email( $user_login )
+{
+	global $bbdb;
+
+	$user_login = sanitize_user( $user_login, true );
+
+	if ( !$user = $bbdb->get_row( $bbdb->prepare( "SELECT * FROM $bbdb->users WHERE user_login = %s", $user_login ) ) ) {
+		return new WP_Error( 'user_does_not_exist', __( 'The specified user does not exist.' ) );
+	}
+
+	$resetkey = substr( md5( bb_generate_password() ), 0, 15 );
+	bb_update_usermeta( $user->ID, 'newpwdkey', $resetkey );
+
+	$reseturi = bb_get_uri(
+		'bb-reset-password.php',
+		array( 'key' => $resetkey ),
+		BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_BB_USER_FORMS
+	);
+
+	$message = sprintf(
+		__( "If you wanted to reset your password, you may do so by visiting the following address:\n\n%s\n\nIf you don't want to reset your password, just ignore this email. Thanks!" ),
+		$reseturi
+	);
+	$message = apply_filters( 'bb_reset_email_message', $message, $user, $reseturi, $resetkey );
+
+	$subject = sprintf(
+		__( '%s: Password Reset' ),
+		bb_get_option( 'name' )
+	);
+	$subject = apply_filters( 'bb_reset_email_subject', $subject, $user );
+
+	$mail_result = bb_mail(
+		bb_get_user_email( $user->ID ),
+		$subject,
+		$message
+	);
+
+	if ( !$mail_result ) {
+		return new WP_Error( 'sending_mail_failed', __( 'The email containing the password reset link could not be sent.' ) );
+	}
+
+	return true;
+}
+
+/**
+ * Handles the resetting of users' passwords
+ *
+ * Handles resetting a user's password, prompted by an email sent by
+ * {@see bb_reset_email()}
+ *
+ * @since 0.7.2
+ * @global bbdb $bbdb
+ *
+ * @param string $key
+ * @return unknown
+ */
+function bb_reset_password( $key )
+{
+	global $bbdb;
+
+	$key = sanitize_user( $key, true );
+
+	if ( empty( $key ) || !is_string( $key ) ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	}
+
+	if ( !$user_id = $bbdb->get_var( $bbdb->prepare( "SELECT user_id FROM $bbdb->usermeta WHERE meta_key = 'newpwdkey' AND meta_value = %s", $key ) ) ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	}
+
+	$user = new BP_User( $user_id );
+
+	if ( !$user || is_wp_error( $user ) ) {
+		return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+	}
+
+	if ( bb_has_broken_pass( $user->ID ) ) {
+		bb_block_current_user();
+	}
+
+	if ( !$user->has_cap( 'change_user_password', $user->ID ) ) {
+		return new WP_Error( 'permission_denied', __( 'You are not allowed to change your password.' ) );
+	}
+
+	$newpass = bb_generate_password();
+	bb_update_user_password( $user->ID, $newpass );
+	if ( !bb_send_pass( $user->ID, $newpass ) ) {
+		return new WP_Error( 'sending_mail_failed', __( 'The email containing the new password could not be sent.' ) );
+	}
+
+	bb_update_usermeta( $user->ID, 'newpwdkey', '' );
+	return true;
+}
+
+/**
+ * Updates a user's password in the database
+ *
+ * {@internal Missing Long Description}}
+ *
+ * @since 0.7.2
+ * @global bbdb $bbdb
+ *
+ * @param int $user_id
+ * @param string $password
+ * @return int
+ */
+function bb_update_user_password( $user_id, $password ) {
+	global $wp_users_object;
+
+	$user_id = (int) $user_id;
+
+	$wp_users_object->set_password( $password, $user_id );
+
+	do_action('bb_update_user_password', $user_id);
+	return $user_id;
+}
+
+/**
+ * Sends an email with the user's new password
+ *
+ * {@internal Missing Long Description}}
+ *
+ * @since 0.7.2
+ * @global bbdb $bbdb {@internal Not used}}
+ *
+ * @param int|string $user
+ * @param string $pass
+ * @return bool
+ */
+function bb_send_pass( $user, $pass )
+{
+	if ( !$user = bb_get_user( $user ) ) {
+		return false;
+	}
+
+	$message = sprintf(
+		__( "Your username is: %1\$s \nYour password is: %2\$s \nYou can now log in: %3\$s \n\nEnjoy!" ),
+		$user->user_login,
+		$pass,
+		bb_get_uri( null, null, BB_URI_CONTEXT_TEXT )
+	);
+	$message = apply_filters( 'bb_send_pass_message', $message, $user, $pass );
+
+	$subject = sprintf(
+		__( '%s: Password' ),
+		bb_get_option( 'name' )
+	);
+	$subject = apply_filters( 'bb_send_pass_subject', $subject, $user );
+
+	return bb_mail(
+		bb_get_user_email( $user->ID ),
+		$subject,
+		$message
+	);
+}
+
+
+
+/* Favorites */
+
+function get_user_favorites( $user_id, $topics = false ) {
+	$user = bb_get_user( $user_id );
+	if ( !empty($user->favorites) ) {
+		if ( $topics )
+			$query = new BB_Query( 'topic', array('favorites' => $user_id, 'index_hint' => 'USE INDEX (`forum_time`)'), 'get_user_favorites' );
+		else
+			$query = new BB_Query( 'post', array('favorites' => $user_id), 'get_user_favorites' );
+		return $query->results;
+	}
+}
+
+function is_user_favorite( $user_id = 0, $topic_id = 0 ) {
+	if ( $user_id )
+		$user = bb_get_user( $user_id );
+	else
+	 	global $user;
+	if ( $topic_id )
+		$topic = get_topic( $topic_id );
+	else
+		global $topic;
+	if ( !$user || !$topic )
+		return;
+
+	if ( isset($user->favorites) )
+	        return in_array($topic->topic_id, explode(',', $user->favorites));
+	return false;
+}
+
+function bb_add_user_favorite( $user_id, $topic_id ) {
+	global $bbdb;
+	$user_id = (int) $user_id;
+	$topic_id = (int) $topic_id;
+	$user = bb_get_user( $user_id );
+	$topic = get_topic( $topic_id );
+	if ( !$user || !$topic )
+		return false;
+
+	$favorites_key = $bbdb->prefix . 'favorites';
+	$fav = $user->$favorites_key ? explode(',', $user->$favorites_key) : array();
+	if ( ! in_array( $topic_id, $fav ) ) {
+		$fav[] = $topic_id;
+		$fav = implode(',', $fav);
+		bb_update_usermeta( $user->ID, $favorites_key, $fav );
+	}
+	do_action('bb_add_user_favorite', $user_id, $topic_id);
+	return true;
+}
+
+function bb_remove_user_favorite( $user_id, $topic_id ) {
+	global $bbdb;
+	$user_id = (int) $user_id;
+	$topic_id = (int) $topic_id;
+	$user = bb_get_user( $user_id );
+	if ( !$user )
+		return false;
+
+	$favorites_key = $bbdb->prefix . 'favorites';
+	$fav = explode(',', $user->$favorites_key);
+	if ( is_int( $pos = array_search($topic_id, $fav) ) ) {
+		array_splice($fav, $pos, 1);
+		$fav = implode(',', $fav);
+		bb_update_usermeta( $user->ID, $favorites_key, $fav);
+	}
+	do_action('bb_remove_user_favorite', $user_id, $topic_id);
+	return true;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/hoverIntent.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/hoverIntent.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/hoverIntent.js	(revision 5057)
@@ -0,0 +1,128 @@
+/**
+* hoverIntent is similar to jQuery's built-in "hover" function except that
+* instead of firing the onMouseOver event immediately, hoverIntent checks
+* to see if the user's mouse has slowed down (beneath the sensitivity
+* threshold) before firing the onMouseOver event.
+* 
+* hoverIntent r5 // 2007.03.27 // jQuery 1.1.2+
+* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
+* 
+* hoverIntent is currently available for use in all personal or commercial 
+* projects under both MIT and GPL licenses. This means that you can choose 
+* the license that best suits your project, and use it accordingly.
+* 
+* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
+* $("ul li").hoverIntent( showNav , hideNav );
+* 
+* // advanced usage receives configuration object only
+* $("ul li").hoverIntent({
+*	sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
+*	interval: 100,   // number = milliseconds of polling interval
+*	over: showNav,  // function = onMouseOver callback (required)
+*	timeout: 0,   // number = milliseconds delay before onMouseOut function call
+*	out: hideNav    // function = onMouseOut callback (required)
+* });
+* 
+* @param  f  onMouseOver function || An object with configuration options
+* @param  g  onMouseOut function  || Nothing (use configuration options object)
+* @author    Brian Cherne <brian@cherne.net>
+*/
+(function($) {
+	$.fn.hoverIntent = function(f,g) {
+		// default configuration options
+		var cfg = {
+			sensitivity: 7,
+			interval: 100,
+			timeout: 0
+		};
+		// override configuration options with user supplied object
+		cfg = $.extend(cfg, g ? { over: f, out: g } : f );
+
+		// instantiate variables
+		// cX, cY = current X and Y position of mouse, updated by mousemove event
+		// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
+		var cX, cY, pX, pY;
+
+		// A private function for getting mouse position
+		var track = function(ev) {
+			cX = ev.pageX;
+			cY = ev.pageY;
+		};
+
+		// A private function for comparing current and previous mouse position
+		var compare = function(ev,ob) {
+			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+			// compare mouse positions to see if they've crossed the threshold
+			if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
+				$(ob).unbind("mousemove",track);
+				// set hoverIntent state to true (so mouseOut can be called)
+				ob.hoverIntent_s = 1;
+				return cfg.over.apply(ob,[ev]);
+			} else {
+				// set previous coordinates for next time
+				pX = cX; pY = cY;
+				// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+				ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
+			}
+		};
+
+		// A private function for delaying the mouseOut function
+		var delay = function(ev,ob) {
+			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+			ob.hoverIntent_s = 0;
+			return cfg.out.apply(ob,[ev]);
+		};
+		
+		// workaround for Mozilla bug: not firing mouseout/mouseleave on absolute positioned elements over textareas and input type="text"
+		var handleHover = function(e) {
+			var t = this;
+			
+			// next two lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
+			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+			while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
+			if ( p == this ) {
+				if ( $.browser.mozilla ) {
+					if ( e.type == "mouseout" ) {
+						t.mtout = setTimeout( function(){doHover(e,t);}, 30 );
+					} else {
+						if (t.mtout) { t.mtout = clearTimeout(t.mtout); }
+					}
+				}
+				return;
+			} else {
+				if (t.mtout) { t.mtout = clearTimeout(t.mtout); }
+				doHover(e,t);
+			}
+		};
+
+		// A private function for handling mouse 'hovering'
+		var doHover = function(e,ob) {
+
+			// copy objects to be passed into t (required for event object to be passed in IE)
+			var ev = jQuery.extend({},e);
+
+			// cancel hoverIntent timer if it exists
+			if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
+
+			// else e.type == "onmouseover"
+			if (e.type == "mouseover") {
+				// set "previous" X and Y position based on initial entry point
+				pX = ev.pageX; pY = ev.pageY;
+				// update "current" X and Y position based on mousemove
+				$(ob).bind("mousemove",track);
+				// start polling interval (self-calling timeout) to compare mouse coordinates over time
+				if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
+
+			// else e.type == "onmouseout"
+			} else {
+				// unbind expensive mousemove event
+				$(ob).unbind("mousemove",track);
+				// if hoverIntent state is true, then call the mouseOut function after the specified delay
+				if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
+			}
+		};
+
+		// bind the function to the two event listeners
+		return this.mouseover(handleHover).mouseout(handleHover);
+	};
+})(jQuery);
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/interface.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/interface.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/interface.js	(revision 5057)
@@ -0,0 +1,12 @@
+/**
+ * Interface Elements for jQuery
+ * 
+ * http://interface.eyecon.ro
+ * 
+ * Copyright (c) 2006 Stefan Petre
+ * Dual licensed under the MIT (MIT-LICENSE.txt) 
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *   
+ *
+ */
+ eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('k.f2={2r:u(M){E q.1E(u(){if(!M.aR||!M.aZ)E;D el=q;el.2l={aq:M.aq||cO,aR:M.aR,aZ:M.aZ,8e:M.8e||\'fV\',aJ:M.aJ||\'fV\',2Y:M.2Y&&2g M.2Y==\'u\'?M.2Y:I,3i:M.2Y&&2g M.3i==\'u\'?M.3i:I,7U:M.7U&&2g M.7U==\'u\'?M.7U:I,as:k(M.aR,q),8f:k(M.aZ,q),H:M.H||8J,67:M.67||0};el.2l.8f.2G().B(\'W\',\'9R\').eq(0).B({W:el.2l.aq+\'U\',19:\'2B\'}).2T();el.2l.as.1E(u(2N){q.7X=2N}).gC(u(){k(q).2R(el.2l.aJ)},u(){k(q).4i(el.2l.aJ)}).1J(\'5h\',u(e){if(el.2l.67==q.7X)E;el.2l.as.eq(el.2l.67).4i(el.2l.8e).2T().eq(q.7X).2R(el.2l.8e).2T();el.2l.8f.eq(el.2l.67).5w({W:0},el.2l.H,u(){q.14.19=\'1o\';if(el.2l.3i){el.2l.3i.1D(el,[q])}}).2T().eq(q.7X).1Y().5w({W:el.2l.aq},el.2l.H,u(){q.14.19=\'2B\';if(el.2l.2Y){el.2l.2Y.1D(el,[q])}}).2T();if(el.2l.7U){el.2l.7U.1D(el,[q,el.2l.8f.K(q.7X),el.2l.as.K(el.2l.67),el.2l.8f.K(el.2l.67)])}el.2l.67=q.7X}).eq(0).2R(el.2l.8e).2T();k(q).B(\'W\',k(q).B(\'W\')).B(\'2U\',\'2K\')})}};k.fn.gN=k.f2.2r;k.aA={2r:u(M){E q.1E(u(){D el=q;D 7E=2*18.2Q/f1;D an=2*18.2Q;if(k(el).B(\'Y\')!=\'2s\'&&k(el).B(\'Y\')!=\'1P\'){k(el).B(\'Y\',\'2s\')}el.1l={1R:k(M.1R,q),2F:M.2F,6q:M.6q,aD:M.aD,an:an,1N:k.1a.2o(q),Y:k.1a.3w(q),26:18.2Q/2,bi:M.bi,8p:M.6r,6r:[],aG:I,7E:2*18.2Q/f1};el.1l.fB=(el.1l.1N.w-el.1l.2F)/2;el.1l.7D=(el.1l.1N.h-el.1l.6q-el.1l.6q*el.1l.8p)/2;el.1l.2D=2*18.2Q/el.1l.1R.1N();el.1l.ba=el.1l.1N.w/2;el.1l.b9=el.1l.1N.h/2-el.1l.6q*el.1l.8p;D ak=1h.3F(\'22\');k(ak).B({Y:\'1P\',3I:1,Q:0,O:0});k(el).1S(ak);el.1l.1R.1E(u(2N){a6=k(\'1T\',q).K(0);W=T(el.1l.6q*el.1l.8p);if(k.3a.4t){3E=1h.3F(\'1T\');k(3E).B(\'Y\',\'1P\');3E.2J=a6.2J;3E.14.5E=\'gE 9n:9w.9y.cC(1G=60, 14=1, gB=0, gA=0, gv=0, gF=0)\'}P{3E=1h.3F(\'3E\');if(3E.fD){4L=3E.fD("2d");3E.14.Y=\'1P\';3E.14.W=W+\'U\';3E.14.Z=el.1l.2F+\'U\';3E.W=W;3E.Z=el.1l.2F;4L.gu();4L.gO(0,W);4L.gk(1,-1);4L.gp(a6,0,0,el.1l.2F,W);4L.6H();4L.gm="gG-4l";D ap=4L.hy(0,0,0,W);ap.fs(1,"fr(1V, 1V, 1V, 1)");ap.fs(0,"fr(1V, 1V, 1V, 0.6)");4L.hx=ap;if(hA.hB.3J(\'hw\')!=-1){4L.hv()}P{4L.hu(0,0,el.1l.2F,W)}}}el.1l.6r[2N]=3E;k(ak).1S(3E)}).1J(\'9z\',u(e){el.1l.aG=1b;el.1l.H=el.1l.7E*0.1*el.1l.H/18.3S(el.1l.H);E I}).1J(\'8B\',u(e){el.1l.aG=I;E I});k.aA.7T(el);el.1l.H=el.1l.7E*0.2;el.1l.ht=1X.6V(u(){el.1l.26+=el.1l.H;if(el.1l.26>an)el.1l.26=0;k.aA.7T(el)},20);k(el).1J(\'8B\',u(){el.1l.H=el.1l.7E*0.2*el.1l.H/18.3S(el.1l.H)}).1J(\'3D\',u(e){if(el.1l.aG==I){1s=k.1a.4a(e);fz=el.1l.1N.w-1s.x+el.1l.Y.x;el.1l.H=el.1l.bi*el.1l.7E*(el.1l.1N.w/2-fz)/(el.1l.1N.w/2)}})})},7T:u(el){el.1l.1R.1E(u(2N){b8=el.1l.26+2N*el.1l.2D;x=el.1l.fB*18.5H(b8);y=el.1l.7D*18.83(b8);f9=T(2a*(el.1l.7D+y)/(2*el.1l.7D));fk=(el.1l.7D+y)/(2*el.1l.7D);Z=T((el.1l.2F-el.1l.aD)*fk+el.1l.aD);W=T(Z*el.1l.6q/el.1l.2F);q.14.Q=el.1l.b9+y-W/2+"U";q.14.O=el.1l.ba+x-Z/2+"U";q.14.Z=Z+"U";q.14.W=W+"U";q.14.3I=f9;el.1l.6r[2N].14.Q=T(el.1l.b9+y+W-1-W/2)+"U";el.1l.6r[2N].14.O=T(el.1l.ba+x-Z/2)+"U";el.1l.6r[2N].14.Z=Z+"U";el.1l.6r[2N].14.W=T(W*el.1l.8p)+"U"})}};k.fn.hI=k.aA.2r;k.23({G:{c8:u(p,n,1W,1H,1m){E((-18.5H(p*18.2Q)/2)+0.5)*1H+1W},hK:u(p,n,1W,1H,1m){E 1H*(n/=1m)*n*n+1W},fl:u(p,n,1W,1H,1m){E-1H*((n=n/1m-1)*n*n*n-1)+1W},hm:u(p,n,1W,1H,1m){if((n/=1m/2)<1)E 1H/2*n*n*n*n+1W;E-1H/2*((n-=2)*n*n*n-2)+1W},8l:u(p,n,1W,1H,1m){if((n/=1m)<(1/2.75)){E 1H*(7.aB*n*n)+1W}P if(n<(2/2.75)){E 1H*(7.aB*(n-=(1.5/2.75))*n+.75)+1W}P if(n<(2.5/2.75)){E 1H*(7.aB*(n-=(2.25/2.75))*n+.gY)+1W}P{E 1H*(7.aB*(n-=(2.h2/2.75))*n+.gX)+1W}},cr:u(p,n,1W,1H,1m){if(k.G.8l)E 1H-k.G.8l(p,1m-n,0,1H,1m)+1W;E 1W+1H},gW:u(p,n,1W,1H,1m){if(k.G.cr&&k.G.8l)if(n<1m/2)E k.G.cr(p,n*2,0,1H,1m)*.5+1W;E k.G.8l(p,n*2-1m,0,1H,1m)*.5+1H*.5+1W;E 1W+1H},gQ:u(p,n,1W,1H,1m){D a,s;if(n==0)E 1W;if((n/=1m)==1)E 1W+1H;a=1H*0.3;p=1m*.3;if(a<18.3S(1H)){a=1H;s=p/4}P{s=p/(2*18.2Q)*18.cb(1H/a)}E-(a*18.6b(2,10*(n-=1))*18.83((n*1m-s)*(2*18.2Q)/p))+1W},gT:u(p,n,1W,1H,1m){D a,s;if(n==0)E 1W;if((n/=1m/2)==2)E 1W+1H;a=1H*0.3;p=1m*.3;if(a<18.3S(1H)){a=1H;s=p/4}P{s=p/(2*18.2Q)*18.cb(1H/a)}E a*18.6b(2,-10*n)*18.83((n*1m-s)*(2*18.2Q)/p)+1H+1W},gV:u(p,n,1W,1H,1m){D a,s;if(n==0)E 1W;if((n/=1m/2)==2)E 1W+1H;a=1H*0.3;p=1m*.3;if(a<18.3S(1H)){a=1H;s=p/4}P{s=p/(2*18.2Q)*18.cb(1H/a)}if(n<1){E-.5*(a*18.6b(2,10*(n-=1))*18.83((n*1m-s)*(2*18.2Q)/p))+1W}E a*18.6b(2,-10*(n-=1))*18.83((n*1m-s)*(2*18.2Q)/p)*.5+1H+1W}}});k.6n={2r:u(M){E q.1E(u(){D el=q;el.1F={1R:k(M.1R,q),1Z:k(M.1Z,q),1M:k.1a.3w(q),2F:M.2F,ax:M.ax,7Y:M.7Y,ge:M.ge,51:M.51,6x:M.6x};k.6n.aH(el,0);k(1X).1J(\'gU\',u(){el.1F.1M=k.1a.3w(el);k.6n.aH(el,0);k.6n.7T(el)});k.6n.7T(el);el.1F.1R.1J(\'9z\',u(){k(el.1F.ax,q).K(0).14.19=\'2B\'}).1J(\'8B\',u(){k(el.1F.ax,q).K(0).14.19=\'1o\'});k(1h).1J(\'3D\',u(e){D 1s=k.1a.4a(e);D 5s=0;if(el.1F.51&&el.1F.51==\'cv\')D aI=1s.x-el.1F.1M.x-(el.4c-el.1F.2F*el.1F.1R.1N())/2-el.1F.2F/2;P if(el.1F.51&&el.1F.51==\'2L\')D aI=1s.x-el.1F.1M.x-el.4c+el.1F.2F*el.1F.1R.1N();P D aI=1s.x-el.1F.1M.x;D fP=18.6b(1s.y-el.1F.1M.y-el.5W/2,2);el.1F.1R.1E(u(2N){45=18.ez(18.6b(aI-2N*el.1F.2F,2)+fP);45-=el.1F.2F/2;45=45<0?0:45;45=45>el.1F.7Y?el.1F.7Y:45;45=el.1F.7Y-45;bB=el.1F.6x*45/el.1F.7Y;q.14.Z=el.1F.2F+bB+\'U\';q.14.O=el.1F.2F*2N+5s+\'U\';5s+=bB});k.6n.aH(el,5s)})})},aH:u(el,5s){if(el.1F.51)if(el.1F.51==\'cv\')el.1F.1Z.K(0).14.O=(el.4c-el.1F.2F*el.1F.1R.1N())/2-5s/2+\'U\';P if(el.1F.51==\'O\')el.1F.1Z.K(0).14.O=-5s/el.1F.1R.1N()+\'U\';P if(el.1F.51==\'2L\')el.1F.1Z.K(0).14.O=(el.4c-el.1F.2F*el.1F.1R.1N())-5s/2+\'U\';el.1F.1Z.K(0).14.Z=el.1F.2F*el.1F.1R.1N()+5s+\'U\'},7T:u(el){el.1F.1R.1E(u(2N){q.14.Z=el.1F.2F+\'U\';q.14.O=el.1F.2F*2N+\'U\'})}};k.fn.hi=k.6n.2r;k.N={1c:S,8R:S,3A:S,2I:S,4y:S,cl:S,1d:S,2h:S,1R:S,5o:u(){k.N.8R.5o();if(k.N.3A){k.N.3A.2G()}},4w:u(){k.N.1R=S;k.N.2h=S;k.N.4y=k.N.1d.2y;if(k.N.1c.B(\'19\')==\'2B\'){if(k.N.1d.1f.fx){3m(k.N.1d.1f.fx.1u){1e\'c6\':k.N.1c.7a(k.N.1d.1f.fx.1m,k.N.5o);1r;1e\'1z\':k.N.1c.fq(k.N.1d.1f.fx.1m,k.N.5o);1r;1e\'a7\':k.N.1c.g3(k.N.1d.1f.fx.1m,k.N.5o);1r}}P{k.N.1c.2G()}if(k.N.1d.1f.3i)k.N.1d.1f.3i.1D(k.N.1d,[k.N.1c,k.N.3A])}P{k.N.5o()}1X.bH(k.N.2I)},dQ:u(){D 1d=k.N.1d;D 4d=k.N.aY(1d);if(1d&&4d.3o!=k.N.4y&&4d.3o.1g>=1d.1f.aL){k.N.4y=4d.3o;k.N.cl=4d.3o;81={2n:k(1d).1p(\'hj\')||\'2n\',2y:4d.3o};k.hl({1u:\'hk\',81:k.hf(81),he:u(fZ){1d.1f.4e=k(\'3o\',fZ);1N=1d.1f.4e.1N();if(1N>0){D 5p=\'\';1d.1f.4e.1E(u(2N){5p+=\'<8P 4I="\'+k(\'2y\',q).3g()+\'" 8K="\'+2N+\'" 14="9b: ad;">\'+k(\'3g\',q).3g()+\'</8P>\'});if(1d.1f.aU){D 3M=k(\'2y\',1d.1f.4e.K(0)).3g();1d.2y=4d.3j+3M+1d.1f.3N+4d.66;k.N.6J(1d,4d.3o.1g!=3M.1g?(4d.3j.1g+4d.3o.1g):3M.1g,4d.3o.1g!=3M.1g?(4d.3j.1g+3M.1g):3M.1g)}if(1N>0){k.N.cj(1d,5p)}P{k.N.4w()}}P{k.N.4w()}},5N:1d.1f.aN})}},cj:u(1d,5p){k.N.8R.3x(5p);k.N.1R=k(\'8P\',k.N.8R.K(0));k.N.1R.9z(k.N.di).1J(\'5h\',k.N.dj);D Y=k.1a.3w(1d);D 1N=k.1a.2o(1d);k.N.1c.B(\'Q\',Y.y+1N.hb+\'U\').B(\'O\',Y.x+\'U\').2R(1d.1f.aM);if(k.N.3A){k.N.3A.B(\'19\',\'2B\').B(\'Q\',Y.y+1N.hb+\'U\').B(\'O\',Y.x+\'U\').B(\'Z\',k.N.1c.B(\'Z\')).B(\'W\',k.N.1c.B(\'W\'))}k.N.2h=0;k.N.1R.K(0).3l=1d.1f.7H;k.N.8Q(1d,1d.1f.4e.K(0),\'7J\');if(k.N.1c.B(\'19\')==\'1o\'){if(1d.1f.bV){D cp=k.1a.aT(1d,1b);D cm=k.1a.6U(1d,1b);k.N.1c.B(\'Z\',1d.4c-(k.dF?(cp.l+cp.r+cm.l+cm.r):0)+\'U\')}if(1d.1f.fx){3m(1d.1f.fx.1u){1e\'c6\':k.N.1c.7f(1d.1f.fx.1m);1r;1e\'1z\':k.N.1c.fo(1d.1f.fx.1m);1r;1e\'a7\':k.N.1c.gb(1d.1f.fx.1m);1r}}P{k.N.1c.1Y()}if(k.N.1d.1f.2Y)k.N.1d.1f.2Y.1D(k.N.1d,[k.N.1c,k.N.3A])}},dO:u(){D 1d=q;if(1d.1f.4e){k.N.4y=1d.2y;k.N.cl=1d.2y;D 5p=\'\';1d.1f.4e.1E(u(2N){2y=k(\'2y\',q).3g().6c();fY=1d.2y.6c();if(2y.3J(fY)==0){5p+=\'<8P 4I="\'+k(\'2y\',q).3g()+\'" 8K="\'+2N+\'" 14="9b: ad;">\'+k(\'3g\',q).3g()+\'</8P>\'}});if(5p!=\'\'){k.N.cj(1d,5p);q.1f.9x=1b;E}}1d.1f.4e=S;q.1f.9x=I},6J:u(2n,26,2T){if(2n.b1){D 6t=2n.b1();6t.hp(1b);6t.dI("ck",26);6t.ha("ck",-2T+26);6t.8C()}P if(2n.aF){2n.aF(26,2T)}P{if(2n.5q){2n.5q=26;2n.dN=2T}}2n.6K()},f0:u(2n){if(2n.5q)E 2n.5q;P if(2n.b1){D 6t=1h.6J.dZ();D eX=6t.h9();E 0-eX.dI(\'ck\',-h6)}},aY:u(2n){D 4P={2y:2n.2y,3j:\'\',66:\'\',3o:\'\'};if(2n.1f.aQ){D 8N=I;D 5q=k.N.f0(2n)||0;D 4T=4P.2y.7C(2n.1f.3N);24(D i=0;i<4T.1g;i++){if((4P.3j.1g+4T[i].1g>=5q||5q==0)&&!8N){if(4P.3j.1g<=5q)4P.3o=4T[i];P 4P.66+=4T[i]+(4T[i]!=\'\'?2n.1f.3N:\'\');8N=1b}P if(8N){4P.66+=4T[i]+(4T[i]!=\'\'?2n.1f.3N:\'\')}if(!8N){4P.3j+=4T[i]+(4T.1g>1?2n.1f.3N:\'\')}}}P{4P.3o=4P.2y}E 4P},bU:u(e){1X.bH(k.N.2I);D 1d=k.N.aY(q);D 3K=e.7L||e.7K||-1;if(/13|27|35|36|38|40|9/.48(3K)&&k.N.1R){if(1X.2k){1X.2k.bT=1b;1X.2k.c0=I}P{e.aP();e.aW()}if(k.N.2h!=S)k.N.1R.K(k.N.2h||0).3l=\'\';P k.N.2h=-1;3m(3K){1e 9:1e 13:if(k.N.2h==-1)k.N.2h=0;D 2h=k.N.1R.K(k.N.2h||0);D 3M=2h.5C(\'4I\');q.2y=1d.3j+3M+q.1f.3N+1d.66;k.N.4y=1d.3o;k.N.6J(q,1d.3j.1g+3M.1g+q.1f.3N.1g,1d.3j.1g+3M.1g+q.1f.3N.1g);k.N.4w();if(q.1f.68){4u=T(2h.5C(\'8K\'))||0;k.N.8Q(q,q.1f.4e.K(4u),\'68\')}if(q.7W)q.7W(I);E 3K!=13;1r;1e 27:q.2y=1d.3j+k.N.4y+q.1f.3N+1d.66;q.1f.4e=S;k.N.4w();if(q.7W)q.7W(I);E I;1r;1e 35:k.N.2h=k.N.1R.1N()-1;1r;1e 36:k.N.2h=0;1r;1e 38:k.N.2h--;if(k.N.2h<0)k.N.2h=k.N.1R.1N()-1;1r;1e 40:k.N.2h++;if(k.N.2h==k.N.1R.1N())k.N.2h=0;1r}k.N.8Q(q,q.1f.4e.K(k.N.2h||0),\'7J\');k.N.1R.K(k.N.2h||0).3l=q.1f.7H;if(k.N.1R.K(k.N.2h||0).7W)k.N.1R.K(k.N.2h||0).7W(I);if(q.1f.aU){D aK=k.N.1R.K(k.N.2h||0).5C(\'4I\');q.2y=1d.3j+aK+q.1f.3N+1d.66;if(k.N.4y.1g!=aK.1g)k.N.6J(q,1d.3j.1g+k.N.4y.1g,1d.3j.1g+aK.1g)}E I}k.N.dO.1D(q);if(q.1f.9x==I){if(1d.3o!=k.N.4y&&1d.3o.1g>=q.1f.aL)k.N.2I=1X.9T(k.N.dQ,q.1f.54);if(k.N.1R){k.N.4w()}}E 1b},8Q:u(2n,3o,1u){if(2n.1f[1u]){D 81={};ar=3o.f3(\'*\');24(i=0;i<ar.1g;i++){81[ar[i].4Y]=ar[i].7c.h4}2n.1f[1u].1D(2n,[81])}},di:u(e){if(k.N.1R){if(k.N.2h!=S)k.N.1R.K(k.N.2h||0).3l=\'\';k.N.1R.K(k.N.2h||0).3l=\'\';k.N.2h=T(q.5C(\'8K\'))||0;k.N.1R.K(k.N.2h||0).3l=k.N.1d.1f.7H}},dj:u(2k){1X.bH(k.N.2I);2k=2k||k.2k.gS(1X.2k);2k.aP();2k.aW();D 1d=k.N.aY(k.N.1d);D 3M=q.5C(\'4I\');k.N.1d.2y=1d.3j+3M+k.N.1d.1f.3N+1d.66;k.N.4y=q.5C(\'4I\');k.N.6J(k.N.1d,1d.3j.1g+3M.1g+k.N.1d.1f.3N.1g,1d.3j.1g+3M.1g+k.N.1d.1f.3N.1g);k.N.4w();if(k.N.1d.1f.68){4u=T(q.5C(\'8K\'))||0;k.N.8Q(k.N.1d,k.N.1d.1f.4e.K(4u),\'68\')}E I},eJ:u(e){3K=e.7L||e.7K||-1;if(/13|27|35|36|38|40/.48(3K)&&k.N.1R){if(1X.2k){1X.2k.bT=1b;1X.2k.c0=I}P{e.aP();e.aW()}E I}},2r:u(M){if(!M.aN||!k.1a){E}if(!k.N.1c){if(k.3a.4t){k(\'2e\',1h).1S(\'<3A 14="19:1o;Y:1P;5E:9n:9w.9y.cC(1G=0);" id="ds" 2J="ek:I;" ej="0" ep="cD"></3A>\');k.N.3A=k(\'#ds\')}k(\'2e\',1h).1S(\'<22 id="dr" 14="Y: 1P; Q: 0; O: 0; z-cZ: h3; 19: 1o;"><9h 14="6w: 0;8F: 0; h1-14: 1o; z-cZ: h0;">&7k;</9h></22>\');k.N.1c=k(\'#dr\');k.N.8R=k(\'9h\',k.N.1c)}E q.1E(u(){if(q.4Y!=\'ch\'&&q.5C(\'1u\')!=\'3g\')E;q.1f={};q.1f.aN=M.aN;q.1f.aL=18.3S(T(M.aL)||1);q.1f.aM=M.aM?M.aM:\'\';q.1f.7H=M.7H?M.7H:\'\';q.1f.68=M.68&&M.68.1K==2A?M.68:S;q.1f.2Y=M.2Y&&M.2Y.1K==2A?M.2Y:S;q.1f.3i=M.3i&&M.3i.1K==2A?M.3i:S;q.1f.7J=M.7J&&M.7J.1K==2A?M.7J:S;q.1f.bV=M.bV||I;q.1f.aQ=M.aQ||I;q.1f.3N=q.1f.aQ?(M.3N||\', \'):\'\';q.1f.aU=M.aU?1b:I;q.1f.54=18.3S(T(M.54)||aC);if(M.fx&&M.fx.1K==7M){if(!M.fx.1u||!/c6|1z|a7/.48(M.fx.1u)){M.fx.1u=\'1z\'}if(M.fx.1u==\'1z\'&&!k.fx.1z)E;if(M.fx.1u==\'a7\'&&!k.fx.61)E;M.fx.1m=18.3S(T(M.fx.1m)||8J);if(M.fx.1m>q.1f.54){M.fx.1m=q.1f.54-2a}q.1f.fx=M.fx}q.1f.4e=S;q.1f.9x=I;k(q).1p(\'bU\',\'eN\').6K(u(){k.N.1d=q;k.N.4y=q.2y}).dH(k.N.eJ).6y(k.N.bU).5B(u(){k.N.2I=1X.9T(k.N.4w,hM)})})}};k.fn.hR=k.N.2r;k.1y={2I:S,4Q:S,29:S,2D:10,26:u(el,4J,2D,eG){k.1y.4Q=el;k.1y.29=4J;k.1y.2D=T(2D)||10;k.1y.2I=1X.6V(k.1y.eF,T(eG)||40)},eF:u(){24(i=0;i<k.1y.29.1g;i++){if(!k.1y.29[i].2X){k.1y.29[i].2X=k.23(k.1a.7G(k.1y.29[i]),k.1a.74(k.1y.29[i]),k.1a.6z(k.1y.29[i]))}P{k.1y.29[i].2X.t=k.1y.29[i].3d;k.1y.29[i].2X.l=k.1y.29[i].3c}if(k.1y.4Q.A&&k.1y.4Q.A.7q==1b){69={x:k.1y.4Q.A.2v,y:k.1y.4Q.A.2q,1C:k.1y.4Q.A.1B.1C,hb:k.1y.4Q.A.1B.hb}}P{69=k.23(k.1a.7G(k.1y.4Q),k.1a.74(k.1y.4Q))}if(k.1y.29[i].2X.t>0&&k.1y.29[i].2X.y+k.1y.29[i].2X.t>69.y){k.1y.29[i].3d-=k.1y.2D}P if(k.1y.29[i].2X.t<=k.1y.29[i].2X.h&&k.1y.29[i].2X.t+k.1y.29[i].2X.hb<69.y+69.hb){k.1y.29[i].3d+=k.1y.2D}if(k.1y.29[i].2X.l>0&&k.1y.29[i].2X.x+k.1y.29[i].2X.l>69.x){k.1y.29[i].3c-=k.1y.2D}P if(k.1y.29[i].2X.l<=k.1y.29[i].2X.hP&&k.1y.29[i].2X.l+k.1y.29[i].2X.1C<69.x+69.1C){k.1y.29[i].3c+=k.1y.2D}}},8o:u(){1X.5T(k.1y.2I);k.1y.4Q=S;k.1y.29=S;24(i in k.1y.29){k.1y.29[i].2X=S}}};k.11={1c:S,F:S,4U:u(){E q.1E(u(){if(q.9I){q.A.5e.3q(\'5v\',k.11.bN);q.A=S;q.9I=I;if(k.3a.4t){q.bE="eN"}P{q.14.hq=\'\';q.14.e1=\'\';q.14.e7=\'\'}}})},bN:u(e){if(k.11.F!=S){k.11.9A(e);E I}D C=q.3U;k(1h).1J(\'3D\',k.11.bX).1J(\'5P\',k.11.9A);C.A.1s=k.1a.4a(e);C.A.4B=C.A.1s;C.A.7q=I;C.A.ho=q!=q.3U;k.11.F=C;if(C.A.5i&&q!=q.3U){bS=k.1a.3w(C.31);bQ=k.1a.2o(C);bR={x:T(k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};dx=C.A.4B.x-bS.x-bQ.1C/2-bR.x;dy=C.A.4B.y-bS.y-bQ.hb/2-bR.y;k.3b.5c(C,[dx,dy])}E k.7n||I},ea:u(e){D C=k.11.F;C.A.7q=1b;D 9G=C.14;C.A.7V=k.B(C,\'19\');C.A.4n=k.B(C,\'Y\');if(!C.A.cz)C.A.cz=C.A.4n;C.A.2c={x:T(k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};C.A.9B=0;C.A.ai=0;if(k.3a.4t){D bW=k.1a.6U(C,1b);C.A.9B=bW.l||0;C.A.ai=bW.t||0}C.A.1B=k.23(k.1a.3w(C),k.1a.2o(C));if(C.A.4n!=\'2s\'&&C.A.4n!=\'1P\'){9G.Y=\'2s\'}k.11.1c.5o();D 5g=C.fI(1b);k(5g).B({19:\'2B\',O:\'2P\',Q:\'2P\'});5g.14.5K=\'0\';5g.14.5z=\'0\';5g.14.5k=\'0\';5g.14.5j=\'0\';k.11.1c.1S(5g);D 3Y=k.11.1c.K(0).14;if(C.A.bD){3Y.Z=\'9F\';3Y.W=\'9F\'}P{3Y.W=C.A.1B.hb+\'U\';3Y.Z=C.A.1B.1C+\'U\'}3Y.19=\'2B\';3Y.5K=\'2P\';3Y.5z=\'2P\';3Y.5k=\'2P\';3Y.5j=\'2P\';k.23(C.A.1B,k.1a.2o(5g));if(C.A.2V){if(C.A.2V.O){C.A.2c.x+=C.A.1s.x-C.A.1B.x-C.A.2V.O;C.A.1B.x=C.A.1s.x-C.A.2V.O}if(C.A.2V.Q){C.A.2c.y+=C.A.1s.y-C.A.1B.y-C.A.2V.Q;C.A.1B.y=C.A.1s.y-C.A.2V.Q}if(C.A.2V.2L){C.A.2c.x+=C.A.1s.x-C.A.1B.x-C.A.1B.hb+C.A.2V.2L;C.A.1B.x=C.A.1s.x-C.A.1B.1C+C.A.2V.2L}if(C.A.2V.4D){C.A.2c.y+=C.A.1s.y-C.A.1B.y-C.A.1B.hb+C.A.2V.4D;C.A.1B.y=C.A.1s.y-C.A.1B.hb+C.A.2V.4D}}C.A.2v=C.A.2c.x;C.A.2q=C.A.2c.y;if(C.A.8s||C.A.2p==\'94\'){8U=k.1a.6U(C.31,1b);C.A.1B.x=C.8t+(k.3a.4t?0:k.3a.7I?-8U.l:8U.l);C.A.1B.y=C.8G+(k.3a.4t?0:k.3a.7I?-8U.t:8U.t);k(C.31).1S(k.11.1c.K(0))}if(C.A.2p){k.11.c5(C);C.A.5t.2p=k.11.ce}if(C.A.5i){k.3b.ct(C)}3Y.O=C.A.1B.x-C.A.9B+\'U\';3Y.Q=C.A.1B.y-C.A.ai+\'U\';3Y.Z=C.A.1B.1C+\'U\';3Y.W=C.A.1B.hb+\'U\';k.11.F.A.9E=I;if(C.A.gx){C.A.5t.6a=k.11.c7}if(C.A.3I!=I){k.11.1c.B(\'3I\',C.A.3I)}if(C.A.1G){k.11.1c.B(\'1G\',C.A.1G);if(1X.71){k.11.1c.B(\'5E\',\'8V(1G=\'+C.A.1G*2a+\')\')}}if(C.A.7O){k.11.1c.2R(C.A.7O);k.11.1c.K(0).7c.14.19=\'1o\'}if(C.A.4o)C.A.4o.1D(C,[5g,C.A.2c.x,C.A.2c.y]);if(k.1x&&k.1x.8D>0){k.1x.ed(C)}if(C.A.46==I){9G.19=\'1o\'}E I},c5:u(C){if(C.A.2p.1K==b0){if(C.A.2p==\'94\'){C.A.28=k.23({x:0,y:0},k.1a.2o(C.31));D 8S=k.1a.6U(C.31,1b);C.A.28.w=C.A.28.1C-8S.l-8S.r;C.A.28.h=C.A.28.hb-8S.t-8S.b}P if(C.A.2p==\'1h\'){D bY=k.1a.bm();C.A.28={x:0,y:0,w:bY.w,h:bY.h}}}P if(C.A.2p.1K==7F){C.A.28={x:T(C.A.2p[0])||0,y:T(C.A.2p[1])||0,w:T(C.A.2p[2])||0,h:T(C.A.2p[3])||0}}C.A.28.dx=C.A.28.x-C.A.1B.x;C.A.28.dy=C.A.28.y-C.A.1B.y},9H:u(F){if(F.A.8s||F.A.2p==\'94\'){k(\'2e\',1h).1S(k.11.1c.K(0))}k.11.1c.5o().2G().B(\'1G\',1);if(1X.71){k.11.1c.B(\'5E\',\'8V(1G=2a)\')}},9A:u(e){k(1h).3q(\'3D\',k.11.bX).3q(\'5P\',k.11.9A);if(k.11.F==S){E}D F=k.11.F;k.11.F=S;if(F.A.7q==I){E I}if(F.A.44==1b){k(F).B(\'Y\',F.A.4n)}D 9G=F.14;if(F.5i){k.11.1c.B(\'9b\',\'8j\')}if(F.A.7O){k.11.1c.4i(F.A.7O)}if(F.A.6N==I){if(F.A.fx>0){if(!F.A.1O||F.A.1O==\'4j\'){D x=12 k.fx(F,{1m:F.A.fx},\'O\');x.1L(F.A.2c.x,F.A.8y)}if(!F.A.1O||F.A.1O==\'49\'){D y=12 k.fx(F,{1m:F.A.fx},\'Q\');y.1L(F.A.2c.y,F.A.8v)}}P{if(!F.A.1O||F.A.1O==\'4j\')F.14.O=F.A.8y+\'U\';if(!F.A.1O||F.A.1O==\'49\')F.14.Q=F.A.8v+\'U\'}k.11.9H(F);if(F.A.46==I){k(F).B(\'19\',F.A.7V)}}P if(F.A.fx>0){F.A.9E=1b;D dh=I;if(k.1x&&k.1t&&F.A.44){dh=k.1a.3w(k.1t.1c.K(0))}k.11.1c.5w({O:dh?dh.x:F.A.1B.x,Q:dh?dh.y:F.A.1B.y},F.A.fx,u(){F.A.9E=I;if(F.A.46==I){F.14.19=F.A.7V}k.11.9H(F)})}P{k.11.9H(F);if(F.A.46==I){k(F).B(\'19\',F.A.7V)}}if(k.1x&&k.1x.8D>0){k.1x.eO(F)}if(k.1t&&F.A.44){k.1t.fC(F)}if(F.A.2Z&&(F.A.8y!=F.A.2c.x||F.A.8v!=F.A.2c.y)){F.A.2Z.1D(F,F.A.b3||[0,0,F.A.8y,F.A.8v])}if(F.A.3T)F.A.3T.1D(F);E I},c7:u(x,y,dx,dy){if(dx!=0)dx=T((dx+(q.A.gx*dx/18.3S(dx))/2)/q.A.gx)*q.A.gx;if(dy!=0)dy=T((dy+(q.A.gy*dy/18.3S(dy))/2)/q.A.gy)*q.A.gy;E{dx:dx,dy:dy,x:0,y:0}},ce:u(x,y,dx,dy){dx=18.3L(18.3r(dx,q.A.28.dx),q.A.28.w+q.A.28.dx-q.A.1B.1C);dy=18.3L(18.3r(dy,q.A.28.dy),q.A.28.h+q.A.28.dy-q.A.1B.hb);E{dx:dx,dy:dy,x:0,y:0}},bX:u(e){if(k.11.F==S||k.11.F.A.9E==1b){E}D F=k.11.F;F.A.4B=k.1a.4a(e);if(F.A.7q==I){45=18.ez(18.6b(F.A.1s.x-F.A.4B.x,2)+18.6b(F.A.1s.y-F.A.4B.y,2));if(45<F.A.6M){E}P{k.11.ea(e)}}D dx=F.A.4B.x-F.A.1s.x;D dy=F.A.4B.y-F.A.1s.y;24(D i in F.A.5t){D 3y=F.A.5t[i].1D(F,[F.A.2c.x+dx,F.A.2c.y+dy,dx,dy]);if(3y&&3y.1K==7M){dx=i!=\'7R\'?3y.dx:(3y.x-F.A.2c.x);dy=i!=\'7R\'?3y.dy:(3y.y-F.A.2c.y)}}F.A.2v=F.A.1B.x+dx-F.A.9B;F.A.2q=F.A.1B.y+dy-F.A.ai;if(F.A.5i&&(F.A.3H||F.A.2Z)){k.3b.3H(F,F.A.2v,F.A.2q)}if(F.A.4m)F.A.4m.1D(F,[F.A.2c.x+dx,F.A.2c.y+dy]);if(!F.A.1O||F.A.1O==\'4j\'){F.A.8y=F.A.2c.x+dx;k.11.1c.K(0).14.O=F.A.2v+\'U\'}if(!F.A.1O||F.A.1O==\'49\'){F.A.8v=F.A.2c.y+dy;k.11.1c.K(0).14.Q=F.A.2q+\'U\'}if(k.1x&&k.1x.8D>0){k.1x.al(F)}E I},2r:u(o){if(!k.11.1c){k(\'2e\',1h).1S(\'<22 id="e8"></22>\');k.11.1c=k(\'#e8\');D el=k.11.1c.K(0);D 4J=el.14;4J.Y=\'1P\';4J.19=\'1o\';4J.9b=\'8j\';4J.eu=\'1o\';4J.2U=\'2K\';if(1X.71){el.bE="e4"}P{4J.gi=\'1o\';4J.e7=\'1o\';4J.e1=\'1o\'}}if(!o){o={}}E q.1E(u(){if(q.9I||!k.1a)E;if(1X.71){q.gh=u(){E I};q.gj=u(){E I}}D el=q;D 5e=o.3v?k(q).gf(o.3v):k(q);if(k.3a.4t){5e.1E(u(){q.bE="e4"})}P{5e.B(\'-gI-7R-8C\',\'1o\');5e.B(\'7R-8C\',\'1o\');5e.B(\'-gH-7R-8C\',\'1o\')}q.A={5e:5e,6N:o.6N?1b:I,46:o.46?1b:I,44:o.44?o.44:I,5i:o.5i?o.5i:I,8s:o.8s?o.8s:I,3I:o.3I?T(o.3I)||0:I,1G:o.1G?2m(o.1G):I,fx:T(o.fx)||S,6R:o.6R?o.6R:I,5t:{},1s:{},4o:o.4o&&o.4o.1K==2A?o.4o:I,3T:o.3T&&o.3T.1K==2A?o.3T:I,2Z:o.2Z&&o.2Z.1K==2A?o.2Z:I,1O:/49|4j/.48(o.1O)?o.1O:I,6M:o.6M?T(o.6M)||0:0,2V:o.2V?o.2V:I,bD:o.bD?1b:I,7O:o.7O||I};if(o.5t&&o.5t.1K==2A)q.A.5t.7R=o.5t;if(o.4m&&o.4m.1K==2A)q.A.4m=o.4m;if(o.2p&&((o.2p.1K==b0&&(o.2p==\'94\'||o.2p==\'1h\'))||(o.2p.1K==7F&&o.2p.1g==4))){q.A.2p=o.2p}if(o.2O){q.A.2O=o.2O}if(o.6a){if(2g o.6a==\'gz\'){q.A.gx=T(o.6a)||1;q.A.gy=T(o.6a)||1}P if(o.6a.1g==2){q.A.gx=T(o.6a[0])||1;q.A.gy=T(o.6a[1])||1}}if(o.3H&&o.3H.1K==2A){q.A.3H=o.3H}q.9I=1b;5e.1E(u(){q.3U=el});5e.1J(\'5v\',k.11.bN)})}};k.fn.23({aS:k.11.4U,7t:k.11.2r});k.1x={du:u(5J,5G,7Q,7S){E 5J<=k.11.F.A.2v&&(5J+7Q)>=(k.11.F.A.2v+k.11.F.A.1B.w)&&5G<=k.11.F.A.2q&&(5G+7S)>=(k.11.F.A.2q+k.11.F.A.1B.h)?1b:I},cV:u(5J,5G,7Q,7S){E!(5J>(k.11.F.A.2v+k.11.F.A.1B.w)||(5J+7Q)<k.11.F.A.2v||5G>(k.11.F.A.2q+k.11.F.A.1B.h)||(5G+7S)<k.11.F.A.2q)?1b:I},1s:u(5J,5G,7Q,7S){E 5J<k.11.F.A.4B.x&&(5J+7Q)>k.11.F.A.4B.x&&5G<k.11.F.A.4B.y&&(5G+7S)>k.11.F.A.4B.y?1b:I},5r:I,3Q:{},8D:0,3P:{},ed:u(C){if(k.11.F==S){E}D i;k.1x.3Q={};D bJ=I;24(i in k.1x.3P){if(k.1x.3P[i]!=S){D 1j=k.1x.3P[i].K(0);if(k(k.11.F).is(\'.\'+1j.1i.a)){if(1j.1i.m==I){1j.1i.p=k.23(k.1a.7G(1j),k.1a.74(1j));1j.1i.m=1b}if(1j.1i.ac){k.1x.3P[i].2R(1j.1i.ac)}k.1x.3Q[i]=k.1x.3P[i];if(k.1t&&1j.1i.s&&k.11.F.A.44){1j.1i.el=k(\'.\'+1j.1i.a,1j);C.14.19=\'1o\';k.1t.cT(1j);1j.1i.ay=k.1t.8x(k.1p(1j,\'id\')).7l;C.14.19=C.A.7V;bJ=1b}if(1j.1i.9i){1j.1i.9i.1D(k.1x.3P[i].K(0),[k.11.F])}}}}if(bJ){k.1t.26()}},dS:u(){k.1x.3Q={};24(i in k.1x.3P){if(k.1x.3P[i]!=S){D 1j=k.1x.3P[i].K(0);if(k(k.11.F).is(\'.\'+1j.1i.a)){1j.1i.p=k.23(k.1a.7G(1j),k.1a.74(1j));if(1j.1i.ac){k.1x.3P[i].2R(1j.1i.ac)}k.1x.3Q[i]=k.1x.3P[i];if(k.1t&&1j.1i.s&&k.11.F.A.44){1j.1i.el=k(\'.\'+1j.1i.a,1j);C.14.19=\'1o\';k.1t.cT(1j);C.14.19=C.A.7V}}}}},al:u(e){if(k.11.F==S){E}k.1x.5r=I;D i;D bK=I;D eQ=0;24(i in k.1x.3Q){D 1j=k.1x.3Q[i].K(0);if(k.1x.5r==I&&k.1x[1j.1i.t](1j.1i.p.x,1j.1i.p.y,1j.1i.p.1C,1j.1i.p.hb)){if(1j.1i.hc&&1j.1i.h==I){k.1x.3Q[i].2R(1j.1i.hc)}if(1j.1i.h==I&&1j.1i.7x){bK=1b}1j.1i.h=1b;k.1x.5r=1j;if(k.1t&&1j.1i.s&&k.11.F.A.44){k.1t.1c.K(0).3l=1j.1i.eV;k.1t.al(1j)}eQ++}P if(1j.1i.h==1b){if(1j.1i.7y){1j.1i.7y.1D(1j,[e,k.11.1c.K(0).7c,1j.1i.fx])}if(1j.1i.hc){k.1x.3Q[i].4i(1j.1i.hc)}1j.1i.h=I}}if(k.1t&&!k.1x.5r&&k.11.F.44){k.1t.1c.K(0).14.19=\'1o\'}if(bK){k.1x.5r.1i.7x.1D(k.1x.5r,[e,k.11.1c.K(0).7c])}},eO:u(e){D i;24(i in k.1x.3Q){D 1j=k.1x.3Q[i].K(0);if(1j.1i.ac){k.1x.3Q[i].4i(1j.1i.ac)}if(1j.1i.hc){k.1x.3Q[i].4i(1j.1i.hc)}if(1j.1i.s){k.1t.7s[k.1t.7s.1g]=i}if(1j.1i.9l&&1j.1i.h==1b){1j.1i.h=I;1j.1i.9l.1D(1j,[e,1j.1i.fx])}1j.1i.m=I;1j.1i.h=I}k.1x.3Q={}},4U:u(){E q.1E(u(){if(q.9j){if(q.1i.s){id=k.1p(q,\'id\');k.1t.5L[id]=S;k(\'.\'+q.1i.a,q).aS()}k.1x.3P[\'d\'+q.c2]=S;q.9j=I;q.f=S}})},2r:u(o){E q.1E(u(){if(q.9j==1b||!o.3C||!k.1a||!k.11){E}q.1i={a:o.3C,ac:o.9J||I,hc:o.a5||I,eV:o.58||I,9l:o.gq||o.9l||I,7x:o.7x||o.dC||I,7y:o.7y||o.fO||I,9i:o.9i||I,t:o.6I&&(o.6I==\'du\'||o.6I==\'cV\')?o.6I:\'1s\',fx:o.fx?o.fx:I,m:I,h:I};if(o.cQ==1b&&k.1t){id=k.1p(q,\'id\');k.1t.5L[id]=q.1i.a;q.1i.s=1b;if(o.2Z){q.1i.2Z=o.2Z;q.1i.ay=k.1t.8x(id).7l}}q.9j=1b;q.c2=T(18.6o()*c9);k.1x.3P[\'d\'+q.c2]=k(q);k.1x.8D++})}};k.fn.23({dR:k.1x.4U,do:k.1x.2r});k.gD=k.1x.dS;k.3B={1c:S,8L:u(){3g=q.2y;if(!3g)E;14={dz:k(q).B(\'dz\')||\'\',4A:k(q).B(\'4A\')||\'\',8Z:k(q).B(\'8Z\')||\'\',dP:k(q).B(\'dP\')||\'\',dT:k(q).B(\'dT\')||\'\',dU:k(q).B(\'dU\')||\'\',c3:k(q).B(\'c3\')||\'\',dY:k(q).B(\'dY\')||\'\'};k.3B.1c.B(14);3x=k.3B.dX(3g);3x=3x.4E(12 bb("\\\\n","g"),"<br />");k.3B.1c.3x(\'gL\');ci=k.3B.1c.K(0).4c;k.3B.1c.3x(3x);Z=k.3B.1c.K(0).4c+ci;if(q.6l.2M&&Z>q.6l.2M[0]){Z=q.6l.2M[0]}q.14.Z=Z+\'U\';if(q.4Y==\'cf\'){W=k.3B.1c.K(0).5W+ci;if(q.6l.2M&&W>q.6l.2M[1]){W=q.6l.2M[1]}q.14.W=W+\'U\'}},dX:u(3g){cg={\'&\':\'&gK;\',\'<\':\'&gJ;\',\'>\':\'&gt;\',\'"\':\'&gs;\'};24(i in cg){3g=3g.4E(12 bb(i,\'g\'),cg[i])}E 3g},2r:u(2M){if(k.3B.1c==S){k(\'2e\',1h).1S(\'<22 id="dE" 14="Y: 1P; Q: 0; O: 0; 3n: 2K;"></22>\');k.3B.1c=k(\'#dE\')}E q.1E(u(){if(/cf|ch/.48(q.4Y)){if(q.4Y==\'ch\'){dB=q.5C(\'1u\');if(!/3g|gr/.48(dB)){E}}if(2M&&(2M.1K==bn||(2M.1K==7F&&2M.1g==2))){if(2M.1K==bn)2M=[2M,2M];P{2M[0]=T(2M[0])||8J;2M[1]=T(2M[1])||8J}q.6l={2M:2M}}k(q).5B(k.3B.8L).6y(k.3B.8L).dH(k.3B.8L);k.3B.8L.1D(q)}})}};k.fn.kc=k.3B.2r;k.4K=u(e){if(/^kd$|^ke$|^ka$|^6L$|^k9$|^k5$|^k4$|^k6$|^k7$|^2e$|^k8$|^kf$|^kg$|^kn$|^ko$|^kp$|^kq$/i.48(e.9N))E I;P E 1b};k.fx.a0=u(e,65){D c=e.7c;D cs=c.14;cs.Y=65.Y;cs.5K=65.3G.t;cs.5j=65.3G.l;cs.5k=65.3G.b;cs.5z=65.3G.r;cs.Q=65.Q+\'U\';cs.O=65.O+\'U\';e.31.ew(c,e);e.31.km(e)};k.fx.9P=u(e){if(!k.4K(e))E I;D t=k(e);D es=e.14;D 73=I;if(t.B(\'19\')==\'1o\'){5Y=t.B(\'3n\');t.B(\'3n\',\'2K\').1Y();73=1b}D V={};V.Y=t.B(\'Y\');V.1q=k.1a.2o(e);V.3G=k.1a.cy(e);D co=e.4Z?e.4Z.ei:t.B(\'hU\');V.Q=T(t.B(\'Q\'))||0;V.O=T(t.B(\'O\'))||0;D eo=\'kl\'+T(18.6o()*c9);D 6u=1h.3F(/^1T$|^br$|^kh$|^hr$|^8C$|^kj$|^8T$|^3A$|^kk$|^k3$|^k2$|^9h$|^dl$|^jM$/i.48(e.9N)?\'22\':e.9N);k.1p(6u,\'id\',eo);D jN=k(6u).2R(\'jO\');D 4h=6u.14;D Q=0;D O=0;if(V.Y==\'2s\'||V.Y==\'1P\'){Q=V.Q;O=V.O}4h.Q=Q+\'U\';4h.O=O+\'U\';4h.Y=V.Y!=\'2s\'&&V.Y!=\'1P\'?\'2s\':V.Y;4h.W=V.1q.hb+\'U\';4h.Z=V.1q.1C+\'U\';4h.5K=V.3G.t;4h.5z=V.3G.r;4h.5k=V.3G.b;4h.5j=V.3G.l;4h.2U=\'2K\';if(k.3a.4t){4h.ei=co}P{4h.jK=co}if(k.3a=="4t"){es.5E="8V(1G="+0.ex*2a+")"}es.1G=0.ex;e.31.ew(6u,e);6u.jF(e);es.5K=\'2P\';es.5z=\'2P\';es.5k=\'2P\';es.5j=\'2P\';es.Y=\'1P\';es.eu=\'1o\';es.Q=\'2P\';es.O=\'2P\';if(73){t.2G();es.3n=5Y}E{V:V,3p:k(6u)}};k.fx.8E={jE:[0,1V,1V],jG:[eD,1V,1V],jH:[e6,e6,jI],jP:[0,0,0],ks:[0,0,1V],jY:[dv,42,42],jZ:[0,1V,1V],k0:[0,0,7w],k1:[0,7w,7w],jX:[cn,cn,cn],jS:[0,2a,0],jR:[jT,jU,eb],jV:[7w,0,7w],kr:[85,eb,47],kP:[1V,eA,0],kN:[kO,50,kx],kF:[7w,0,0],kD:[ku,f8,kt],ky:[kH,0,9C],kL:[1V,0,1V],kM:[1V,kJ,0],kv:[0,6C,0],kA:[75,0,kE],kC:[eD,eB,eA],kG:[kI,kB,eB],kw:[e0,1V,1V],kz:[eL,kK,eL],kQ:[9C,9C,9C],jC:[1V,iy,iz],iA:[1V,1V,e0],iB:[0,1V,0],ix:[1V,0,1V],iv:[6C,0,0],iq:[0,0,6C],ip:[6C,6C,0],ir:[1V,dv,0],it:[1V,ah,iu],iC:[6C,0,6C],iD:[1V,0,0],iK:[ah,ah,ah],iL:[1V,1V,1V],iM:[1V,1V,0]};k.fx.6D=u(4x,dm){if(k.fx.8E[4x])E{r:k.fx.8E[4x][0],g:k.fx.8E[4x][1],b:k.fx.8E[4x][2]};P if(2W=/^6Y\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*\\)$/.a4(4x))E{r:T(2W[1]),g:T(2W[2]),b:T(2W[3])};P if(2W=/6Y\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*\\)$/.a4(4x))E{r:2m(2W[1])*2.55,g:2m(2W[2])*2.55,b:2m(2W[3])*2.55};P if(2W=/^#([a-fA-79-9])([a-fA-79-9])([a-fA-79-9])$/.a4(4x))E{r:T("77"+2W[1]+2W[1]),g:T("77"+2W[2]+2W[2]),b:T("77"+2W[3]+2W[3])};P if(2W=/^#([a-fA-79-9]{2})([a-fA-79-9]{2})([a-fA-79-9]{2})$/.a4(4x))E{r:T("77"+2W[1]),g:T("77"+2W[2]),b:T("77"+2W[3])};P E dm==1b?I:{r:1V,g:1V,b:1V}};k.fx.dD={5Q:1,5b:1,5O:1,4S:1,4D:1,4A:1,W:1,O:1,c3:1,iI:1,5k:1,5j:1,5z:1,5K:1,8b:1,6x:1,8c:1,av:1,1G:1,iE:1,iF:1,5n:1,4X:1,5U:1,5M:1,2L:1,jD:1,Q:1,Z:1,3I:1};k.fx.dA={7i:1,iG:1,iH:1,io:1,im:1,4x:1,i2:1};k.fx.8A=[\'i3\',\'i4\',\'i5\',\'i1\'];k.fx.cc={\'cd\':[\'2E\',\'dK\'],\'a8\':[\'2E\',\'bh\'],\'6w\':[\'6w\',\'\'],\'8F\':[\'8F\',\'\']};k.fn.23({5w:u(5X,H,G,J){E q.1w(u(){D a1=k.H(H,G,J);D e=12 k.dM(q,a1,5X)})},c4:u(H,J){E q.1w(u(){D a1=k.H(H,J);D e=12 k.c4(q,a1)})},8o:u(2D){E q.1E(u(){if(q.6d)k.by(q,2D)})},i0:u(2D){E q.1E(u(){if(q.6d)k.by(q,2D);if(q.1w&&q.1w[\'fx\'])q.1w.fx=[]})}});k.23({c4:u(2f,M){D z=q,3t;z.2D=u(){if(k.fQ(M.21))M.21.1D(2f)};z.2I=6V(u(){z.2D()},M.1m);2f.6d=z},G:{c8:u(p,n,1W,1H,1m){E((-18.5H(p*18.2Q)/2)+0.5)*1H+1W}},dM:u(2f,M,5X){D z=q,3t;D y=2f.14;D fR=k.B(2f,"2U");D 72=k.B(2f,"19");D 2j={};z.9O=(12 7g()).7z();M.G=M.G&&k.G[M.G]?M.G:\'c8\';z.ag=u(2w,43){if(k.fx.dD[2w]){if(43==\'1Y\'||43==\'2G\'||43==\'3R\'){if(!2f.6v)2f.6v={};D r=2m(k.6E(2f,2w));2f.6v[2w]=r&&r>-c9?r:(2m(k.B(2f,2w))||0);43=43==\'3R\'?(72==\'1o\'?\'1Y\':\'2G\'):43;M[43]=1b;2j[2w]=43==\'1Y\'?[0,2f.6v[2w]]:[2f.6v[2w],0];if(2w!=\'1G\')y[2w]=2j[2w][0]+(2w!=\'3I\'&&2w!=\'8Z\'?\'U\':\'\');P k.1p(y,"1G",2j[2w][0])}P{2j[2w]=[2m(k.6E(2f,2w)),2m(43)||0]}}P if(k.fx.dA[2w])2j[2w]=[k.fx.6D(k.6E(2f,2w)),k.fx.6D(43)];P if(/^6w$|8F$|2E$|a8$|cd$/i.48(2w)){D m=43.4E(/\\s+/g,\' \').4E(/6Y\\s*\\(\\s*/g,\'6Y(\').4E(/\\s*,\\s*/g,\',\').4E(/\\s*\\)/g,\')\').d5(/([^\\s]+)/g);3m(2w){1e\'6w\':1e\'8F\':1e\'cd\':1e\'a8\':m[3]=m[3]||m[1]||m[0];m[2]=m[2]||m[0];m[1]=m[1]||m[0];24(D i=0;i<k.fx.8A.1g;i++){D 64=k.fx.cc[2w][0]+k.fx.8A[i]+k.fx.cc[2w][1];2j[64]=2w==\'a8\'?[k.fx.6D(k.6E(2f,64)),k.fx.6D(m[i])]:[2m(k.6E(2f,64)),2m(m[i])]}1r;1e\'2E\':24(D i=0;i<m.1g;i++){D bd=2m(m[i]);D a9=!hX(bd)?\'dK\':(!/cu|1o|2K|hY|hZ|i6|i7|ii|ij|ik|il/i.48(m[i])?\'bh\':I);if(a9){24(D j=0;j<k.fx.8A.1g;j++){64=\'2E\'+k.fx.8A[j]+a9;2j[64]=a9==\'bh\'?[k.fx.6D(k.6E(2f,64)),k.fx.6D(m[i])]:[2m(k.6E(2f,64)),bd]}}P{y[\'ie\']=m[i]}}1r}}P{y[2w]=43}E I};24(p in 5X){if(p==\'14\'){D 5f=k.bl(5X[p]);24(7A in 5f){q.ag(7A,5f[7A])}}P if(p==\'3l\'){if(1h.af)24(D i=0;i<1h.af.1g;i++){D 7e=1h.af[i].7e||1h.af[i].i9||S;if(7e){24(D j=0;j<7e.1g;j++){if(7e[j].i8==\'.\'+5X[p]){D 6X=12 bb(\'\\.\'+5X[p]+\' {\');D 5Z=7e[j].14.9X;D 5f=k.bl(5Z.4E(6X,\'\').4E(/}/g,\'\'));24(7A in 5f){q.ag(7A,5f[7A])}}}}}}P{q.ag(p,5X[p])}}y.19=72==\'1o\'?\'2B\':72;y.2U=\'2K\';z.2D=u(){D t=(12 7g()).7z();if(t>M.1m+z.9O){5T(z.2I);z.2I=S;24(p in 2j){if(p=="1G")k.1p(y,"1G",2j[p][1]);P if(2g 2j[p][1]==\'8T\')y[p]=\'6Y(\'+2j[p][1].r+\',\'+2j[p][1].g+\',\'+2j[p][1].b+\')\';P y[p]=2j[p][1]+(p!=\'3I\'&&p!=\'8Z\'?\'U\':\'\')}if(M.2G||M.1Y)24(D p in 2f.6v)if(p=="1G")k.1p(y,p,2f.6v[p]);P y[p]="";y.19=M.2G?\'1o\':(72!=\'1o\'?72:\'2B\');y.2U=fR;2f.6d=S;if(k.fQ(M.21))M.21.1D(2f)}P{D n=t-q.9O;D 8w=n/M.1m;24(p in 2j){if(2g 2j[p][1]==\'8T\'){y[p]=\'6Y(\'+T(k.G[M.G](8w,n,2j[p][0].r,(2j[p][1].r-2j[p][0].r),M.1m))+\',\'+T(k.G[M.G](8w,n,2j[p][0].g,(2j[p][1].g-2j[p][0].g),M.1m))+\',\'+T(k.G[M.G](8w,n,2j[p][0].b,(2j[p][1].b-2j[p][0].b),M.1m))+\')\'}P{D bz=k.G[M.G](8w,n,2j[p][0],(2j[p][1]-2j[p][0]),M.1m);if(p=="1G")k.1p(y,"1G",bz);P y[p]=bz+(p!=\'3I\'&&p!=\'8Z\'?\'U\':\'\')}}}};z.2I=6V(u(){z.2D()},13);2f.6d=z},by:u(2f,2D){if(2D)2f.6d.9O-=iO;P{1X.5T(2f.6d.2I);2f.6d=S;k.2H(2f,"fx")}}});k.bl=u(5Z){D 5f={};if(2g 5Z==\'4V\'){5Z=5Z.6c().7C(\';\');24(D i=0;i<5Z.1g;i++){6X=5Z[i].7C(\':\');if(6X.1g==2){5f[k.g6(6X[0].4E(/\\-(\\w)/g,u(m,c){E c.jo()}))]=k.g6(6X[1])}}}E 5f};k.fn.23({g3:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'4F\',G)})},gb:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'4r\',G)})},jl:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'fJ\',G)})},jk:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'O\',G)})},jg:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'2L\',G)})},jf:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.61(q,H,J,\'fh\',G)})}});k.fx.61=u(e,H,J,2S,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;z.el=k(e);z.1N=k.1a.2o(e);z.G=2g J==\'4V\'?J:G||S;if(!e.4s)e.4s=z.el.B(\'19\');if(2S==\'fJ\'){2S=z.el.B(\'19\')==\'1o\'?\'4r\':\'4F\'}P if(2S==\'fh\'){2S=z.el.B(\'19\')==\'1o\'?\'2L\':\'O\'}z.el.1Y();z.H=H;z.J=2g J==\'u\'?J:S;z.fx=k.fx.9P(e);z.2S=2S;z.21=u(){if(z.J&&z.J.1K==2A){z.J.1D(z.el.K(0))}if(z.2S==\'4r\'||z.2S==\'2L\'){z.el.B(\'19\',z.el.K(0).4s==\'1o\'?\'2B\':z.el.K(0).4s)}P{z.el.2G()}k.fx.a0(z.fx.3p.K(0),z.fx.V);k.2H(z.el.K(0),\'1n\')};3m(z.2S){1e\'4F\':63=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'W\');63.1L(z.fx.V.1q.hb,0);1r;1e\'4r\':z.fx.3p.B(\'W\',\'9R\');z.el.1Y();63=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'W\');63.1L(0,z.fx.V.1q.hb);1r;1e\'O\':63=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'Z\');63.1L(z.fx.V.1q.1C,0);1r;1e\'2L\':z.fx.3p.B(\'Z\',\'9R\');z.el.1Y();63=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'Z\');63.1L(0,z.fx.V.1q.1C);1r}};k.fn.ji=u(5D,J){E q.1w(\'1n\',u(){if(!k.4K(q)){k.2H(q,\'1n\');E I}D e=12 k.fx.f4(q,5D,J);e.bp()})};k.fx.f4=u(e,5D,J){D z=q;z.el=k(e);z.el.1Y();z.J=J;z.5D=T(5D)||40;z.V={};z.V.Y=z.el.B(\'Y\');z.V.Q=T(z.el.B(\'Q\'))||0;z.V.O=T(z.el.B(\'O\'))||0;if(z.V.Y!=\'2s\'&&z.V.Y!=\'1P\'){z.el.B(\'Y\',\'2s\')}z.3V=5;z.5y=1;z.bp=u(){z.5y++;z.e=12 k.fx(z.el.K(0),{1m:jj,21:u(){z.e=12 k.fx(z.el.K(0),{1m:80,21:u(){z.5D=T(z.5D/2);if(z.5y<=z.3V)z.bp();P{z.el.B(\'Y\',z.V.Y).B(\'Q\',z.V.Q+\'U\').B(\'O\',z.V.O+\'U\');k.2H(z.el.K(0),\'1n\');if(z.J&&z.J.1K==2A){z.J.1D(z.el.K(0))}}}},\'Q\');z.e.1L(z.V.Q-z.5D,z.V.Q)}},\'Q\');z.e.1L(z.V.Q,z.V.Q-z.5D)}};k.fn.23({jy:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4r\',\'4l\',G)})},jz:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4r\',\'in\',G)})},jA:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4r\',\'3R\',G)})},jB:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4F\',\'4l\',G)})},jx:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4F\',\'in\',G)})},jw:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'4F\',\'3R\',G)})},js:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'O\',\'4l\',G)})},jt:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'O\',\'in\',G)})},ju:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'O\',\'3R\',G)})},jv:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'2L\',\'4l\',G)})},je:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'2L\',\'in\',G)})},jd:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.4f(q,H,J,\'2L\',\'3R\',G)})}});k.fx.4f=u(e,H,J,2S,1u,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;z.el=k(e);z.G=2g J==\'4V\'?J:G||S;z.V={};z.V.Y=z.el.B(\'Y\');z.V.Q=z.el.B(\'Q\');z.V.O=z.el.B(\'O\');if(!e.4s)e.4s=z.el.B(\'19\');if(1u==\'3R\'){1u=z.el.B(\'19\')==\'1o\'?\'in\':\'4l\'}z.el.1Y();if(z.V.Y!=\'2s\'&&z.V.Y!=\'1P\'){z.el.B(\'Y\',\'2s\')}z.1u=1u;J=2g J==\'u\'?J:S;8H=1;3m(2S){1e\'4F\':z.e=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Q\');z.62=2m(z.V.Q)||0;z.9K=z.fG;8H=-1;1r;1e\'4r\':z.e=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Q\');z.62=2m(z.V.Q)||0;z.9K=z.fG;1r;1e\'2L\':z.e=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'O\');z.62=2m(z.V.O)||0;z.9K=z.fy;1r;1e\'O\':z.e=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'O\');z.62=2m(z.V.O)||0;z.9K=z.fy;8H=-1;1r}z.e2=12 k.fx(z.el.K(0),k.H(H,z.G,u(){z.el.B(z.V);if(z.1u==\'4l\'){z.el.B(\'19\',\'1o\')}P z.el.B(\'19\',z.el.K(0).4s==\'1o\'?\'2B\':z.el.K(0).4s);k.2H(z.el.K(0),\'1n\')}),\'1G\');if(1u==\'in\'){z.e.1L(z.62+2a*8H,z.62);z.e2.1L(0,1)}P{z.e.1L(z.62,z.62+2a*8H);z.e2.1L(1,0)}};k.fn.23({j0:u(H,W,J,G){E q.1w(\'1n\',u(){12 k.fx.9L(q,H,W,J,\'fp\',G)})},iW:u(H,W,J,G){E q.1w(\'1n\',u(){12 k.fx.9L(q,H,W,J,\'9M\',G)})},iV:u(H,W,J,G){E q.1w(\'1n\',u(){12 k.fx.9L(q,H,W,J,\'3R\',G)})}});k.fx.9L=u(e,H,W,J,1u,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;z.el=k(e);z.G=2g J==\'4V\'?J:G||S;z.J=2g J==\'u\'?J:S;if(1u==\'3R\'){1u=z.el.B(\'19\')==\'1o\'?\'9M\':\'fp\'}z.H=H;z.W=W&&W.1K==bn?W:20;z.fx=k.fx.9P(e);z.1u=1u;z.21=u(){if(z.J&&z.J.1K==2A){z.J.1D(z.el.K(0))}if(z.1u==\'9M\'){z.el.1Y()}P{z.el.2G()}k.fx.a0(z.fx.3p.K(0),z.fx.V);k.2H(z.el.K(0),\'1n\')};if(z.1u==\'9M\'){z.el.1Y();z.fx.3p.B(\'W\',z.W+\'U\').B(\'Z\',\'9R\');z.ef=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,u(){z.ef=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'W\');z.ef.1L(z.W,z.fx.V.1q.hb)}),\'Z\');z.ef.1L(0,z.fx.V.1q.1C)}P{z.ef=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,u(){z.ef=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G,z.21),\'Z\');z.ef.1L(z.fx.V.1q.1C,0)}),\'W\');z.ef.1L(z.fx.V.1q.hb,z.W)}};k.fn.iR=u(H,4x,J,G){E q.1w(\'fv\',u(){q.6W=k(q).1p("14")||\'\';G=2g J==\'4V\'?J:G||S;J=2g J==\'u\'?J:S;D 9S=k(q).B(\'7i\');D 8I=q.31;7d(9S==\'cu\'&&8I){9S=k(8I).B(\'7i\');8I=8I.31}k(q).B(\'7i\',4x);if(2g q.6W==\'8T\')q.6W=q.6W["9X"];k(q).5w({\'7i\':9S},H,G,u(){k.2H(q,\'fv\');if(2g k(q).1p("14")==\'8T\'){k(q).1p("14")["9X"]="";k(q).1p("14")["9X"]=q.6W}P{k(q).1p("14",q.6W)}if(J)J.1D(q)})})};k.fn.23({iT:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.5m(q,H,J,\'49\',\'6g\',G)})},iU:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.5m(q,H,J,\'4j\',\'6g\',G)})},j1:u(H,J,G){E q.1w(\'1n\',u(){if(k.B(q,\'19\')==\'1o\'){12 k.fx.5m(q,H,J,\'4j\',\'6Z\',G)}P{12 k.fx.5m(q,H,J,\'4j\',\'6g\',G)}})},j2:u(H,J,G){E q.1w(\'1n\',u(){if(k.B(q,\'19\')==\'1o\'){12 k.fx.5m(q,H,J,\'49\',\'6Z\',G)}P{12 k.fx.5m(q,H,J,\'49\',\'6g\',G)}})},j9:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.5m(q,H,J,\'49\',\'6Z\',G)})},ja:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.5m(q,H,J,\'4j\',\'6Z\',G)})}});k.fx.5m=u(e,H,J,2S,1u,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;D 73=I;z.el=k(e);z.G=2g J==\'4V\'?J:G||S;z.J=2g J==\'u\'?J:S;z.1u=1u;z.H=H;z.2i=k.1a.2o(e);z.V={};z.V.Y=z.el.B(\'Y\');z.V.19=z.el.B(\'19\');if(z.V.19==\'1o\'){5Y=z.el.B(\'3n\');z.el.1Y();73=1b}z.V.Q=z.el.B(\'Q\');z.V.O=z.el.B(\'O\');if(73){z.el.2G();z.el.B(\'3n\',5Y)}z.V.Z=z.2i.w+\'U\';z.V.W=z.2i.h+\'U\';z.V.2U=z.el.B(\'2U\');z.2i.Q=T(z.V.Q)||0;z.2i.O=T(z.V.O)||0;if(z.V.Y!=\'2s\'&&z.V.Y!=\'1P\'){z.el.B(\'Y\',\'2s\')}z.el.B(\'2U\',\'2K\').B(\'W\',1u==\'6Z\'&&2S==\'49\'?1:z.2i.h+\'U\').B(\'Z\',1u==\'6Z\'&&2S==\'4j\'?1:z.2i.w+\'U\');z.21=u(){z.el.B(z.V);if(z.1u==\'6g\')z.el.2G();P z.el.1Y();k.2H(z.el.K(0),\'1n\')};3m(2S){1e\'49\':z.eh=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'W\');z.et=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'Q\');if(z.1u==\'6g\'){z.eh.1L(z.2i.h,0);z.et.1L(z.2i.Q,z.2i.Q+z.2i.h/2)}P{z.eh.1L(0,z.2i.h);z.et.1L(z.2i.Q+z.2i.h/2,z.2i.Q)}1r;1e\'4j\':z.eh=12 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Z\');z.et=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'O\');if(z.1u==\'6g\'){z.eh.1L(z.2i.w,0);z.et.1L(z.2i.O,z.2i.O+z.2i.w/2)}P{z.eh.1L(0,z.2i.w);z.et.1L(z.2i.O+z.2i.w/2,z.2i.O)}1r}};k.fn.bg=u(H,3V,J){E q.1w(\'1n\',u(){if(!k.4K(q)){k.2H(q,\'1n\');E I}D fx=12 k.fx.bg(q,H,3V,J);fx.bf()})};k.fx.bg=u(el,H,3V,J){D z=q;z.3V=3V;z.5y=1;z.el=el;z.H=H;z.J=J;k(z.el).1Y();z.bf=u(){z.5y++;z.e=12 k.fx(z.el,k.H(z.H,u(){z.ef=12 k.fx(z.el,k.H(z.H,u(){if(z.5y<=z.3V)z.bf();P{k.2H(z.el,\'1n\');if(z.J&&z.J.1K==2A){z.J.1D(z.el)}}}),\'1G\');z.ef.1L(0,1)}),\'1G\');z.e.1L(1,0)}};k.fn.23({jb:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.6G(q,H,1,2a,1b,J,\'fa\',G)})},jc:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.6G(q,H,2a,1,1b,J,\'b4\',G)})},j8:u(H,J,G){E q.1w(\'1n\',u(){D G=G||\'fl\';12 k.fx.6G(q,H,2a,f8,1b,J,\'6h\',G)})},6G:u(H,57,30,6H,J,G){E q.1w(\'1n\',u(){12 k.fx.6G(q,H,57,30,6H,J,\'6G\',G)})}});k.fx.6G=u(e,H,57,30,6H,J,1u,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;z.el=k(e);z.57=T(57)||2a;z.30=T(30)||2a;z.G=2g J==\'4V\'?J:G||S;z.J=2g J==\'u\'?J:S;z.1m=k.H(H).1m;z.6H=6H||S;z.2i=k.1a.2o(e);z.V={Z:z.el.B(\'Z\'),W:z.el.B(\'W\'),4A:z.el.B(\'4A\')||\'2a%\',Y:z.el.B(\'Y\'),19:z.el.B(\'19\'),Q:z.el.B(\'Q\'),O:z.el.B(\'O\'),2U:z.el.B(\'2U\'),4S:z.el.B(\'4S\'),5O:z.el.B(\'5O\'),5Q:z.el.B(\'5Q\'),5b:z.el.B(\'5b\'),5M:z.el.B(\'5M\'),5U:z.el.B(\'5U\'),5n:z.el.B(\'5n\'),4X:z.el.B(\'4X\')};z.Z=T(z.V.Z)||e.4c||0;z.W=T(z.V.W)||e.5W||0;z.Q=T(z.V.Q)||0;z.O=T(z.V.O)||0;1q=[\'em\',\'U\',\'j7\',\'%\'];24(i in 1q){if(z.V.4A.3J(1q[i])>0){z.fg=1q[i];z.4A=2m(z.V.4A)}if(z.V.4S.3J(1q[i])>0){z.fc=1q[i];z.bw=2m(z.V.4S)||0}if(z.V.5O.3J(1q[i])>0){z.fe=1q[i];z.bc=2m(z.V.5O)||0}if(z.V.5Q.3J(1q[i])>0){z.fL=1q[i];z.bA=2m(z.V.5Q)||0}if(z.V.5b.3J(1q[i])>0){z.g8=1q[i];z.bt=2m(z.V.5b)||0}if(z.V.5M.3J(1q[i])>0){z.g4=1q[i];z.bx=2m(z.V.5M)||0}if(z.V.5U.3J(1q[i])>0){z.g9=1q[i];z.bv=2m(z.V.5U)||0}if(z.V.5n.3J(1q[i])>0){z.gc=1q[i];z.bj=2m(z.V.5n)||0}if(z.V.4X.3J(1q[i])>0){z.fK=1q[i];z.b7=2m(z.V.4X)||0}}if(z.V.Y!=\'2s\'&&z.V.Y!=\'1P\'){z.el.B(\'Y\',\'2s\')}z.el.B(\'2U\',\'2K\');z.1u=1u;3m(z.1u){1e\'fa\':z.4b=z.Q+z.2i.h/2;z.5a=z.Q;z.4k=z.O+z.2i.w/2;z.59=z.O;1r;1e\'b4\':z.5a=z.Q+z.2i.h/2;z.4b=z.Q;z.59=z.O+z.2i.w/2;z.4k=z.O;1r;1e\'6h\':z.5a=z.Q-z.2i.h/4;z.4b=z.Q;z.59=z.O-z.2i.w/4;z.4k=z.O;1r}z.be=I;z.t=(12 7g).7z();z.4w=u(){5T(z.2I);z.2I=S};z.2D=u(){if(z.be==I){z.el.1Y();z.be=1b}D t=(12 7g).7z();D n=t-z.t;D p=n/z.1m;if(t>=z.1m+z.t){9T(u(){o=1;if(z.1u){t=z.5a;l=z.59;if(z.1u==\'6h\')o=0}z.bs(z.30,l,t,1b,o)},13);z.4w()}P{o=1;if(!k.G||!k.G[z.G]){s=((-18.5H(p*18.2Q)/2)+0.5)*(z.30-z.57)+z.57}P{s=k.G[z.G](p,n,z.57,(z.30-z.57),z.1m)}if(z.1u){if(!k.G||!k.G[z.G]){t=((-18.5H(p*18.2Q)/2)+0.5)*(z.5a-z.4b)+z.4b;l=((-18.5H(p*18.2Q)/2)+0.5)*(z.59-z.4k)+z.4k;if(z.1u==\'6h\')o=((-18.5H(p*18.2Q)/2)+0.5)*(-0.9Y)+0.9Y}P{t=k.G[z.G](p,n,z.4b,(z.5a-z.4b),z.1m);l=k.G[z.G](p,n,z.4k,(z.59-z.4k),z.1m);if(z.1u==\'6h\')o=k.G[z.G](p,n,0.9Y,-0.9Y,z.1m)}}z.bs(s,l,t,I,o)}};z.2I=6V(u(){z.2D()},13);z.bs=u(4q,O,Q,fM,1G){z.el.B(\'W\',z.W*4q/2a+\'U\').B(\'Z\',z.Z*4q/2a+\'U\').B(\'O\',O+\'U\').B(\'Q\',Q+\'U\').B(\'4A\',z.4A*4q/2a+z.fg);if(z.bw)z.el.B(\'4S\',z.bw*4q/2a+z.fc);if(z.bc)z.el.B(\'5O\',z.bc*4q/2a+z.fe);if(z.bA)z.el.B(\'5Q\',z.bA*4q/2a+z.fL);if(z.bt)z.el.B(\'5b\',z.bt*4q/2a+z.g8);if(z.bx)z.el.B(\'5M\',z.bx*4q/2a+z.g4);if(z.bv)z.el.B(\'5U\',z.bv*4q/2a+z.g9);if(z.bj)z.el.B(\'5n\',z.bj*4q/2a+z.gc);if(z.b7)z.el.B(\'4X\',z.b7*4q/2a+z.fK);if(z.1u==\'6h\'){if(1X.71)z.el.K(0).14.5E="8V(1G="+1G*2a+")";z.el.K(0).14.1G=1G}if(fM){if(z.6H){z.el.B(z.V)}if(z.1u==\'b4\'||z.1u==\'6h\'){z.el.B(\'19\',\'1o\');if(z.1u==\'6h\'){if(1X.71)z.el.K(0).14.5E="8V(1G="+2a+")";z.el.K(0).14.1G=1}}P z.el.B(\'19\',\'2B\');if(z.J)z.J.1D(z.el.K(0));k.2H(z.el.K(0),\'1n\')}}};k.fn.23({9U:u(H,1O,G){o=k.H(H);E q.1w(\'1n\',u(){12 k.fx.9U(q,o,1O,G)})},j6:u(H,1O,G){E q.1E(u(){k(\'a[@3h*="#"]\',q).5h(u(e){fW=q.3h.7C(\'#\');k(\'#\'+fW[1]).9U(H,1O,G);E I})})}});k.fx.9U=u(e,o,1O,G){D z=q;z.o=o;z.e=e;z.1O=/fT|gd/.48(1O)?1O:I;z.G=G;p=k.1a.3w(e);s=k.1a.6z();z.4w=u(){5T(z.2I);z.2I=S;k.2H(z.e,\'1n\')};z.t=(12 7g).7z();s.h=s.h>s.ih?(s.h-s.ih):s.h;s.w=s.w>s.iw?(s.w-s.iw):s.w;z.5a=p.y>s.h?s.h:p.y;z.59=p.x>s.w?s.w:p.x;z.4b=s.t;z.4k=s.l;z.2D=u(){D t=(12 7g).7z();D n=t-z.t;D p=n/z.o.1m;if(t>=z.o.1m+z.t){z.4w();9T(u(){z.d3(z.5a,z.59)},13)}P{if(!z.1O||z.1O==\'fT\'){if(!k.G||!k.G[z.G]){9V=((-18.5H(p*18.2Q)/2)+0.5)*(z.5a-z.4b)+z.4b}P{9V=k.G[z.G](p,n,z.4b,(z.5a-z.4b),z.o.1m)}}P{9V=z.4b}if(!z.1O||z.1O==\'gd\'){if(!k.G||!k.G[z.G]){9W=((-18.5H(p*18.2Q)/2)+0.5)*(z.59-z.4k)+z.4k}P{9W=k.G[z.G](p,n,z.4k,(z.59-z.4k),z.o.1m)}}P{9W=z.4k}z.d3(9V,9W)}};z.d3=u(t,l){1X.j4(l,t)};z.2I=6V(u(){z.2D()},13)};k.fn.cY=u(3V,J){E q.1w(\'1n\',u(){if(!k.4K(q)){k.2H(q,\'1n\');E I}D e=12 k.fx.cY(q,3V,J);e.cG()})};k.fx.cY=u(e,3V,J){D z=q;z.el=k(e);z.el.1Y();z.3V=T(3V)||3;z.J=J;z.5y=1;z.V={};z.V.Y=z.el.B(\'Y\');z.V.Q=T(z.el.B(\'Q\'))||0;z.V.O=T(z.el.B(\'O\'))||0;if(z.V.Y!=\'2s\'&&z.V.Y!=\'1P\'){z.el.B(\'Y\',\'2s\')}z.cG=u(){z.5y++;z.e=12 k.fx(z.el.K(0),{1m:60,21:u(){z.e=12 k.fx(z.el.K(0),{1m:60,21:u(){z.e=12 k.fx(e,{1m:60,21:u(){if(z.5y<=z.3V)z.cG();P{z.el.B(\'Y\',z.V.Y).B(\'Q\',z.V.Q+\'U\').B(\'O\',z.V.O+\'U\');k.2H(z.el.K(0),\'1n\');if(z.J&&z.J.1K==2A){z.J.1D(z.el.K(0))}}}},\'O\');z.e.1L(z.V.O-20,z.V.O)}},\'O\');z.e.1L(z.V.O+20,z.V.O-20)}},\'O\');z.e.1L(z.V.O,z.V.O+20)}};k.fn.23({fo:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4F\',\'in\',G)})},fq:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4F\',\'4l\',G)})},iY:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4F\',\'3R\',G)})},iX:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4r\',\'in\',G)})},jr:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4r\',\'4l\',G)})},jq:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'4r\',\'3R\',G)})},jp:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'O\',\'in\',G)})},jn:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'O\',\'4l\',G)})},jm:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'O\',\'3R\',G)})},iP:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'2L\',\'in\',G)})},ic:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'2L\',\'4l\',G)})},ib:u(H,J,G){E q.1w(\'1n\',u(){12 k.fx.1z(q,H,J,\'2L\',\'3R\',G)})}});k.fx.1z=u(e,H,J,2S,1u,G){if(!k.4K(e)){k.2H(e,\'1n\');E I}D z=q;z.el=k(e);z.G=2g J==\'4V\'?J:G||S;z.J=2g J==\'u\'?J:S;if(1u==\'3R\'){1u=z.el.B(\'19\')==\'1o\'?\'in\':\'4l\'}if(!e.4s)e.4s=z.el.B(\'19\');z.el.1Y();z.H=H;z.fx=k.fx.9P(e);z.1u=1u;z.2S=2S;z.21=u(){if(z.1u==\'4l\')z.el.B(\'3n\',\'2K\');k.fx.a0(z.fx.3p.K(0),z.fx.V);if(z.1u==\'in\'){z.el.B(\'19\',z.el.K(0).4s==\'1o\'?\'2B\':z.el.K(0).4s)}P{z.el.B(\'19\',\'1o\');z.el.B(\'3n\',\'dd\')}if(z.J&&z.J.1K==2A){z.J.1D(z.el.K(0))}k.2H(z.el.K(0),\'1n\')};3m(z.2S){1e\'4F\':z.ef=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'Q\');z.7v=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G),\'W\');if(z.1u==\'in\'){z.ef.1L(-z.fx.V.1q.hb,0);z.7v.1L(0,z.fx.V.1q.hb)}P{z.ef.1L(0,-z.fx.V.1q.hb);z.7v.1L(z.fx.V.1q.hb,0)}1r;1e\'4r\':z.ef=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'Q\');if(z.1u==\'in\'){z.ef.1L(z.fx.V.1q.hb,0)}P{z.ef.1L(0,z.fx.V.1q.hb)}1r;1e\'O\':z.ef=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'O\');z.7v=12 k.fx(z.fx.3p.K(0),k.H(z.H,z.G),\'Z\');if(z.1u==\'in\'){z.ef.1L(-z.fx.V.1q.1C,0);z.7v.1L(0,z.fx.V.1q.1C)}P{z.ef.1L(0,-z.fx.V.1q.1C);z.7v.1L(z.fx.V.1q.1C,0)}1r;1e\'2L\':z.ef=12 k.fx(z.el.K(0),k.H(z.H,z.G,z.21),\'O\');if(z.1u==\'in\'){z.ef.1L(z.fx.V.1q.1C,0)}P{z.ef.1L(0,z.fx.V.1q.1C)}1r}};k.3f=S;k.fn.ig=u(o){E q.1w(\'1n\',u(){12 k.fx.dG(q,o)})};k.fx.dG=u(e,o){if(k.3f==S){k(\'2e\',1h).1S(\'<22 id="3f"></22>\');k.3f=k(\'#3f\')}k.3f.B(\'19\',\'2B\').B(\'Y\',\'1P\');D z=q;z.el=k(e);if(!o||!o.30){E}if(o.30.1K==b0&&1h.9e(o.30)){o.30=1h.9e(o.30)}P if(!o.30.dq){E}if(!o.1m){o.1m=g5}z.1m=o.1m;z.30=o.30;z.8r=o.3l;z.21=o.21;if(z.8r){k.3f.2R(z.8r)}z.a3=0;z.a2=0;if(k.dF){z.a3=(T(k.3f.B(\'5b\'))||0)+(T(k.3f.B(\'5O\'))||0)+(T(k.3f.B(\'4X\'))||0)+(T(k.3f.B(\'5U\'))||0);z.a2=(T(k.3f.B(\'4S\'))||0)+(T(k.3f.B(\'5Q\'))||0)+(T(k.3f.B(\'5M\'))||0)+(T(k.3f.B(\'5n\'))||0)}z.26=k.23(k.1a.3w(z.el.K(0)),k.1a.2o(z.el.K(0)));z.2T=k.23(k.1a.3w(z.30),k.1a.2o(z.30));z.26.1C-=z.a3;z.26.hb-=z.a2;z.2T.1C-=z.a3;z.2T.hb-=z.a2;z.J=o.21;k.3f.B(\'Z\',z.26.1C+\'U\').B(\'W\',z.26.hb+\'U\').B(\'Q\',z.26.y+\'U\').B(\'O\',z.26.x+\'U\').5w({Q:z.2T.y,O:z.2T.x,Z:z.2T.1C,W:z.2T.hb},z.1m,u(){if(z.8r)k.3f.4i(z.8r);k.3f.B(\'19\',\'1o\');if(z.21&&z.21.1K==2A){z.21.1D(z.el.K(0),[z.30])}k.2H(z.el.K(0),\'1n\')})};k.1v={M:{2E:10,ec:\'1Q/iJ.eZ\',e3:\'<1T 2J="1Q/6g.da" />\',eW:0.8,d8:\'iN a6\',dc:\'57\',3W:8J},jQ:I,jW:I,6j:S,8m:I,8k:I,d1:u(2k){if(!k.1v.8k||k.1v.8m)E;D 3K=2k.7L||2k.7K||-1;3m(3K){1e 35:if(k.1v.6j)k.1v.26(S,k(\'a[@4I=\'+k.1v.6j+\']:jJ\').K(0));1r;1e 36:if(k.1v.6j)k.1v.26(S,k(\'a[@4I=\'+k.1v.6j+\']:jL\').K(0));1r;1e 37:1e 8:1e 33:1e 80:1e kb:D 9p=k(\'#87\');if(9p.K(0).53!=S){9p.K(0).53.1D(9p.K(0))}1r;1e 38:1r;1e 39:1e 34:1e 32:1e gl:1e 78:D 9k=k(\'#88\');if(9k.K(0).53!=S){9k.K(0).53.1D(9k.K(0))}1r;1e 40:1r;1e 27:k.1v.au();1r}},7q:u(M){if(M)k.23(k.1v.M,M);if(1X.2k){k(\'2e\',1h).1J(\'6y\',k.1v.d1)}P{k(1h).1J(\'6y\',k.1v.d1)}k(\'a\').1E(u(){el=k(q);en=el.1p(\'4I\')||\'\';e9=el.1p(\'3h\')||\'\';ev=/\\.da|\\.gw|\\.8X|\\.eZ|\\.gn/g;if(e9.6c().d5(ev)!=S&&en.6c().3J(\'eU\')==0){el.1J(\'5h\',k.1v.26)}});if(k.3a.4t){3A=1h.3F(\'3A\');k(3A).1p({id:\'cN\',2J:\'ek:I;\',ej:\'cD\',ep:\'cD\'}).B({19:\'1o\',Y:\'1P\',Q:\'0\',O:\'0\',5E:\'9n:9w.9y.cC(1G=0)\'});k(\'2e\').1S(3A)}8n=1h.3F(\'22\');k(8n).1p(\'id\',\'cP\').B({Y:\'1P\',19:\'1o\',Q:\'0\',O:\'0\',1G:0}).1S(1h.8M(\' \')).1J(\'5h\',k.1v.au);6A=1h.3F(\'22\');k(6A).1p(\'id\',\'eK\').B({4X:k.1v.M.2E+\'U\'}).1S(1h.8M(\' \'));cE=1h.3F(\'22\');k(cE).1p(\'id\',\'dg\').B({4X:k.1v.M.2E+\'U\',5n:k.1v.M.2E+\'U\'}).1S(1h.8M(\' \'));cF=1h.3F(\'a\');k(cF).1p({id:\'gg\',3h:\'#\'}).B({Y:\'1P\',2L:k.1v.M.2E+\'U\',Q:\'0\'}).1S(k.1v.M.e3).1J(\'5h\',k.1v.au);7m=1h.3F(\'22\');k(7m).1p(\'id\',\'cM\').B({Y:\'2s\',cA:\'O\',6w:\'0 9F\',3I:1}).1S(6A).1S(cE).1S(cF);2b=1h.3F(\'1T\');2b.2J=k.1v.M.ec;k(2b).1p(\'id\',\'eM\').B({Y:\'1P\'});4G=1h.3F(\'a\');k(4G).1p({id:\'87\',3h:\'#\'}).B({Y:\'1P\',19:\'1o\',2U:\'2K\',ey:\'1o\'}).1S(1h.8M(\' \'));4M=1h.3F(\'a\');k(4M).1p({id:\'88\',3h:\'#\'}).B({Y:\'1P\',2U:\'2K\',ey:\'1o\'}).1S(1h.8M(\' \'));1Z=1h.3F(\'22\');k(1Z).1p(\'id\',\'eE\').B({19:\'1o\',Y:\'2s\',2U:\'2K\',cA:\'O\',6w:\'0 9F\',Q:\'0\',O:\'0\',3I:2}).1S([2b,4G,4M]);6F=1h.3F(\'22\');k(6F).1p(\'id\',\'ao\').B({19:\'1o\',Y:\'1P\',2U:\'2K\',Q:\'0\',O:\'0\',cA:\'cv\',7i:\'cu\',hC:\'0\'}).1S([1Z,7m]);k(\'2e\').1S(8n).1S(6F)},26:u(e,C){el=C?k(C):k(q);9t=el.1p(\'4I\');D 6B,4u,4G,4M;if(9t!=\'eU\'){k.1v.6j=9t;8Y=k(\'a[@4I=\'+9t+\']\');6B=8Y.1N();4u=8Y.cZ(C?C:q);4G=8Y.K(4u-1);4M=8Y.K(4u+1)}89=el.1p(\'3h\');6A=el.1p(\'4g\');3O=k.1a.6z();8n=k(\'#cP\');if(!k.1v.8k){k.1v.8k=1b;if(k.3a.4t){k(\'#cN\').B(\'W\',18.3r(3O.ih,3O.h)+\'U\').B(\'Z\',18.3r(3O.iw,3O.w)+\'U\').1Y()}8n.B(\'W\',18.3r(3O.ih,3O.h)+\'U\').B(\'Z\',18.3r(3O.iw,3O.w)+\'U\').1Y().fX(cO,k.1v.M.eW,u(){k.1v.cw(89,6A,3O,6B,4u,4G,4M)});k(\'#ao\').B(\'Z\',18.3r(3O.iw,3O.w)+\'U\')}P{k(\'#87\').K(0).53=S;k(\'#88\').K(0).53=S;k.1v.cw(89,6A,3O,6B,4u,4G,4M)}E I},cw:u(89,gP,3O,6B,4u,4G,4M){k(\'#cW\').bk();aX=k(\'#87\');aX.2G();aO=k(\'#88\');aO.2G();2b=k(\'#eM\');1Z=k(\'#eE\');6F=k(\'#ao\');7m=k(\'#cM\').B(\'3n\',\'2K\');k(\'#eK\').3x(6A);k.1v.8m=1b;if(6B)k(\'#dg\').3x(k.1v.M.d8+\' \'+(4u+1)+\' \'+k.1v.M.dc+\' \'+6B);if(4G){aX.K(0).53=u(){q.5B();k.1v.26(S,4G);E I}}if(4M){aO.K(0).53=u(){q.5B();k.1v.26(S,4M);E I}}2b.1Y();82=k.1a.2o(1Z.K(0));56=18.3r(82.1C,2b.K(0).Z+k.1v.M.2E*2);6f=18.3r(82.hb,2b.K(0).W+k.1v.M.2E*2);2b.B({O:(56-2b.K(0).Z)/2+\'U\',Q:(6f-2b.K(0).W)/2+\'U\'});1Z.B({Z:56+\'U\',W:6f+\'U\'}).1Y();dw=k.1a.bm();6F.B(\'Q\',3O.t+(dw.h/15)+\'U\');if(6F.B(\'19\')==\'1o\'){6F.1Y().7f(k.1v.M.3W)}6k=12 9s;k(6k).1p(\'id\',\'cW\').1J(\'hJ\',u(){56=6k.Z+k.1v.M.2E*2;6f=6k.W+k.1v.M.2E*2;2b.2G();1Z.5w({W:6f},82.hb!=6f?k.1v.M.3W:1,u(){1Z.5w({Z:56},82.1C!=56?k.1v.M.3W:1,u(){1Z.bG(6k);k(6k).B({Y:\'1P\',O:k.1v.M.2E+\'U\',Q:k.1v.M.2E+\'U\'}).7f(k.1v.M.3W,u(){db=k.1a.2o(7m.K(0));if(4G){aX.B({O:k.1v.M.2E+\'U\',Q:k.1v.M.2E+\'U\',Z:56/2-k.1v.M.2E*3+\'U\',W:6f-k.1v.M.2E*2+\'U\'}).1Y()}if(4M){aO.B({O:56/2+k.1v.M.2E*2+\'U\',Q:k.1v.M.2E+\'U\',Z:56/2-k.1v.M.2E*3+\'U\',W:6f-k.1v.M.2E*2+\'U\'}).1Y()}7m.B({Z:56+\'U\',Q:-db.hb+\'U\',3n:\'dd\'}).5w({Q:-1},k.1v.M.3W,u(){k.1v.8m=I})})})})});6k.2J=89},au:u(){k(\'#cW\').bk();k(\'#ao\').2G();k(\'#cM\').B(\'3n\',\'2K\');k(\'#cP\').fX(cO,0,u(){k(q).2G();if(k.3a.4t){k(\'#cN\').2G()}});k(\'#87\').K(0).53=S;k(\'#88\').K(0).53=S;k.1v.6j=S;k.1v.8k=I;k.1v.8m=I;E I}};k.R={1A:S,41:S,F:S,1s:S,1q:S,Y:S,9a:u(e){k.R.F=(q.d0)?q.d0:q;k.R.1s=k.1a.4a(e);k.R.1q={Z:T(k(k.R.F).B(\'Z\'))||0,W:T(k(k.R.F).B(\'W\'))||0};k.R.Y={Q:T(k(k.R.F).B(\'Q\'))||0,O:T(k(k.R.F).B(\'O\'))||0};k(1h).1J(\'3D\',k.R.cR).1J(\'5P\',k.R.cK);if(2g k.R.F.1k.g2===\'u\'){k.R.F.1k.g2.1D(k.R.F)}E I},cK:u(e){k(1h).3q(\'3D\',k.R.cR).3q(\'5P\',k.R.cK);if(2g k.R.F.1k.fN===\'u\'){k.R.F.1k.fN.1D(k.R.F)}k.R.F=S},cR:u(e){if(!k.R.F){E}1s=k.1a.4a(e);7p=k.R.Y.Q-k.R.1s.y+1s.y;7r=k.R.Y.O-k.R.1s.x+1s.x;7p=18.3r(18.3L(7p,k.R.F.1k.8g-k.R.1q.W),k.R.F.1k.7h);7r=18.3r(18.3L(7r,k.R.F.1k.8h-k.R.1q.Z),k.R.F.1k.70);if(2g k.R.F.1k.4m===\'u\'){D 8a=k.R.F.1k.4m.1D(k.R.F,[7r,7p]);if(2g 8a==\'hh\'&&8a.1g==2){7r=8a[0];7p=8a[1]}}k.R.F.14.Q=7p+\'U\';k.R.F.14.O=7r+\'U\';E I},26:u(e){k(1h).1J(\'3D\',k.R.8j).1J(\'5P\',k.R.8o);k.R.1A=q.1A;k.R.41=q.41;k.R.1s=k.1a.4a(e);k.R.1q={Z:T(k(q.1A).B(\'Z\'))||0,W:T(k(q.1A).B(\'W\'))||0};k.R.Y={Q:T(k(q.1A).B(\'Q\'))||0,O:T(k(q.1A).B(\'O\'))||0};if(k.R.1A.1k.4o){k.R.1A.1k.4o.1D(k.R.1A,[q])}E I},8o:u(){k(1h).3q(\'3D\',k.R.8j).3q(\'5P\',k.R.8o);if(k.R.1A.1k.3T){k.R.1A.1k.3T.1D(k.R.1A,[k.R.41])}k.R.1A=S;k.R.41=S},6i:u(dx,az){E 18.3L(18.3r(k.R.1q.Z+dx*az,k.R.1A.1k.av),k.R.1A.1k.6x)},6m:u(dy,az){E 18.3L(18.3r(k.R.1q.W+dy*az,k.R.1A.1k.8c),k.R.1A.1k.8b)},fb:u(W){E 18.3L(18.3r(W,k.R.1A.1k.8c),k.R.1A.1k.8b)},8j:u(e){if(k.R.1A==S){E}1s=k.1a.4a(e);dx=1s.x-k.R.1s.x;dy=1s.y-k.R.1s.y;1I={Z:k.R.1q.Z,W:k.R.1q.W};2z={Q:k.R.Y.Q,O:k.R.Y.O};3m(k.R.41){1e\'e\':1I.Z=k.R.6i(dx,1);1r;1e\'fj\':1I.Z=k.R.6i(dx,1);1I.W=k.R.6m(dy,1);1r;1e\'w\':1I.Z=k.R.6i(dx,-1);2z.O=k.R.Y.O-1I.Z+k.R.1q.Z;1r;1e\'5F\':1I.Z=k.R.6i(dx,-1);2z.O=k.R.Y.O-1I.Z+k.R.1q.Z;1I.W=k.R.6m(dy,1);1r;1e\'76\':1I.W=k.R.6m(dy,-1);2z.Q=k.R.Y.Q-1I.W+k.R.1q.W;1I.Z=k.R.6i(dx,-1);2z.O=k.R.Y.O-1I.Z+k.R.1q.Z;1r;1e\'n\':1I.W=k.R.6m(dy,-1);2z.Q=k.R.Y.Q-1I.W+k.R.1q.W;1r;1e\'at\':1I.W=k.R.6m(dy,-1);2z.Q=k.R.Y.Q-1I.W+k.R.1q.W;1I.Z=k.R.6i(dx,1);1r;1e\'s\':1I.W=k.R.6m(dy,1);1r}if(k.R.1A.1k.4v){if(k.R.41==\'n\'||k.R.41==\'s\')4p=1I.W*k.R.1A.1k.4v;P 4p=1I.Z;4W=k.R.fb(4p*k.R.1A.1k.4v);4p=4W/k.R.1A.1k.4v;3m(k.R.41){1e\'n\':1e\'76\':1e\'at\':2z.Q+=1I.W-4W;1r}3m(k.R.41){1e\'76\':1e\'w\':1e\'5F\':2z.O+=1I.Z-4p;1r}1I.W=4W;1I.Z=4p}if(2z.Q<k.R.1A.1k.7h){4W=1I.W+2z.Q-k.R.1A.1k.7h;2z.Q=k.R.1A.1k.7h;if(k.R.1A.1k.4v){4p=4W/k.R.1A.1k.4v;3m(k.R.41){1e\'76\':1e\'w\':1e\'5F\':2z.O+=1I.Z-4p;1r}1I.Z=4p}1I.W=4W}if(2z.O<k.R.1A.1k.70){4p=1I.Z+2z.O-k.R.1A.1k.70;2z.O=k.R.1A.1k.70;if(k.R.1A.1k.4v){4W=4p*k.R.1A.1k.4v;3m(k.R.41){1e\'n\':1e\'76\':1e\'at\':2z.Q+=1I.W-4W;1r}1I.W=4W}1I.Z=4p}if(2z.Q+1I.W>k.R.1A.1k.8g){1I.W=k.R.1A.1k.8g-2z.Q;if(k.R.1A.1k.4v){1I.Z=1I.W/k.R.1A.1k.4v}}if(2z.O+1I.Z>k.R.1A.1k.8h){1I.Z=k.R.1A.1k.8h-2z.O;if(k.R.1A.1k.4v){1I.W=1I.Z*k.R.1A.1k.4v}}D 6p=I;if(k.R.1A.1k.f7){6p=k.R.1A.1k.f7.1D(k.R.1A,[1I,2z]);if(6p){if(6p.1q){k.23(1I,6p.1q)}if(6p.Y){k.23(2z,6p.Y)}}}8d=k.R.1A.14;8d.O=2z.O+\'U\';8d.Q=2z.Q+\'U\';8d.Z=1I.Z+\'U\';8d.W=1I.W+\'U\';E I},2r:u(M){if(!M||!M.3Z||M.3Z.1K!=7M){E}E q.1E(u(){D el=q;el.1k=M;el.1k.av=M.av||10;el.1k.8c=M.8c||10;el.1k.6x=M.6x||6P;el.1k.8b=M.8b||6P;el.1k.7h=M.7h||-aC;el.1k.70=M.70||-aC;el.1k.8h=M.8h||6P;el.1k.8g=M.8g||6P;d6=k(el).B(\'Y\');if(!(d6==\'2s\'||d6==\'1P\')){el.14.Y=\'2s\'}fS=/n|at|e|fj|s|5F|w|76/g;24(i in el.1k.3Z){if(i.6c().d5(fS)!=S){if(el.1k.3Z[i].1K==b0){3v=k(el.1k.3Z[i]);if(3v.1N()>0){el.1k.3Z[i]=3v.K(0)}}if(el.1k.3Z[i].4Y){el.1k.3Z[i].1A=el;el.1k.3Z[i].41=i;k(el.1k.3Z[i]).1J(\'5v\',k.R.26)}}}if(el.1k.5S){if(2g el.1k.5S===\'4V\'){aV=k(el.1k.5S);if(aV.1N()>0){aV.1E(u(){q.d0=el});aV.1J(\'5v\',k.R.9a)}}P if(el.1k.5S==1b){k(q).1J(\'5v\',k.R.9a)}}})},4U:u(){E q.1E(u(){D el=q;24(i in el.1k.3Z){el.1k.3Z[i].1A=S;el.1k.3Z[i].41=S;k(el.1k.3Z[i]).3q(\'5v\',k.R.26)}if(el.1k.5S){if(2g el.1k.5S===\'4V\'){3v=k(el.1k.5S);if(3v.1N()>0){3v.3q(\'5v\',k.R.9a)}}P if(el.1k.5S==1b){k(q).3q(\'5v\',k.R.9a)}}el.1k=S})}};k.fn.23({hz:k.R.2r,hs:k.R.4U});k.2C=S;k.7n=I;k.3k=S;k.7o=[];k.9v=u(e){D 3K=e.7L||e.7K||-1;if(3K==17||3K==16){k.7n=1b}};k.9u=u(e){k.7n=I};k.dL=u(e){q.f.1s=k.1a.4a(e);q.f.1M=k.23(k.1a.3w(q),k.1a.2o(q));q.f.3e=k.1a.6z(q);q.f.1s.x-=q.f.1M.x;q.f.1s.y-=q.f.1M.y;k(q).1S(k.2C.K(0));if(q.f.hc)k.2C.2R(q.f.hc).B(\'19\',\'2B\');k.2C.B({19:\'2B\',Z:\'2P\',W:\'2P\'});if(q.f.o){k.2C.B(\'1G\',q.f.o)}k.3k=q;k.96=I;k.7o=[];q.f.el.1E(u(){q.1M={x:q.8t+(q.4Z&&!k.3a.7I?T(q.4Z.5b)||0:0)+(k.3k.3c||0),y:q.8G+(q.4Z&&!k.3a.7I?T(q.4Z.4S)||0:0)+(k.3k.3d||0),1C:q.4c,hb:q.5W};if(q.s==1b){if(k.7n==I){q.s=I;k(q).4i(k.3k.f.7j)}P{k.96=1b;k.7o[k.7o.1g]=k.1p(q,\'id\')}}});k.am.1D(q,[e]);k(1h).1J(\'3D\',k.am).1J(\'5P\',k.cX);E I};k.am=u(e){if(!k.3k)E;k.fd.1D(k.3k,[e])};k.fd=u(e){if(!k.3k)E;D 1s=k.1a.4a(e);D 3e=k.1a.6z(k.3k);1s.x+=3e.l-q.f.3e.l-q.f.1M.x;1s.y+=3e.t-q.f.3e.t-q.f.1M.y;D 93=18.3L(1s.x,q.f.1s.x);D 5F=18.3L(18.3S(1s.x-q.f.1s.x),18.3S(q.f.3e.w-93));D 99=18.3L(1s.y,q.f.1s.y);D 9g=18.3L(18.3S(1s.y-q.f.1s.y),18.3S(q.f.3e.h-99));if(q.3d>0&&1s.y-20<q.3d){D 3X=18.3L(3e.t,10);99-=3X;9g+=3X;q.3d-=3X}P if(q.3d+q.f.1M.h<q.f.3e.h&&1s.y+20>q.3d+q.f.1M.h){D 3X=18.3L(q.f.3e.h-q.3d,10);q.3d+=3X;if(q.3d!=3e.t)9g+=3X}if(q.3c>0&&1s.x-20<q.3c){D 3X=18.3L(3e.l,10);93-=3X;5F+=3X;q.3c-=3X}P if(q.3c+q.f.1M.w<q.f.3e.w&&1s.x+20>q.3c+q.f.1M.w){D 3X=18.3L(q.f.3e.w-q.3c,10);q.3c+=3X;if(q.3c!=3e.l)5F+=3X}k.2C.B({O:93+\'U\',Q:99+\'U\',Z:5F+\'U\',W:9g+\'U\'});k.2C.l=93+q.f.3e.l;k.2C.t=99+q.f.3e.t;k.2C.r=k.2C.l+5F;k.2C.b=k.2C.t+9g;k.96=I;q.f.el.1E(u(){aw=k.7o.3J(k.1p(q,\'id\'));if(!(q.1M.x>k.2C.r||(q.1M.x+q.1M.1C)<k.2C.l||q.1M.y>k.2C.b||(q.1M.y+q.1M.hb)<k.2C.t)){k.96=1b;if(q.s!=1b){q.s=1b;k(q).2R(k.3k.f.7j)}if(aw!=-1){q.s=I;k(q).4i(k.3k.f.7j)}}P if((q.s==1b)&&(aw==-1)){q.s=I;k(q).4i(k.3k.f.7j)}P if((!q.s)&&(k.7n==1b)&&(aw!=-1)){q.s=1b;k(q).2R(k.3k.f.7j)}});E I};k.cX=u(e){if(!k.3k)E;k.g0.1D(k.3k,[e])};k.g0=u(e){k(1h).3q(\'3D\',k.am).3q(\'5P\',k.cX);if(!k.3k)E;k.2C.B(\'19\',\'1o\');if(q.f.hc)k.2C.4i(q.f.hc);k.3k=I;k(\'2e\').1S(k.2C.K(0));if(k.96==1b){if(q.f.98)q.f.98(k.cJ(k.1p(q,\'id\')))}P{if(q.f.9d)q.f.9d(k.cJ(k.1p(q,\'id\')))}k.7o=[]};k.cJ=u(s){D h=\'\';D o=[];if(a=k(\'#\'+s)){a.K(0).f.el.1E(u(){if(q.s==1b){if(h.1g>0){h+=\'&\'}h+=s+\'[]=\'+k.1p(q,\'id\');o[o.1g]=k.1p(q,\'id\')}})}E{7l:h,o:o}};k.fn.gZ=u(o){if(!k.2C){k(\'2e\',1h).1S(\'<22 id="2C"></22>\').1J(\'7B\',k.9v).1J(\'6y\',k.9u);k.2C=k(\'#2C\');k.2C.B({Y:\'1P\',19:\'1o\'});if(1X.2k){k(\'2e\',1h).1J(\'7B\',k.9v).1J(\'6y\',k.9u)}P{k(1h).1J(\'7B\',k.9v).1J(\'6y\',k.9u)}}if(!o){o={}}E q.1E(u(){if(q.eP)E;q.eP=1b;q.f={a:o.3C,o:o.1G?2m(o.1G):I,7j:o.eS?o.eS:I,hc:o.58?o.58:I,98:o.98?o.98:I,9d:o.9d?o.9d:I};q.f.el=k(\'.\'+o.3C);k(q).1J(\'5v\',k.dL).B(\'Y\',\'2s\')})};k.3b={bM:1,eH:u(3t){D 3t=3t;E q.1E(u(){q.4z.6s.1E(u(ab){k.3b.5c(q,3t[ab])})})},K:u(){D 3t=[];q.1E(u(cL){if(q.bI){3t[cL]=[];D C=q;D 1q=k.1a.2o(q);q.4z.6s.1E(u(ab){D x=q.8t;D y=q.8G;92=T(x*2a/(1q.w-q.4c));91=T(y*2a/(1q.h-q.5W));3t[cL][ab]=[92||0,91||0,x||0,y||0]})}});E 3t},ct:u(C){C.A.fu=C.A.28.w-C.A.1B.1C;C.A.fw=C.A.28.h-C.A.1B.hb;if(C.9r.4z.bC){9Z=C.9r.4z.6s.K(C.bF+1);if(9Z){C.A.28.w=(T(k(9Z).B(\'O\'))||0)+C.A.1B.1C;C.A.28.h=(T(k(9Z).B(\'Q\'))||0)+C.A.1B.hb}9Q=C.9r.4z.6s.K(C.bF-1);if(9Q){D cU=T(k(9Q).B(\'O\'))||0;D cH=T(k(9Q).B(\'O\'))||0;C.A.28.x+=cU;C.A.28.y+=cH;C.A.28.w-=cU;C.A.28.h-=cH}}C.A.g7=C.A.28.w-C.A.1B.1C;C.A.eC=C.A.28.h-C.A.1B.hb;if(C.A.2O){C.A.gx=((C.A.28.w-C.A.1B.1C)/C.A.2O)||1;C.A.gy=((C.A.28.h-C.A.1B.hb)/C.A.2O)||1;C.A.fU=C.A.g7/C.A.2O;C.A.fH=C.A.eC/C.A.2O}C.A.28.dx=C.A.28.x-C.A.2c.x;C.A.28.dy=C.A.28.y-C.A.2c.y;k.11.1c.B(\'9b\',\'ad\')},3H:u(C,x,y){if(C.A.2O){fE=T(x/C.A.fU);92=fE*2a/C.A.2O;ft=T(y/C.A.fH);91=ft*2a/C.A.2O}P{92=T(x*2a/C.A.fu);91=T(y*2a/C.A.fw)}C.A.b3=[92||0,91||0,x||0,y||0];if(C.A.3H)C.A.3H.1D(C,C.A.b3)},eI:u(2k){3K=2k.7L||2k.7K||-1;3m(3K){1e 35:k.3b.5c(q.3U,[ae,ae]);1r;1e 36:k.3b.5c(q.3U,[-ae,-ae]);1r;1e 37:k.3b.5c(q.3U,[-q.3U.A.gx||-1,0]);1r;1e 38:k.3b.5c(q.3U,[0,-q.3U.A.gy||-1]);1r;1e 39:k.3b.5c(q.3U,[q.3U.A.gx||1,0]);1r;1e 40:k.11.5c(q.3U,[0,q.3U.A.gy||1]);1r}},5c:u(C,Y){if(!C.A){E}C.A.1B=k.23(k.1a.3w(C),k.1a.2o(C));C.A.2c={x:T(k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};C.A.4n=k.B(C,\'Y\');if(C.A.4n!=\'2s\'&&C.A.4n!=\'1P\'){C.14.Y=\'2s\'}k.11.c5(C);k.3b.ct(C);dx=T(Y[0])||0;dy=T(Y[1])||0;2v=C.A.2c.x+dx;2q=C.A.2c.y+dy;if(C.A.2O){3y=k.11.c7.1D(C,[2v,2q,dx,dy]);if(3y.1K==7M){dx=3y.dx;dy=3y.dy}2v=C.A.2c.x+dx;2q=C.A.2c.y+dy}3y=k.11.ce.1D(C,[2v,2q,dx,dy]);if(3y&&3y.1K==7M){dx=3y.dx;dy=3y.dy}2v=C.A.2c.x+dx;2q=C.A.2c.y+dy;if(C.A.5i&&(C.A.3H||C.A.2Z)){k.3b.3H(C,2v,2q)}2v=!C.A.1O||C.A.1O==\'4j\'?2v:C.A.2c.x||0;2q=!C.A.1O||C.A.1O==\'49\'?2q:C.A.2c.y||0;C.14.O=2v+\'U\';C.14.Q=2q+\'U\'},2r:u(o){E q.1E(u(){if(q.bI==1b||!o.3C||!k.1a||!k.11||!k.1x){E}5x=k(o.3C,q);if(5x.1N()==0){E}D 4N={2p:\'94\',5i:1b,3H:o.3H&&o.3H.1K==2A?o.3H:S,2Z:o.2Z&&o.2Z.1K==2A?o.2Z:S,3v:q,1G:o.1G||I};if(o.2O&&T(o.2O)){4N.2O=T(o.2O)||1;4N.2O=4N.2O>0?4N.2O:1}if(5x.1N()==1)5x.7t(4N);P{k(5x.K(0)).7t(4N);4N.3v=S;5x.7t(4N)}5x.7B(k.3b.eI);5x.1p(\'bM\',k.3b.bM++);q.bI=1b;q.4z={};q.4z.er=4N.er;q.4z.2O=4N.2O;q.4z.6s=5x;q.4z.bC=o.bC?1b:I;bZ=q;bZ.4z.6s.1E(u(2N){q.bF=2N;q.9r=bZ});if(o.3t&&o.3t.1K==7F){24(i=o.3t.1g-1;i>=0;i--){if(o.3t[i].1K==7F&&o.3t[i].1g==2){el=q.4z.6s.K(i);if(el.4Y){k.3b.5c(el,o.3t[i])}}}}})}};k.fn.23({hN:k.3b.2r,hS:k.3b.eH,hG:k.3b.K});k.2u={5I:[],eg:u(){q.5B();X=q.31;id=k.1p(X,\'id\');if(k.2u.5I[id]!=S){1X.5T(k.2u.5I[id])}1z=X.L.3u+1;if(X.L.1Q.1g<1z){1z=1}1Q=k(\'1T\',X.L.5u);X.L.3u=1z;if(1Q.1N()>0){1Q.7a(X.L.3W,k.2u.95)}},dp:u(){q.5B();X=q.31;id=k.1p(X,\'id\');if(k.2u.5I[id]!=S){1X.5T(k.2u.5I[id])}1z=X.L.3u-1;1Q=k(\'1T\',X.L.5u);if(1z<1){1z=X.L.1Q.1g}X.L.3u=1z;if(1Q.1N()>0){1Q.7a(X.L.3W,k.2u.95)}},2I:u(c){X=1h.9e(c);if(X.L.6o){1z=X.L.3u;7d(1z==X.L.3u){1z=1+T(18.6o()*X.L.1Q.1g)}}P{1z=X.L.3u+1;if(X.L.1Q.1g<1z){1z=1}}1Q=k(\'1T\',X.L.5u);X.L.3u=1z;if(1Q.1N()>0){1Q.7a(X.L.3W,k.2u.95)}},go:u(o){D X;if(o&&o.1K==7M){if(o.2b){X=1h.9e(o.2b.X);5N=1X.hn.3h.7C("#");o.2b.6S=S;if(5N.1g==2){1z=T(5N[1]);1Y=5N[1].4E(1z,\'\');if(k.1p(X,\'id\')!=1Y){1z=1}}P{1z=1}}if(o.90){o.90.5B();X=o.90.31.31;id=k.1p(X,\'id\');if(k.2u.5I[id]!=S){1X.5T(k.2u.5I[id])}5N=o.90.3h.7C("#");1z=T(5N[1]);1Y=5N[1].4E(1z,\'\');if(k.1p(X,\'id\')!=1Y){1z=1}}if(X.L.1Q.1g<1z||1z<1){1z=1}X.L.3u=1z;52=k.1a.2o(X);dt=k.1a.aT(X);d9=k.1a.6U(X);if(X.L.3z){X.L.3z.o.B(\'19\',\'1o\')}if(X.L.3s){X.L.3s.o.B(\'19\',\'1o\')}if(X.L.2b){y=T(dt.t)+T(d9.t);if(X.L.1U){if(X.L.1U.5A==\'Q\'){y+=X.L.1U.4C.hb}P{52.h-=X.L.1U.4C.hb}}if(X.L.2x){if(X.L.2x&&X.L.2x.6Q==\'Q\'){y+=X.L.2x.4C.hb}P{52.h-=X.L.2x.4C.hb}}if(!X.L.c1){X.L.df=o.2b?o.2b.W:(T(X.L.2b.B(\'W\'))||0);X.L.c1=o.2b?o.2b.Z:(T(X.L.2b.B(\'Z\'))||0)}X.L.2b.B(\'Q\',y+(52.h-X.L.df)/2+\'U\');X.L.2b.B(\'O\',(52.1C-X.L.c1)/2+\'U\');X.L.2b.B(\'19\',\'2B\')}1Q=k(\'1T\',X.L.5u);if(1Q.1N()>0){1Q.7a(X.L.3W,k.2u.95)}P{aj=k(\'a\',X.L.1U.o).K(1z-1);k(aj).2R(X.L.1U.5R);D 1T=12 9s();1T.X=k.1p(X,\'id\');1T.1z=1z-1;1T.2J=X.L.1Q[X.L.3u-1].2J;if(1T.21){1T.6S=S;k.2u.19.1D(1T)}P{1T.6S=k.2u.19}if(X.L.2x){X.L.2x.o.3x(X.L.1Q[1z-1].6L)}}}},95:u(){X=q.31.31;X.L.5u.B(\'19\',\'1o\');if(X.L.1U.5R){aj=k(\'a\',X.L.1U.o).4i(X.L.1U.5R).K(X.L.3u-1);k(aj).2R(X.L.1U.5R)}D 1T=12 9s();1T.X=k.1p(X,\'id\');1T.1z=X.L.3u-1;1T.2J=X.L.1Q[X.L.3u-1].2J;if(1T.21){1T.6S=S;k.2u.19.1D(1T)}P{1T.6S=k.2u.19}if(X.L.2x){X.L.2x.o.3x(X.L.1Q[X.L.3u-1].6L)}},19:u(){X=1h.9e(q.X);if(X.L.3z){X.L.3z.o.B(\'19\',\'1o\')}if(X.L.3s){X.L.3s.o.B(\'19\',\'1o\')}52=k.1a.2o(X);y=0;if(X.L.1U){if(X.L.1U.5A==\'Q\'){y+=X.L.1U.4C.hb}P{52.h-=X.L.1U.4C.hb}}if(X.L.2x){if(X.L.2x&&X.L.2x.6Q==\'Q\'){y+=X.L.2x.4C.hb}P{52.h-=X.L.2x.4C.hb}}hg=k(\'.ca\',X);y=y+(52.h-q.W)/2;x=(52.1C-q.Z)/2;X.L.5u.B(\'Q\',y+\'U\').B(\'O\',x+\'U\').3x(\'<1T 2J="\'+q.2J+\'" />\');X.L.5u.7f(X.L.3W);3s=X.L.3u+1;if(3s>X.L.1Q.1g){3s=1}3z=X.L.3u-1;if(3z<1){3z=X.L.1Q.1g}X.L.3s.o.B(\'19\',\'2B\').B(\'Q\',y+\'U\').B(\'O\',x+2*q.Z/3+\'U\').B(\'Z\',q.Z/3+\'U\').B(\'W\',q.W+\'U\').1p(\'4g\',X.L.1Q[3s-1].6L);X.L.3s.o.K(0).3h=\'#\'+3s+k.1p(X,\'id\');X.L.3z.o.B(\'19\',\'2B\').B(\'Q\',y+\'U\').B(\'O\',x+\'U\').B(\'Z\',q.Z/3+\'U\').B(\'W\',q.W+\'U\').1p(\'4g\',X.L.1Q[3z-1].6L);X.L.3z.o.K(0).3h=\'#\'+3z+k.1p(X,\'id\')},2r:u(o){if(!o||!o.1Z||k.2u.5I[o.1Z])E;D 1Z=k(\'#\'+o.1Z);D el=1Z.K(0);if(el.14.Y!=\'1P\'&&el.14.Y!=\'2s\'){el.14.Y=\'2s\'}el.14.2U=\'2K\';if(1Z.1N()==0)E;el.L={};el.L.1Q=o.1Q?o.1Q:[];el.L.6o=o.6o&&o.6o==1b||I;97=el.f3(\'hL\');24(i=0;i<97.1g;i++){7Z=el.L.1Q.1g;el.L.1Q[7Z]={2J:97[i].2J,6L:97[i].4g||97[i].hD||\'\'}}if(el.L.1Q.1g==0){E}el.L.4n=k.23(k.1a.3w(el),k.1a.2o(el));el.L.b5=k.1a.aT(el);el.L.bu=k.1a.6U(el);t=T(el.L.b5.t)+T(el.L.bu.t);b=T(el.L.b5.b)+T(el.L.bu.b);k(\'1T\',el).bk();el.L.3W=o.3W?o.3W:g5;if(o.5A||o.9f||o.5R){el.L.1U={};1Z.1S(\'<22 6T="g1"></22>\');el.L.1U.o=k(\'.g1\',el);if(o.9f){el.L.1U.9f=o.9f;el.L.1U.o.2R(o.9f)}if(o.5R){el.L.1U.5R=o.5R}el.L.1U.o.B(\'Y\',\'1P\').B(\'Z\',el.L.4n.w+\'U\');if(o.5A&&o.5A==\'Q\'){el.L.1U.5A=\'Q\';el.L.1U.o.B(\'Q\',t+\'U\')}P{el.L.1U.5A=\'4D\';el.L.1U.o.B(\'4D\',b+\'U\')}el.L.1U.aE=o.aE?o.aE:\' \';24(D i=0;i<el.L.1Q.1g;i++){7Z=T(i)+1;el.L.1U.o.1S(\'<a 3h="#\'+7Z+o.1Z+\'" 6T="gR" 4g="\'+el.L.1Q[i].6L+\'">\'+7Z+\'</a>\'+(7Z!=el.L.1Q.1g?el.L.1U.aE:\'\'))}k(\'a\',el.L.1U.o).1J(\'5h\',u(){k.2u.go({90:q})});el.L.1U.4C=k.1a.2o(el.L.1U.o.K(0))}if(o.6Q||o.9c){el.L.2x={};1Z.1S(\'<22 6T="dn">&7k;</22>\');el.L.2x.o=k(\'.dn\',el);if(o.9c){el.L.2x.9c=o.9c;el.L.2x.o.2R(o.9c)}el.L.2x.o.B(\'Y\',\'1P\').B(\'Z\',el.L.4n.w+\'U\');if(o.6Q&&o.6Q==\'Q\'){el.L.2x.6Q=\'Q\';el.L.2x.o.B(\'Q\',(el.L.1U&&el.L.1U.5A==\'Q\'?el.L.1U.4C.hb+t:t)+\'U\')}P{el.L.2x.6Q=\'4D\';el.L.2x.o.B(\'4D\',(el.L.1U&&el.L.1U.5A==\'4D\'?el.L.1U.4C.hb+b:b)+\'U\')}el.L.2x.4C=k.1a.2o(el.L.2x.o.K(0))}if(o.9D){el.L.3s={9D:o.9D};1Z.1S(\'<a 3h="#2\'+o.1Z+\'" 6T="eY">&7k;</a>\');el.L.3s.o=k(\'.eY\',el);el.L.3s.o.B(\'Y\',\'1P\').B(\'19\',\'1o\').B(\'2U\',\'2K\').B(\'4A\',\'eR\').2R(el.L.3s.9D);el.L.3s.o.1J(\'5h\',k.2u.eg)}if(o.9o){el.L.3z={9o:o.9o};1Z.1S(\'<a 3h="#0\'+o.1Z+\'" 6T="ee">&7k;</a>\');el.L.3z.o=k(\'.ee\',el);el.L.3z.o.B(\'Y\',\'1P\').B(\'19\',\'1o\').B(\'2U\',\'2K\').B(\'4A\',\'eR\').2R(el.L.3z.9o);el.L.3z.o.1J(\'5h\',k.2u.dp)}1Z.bG(\'<22 6T="ca"></22>\');el.L.5u=k(\'.ca\',el);el.L.5u.B(\'Y\',\'1P\').B(\'Q\',\'2P\').B(\'O\',\'2P\').B(\'19\',\'1o\');if(o.2b){1Z.bG(\'<22 6T="dW" 14="19: 1o;"><1T 2J="\'+o.2b+\'" /></22>\');el.L.2b=k(\'.dW\',el);el.L.2b.B(\'Y\',\'1P\');D 1T=12 9s();1T.X=o.1Z;1T.2J=o.2b;if(1T.21){1T.6S=S;k.2u.go({2b:1T})}P{1T.6S=u(){k.2u.go({2b:q})}}}P{k.2u.go({1Z:el})}if(o.cS){fi=T(o.cS)*aC}k.2u.5I[o.1Z]=o.cS?1X.6V(\'k.2u.2I(\\\'\'+o.1Z+\'\\\')\',fi):S}};k.X=k.2u.2r;k.1t={7s:[],5L:{},1c:I,7u:S,26:u(){if(k.11.F==S){E}D 4O,3G,c,cs;k.1t.1c.K(0).3l=k.11.F.A.6R;4O=k.1t.1c.K(0).14;4O.19=\'2B\';k.1t.1c.1B=k.23(k.1a.3w(k.1t.1c.K(0)),k.1a.2o(k.1t.1c.K(0)));4O.Z=k.11.F.A.1B.1C+\'U\';4O.W=k.11.F.A.1B.hb+\'U\';3G=k.1a.cy(k.11.F);4O.5K=3G.t;4O.5z=3G.r;4O.5k=3G.b;4O.5j=3G.l;if(k.11.F.A.46==1b){c=k.11.F.fI(1b);cs=c.14;cs.5K=\'2P\';cs.5z=\'2P\';cs.5k=\'2P\';cs.5j=\'2P\';cs.19=\'2B\';k.1t.1c.5o().1S(c)}k(k.11.F).f5(k.1t.1c.K(0));k.11.F.14.19=\'1o\'},fC:u(e){if(!e.A.44&&k.1x.5r.cQ){if(e.A.3T)e.A.3T.1D(F);k(e).B(\'Y\',e.A.cz||e.A.4n);k(e).aS();k(k.1x.5r).f6(e)}k.1t.1c.4i(e.A.6R).3x(\'&7k;\');k.1t.7u=S;D 4O=k.1t.1c.K(0).14;4O.19=\'1o\';k.1t.1c.f5(e);if(e.A.fx>0){k(e).7f(e.A.fx)}k(\'2e\').1S(k.1t.1c.K(0));D 86=[];D 8q=I;24(D i=0;i<k.1t.7s.1g;i++){D 1j=k.1x.3P[k.1t.7s[i]].K(0);D id=k.1p(1j,\'id\');D 8i=k.1t.8x(id);if(1j.1i.ay!=8i.7l){1j.1i.ay=8i.7l;if(8q==I&&1j.1i.2Z){8q=1j.1i.2Z}8i.id=id;86[86.1g]=8i}}k.1t.7s=[];if(8q!=I&&86.1g>0){8q(86)}},al:u(e,o){if(!k.11.F)E;D 6e=I;D i=0;if(e.1i.el.1N()>0){24(i=e.1i.el.1N();i>0;i--){if(e.1i.el.K(i-1)!=k.11.F){if(!e.5V.b2){if((e.1i.el.K(i-1).1M.y+e.1i.el.K(i-1).1M.hb/2)>k.11.F.A.2q){6e=e.1i.el.K(i-1)}P{1r}}P{if((e.1i.el.K(i-1).1M.x+e.1i.el.K(i-1).1M.1C/2)>k.11.F.A.2v&&(e.1i.el.K(i-1).1M.y+e.1i.el.K(i-1).1M.hb/2)>k.11.F.A.2q){6e=e.1i.el.K(i-1)}}}}}if(6e&&k.1t.7u!=6e){k.1t.7u=6e;k(6e).h5(k.1t.1c.K(0))}P if(!6e&&(k.1t.7u!=S||k.1t.1c.K(0).31!=e)){k.1t.7u=S;k(e).1S(k.1t.1c.K(0))}k.1t.1c.K(0).14.19=\'2B\'},cT:u(e){if(k.11.F==S){E}e.1i.el.1E(u(){q.1M=k.23(k.1a.74(q),k.1a.7G(q))})},8x:u(s){D i;D h=\'\';D o={};if(s){if(k.1t.5L[s]){o[s]=[];k(\'#\'+s+\' .\'+k.1t.5L[s]).1E(u(){if(h.1g>0){h+=\'&\'}h+=s+\'[]=\'+k.1p(q,\'id\');o[s][o[s].1g]=k.1p(q,\'id\')})}P{24(a in s){if(k.1t.5L[s[a]]){o[s[a]]=[];k(\'#\'+s[a]+\' .\'+k.1t.5L[s[a]]).1E(u(){if(h.1g>0){h+=\'&\'}h+=s[a]+\'[]=\'+k.1p(q,\'id\');o[s[a]][o[s[a]].1g]=k.1p(q,\'id\')})}}}}P{24(i in k.1t.5L){o[i]=[];k(\'#\'+i+\' .\'+k.1t.5L[i]).1E(u(){if(h.1g>0){h+=\'&\'}h+=i+\'[]=\'+k.1p(q,\'id\');o[i][o[i].1g]=k.1p(q,\'id\')})}}E{7l:h,o:o}},fF:u(e){if(!e.dq){E}E q.1E(u(){if(!q.5V||!k(e).is(\'.\'+q.5V.3C))k(e).2R(q.5V.3C);k(e).7t(q.5V.A)})},4U:u(){E q.1E(u(){k(\'.\'+q.5V.3C).aS();k(q).dR();q.5V=S;q.fm=S})},2r:u(o){if(o.3C&&k.1a&&k.11&&k.1x){if(!k.1t.1c){k(\'2e\',1h).1S(\'<22 id="e5">&7k;</22>\');k.1t.1c=k(\'#e5\');k.1t.1c.K(0).14.19=\'1o\'}q.do({3C:o.3C,9J:o.9J?o.9J:I,a5:o.a5?o.a5:I,58:o.58?o.58:I,7x:o.7x||o.dC,7y:o.7y||o.fO,cQ:1b,2Z:o.2Z||o.ia,fx:o.fx?o.fx:I,46:o.46?1b:I,6I:o.6I?o.6I:\'cV\'});E q.1E(u(){D A={6N:o.6N?1b:I,ff:6P,1G:o.1G?2m(o.1G):I,6R:o.58?o.58:I,fx:o.fx?o.fx:I,44:1b,46:o.46?1b:I,3v:o.3v?o.3v:S,2p:o.2p?o.2p:S,4o:o.4o&&o.4o.1K==2A?o.4o:I,4m:o.4m&&o.4m.1K==2A?o.4m:I,3T:o.3T&&o.3T.1K==2A?o.3T:I,1O:/49|4j/.48(o.1O)?o.1O:I,6M:o.6M?T(o.6M)||0:I,2V:o.2V?o.2V:I};k(\'.\'+o.3C,q).7t(A);q.fm=1b;q.5V={3C:o.3C,6N:o.6N?1b:I,ff:6P,1G:o.1G?2m(o.1G):I,6R:o.58?o.58:I,fx:o.fx?o.fx:I,44:1b,46:o.46?1b:I,3v:o.3v?o.3v:S,2p:o.2p?o.2p:S,b2:o.b2?1b:I,A:A}})}}};k.fn.23({j3:k.1t.2r,f6:k.1t.fF,iS:k.1t.4U});k.iZ=k.1t.8x;k.2t={6O:S,7b:I,9m:S,6K:u(e){k.2t.7b=1b;k.2t.1Y(e,q,1b)},cq:u(e){if(k.2t.6O!=q)E;k.2t.7b=I;k.2t.2G(e,q)},1Y:u(e,el,7b){if(k.2t.6O!=S)E;if(!el){el=q}k.2t.6O=el;1M=k.23(k.1a.3w(el),k.1a.2o(el));8u=k(el);4g=8u.1p(\'4g\');3h=8u.1p(\'3h\');if(4g){k.2t.9m=4g;8u.1p(\'4g\',\'\');k(\'#eT\').3x(4g);if(3h)k(\'#bL\').3x(3h.4E(\'jh://\',\'\'));P k(\'#bL\').3x(\'\');1c=k(\'#8z\');if(el.4H.3l){1c.K(0).3l=el.4H.3l}P{1c.K(0).3l=\'\'}bo=k.1a.2o(1c.K(0));ga=7b&&el.4H.Y==\'bO\'?\'4D\':el.4H.Y;3m(ga){1e\'Q\':2q=1M.y-bo.hb;2v=1M.x;1r;1e\'O\':2q=1M.y;2v=1M.x-bo.1C;1r;1e\'2L\':2q=1M.y;2v=1M.x+1M.1C;1r;1e\'bO\':k(\'2e\').1J(\'3D\',k.2t.3D);1s=k.1a.4a(e);2q=1s.y+15;2v=1s.x+15;1r;ad:2q=1M.y+1M.hb;2v=1M.x;1r}1c.B({Q:2q+\'U\',O:2v+\'U\'});if(el.4H.54==I){1c.1Y()}P{1c.7f(el.4H.54)}if(el.4H.2Y)el.4H.2Y.1D(el);8u.1J(\'8B\',k.2t.2G).1J(\'5B\',k.2t.cq)}},3D:u(e){if(k.2t.6O==S){k(\'2e\').3q(\'3D\',k.2t.3D);E}1s=k.1a.4a(e);k(\'#8z\').B({Q:1s.y+15+\'U\',O:1s.x+15+\'U\'})},2G:u(e,el){if(!el){el=q}if(k.2t.7b!=1b&&k.2t.6O==el){k.2t.6O=S;k(\'#8z\').7a(1);k(el).1p(\'4g\',k.2t.9m).3q(\'8B\',k.2t.2G).3q(\'5B\',k.2t.cq);if(el.4H.3i)el.4H.3i.1D(el);k.2t.9m=S}},2r:u(M){if(!k.2t.1c){k(\'2e\').1S(\'<22 id="8z"><22 id="eT"></22><22 id="bL"></22></22>\');k(\'#8z\').B({Y:\'1P\',3I:6P,19:\'1o\'});k.2t.1c=1b}E q.1E(u(){if(k.1p(q,\'4g\')){q.4H={Y:/Q|4D|O|2L|bO/.48(M.Y)?M.Y:\'4D\',3l:M.3l?M.3l:I,54:M.54?M.54:I,2Y:M.2Y&&M.2Y.1K==2A?M.2Y:I,3i:M.3i&&M.3i.1K==2A?M.3i:I};D el=k(q);el.1J(\'9z\',k.2t.1Y);el.1J(\'6K\',k.2t.6K)}})}};k.fn.hO=k.2t.2r;k.84={bq:u(e){3K=e.7L||e.7K||-1;if(3K==9){if(1X.2k){1X.2k.bT=1b;1X.2k.c0=I}P{e.aP();e.aW()}if(q.b1){1h.6J.dZ().3g="\\t";q.dV=u(){q.6K();q.dV=S}}P if(q.aF){26=q.5q;2T=q.dN;q.2y=q.2y.hd(0,26)+"\\t"+q.2y.h8(2T);q.aF(26+1,26+1);q.6K()}E I}},4U:u(){E q.1E(u(){if(q.7P&&q.7P==1b){k(q).3q(\'7B\',k.84.bq);q.7P=I}})},2r:u(){E q.1E(u(){if(q.4Y==\'cf\'&&(!q.7P||q.7P==I)){k(q).1J(\'7B\',k.84.bq);q.7P=1b}})}};k.fn.23({j5:k.84.2r,hH:k.84.4U});k.1a={3w:u(e){D x=0;D y=0;D es=e.14;D bP=I;if(k(e).B(\'19\')==\'1o\'){D 5Y=es.3n;D 9q=es.Y;bP=1b;es.3n=\'2K\';es.19=\'2B\';es.Y=\'1P\'}D el=e;7d(el){x+=el.8t+(el.4Z&&!k.3a.7I?T(el.4Z.5b)||0:0);y+=el.8G+(el.4Z&&!k.3a.7I?T(el.4Z.4S)||0:0);el=el.dJ}el=e;7d(el&&el.4Y&&el.4Y.6c()!=\'2e\'){x-=el.3c||0;y-=el.3d||0;el=el.31}if(bP==1b){es.19=\'1o\';es.Y=9q;es.3n=5Y}E{x:x,y:y}},7G:u(el){D x=0,y=0;7d(el){x+=el.8t||0;y+=el.8G||0;el=el.dJ}E{x:x,y:y}},2o:u(e){D w=k.B(e,\'Z\');D h=k.B(e,\'W\');D 1C=0;D hb=0;D es=e.14;if(k(e).B(\'19\')!=\'1o\'){1C=e.4c;hb=e.5W}P{D 5Y=es.3n;D 9q=es.Y;es.3n=\'2K\';es.19=\'2B\';es.Y=\'1P\';1C=e.4c;hb=e.5W;es.19=\'1o\';es.Y=9q;es.3n=5Y}E{w:w,h:h,1C:1C,hb:hb}},74:u(el){E{1C:el.4c||0,hb:el.5W||0}},bm:u(e){D h,w,de;if(e){w=e.8W;h=e.8O}P{de=1h.5d;w=1X.d4||aa.d4||(de&&de.8W)||1h.2e.8W;h=1X.cB||aa.cB||(de&&de.8O)||1h.2e.8O}E{w:w,h:h}},6z:u(e){D t=0,l=0,w=0,h=0,iw=0,ih=0;if(e&&e.9N.6c()!=\'2e\'){t=e.3d;l=e.3c;w=e.d7;h=e.d2;iw=0;ih=0}P{if(1h.5d){t=1h.5d.3d;l=1h.5d.3c;w=1h.5d.d7;h=1h.5d.d2}P if(1h.2e){t=1h.2e.3d;l=1h.2e.3c;w=1h.2e.d7;h=1h.2e.d2}iw=aa.d4||1h.5d.8W||1h.2e.8W||0;ih=aa.cB||1h.5d.8O||1h.2e.8O||0}E{t:t,l:l,w:w,h:h,iw:iw,ih:ih}},cy:u(e,7N){D el=k(e);D t=el.B(\'5K\')||\'\';D r=el.B(\'5z\')||\'\';D b=el.B(\'5k\')||\'\';D l=el.B(\'5j\')||\'\';if(7N)E{t:T(t)||0,r:T(r)||0,b:T(b)||0,l:T(l)};P E{t:t,r:r,b:b,l:l}},aT:u(e,7N){D el=k(e);D t=el.B(\'5M\')||\'\';D r=el.B(\'5U\')||\'\';D b=el.B(\'5n\')||\'\';D l=el.B(\'4X\')||\'\';if(7N)E{t:T(t)||0,r:T(r)||0,b:T(b)||0,l:T(l)};P E{t:t,r:r,b:b,l:l}},6U:u(e,7N){D el=k(e);D t=el.B(\'4S\')||\'\';D r=el.B(\'5O\')||\'\';D b=el.B(\'5Q\')||\'\';D l=el.B(\'5b\')||\'\';if(7N)E{t:T(t)||0,r:T(r)||0,b:T(b)||0,l:T(l)||0};P E{t:t,r:r,b:b,l:l}},4a:u(2k){D x=2k.hT||(2k.gM+(1h.5d.3c||1h.2e.3c))||0;D y=2k.ki||(2k.iQ+(1h.5d.3d||1h.2e.3d))||0;E{x:x,y:y}},cI:u(4R,cx){cx(4R);4R=4R.7c;7d(4R){k.1a.cI(4R,cx);4R=4R.hQ}},h7:u(4R){k.1a.cI(4R,u(el){24(D 1p in el){if(2g el[1p]===\'u\'){el[1p]=S}}})},hV:u(el,1O){D 5l=k.1a.6z();D b6=k.1a.2o(el);if(!1O||1O==\'49\')k(el).B({Q:5l.t+((18.3r(5l.h,5l.ih)-5l.t-b6.hb)/2)+\'U\'});if(!1O||1O==\'4j\')k(el).B({O:5l.l+((18.3r(5l.w,5l.iw)-5l.l-b6.1C)/2)+\'U\'})},hW:u(el,dk){D 1Q=k(\'1T[@2J*="8X"]\',el||1h),8X;1Q.1E(u(){8X=q.2J;q.2J=dk;q.14.5E="9n:9w.9y.hE(2J=\'"+8X+"\')"})}};[].3J||(7F.hF.3J=u(v,n){n=(n==S)?0:n;D m=q.1g;24(D i=n;i<m;i++)if(q[i]==v)E i;E-1});',62,1293,'||||||||||||||||||||jQuery||||||this||||function||||||dragCfg|css|elm|var|return|dragged|easing|speed|false|callback|get|ss|options|iAuto|left|else|top|iResize|null|parseInt|px|oldStyle|height|slideshow|position|width||iDrag|new||style||||Math|display|iUtil|true|helper|subject|case|autoCFG|length|document|dropCfg|iEL|resizeOptions|carouselCfg|duration|interfaceFX|none|attr|sizes|break|pointer|iSort|type|ImageBox|queue|iDrop|iAutoscroller|slide|resizeElement|oC|wb|apply|each|fisheyeCfg|opacity|delta|newSizes|bind|constructor|custom|pos|size|axis|absolute|images|items|append|img|slideslinks|255|firstNum|window|show|container||complete|div|extend|for||start||cont|elsToScroll|100|loader|oR||body|elem|typeof|selectedItem|oldP|props|event|accordionCfg|parseFloat|field|getSize|containment|ny|build|relative|iTooltip|islideshow|nx|tp|slideCaption|value|newPosition|Function|block|selectHelper|step|border|itemWidth|hide|dequeue|timer|src|hidden|right|limit|nr|fractions|0px|PI|addClass|direction|end|overflow|cursorAt|result|parentData|onShow|onChange|to|parentNode|||||||||browser|iSlider|scrollLeft|scrollTop|scr|transferHelper|text|href|onHide|pre|selectdrug|className|switch|visibility|item|wrapper|unbind|max|nextslide|values|currentslide|handle|getPosition|html|newCoords|prevslide|iframe|iExpander|accept|mousemove|canvas|createElement|margins|onSlide|zIndex|indexOf|pressedKey|min|valueToAdd|multipleSeparator|pageSize|zones|highlighted|toggle|abs|onStop|dragElem|times|fadeDuration|diff|dhs|handlers||resizeDirection||vp|so|distance|ghosting||test|vertically|getPointer|startTop|offsetWidth|subjectValue|lastSuggestion|DropOutDirectiont|title|wrs|removeClass|horizontally|startLeft|out|onDrag|oP|onStart|nWidth|percent|down|ifxFirstDisplay|msie|iteration|ratio|clear|color|lastValue|slideCfg|fontSize|currentPointer|dimm|bottom|replace|up|prevImage|tooltipCFG|rel|els|fxCheckTag|context|nextImage|params|shs|fieldData|elToScroll|nodeEl|borderTopWidth|chunks|destroy|string|nHeight|paddingLeft|tagName|currentStyle||halign|slidePos|onclick|delay||containerW|from|helperclass|endLeft|endTop|borderLeftWidth|dragmoveBy|documentElement|dhe|newStyles|clonedEl|click|si|marginLeft|marginBottom|clientScroll|OpenClose|paddingBottom|empty|toWrite|selectionStart|overzone|toAdd|onDragModifier|holder|mousedown|animate|toDrag|cnt|marginRight|linksPosition|blur|getAttribute|hight|filter|sw|zoney|cos|slideshows|zonex|marginTop|collected|paddingTop|url|borderRightWidth|mouseup|borderBottomWidth|activeLinkClass|dragHandle|clearInterval|paddingRight|sortCfg|offsetHeight|prop|oldVisibility|styles||BlindDirection|point|fxh|nmp|old|post|currentPanel|onSelect|elementData|grid|pow|toLowerCase|animationHandler|cur|containerH|close|puff|getWidth|currentRel|imageEl|Expander|getHeight|iFisheye|random|newDimensions|itemHeight|reflections|sliders|selRange|wr|orig|margin|maxWidth|keyup|getScroll|captionText|totalImages|128|parseColor|curCSS|outerContainer|Scale|restore|tolerance|selection|focus|caption|snapDistance|revert|current|3000|captionPosition|hpc|onload|class|getBorder|setInterval|oldStyleAttr|rule|rgb|open|minLeft|ActiveXObject|oldDisplay|restoreStyle|getSizeLite||nw|0x||F0|fadeOut|focused|firstChild|while|cssRules|fadeIn|Date|minTop|backgroundColor|sc|nbsp|hash|captionEl|selectKeyHelper|selectCurrent|newTop|init|newLeft|changed|Draggable|inFrontOf|efx|139|onHover|onOut|getTime|np|keydown|split|radiusY|increment|Array|getPositionLite|selectClass|opera|onHighlight|keyCode|charCode|Object|toInteger|frameClass|hasTabsEnabled|zonew|user|zoneh|positionItems|onClick|oD|scrollIntoView|accordionPos|proximity|indic||data|containerSize|sin|iTTabs||ts|ImageBoxPrevImage|ImageBoxNextImage|imageSrc|newPos|maxHeight|minHeight|elS|activeClass|panels|maxBottom|maxRight|ser|move|opened|bounceout|animationInProgress|overlay|stop|reflectionSize|fnc|classname|insideParent|offsetLeft|jEl|nRy|pr|serialize|nRx|tooltipHelper|cssSides|mouseout|select|count|namedColors|padding|offsetTop|directionIncrement|parentEl|400|dir|expand|createTextNode|finishedPre|clientHeight|li|applyOn|content|contBorders|object|parentBorders|alpha|clientWidth|png|gallery|fontWeight|link|yproc|xproc|sx|parent|showImage|selectedone|imgs|onselect|sy|startDrag|cursor|captionClass|onselectstop|getElementById|linksClass|sh|ul|onActivate|isDroppable|nextEl|onDrop|oldTitle|progid|prevslideClass|prevEl|oldPosition|SliderContainer|Image|linkRel|selectKeyUp|selectKeyDown|DXImageTransform|inCache|Microsoft|mouseover|dragstop|diffX|211|nextslideClass|prot|auto|dEs|hidehelper|isDraggable|activeclass|unit|DoFold|unfold|nodeName|startTime|buildWrapper|prev|1px|oldColor|setTimeout|ScrollTo|st|sl|cssText|9999|next|destroyWrapper|opt|diffHeight|diffWidth|exec|hoverclass|image|blind|borderColor|sideEnd|self|key||default|2000|styleSheets|getValues|192|diffY|lnk|reflexions|checkhover|selectcheck|maxRotation|ImageBoxOuterContainer|gradient|panelHeight|childs|headers|ne|hideImage|minWidth|iIndex|itemsText|os|side|iCarousel|5625|1000|itemMinWidth|linksSeparator|setSelectionRange|protectRotation|positionContainer|posx|hoverClass|valToAdd|minchars|helperClass|source|nextImageEl|preventDefault|multiple|headerSelector|DraggableDestroy|getPadding|autofill|handleEl|stopPropagation|prevImageEl|getFieldValues|panelSelector|String|createTextRange|floats|lastSi|shrink|oPad|windowSize|paddingLeftSize|angle|paddingY|paddingX|RegExp|borderRightSize|floatVal|firstStep|pulse|Pulsate|Color|rotationSpeed|paddingBottomSize|remove|parseStyle|getClient|Number|helperSize|bounce|doTab||zoom|borderLeftSize|oBor|paddingRightSize|borderTopSize|paddingTopSize|stopAnim|pValue|borderBottomSize|extraWidth|restricted|autoSize|unselectable|SliderIteration|prepend|clearTimeout|isSlider|oneIsSortable|applyOnHover|tooltipURL|tabindex|draginit|mouse|restoreStyles|sliderSize|sliderPos|parentPos|cancelBubble|autocomplete|inputWidth|oldBorder|dragmove|clnt|sliderEl|returnValue|loaderWidth|idsa|letterSpacing|pause|getContainment|fade|snapToGrid|linear|10000|slideshowHolder|asin|cssSidesEnd|borderWidth|fitToContainer|TEXTAREA|entities|INPUT|spacer|writeItems|character|currentValue|paddings|169|oldFloat|borders|hidefocused|bouncein||modifyContainer|transparent|center|loadImage|func|getMargins|initialPosition|textAlign|innerHeight|Alpha|no|captionImages|closeEl|shake|prevTop|traverseDOM|Selectserialize|stopDrag|slider|ImageBoxCaption|ImageBoxIframe|300|ImageBoxOverlay|sortable|moveDrag|autoplay|measure|prevLeft|intersect|ImageBoxCurrentImage|selectstop|Shake|index|dragEl|keyPressed|scrollHeight|scroll|innerWidth|match|elPosition|scrollWidth|textImage|slideBor|jpg|captionSize|textImageFrom|visible||loaderHeight|ImageBoxCaptionImages||hoverItem|clickItem|emptyGIF||notColor|slideshowCaption|Droppable|goprev|childNodes|autocompleteHelper|autocompleteIframe|slidePad|fit|165|clientSize|||fontFamily|colorCssProps|elType|onhover|cssProps|expanderHelper|boxModel|itransferTo|keypress|moveStart|offsetParent|Width|selectstart|fxe|selectionEnd|checkCache|fontStyle|update|DroppableDestroy|remeasure|fontStretch|fontVariant|onblur|slideshowLoader|htmlEntities|wordSpacing|createRange|224|KhtmlUserSelect||closeHTML|on|sortHelper|245|userSelect|dragHelper|hrefAttr|dragstart|107|loaderSRC|highlight|slideshowPrevslide||gonext||styleFloat|frameborder|javascript|||relAttr|wid|scrolling||onslide|||listStyle|imageTypes|insertBefore|999|textDecoration|sqrt|140|230|maxy|240|ImageBoxContainer|doScroll|interval|set|dragmoveByKey|protect|ImageBoxCaptionText|144|ImageBoxLoader|off|checkdrop|isSelectable|hlt|30px|selectedclass|tooltipTitle|imagebox|shc|overlayOpacity|selRange2|slideshowNextSlide|gif|getSelectionStart|360|iAccordion|getElementsByTagName|iBounce|after|SortableAddItem|onResize|150|itemZIndex|grow|getHeightMinMax|borderTopUnit|selectcheckApply|borderRightUnit|zindex|fontUnit|togglehor|time|se|parte|easeout|isSortable||SlideInUp|fold|SlideOutUp|rgba|addColorStop|yfrac|containerMaxx|interfaceColorFX|containerMaxy||leftUnit|mousex||radiusX|check|getContext|xfrac|addItem|topUnit|fracH|cloneNode|togglever|paddingLeftUnit|borderBottomUnit|finish|onDragStop|onout|posy|isFunction|oldOverflow|directions|vertical|fracW|fakeAccordionClass|parts|fadeTo|inputValue|xml|selectstopApply|slideshowLinks|onDragStart|BlindUp|paddingTopUnit|500|trim|maxx|borderLeftUnit|paddingRightUnit|filteredPosition|BlindDown|paddingBottomUnit|horizontal|valign|find|ImageBoxClose|onselectstart|mozUserSelect|ondragstart|scale|110|globalCompositeOperation|bmp||drawImage|ondrop|password|quot||save|starty|jpeg|||number|startx|finishOpacity|hover|recallDroppables|flipv|finishx|destination|khtml|moz|lt|amp|pW|clientX|Accordion|translate|captiontext|elasticin|slideshowLink|fix|elasticout|resize|elasticboth|bounceboth|984375|9375|Selectable|30002|list|625|30001|nodeValue|before|100000|purgeEvents|substr|duplicate|moveEnd|||substring|success|param|par|array|Fisheye|name|POST|ajax|easeboth|location|fromHandler|collapse|MozUserSelect||ResizableDestroy|rotationTimer|fillRect|fill|WebKit|fillStyle|createLinearGradient|Resizable|navigator|appVersion|lineHeigt|alt|AlphaImageLoader|prototype|SliderGetValues|DisableTabs|Carousel|load|easein|IMG|200|Slider|ToolTip|wh|nextSibling|Autocomplete|SliderSetValues|pageX|float|centerEl|fixPNG|isNaN|dotted|dashed|stopAll|Left|outlineColor|Top|Right|Bottom|solid|double|selectorText|rules|onchange|SlideToggleRight|SlideOutRight||borderStyle||TransferTo||groove|ridge|inset|outset|borderTopColor||borderRightColor|olive|navy|orange||pink|203|maroon||magenta|182|193|lightyellow|lime|purple|red|outlineOffset|outlineWidth|borderBottomColor|borderLeftColor|lineHeight|loading|silver|white|yellow|Showing|100000000|SlideInRight|clientY|Highlight|SortableDestroy|CloseVertically|CloseHorizontally|FoldToggle|UnFold|SlideInDown|SlideToggleUp|SortSerialize|Fold|SwitchHorizontally|SwitchVertically|Sortable|scrollTo|EnableTabs|ScrollToAnchors|pt|Puff|OpenVertically|OpenHorizontally|Grow|Shrink|DropToggleRight|DropInRight|BlindToggleHorizontally|BlindRight|http|Bounce|120|BlindLeft|BlindToggleVertically|SlideToggleLeft|SlideOutLeft|toUpperCase|SlideInLeft|SlideToggleDown|SlideOutDown|DropOutLeft|DropInLeft|DropToggleLeft|DropOutRight|DropToggleUp|DropInUp|DropOutDown|DropInDown|DropToggleDown|DropOutUp|lightpink|textIndent|aqua|appendChild|azure|beige|220|last|cssFloat|first|ol|wrapEl|fxWrapper|black|imageLoaded|darkkhaki|darkgreen|189|183|darkmagenta|firstResize|darkgrey|brown|cyan|darkblue|darkcyan|table|form|col|tfoot|colgroup|th|header|thead|tbody|112|Autoexpand|tr|td|script|frame|input|pageY|textarea|button|w_|removeChild|frameset|option|optgroup|meta|darkolivegreen|blue|122|233|green|lightcyan|204|darkviolet|lightgreen|indigo|216|khaki|darksalmon|130|darkred|lightblue|148|173|215|238|fuchsia|gold|darkorchid|153|darkorange|lightgrey'.split('|'),0,{}))
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.color.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.color.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.color.js	(revision 5057)
@@ -0,0 +1,128 @@
+/*
+ * jQuery Color Animations
+ * Copyright 2007 John Resig
+ * Released under the MIT and GPL licenses.
+ */
+
+(function(jQuery){
+
+    // We override the animation for all of these color styles
+    jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
+        jQuery.fx.step[attr] = function(fx){
+            if ( fx.state == 0 ) {
+                fx.start = getColor( fx.elem, attr );
+                fx.end = getRGB( fx.end );
+            }
+
+            fx.elem.style[attr] = "rgb(" + [
+                Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
+                Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
+                Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
+            ].join(",") + ")";
+        }
+    });
+
+    // Color Conversion functions from highlightFade
+    // By Blair Mitchelmore
+    // http://jquery.offput.ca/highlightFade/
+
+    // Parse strings looking for color tuples [255,255,255]
+    function getRGB(color) {
+        var result;
+
+        // Check if we're already dealing with an array of colors
+        if ( color && color.constructor == Array && color.length == 3 )
+            return color;
+
+        // Look for rgb(num,num,num)
+        if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+            return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
+
+        // Look for rgb(num%,num%,num%)
+        if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+            return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+        // Look for #a0b1c2
+        if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+            return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+        // Look for #fff
+        if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+            return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+        // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+        if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
+            return colors['transparent']
+
+        // Otherwise, we're most likely dealing with a named color
+        return colors[jQuery.trim(color).toLowerCase()];
+    }
+
+    function getColor(elem, attr) {
+        var color;
+
+        do {
+            color = jQuery.curCSS(elem, attr);
+
+            // Keep going until we find an element that has color, or we hit the body
+            if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
+                break;
+
+            attr = "backgroundColor";
+        } while ( elem = elem.parentNode );
+
+        return getRGB(color);
+    };
+
+    // Some named colors to work with
+    // From Interface by Stefan Petre
+    // http://interface.eyecon.ro/
+
+    var colors = {
+        aqua:[0,255,255],
+        azure:[240,255,255],
+        beige:[245,245,220],
+        black:[0,0,0],
+        blue:[0,0,255],
+        brown:[165,42,42],
+        cyan:[0,255,255],
+        darkblue:[0,0,139],
+        darkcyan:[0,139,139],
+        darkgrey:[169,169,169],
+        darkgreen:[0,100,0],
+        darkkhaki:[189,183,107],
+        darkmagenta:[139,0,139],
+        darkolivegreen:[85,107,47],
+        darkorange:[255,140,0],
+        darkorchid:[153,50,204],
+        darkred:[139,0,0],
+        darksalmon:[233,150,122],
+        darkviolet:[148,0,211],
+        fuchsia:[255,0,255],
+        gold:[255,215,0],
+        green:[0,128,0],
+        indigo:[75,0,130],
+        khaki:[240,230,140],
+        lightblue:[173,216,230],
+        lightcyan:[224,255,255],
+        lightgreen:[144,238,144],
+        lightgrey:[211,211,211],
+        lightpink:[255,182,193],
+        lightyellow:[255,255,224],
+        lime:[0,255,0],
+        magenta:[255,0,255],
+        maroon:[128,0,0],
+        navy:[0,0,128],
+        olive:[128,128,0],
+        orange:[255,165,0],
+        pink:[255,192,203],
+        purple:[128,0,128],
+        violet:[128,0,128],
+        red:[255,0,0],
+        silver:[192,192,192],
+        white:[255,255,255],
+        yellow:[255,255,0],
+        transparent: [255,255,255]
+    };
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/jquery.js	(revision 5057)
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);jQuery.noConflict();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/password-strength-meter.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/password-strength-meter.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/jquery/password-strength-meter.js	(revision 5057)
@@ -0,0 +1,80 @@
+// Password strength meter
+// This jQuery plugin is written by firas kassem [2007.04.05]
+// Firas Kassem  phiras.wordpress.com || phiras at gmail {dot} com
+// for more information : http://phiras.wordpress.com/2007/04/08/password-strength-meter-a-jquery-plugin/
+
+var shortPass = pwsL10n.short
+var badPass = pwsL10n.bad
+var goodPass = pwsL10n.good
+var strongPass = pwsL10n.strong
+
+
+function passwordStrength(password,username) {
+    score = 0
+
+    //password < 4
+    if (password.length < 4 ) { return shortPass }
+
+    //password == username
+    if (password.toLowerCase()==username.toLowerCase()) return badPass
+
+    //password length
+    score += password.length * 4
+    score += ( checkRepetition(1,password).length - password.length ) * 1
+    score += ( checkRepetition(2,password).length - password.length ) * 1
+    score += ( checkRepetition(3,password).length - password.length ) * 1
+    score += ( checkRepetition(4,password).length - password.length ) * 1
+
+    //password has 3 numbers
+    if (password.match(/(.*[0-9].*[0-9].*[0-9])/))  score += 5
+
+    //password has 2 symbols
+    if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5
+
+    //password has Upper and Lower chars
+    if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))  score += 10
+
+    //password has number and chars
+    if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/))  score += 15
+    //
+    //password has number and symbol
+    if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/))  score += 15
+
+    //password has char and symbol
+    if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/))  score += 15
+
+    //password is just numbers or chars
+    if (password.match(/^\w+$/) || password.match(/^\d+$/) )  score -= 10
+
+    //verifing 0 < score < 100
+    if ( score < 0 )  score = 0
+    if ( score > 100 )  score = 100
+
+    if (score < 34 )  return badPass
+    if (score < 68 )  return goodPass
+    return strongPass
+}
+
+
+// checkRepetition(1,'aaaaaaabcbc')   = 'abcbc'
+// checkRepetition(2,'aaaaaaabcbc')   = 'aabc'
+// checkRepetition(2,'aaaaaaabcdbcd') = 'aabcd'
+
+function checkRepetition(pLen,str) {
+    res = ""
+    for ( i=0; i<str.length ; i++ ) {
+        repeated=true
+        for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
+            repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen))
+        if (j<pLen) repeated=false
+        if (repeated) {
+            i+=pLen-1
+            repeated=false
+        }
+        else {
+            res+=str.charAt(i)
+        }
+    }
+    return res
+}
+
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/profile-edit.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/profile-edit.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/profile-edit.js	(revision 5057)
@@ -0,0 +1,38 @@
+function check_pass_strength () {
+	var pass = jQuery('#pass1').val();
+	var user = jQuery('#user_login').val();
+
+	// get the result as an object, i'm tired of typing it
+	var res = jQuery('#pass-strength-result');
+
+	var strength = passwordStrength(pass, user);
+
+	jQuery(res).removeClass('short bad good strong');
+
+	if ( strength == pwsL10n.bad ) {
+		jQuery(res).addClass('bad');
+		jQuery(res).html( pwsL10n.bad );
+	}
+	else if ( strength == pwsL10n.good ) {
+		jQuery(res).addClass('good');
+		jQuery(res).html( pwsL10n.good );
+	}
+	else if ( strength == pwsL10n.strong ) {
+		jQuery(res).addClass('strong');
+		jQuery(res).html( pwsL10n.strong );
+	}
+	else {
+		// this catches 'Too short' and the off chance anything else comes along
+		jQuery(res).addClass('short');
+		jQuery(res).html( pwsL10n.short );
+	}
+}
+
+jQuery(function($) { 
+	$('#pass1').keyup( check_pass_strength ) 
+	$('.color-palette').click(function(){$(this).siblings('input[name=admin_color]').attr('checked', 'checked')});
+});
+
+jQuery(document).ready( function() {
+	jQuery('#pass1,#pass2').attr('autocomplete','off');
+});
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/topic.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/topic.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/topic.js	(revision 5057)
@@ -0,0 +1,64 @@
+bbTopicJS = jQuery.extend( {
+	currentUserId: '0',
+	topicId: '0',
+	favoritesLink: '',
+	isFav: 0,
+	confirmPostDelete: 'Are you sure you want to delete this post?',
+	favLinkYes: 'favorites',
+	favLinkNo: '?',
+	favYes: 'This topic is one of your %favLinkYes% [%favDel%]',
+	favNo: '%favAdd% (%favLinkNo%)',
+	favDel: 'x',
+	favAdd: 'Add this topic to your favorites'
+}, bbTopicJS );
+
+bbTopicJS.isFav = parseInt( bbTopicJS.isFav );
+
+jQuery( function($) {
+	// Tags
+	var tagsDelBefore = function( s ) {
+		s.data['topic_id'] = bbTopicJS.topicId;
+		return s;
+	};
+	$('#tags-list').wpList( { alt: '', delBefore: tagsDelBefore } );
+
+	// Favorites
+	var favoritesToggle = $('#favorite-toggle')
+		.addClass( 'list:favorite' )
+		.wpList( { alt: '', dimAfter: favLinkSetup } );
+
+	var favoritesToggleSpan = favoritesToggle.children( 'span' )
+		[bbTopicJS.isFav ? 'addClass' : 'removeClass' ]( 'is-favorite' );
+	
+	function favLinkSetup() {
+		bbTopicJS.isFav = favoritesToggleSpan.is('.is-favorite');
+		var aLink = "<a href='" + bbTopicJS.favoritesLink + "'>";
+		var aDim  = "<a href='" + favoritesToggleSpan.find( 'a[class^="dim:"]' ).attr( 'href' ) + "' class='dim:favorite-toggle:" + favoritesToggleSpan.attr( 'id' ) + ":is-favorite'>";
+		if ( bbTopicJS.isFav ) {
+			html = bbTopicJS.favYes
+				.replace( /%favLinkYes%/, aLink + bbTopicJS.favLinkYes + "</a>" )
+				.replace( /%favDel%/, aDim + bbTopicJS.favDel + "</a>" );
+		} else {
+			html = bbTopicJS.favNo
+				.replace( /%favLinkNo%/, aLink + bbTopicJS.favLinkNo + "</a>" )
+				.replace( /%favAdd%/, aDim + bbTopicJS.favAdd + "</a>" );
+		}
+		favoritesToggleSpan.html( html );
+		favoritesToggle.get(0).wpList.process( favoritesToggle );
+	}
+
+	// Posts
+	var postConfirm = function(e,s,a) {
+		if ( 'delete' != a ) {
+			return true;
+		}
+		return confirm( bbTopicJS[ $('#' + s.element).is('.deleted') ? 'confirmPostUnDelete' : 'confirmPostDelete'] );
+	};
+
+	$('#thread').addClass( 'list:post' ).wpList( { alt: 'alt', altOffset: 1, confirm: postConfirm, delAfter: function( r, s ) {
+		try {
+			// If we deleted the only post, we got an WP AJAX Response object back with a URL to redirect to
+			document.location = s.parsed.responses[0].data;
+		} catch ( e ) {}
+	} } );
+} );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-ajax-response.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-ajax-response.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-ajax-response.js	(revision 5057)
@@ -0,0 +1,64 @@
+var wpAjax = jQuery.extend( {
+	unserialize: function( s ) {
+		var r = {}, q, pp, i, p;
+		if ( !s ) { return r; }
+		q = s.split('?'); if ( q[1] ) { s = q[1]; }
+		pp = s.split('&');
+		for ( i in pp ) {
+			if ( jQuery.isFunction(pp.hasOwnProperty) && !pp.hasOwnProperty(i) ) { continue; }
+			p = pp[i].split('=');
+			r[p[0]] = p[1];
+		}
+		return r;
+	},
+	parseAjaxResponse: function( x, r, e ) { // 1 = good, 0 = strange (bad data?), -1 = you lack permission
+		var parsed = {}, re = jQuery('#' + r).html(''), err = '';
+
+		if ( x && typeof x == 'object' && x.getElementsByTagName('wp_ajax') ) {
+			parsed.responses = [];
+			parsed.errors = false;
+			jQuery('response', x).each( function() {
+				var th = jQuery(this), child = jQuery(this.firstChild), response;
+				response = { action: th.attr('action'), what: child.get(0).nodeName, id: child.attr('id'), oldId: child.attr('old_id'), position: child.attr('position') };
+				response.data = jQuery( 'response_data', child ).text();
+				response.supplemental = {};
+				if ( !jQuery( 'supplemental', child ).children().each( function() {
+					response.supplemental[this.nodeName] = jQuery(this).text();
+				} ).size() ) { response.supplemental = false }
+				response.errors = [];
+				if ( !jQuery('wp_error', child).each( function() {
+					var code = jQuery(this).attr('code'), anError, errorData, formField;
+					anError = { code: code, message: this.firstChild.nodeValue, data: false };
+					errorData = jQuery('wp_error_data[code="' + code + '"]', x);
+					if ( errorData ) { anError.data = errorData.get(); }
+					formField = jQuery( 'form-field', errorData ).text();
+					if ( formField ) { code = formField; }
+					if ( e ) { wpAjax.invalidateForm( jQuery('#' + e + ' :input[name="' + code + '"]' ).parents('.form-field:first') ); }
+					err += '<p>' + anError.message + '</p>';
+					response.errors.push( anError );
+					parsed.errors = true;
+				} ).size() ) { response.errors = false; }
+				parsed.responses.push( response );
+			} );
+			if ( err.length ) { re.html( '<div class="error">' + err + '</div>' ); }
+			return parsed;
+		}
+		if ( isNaN(x) ) { return !re.html('<div class="error"><p>' + x + '</p></div>'); }
+		x = parseInt(x,10);
+		if ( -1 == x ) { return !re.html('<div class="error"><p>' + wpAjax.noPerm + '</p></div>'); }
+		else if ( 0 === x ) { return !re.html('<div class="error"><p>' + wpAjax.broken  + '</p></div>'); }
+		return true;
+	},
+	invalidateForm: function ( selector ) {
+		return jQuery( selector ).addClass( 'form-invalid' ).change( function() { jQuery(this).removeClass( 'form-invalid' ); } );
+	},
+	validateForm: function( selector ) {
+		selector = jQuery( selector );
+		return !wpAjax.invalidateForm( selector.find('.form-required').filter( function() { return jQuery('input:visible', this).val() == ''; } ) ).size();
+	}
+}, wpAjax || { noPerm: 'You do not have permission to do that.', broken: 'An unidentified error has occurred.' } );
+
+// Basic form validation
+jQuery(document).ready( function($){
+	$('form.validate').submit( function() { return wpAjax.validateForm( $(this) ); } );
+});
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-lists.js
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-lists.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-includes/js/wp-lists.js	(revision 5057)
@@ -0,0 +1,372 @@
+(function($) {
+var currentFormEl = false, fs = {add:'ajaxAdd',del:'ajaxDel',dim:'ajaxDim',process:'process',recolor:'recolor'}, wpList;
+
+wpList = {
+	settings: {
+		url: wpListL10n.url, type: 'POST',
+		response: 'ajax-response',
+
+		what: '',
+		alt: 'alternate', altOffset: 0,
+		addColor: null, delColor: null, dimAddColor: null, dimDelColor: null,
+
+		confirm: null,
+		addBefore: null, addAfter: null,
+		delBefore: null, delAfter: null,
+		dimBefore: null, dimAfter: null
+	},
+
+	nonce: function(e,s) {
+		var url = wpAjax.unserialize(e.attr('href'));
+		return s.nonce || url._ajax_nonce || $('#' + s.element + ' input[name=_ajax_nonce]').val() || url._wpnonce || $('#' + s.element + ' input[name=_wpnonce]').val() || 0;
+	},
+
+	parseClass: function(e,t) {
+		var c = [], cl;
+		try {
+			cl = $(e).attr('class') || '';
+			cl = cl.match(new RegExp(t+':[\\S]+'));
+			if ( cl ) { c = cl[0].split(':'); }
+		} catch(r) {}
+		return c;
+	},
+
+	pre: function(e,s,a) {
+		var bg, r;
+		s = $.extend( {}, this.wpList.settings, {
+			element: null,
+			nonce: 0,
+			target: e.get(0)
+		}, s || {} );
+		if ( $.isFunction( s.confirm ) ) {
+			if ( 'add' != a ) {
+				bg = $('#' + s.element).css('backgroundColor');
+				$('#' + s.element).css('backgroundColor', '#FF9966');
+			}
+			r = s.confirm.call(this,e,s,a,bg);
+			if ( 'add' != a ) { $('#' + s.element).css('backgroundColor', bg ); }
+			if ( !r ) { return false; }
+		}
+		return s;
+	},
+
+	ajaxAdd: function( e, s ) {
+		e = $(e);
+		s = s || {};
+		var list = this, cls = wpList.parseClass(e,'add'), es, valid, formData;
+		s = wpList.pre.call( list, e, s, 'add' );
+
+		s.element = cls[2] || e.attr( 'id' ) || s.element || null;
+		if ( cls[3] ) { s.addColor = '#' + cls[3]; }
+		else { s.addColor = s.addColor || '#FFFF33'; }
+
+		if ( !s ) { return false; }
+
+		if ( !e.is("[class^=add:" + list.id + ":]") ) { return !wpList.add.call( list, e, s ); }
+
+		if ( !s.element ) { return true; }
+
+		s.action = 'add-' + s.what;
+
+		s.nonce = wpList.nonce(e,s);
+
+		es = $('#' + s.element + ' :input').not('[name=_ajax_nonce], [name=_wpnonce], [name=action]');
+		valid = wpAjax.validateForm( '#' + s.element );
+		if ( !valid ) { return false; }
+
+		s.data = $.param( $.extend( { _ajax_nonce: s.nonce, action: s.action }, wpAjax.unserialize( cls[4] || '' ) ) );
+		formData = $.isFunction(es.fieldSerialize) ? es.fieldSerialize() : es.serialize();
+		if ( formData ) { s.data += '&' + formData; }
+
+		if ( $.isFunction(s.addBefore) ) {
+			s = s.addBefore( s );
+			if ( !s ) { return true; }
+		}
+		if ( !s.data.match(/_ajax_nonce=[a-f0-9]+/) ) { return true; }
+
+		s.success = function(r) {
+			var res = wpAjax.parseAjaxResponse(r, s.response, s.element), o;
+			if ( !res || res.errors ) { return false; }
+
+			if ( true === res ) { return true; }
+
+			jQuery.each( res.responses, function() {
+				wpList.add.call( list, this.data, $.extend( {}, s, { // this.firstChild.nodevalue
+					pos: this.position || 0,
+					id: this.id || 0,
+					oldId: this.oldId || null
+				} ) );
+			} );
+
+			if ( $.isFunction(s.addAfter) ) {
+				o = this.complete;
+				this.complete = function(x,st) {
+					var _s = $.extend( { xml: x, status: st, parsed: res }, s );
+					s.addAfter( r, _s );
+					if ( $.isFunction(o) ) { o(x,st); }
+				};
+			}
+			list.wpList.recolor();
+			wpList.clear.call(list,'#' + s.element);
+		};
+
+		$.ajax( s );
+		return false;
+	},
+
+	ajaxDel: function( e, s ) {
+		e = $(e); s = s || {};
+		var list = this, cls = wpList.parseClass(e,'delete'), element, anim;
+		s = wpList.pre.call( list, e, s, 'delete' );
+
+		s.element = cls[2] || s.element || null;
+		if ( cls[3] ) { s.delColor = '#' + cls[3]; }
+		else { s.delColor = s.delColor || '#FF3333'; }
+
+		if ( !s || !s.element ) { return false; }
+
+		s.action = 'delete-' + s.what;
+
+		s.nonce = wpList.nonce(e,s);
+
+		s.data = $.extend(
+			{ action: s.action, id: s.element.split('-').pop(), _ajax_nonce: s.nonce },
+			wpAjax.unserialize( cls[4] || '' )
+		);
+
+		if ( $.isFunction(s.delBefore) ) {
+			s = s.delBefore( s );
+			if ( !s ) { return true; }
+		}
+		if ( !s.data._ajax_nonce ) { return true; }
+
+		element = $('#' + s.element);
+
+		if ( 'none' != s.delColor ) {
+			anim = 'slideUp';
+			if ( element.css( 'display' ).match(/table/) )
+				anim = 'fadeOut'; // Can't slideup table rows and other table elements.  Known jQuery bug
+			element
+				.animate( { backgroundColor: s.delColor }, 'fast'  )[anim]( 'fast' )
+				.queue( function() { list.wpList.recolor(); $(this).dequeue(); } );
+		} else {
+			list.wpList.recolor();
+		}
+
+		s.success = function(r) {
+			var res = wpAjax.parseAjaxResponse(r, s.response, s.element), o;
+			if ( !res || res.errors ) {
+				element.stop().stop().css( 'backgroundColor', '#FF3333' ).show().queue( function() { list.wpList.recolor(); $(this).dequeue(); } );
+				return false;
+			}
+			if ( $.isFunction(s.delAfter) ) {
+				o = this.complete;
+				this.complete = function(x,st) {
+					element.queue( function() {
+						var _s = $.extend( { xml: x, status: st, parsed: res }, s );
+						s.delAfter( r, _s );
+						if ( $.isFunction(o) ) { o(x,st); }
+					} ).dequeue();
+				};
+			}
+		};
+		$.ajax( s );
+		return false;
+	},
+
+	ajaxDim: function( e, s ) {
+		if ( $(e).parent().css('display') == 'none' ) // Prevent hidden links from being clicked by hotkeys
+			return false;
+		e = $(e); s = s || {};
+		var list = this, cls = wpList.parseClass(e,'dim'), element, isClass, color, dimColor;
+		s = wpList.pre.call( list, e, s, 'dim' );
+
+		s.element = cls[2] || s.element || null;
+		s.dimClass =  cls[3] || s.dimClass || null;
+		if ( cls[4] ) { s.dimAddColor = '#' + cls[4]; }
+		else { s.dimAddColor = s.dimAddColor || '#FFFF33'; }
+		if ( cls[5] ) { s.dimDelColor = '#' + cls[5]; }
+		else { s.dimDelColor = s.dimDelColor || '#FF3333'; }
+
+		if ( !s || !s.element || !s.dimClass ) { return true; }
+
+		s.action = 'dim-' + s.what;
+
+		s.nonce = wpList.nonce(e,s);
+
+		s.data = $.extend(
+			{ action: s.action, id: s.element.split('-').pop(), dimClass: s.dimClass, _ajax_nonce : s.nonce },
+			wpAjax.unserialize( cls[6] || '' )
+		);
+
+		if ( $.isFunction(s.dimBefore) ) {
+			s = s.dimBefore( s );
+			if ( !s ) { return true; }
+		}
+
+		element = $('#' + s.element);
+		isClass = element.toggleClass(s.dimClass).is('.' + s.dimClass);
+		color = wpList.getColor( element );
+		element.toggleClass( s.dimClass )
+		dimColor = isClass ? s.dimAddColor : s.dimDelColor;
+		if ( 'none' != dimColor ) {
+			element
+				.animate( { backgroundColor: dimColor }, 'fast' )
+				.queue( function() { element.toggleClass(s.dimClass); $(this).dequeue(); } )
+				.animate( { backgroundColor: color }, { complete: function() { $(this).css( 'backgroundColor', '' ); } } );
+		}
+
+		if ( !s.data._ajax_nonce ) { return true; }
+
+		s.success = function(r) {
+			var res = wpAjax.parseAjaxResponse(r, s.response, s.element), o;
+			if ( !res || res.errors ) {
+				element.stop().stop().css( 'backgroundColor', '#FF3333' )[isClass?'removeClass':'addClass'](s.dimClass).show().queue( function() { list.wpList.recolor(); $(this).dequeue(); } );
+				return false;
+			}
+			if ( $.isFunction(s.dimAfter) ) {
+				o = this.complete;
+				this.complete = function(x,st) {
+					element.queue( function() {
+						var _s = $.extend( { xml: x, status: st, parsed: res }, s );
+						s.dimAfter( r, _s );
+						if ( $.isFunction(o) ) { o(x,st); }
+					} ).dequeue();
+				};
+			}
+		};
+
+		$.ajax( s );
+		return false;
+	},
+
+	// From jquery.color.js: jQuery Color Animation by John Resig
+	getColor: function( el ) {
+		if ( el.constructor == Object )
+			el = el.get(0);
+		var elem = el, color, rgbaTrans = new RegExp( "rgba\\(\\s*0,\\s*0,\\s*0,\\s*0\\s*\\)", "i" );
+		do {
+			color = jQuery.curCSS(elem, 'backgroundColor');
+			if ( color != '' && color != 'transparent' && !color.match(rgbaTrans) || jQuery.nodeName(elem, "body") )
+				break;
+		} while ( elem = elem.parentNode );
+		return color || '#ffffff';
+	},
+
+	add: function( e, s ) {
+		e = $(e);
+
+		var list = $(this), old = false, _s = { pos: 0, id: 0, oldId: null }, ba, ref, color;
+		if ( 'string' == typeof s ) { s = { what: s }; }
+		s = $.extend(_s, this.wpList.settings, s);
+		if ( !e.size() || !s.what ) { return false; }
+		if ( s.oldId ) { old = $('#' + s.what + '-' + s.oldId); }
+		if ( s.id && ( s.id != s.oldId || !old || !old.size() ) ) { $('#' + s.what + '-' + s.id).remove(); }
+
+		if ( old && old.size() ) {
+			old.replaceWith(e);
+		} else if ( isNaN(s.pos) ) {
+			ba = 'after';
+			if ( '-' == s.pos.substr(0,1) ) {
+				s.pos = s.pos.substr(1);
+				ba = 'before';
+			}
+			ref = list.find( '#' + s.pos );
+			if ( 1 === ref.size() ) { ref[ba](e); }
+			else { list.append(e); }
+		} else if ( s.pos < 0 ) {
+			list.prepend(e);
+		} else {
+			list.append(e);
+		}
+
+		if ( s.alt ) {
+			if ( ( list.children(':visible').index( e[0] ) + s.altOffset ) % 2 ) { e.removeClass( s.alt ); }
+			else { e.addClass( s.alt ); }
+		}
+
+		if ( 'none' != s.addColor ) {
+			color = wpList.getColor( e );
+			e.css( 'backgroundColor', s.addColor ).animate( { backgroundColor: color }, { complete: function() { $(this).css( 'backgroundColor', '' ); } } );
+		}
+		list.each( function() { this.wpList.process( e ); } );
+		return e;
+	},
+
+	clear: function(e) {
+		var list = this, t, tag;
+		e = $(e);
+		if ( list.wpList && e.parents( '#' + list.id ).size() ) { return; }
+		e.find(':input').each( function() {
+			if ( $(this).parents('.form-no-clear').size() )
+				return;
+			t = this.type.toLowerCase();
+			tag = this.tagName.toLowerCase();
+			if ( 'text' == t || 'password' == t || 'textarea' == tag ) { this.value = ''; }
+			else if ( 'checkbox' == t || 'radio' == t ) { this.checked = false; }
+			else if ( 'select' == tag ) { this.selectedIndex = null; }
+		});
+	},
+
+	process: function(el) {
+		var list = this;
+		$("[class^=add:" + list.id + ":]", el || null)
+			.filter('form').submit( function() { return list.wpList.add(this); } ).end()
+			.not('form').click( function() { return list.wpList.add(this); } ).each( function() {
+				var addEl = this, c = wpList.parseClass(this,'add')[2] || addEl.id, forms = [], ins = [];
+				if ( !c ) { return; }
+				// this is all really inefficient
+				$('#' + c + ' :input').focus( function() { currentFormEl = this; } ).blur( function() { currentFormEl = false; } ).each( function() {
+					ins.push(this);
+					var f = $(this).parents('form:first').get(0);
+					if ( $.inArray(f,forms) < 0 ) { forms.push(f); }
+				} );
+				$(forms).submit( function() {
+					if ( 0 <= $.inArray(currentFormEl,ins) ) {
+						$(addEl).trigger( 'click' );
+						$(currentFormEl).focus();
+						return false;
+					}
+				} );
+			} );
+		$("[class^=delete:" + list.id + ":]", el || null).click( function() { return list.wpList.del(this); } );
+		$("[class^=dim:" + list.id + ":]", el || null).click( function() { return list.wpList.dim(this); } );
+	},
+
+	recolor: function() {
+		var list = this, items, eo;
+		if ( !list.wpList.settings.alt ) { return; }
+		items = $('.list-item:visible', list);
+		if ( !items.size() ) { items = $(list).children(':visible'); }
+		eo = [':even',':odd'];
+		if ( list.wpList.settings.altOffset % 2 ) { eo.reverse(); }
+		items.filter(eo[0]).addClass(list.wpList.settings.alt).end().filter(eo[1]).removeClass(list.wpList.settings.alt);
+	},
+
+	init: function() {
+		var lists = this;
+		lists.wpList.process = function(a) {
+			lists.each( function() {
+				this.wpList.process(a);
+			} );
+		};
+		lists.wpList.recolor = function() {
+			lists.each( function() {
+				this.wpList.recolor();
+			} );
+		};
+	}
+};
+
+$.fn.wpList = function( settings ) {
+	this.each( function() {
+		var _this = this;
+		this.wpList = { settings: $.extend( {}, wpList.settings, { what: wpList.parseClass(this,'list')[1] || '' }, settings ) };
+		$.each( fs, function(i,f) { _this.wpList[i] = function( e, s ) { return wpList[f].call( _this, e, s ); }; } );
+	} );
+	wpList.init.call(this);
+	this.wpList.process();
+	return this;
+};
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-load.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-load.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-load.php	(revision 5057)
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Initialises the most fundamental parts of bbPress
+ *
+ * You should not have to change this file, all configuration
+ * should be possible in bb-config.php
+ *
+ * @package bbPress
+ */
+
+
+
+/**
+ * Low level reasons to die
+ */
+
+// Die if PHP is not new enough
+if ( version_compare( PHP_VERSION, '4.3', '<' ) ) {
+	die( sprintf( 'Your server is running PHP version %s but bbPress requires at least 4.3', PHP_VERSION ) );
+}
+
+
+
+// Modify error reporting levels to exclude PHP notices
+error_reporting(E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING);
+
+
+/**
+ * bb_timer_start() - PHP 4 standard microtime start capture
+ *
+ * @access private
+ * @global int $bb_timestart Seconds and Microseconds added together from when function is called
+ * @return bool Always returns true
+ */
+function bb_timer_start()
+{
+	global $bb_timestart;
+	$mtime = explode( ' ', microtime() );
+	$bb_timestart = $mtime[1] + $mtime[0];
+	return true;
+}
+bb_timer_start();
+
+
+
+// Server detection
+
+/**
+ * Whether the server software is Apache or something else
+ * @global bool $is_apache
+ */
+$is_apache = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false);
+
+/**
+ * Whether the server software is IIS or something else
+ * @global bool $is_IIS
+ */
+$is_IIS = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer' ) !== false );
+
+/**
+ * Whether the server software is IIS 7.X
+ * @global bool $is_iis7
+ */
+$is_iis7 = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7.' ) !== false );
+
+
+
+/**
+ * Stabilise $_SERVER variables in various PHP environments
+ */
+
+// Fix for IIS, which doesn't set REQUEST_URI
+if ( empty( $_SERVER['REQUEST_URI'] ) ) {
+
+	// IIS Mod-Rewrite
+	if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) {
+		$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
+	}
+	// IIS Isapi_Rewrite
+	else if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
+		$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
+	}
+	else
+	{
+		// Use ORIG_PATH_INFO if there is no PATH_INFO
+		if ( !isset($_SERVER['PATH_INFO']) && isset($_SERVER['ORIG_PATH_INFO']) )
+			$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];
+
+		// Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)
+		if ( isset($_SERVER['PATH_INFO']) ) {
+			if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )
+				$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
+			else
+				$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
+		}
+
+		// Append the query string if it exists and isn't null
+		if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
+			$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
+		}
+	}
+}
+
+// Fix for PHP as CGI hosts that set SCRIPT_FILENAME to something ending in php.cgi for all requests
+if ( isset($_SERVER['SCRIPT_FILENAME']) && ( strpos($_SERVER['SCRIPT_FILENAME'], 'php.cgi') == strlen($_SERVER['SCRIPT_FILENAME']) - 7 ) )
+	$_SERVER['SCRIPT_FILENAME'] = $_SERVER['PATH_TRANSLATED'];
+
+// Fix for Dreamhost and other PHP as CGI hosts
+if (strpos($_SERVER['SCRIPT_NAME'], 'php.cgi') !== false)
+	unset($_SERVER['PATH_INFO']);
+
+// Fix empty PHP_SELF
+$PHP_SELF = $_SERVER['PHP_SELF'];
+if ( empty($PHP_SELF) )
+	$_SERVER['PHP_SELF'] = $PHP_SELF = preg_replace("/(\?.*)?$/",'',$_SERVER["REQUEST_URI"]);
+
+
+
+/**
+ * bbPress logging level constants - same as constants from BP_Log class
+ */
+define( 'BB_LOG_NONE',    0 );
+define( 'BB_LOG_FAIL',    1 );
+define( 'BB_LOG_ERROR',   2 );
+define( 'BB_LOG_WARNING', 4 );
+define( 'BB_LOG_NOTICE',  8 );
+define( 'BB_LOG_DEBUG',   16 );
+
+/**
+ * Combination of all errors (excluding none and debug)
+ */
+define( 'BB_LOG_ALL', BB_LOG_FAIL + BB_LOG_ERROR + BB_LOG_WARNING + BB_LOG_NOTICE );
+
+/**
+ * Define temporary $_bb_path as this files directory, then check for the special BB_PATH config file
+ * which allows override of BB_PATH, but only outside of core files
+ */
+$_bb_path = dirname( __FILE__ ) . '/';
+$_bb_config_path = dirname( $_bb_path ) . '/bb-config-path.php';
+if ( file_exists( $_bb_config_path ) ) {
+	include_once( $_bb_config_path );
+}
+if ( !defined( 'BB_PATH' ) ) {
+	define( 'BB_PATH', $_bb_path );
+}
+unset( $_bb_path, $_bb_config_path );
+
+/**
+ * The bbPress includes path relative to BB_PATH
+ */
+define( 'BB_INC', 'bb-includes/' );
+
+// Initialise $bb object
+$bb = new StdClass();
+
+if ( file_exists( BB_PATH . 'bb-config.php') ) {
+
+	// The config file resides in BB_PATH
+	require_once( BB_PATH . 'bb-config.php');
+
+	// Load bb-settings.php
+	require_once( BB_PATH . 'bb-settings.php' );
+
+} elseif ( file_exists( dirname( BB_PATH ) . '/bb-config.php') ) {
+
+	// The config file resides one level below BB_PATH
+	require_once( dirname( BB_PATH ) . '/bb-config.php' );
+
+	// Load bb-settings.php
+	require_once( BB_PATH . 'bb-settings.php' );
+
+} elseif ( !defined( 'BB_INSTALLING' ) || !BB_INSTALLING ) {
+
+	// The config file doesn't exist and we aren't on the installation page
+
+	// Cut to the chase, go to the installer and use it to deal with errors
+	$install_uri = preg_replace( '|(/bb-admin)?/[^/]+?$|', '/', $_SERVER['PHP_SELF'] ) . 'bb-admin/install.php';
+	header( 'Location: ' . $install_uri );
+	die();
+
+}
+
+if ( isset( $_GET['doit'] ) && 'bb-subscribe' == $_GET['doit'] )
+	require( BB_PATH . 'bb-includes/action.subscribe.php' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-login.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-login.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-login.php	(revision 5057)
@@ -0,0 +1,148 @@
+<?php
+
+// Load bbPress
+require( './bb-load.php' );
+
+// SSL redirect if required
+bb_ssl_redirect();
+
+// Don't cache this page at all
+nocache_headers();
+
+/** Look for redirection ******************************************************/
+
+// Look for 'redirect_to'
+if ( isset( $_REQUEST['redirect_to'] ) )
+	$re = $_REQUEST['redirect_to'];
+
+	// Look for 're'
+	if ( empty( $re ) && isset( $_REQUEST['re'] ) )
+		$re = $_REQUEST['re'];
+
+		// Use referer
+		if ( empty( $re ) )
+			$re = wp_get_referer();
+
+			// Don't redirect to register or password reset pages
+			if ( empty( $re ) ) {
+				// Grab home path and URL for comparison
+				$home_url  = parse_url( bb_get_uri( null, null, BB_URI_CONTEXT_TEXT ) );
+				$home_path = $home_url['path'];
+
+				if ( false !== strpos( $re, $home_path . 'register.php' ) || false !== strpos( $re, $home_path . 'bb-reset-password.php' ) )
+					$re = bb_get_uri( null, null, BB_URI_CONTEXT_HEADER );
+			}
+
+/**
+ * If this page was accessed using SSL, make sure the redirect is a full URL so
+ * that we don't end up on an SSL page again (unless the whole site is under SSL)
+ */
+if ( is_ssl() && 0 === strpos( $re, '/' ) )
+	$re = bb_get_uri( $re , null, BB_URI_CONTEXT_HEADER );
+
+// Clean the redirection destination
+if ( !empty( $re ) ) {
+	$re = esc_url( $re );
+	$re = esc_attr( $re );
+	$redirect_to = $re;
+}
+
+// Fallback to site root
+if ( empty( $re ) )
+	$re = bb_get_uri();
+
+/** Handle logout *************************************************************/
+
+// User is logged in
+if ( bb_is_user_logged_in() ) {
+
+	// Logout requested
+	if ( isset( $_GET['logout'] ) )
+		$_GET['action'] = 'logout';
+
+	// Check logout action
+	if ( isset( $_GET['action'] ) && 'logout' === $_GET['action'] )
+		bb_logout();
+
+	bb_safe_redirect( $re );
+	exit;
+}
+
+/** Handle login **************************************************************/
+
+// Do we allow login by email address
+$email_login = bb_get_option( 'email_login' );
+
+// Get the user from the login details
+if ( empty( $_POST['log'] ) )
+	$_POST['log'] = !empty( $_POST['user_login'] ) ? $_POST['user_login'] : '';
+
+if ( empty( $_POST['pwd'] ) )
+	$_POST['pwd'] = !empty( $_POST['password']   ) ? $_POST['password']   : '';
+
+if ( empty( $_POST['rememberme'] ) )
+	$_POST['rememberme'] = !empty( $_POST['remember']   ) ? 1                    : '';
+
+// Attempt to log the user in
+if ( $user = bb_login( @$_POST['log'], @$_POST['pwd'], @$_POST['rememberme'] ) ) {
+	if ( !is_wp_error( $user ) ) {
+		bb_safe_redirect( $re );
+		exit;
+	} else {
+		$bb_login_error =& $user;
+	}
+	
+// No login so prepare the error
+} else {
+	$bb_login_error = new WP_Error;
+}
+
+/** Handle errors *************************************************************/
+
+// Get error data so we can provide feedback
+$error_data = $bb_login_error->get_error_data();
+
+// Does user actually exist
+if ( isset( $error_data['unique'] ) && false === $error_data['unique'] )
+	$user_exists = true;
+else
+	$user_exists = !empty( $_POST['log'] ) && (bool) bb_get_user( $_POST['log'], array( 'by' => 'login' ) );
+
+// Check for errors on post method
+if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) ) {
+	
+	// If the user doesn't exist then add that error
+	if ( empty( $user_exists ) ) {
+		if ( !empty( $_POST['log'] ) ) {
+			$bb_login_error->add( 'user_login', __( 'User does not exist.' ) );
+		} else {
+			$bb_login_error->add( 'user_login', $email_login ? __( 'Enter a username or email address.' ) : __( 'Enter a username.' ) );
+		}
+	}
+
+	// If the password was wrong then add that error
+	if ( !$bb_login_error->get_error_code() ) {
+		$bb_login_error->add( 'password', __( 'Incorrect password.' ) );
+	}
+}
+
+/**
+ * If trying to log in with email address, don't leak whether or not email
+ * address exists in the db. is_email() is not perfect. Usernames can be
+ * valid email addresses potentially.
+ */
+if ( !empty( $email_login ) && $bb_login_error->get_error_codes() && false !== is_email( @$_POST['log'] ) )
+	$bb_login_error = new WP_Error( 'user_login', __( 'Username and Password do not match.' ) );
+
+/** Prepare for display *******************************************************/
+
+// Sanitze variables for display
+$remember_checked  = @$_POST['rememberme'] ? ' checked="checked"' : '';
+$user_login        = esc_attr( sanitize_user( @$_POST['log'], true ) );
+
+// Load the template
+bb_load_template( 'login.php', array( 'user_exists', 'user_login', 'remember_checked', 'redirect_to', 're', 'bb_login_error' ) );
+
+exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/akismet.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/akismet.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/akismet.php	(revision 5057)
@@ -0,0 +1,525 @@
+<?php
+/*
+Plugin Name: Akismet
+Plugin URI: http://akismet.com/
+Description: Akismet checks posts against the Akismet web service to see if they look like spam or not. You need a <a href="http://wordpress.com/api-keys/">WordPress.com API key</a> to use this service.
+Author: Michael Adams
+Version: 1.1
+Author URI: http://blogwaffe.com/
+*/
+
+
+
+$bb_ksd_api_host = bb_get_option( 'akismet_key' ) . '.rest.akismet.com';
+$bb_ksd_api_port = 80;
+$bb_ksd_user_agent = 'bbPress/' . bb_get_option( 'version' ) . ' | bbAkismet/'. bb_get_option( 'version' );
+
+function bb_akismet_verify_key( $key )
+{
+	global $bb_ksd_api_port;
+	$blog = urlencode( bb_get_uri( null, null, BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_AKISMET ) );
+	$response = bb_ksd_http_post( 'key=' . $key . '&blog=' . $blog, 'rest.akismet.com', '/1.1/verify-key', $bb_ksd_api_port );
+	if ( 'valid' == $response[1] ) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+// Returns array with headers in $response[0] and entity in $response[1]
+function bb_ksd_http_post( $request, $host, $path, $port = 80 )
+{
+	global $bb_ksd_user_agent;
+
+	$http_request  = 'POST ' . $path . ' HTTP/1.0' . "\r\n";
+	$http_request .= 'Host: ' . $host . "\r\n";
+	$http_request .= 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' . "\r\n"; // for now
+	$http_request .= 'Content-Length: ' . strlen($request) . "\r\n";
+	$http_request .= 'User-Agent: ' . $bb_ksd_user_agent . "\r\n";
+	$http_request .= "\r\n";
+	$http_request .= $request;
+	$response = '';
+	if ( false != ( $fs = @fsockopen( $host, $port, $errno, $errstr, 10 ) ) ) {
+		fwrite( $fs, $http_request );
+
+		while ( !feof( $fs ) ) {
+			$response .= fgets( $fs, 1160 ); // One TCP-IP packet
+		}
+		fclose( $fs );
+		$response = explode( "\r\n\r\n", $response, 2 );
+	}
+	return $response;
+}
+
+function bb_ksd_configuration_page()
+{
+?>
+<h2><?php _e( 'Akismet Settings' ); ?></h2>
+<?php do_action( 'bb_admin_notices' ); ?>
+
+<form class="settings" method="post" action="<?php bb_uri( 'bb-admin/admin-base.php', array( 'plugin' => 'bb_ksd_configuration_page'), BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_ADMIN ); ?>">
+	<fieldset>
+		<p><?php printf( __( 'For many people, <a href="%s">Akismet</a> will greatly reduce or even completely eliminate the spam you get on your site. If one does happen to get through, simply mark it as "spam" and Akismet will learn from the mistakes.' ), 'http://akismet.com/' ); ?></p>
+
+<?php
+	$after = '';
+	if ( false !== $key = bb_get_option( 'akismet_key' ) ) {
+		if ( bb_akismet_verify_key( $key ) ) {
+			$after = __( 'This key is valid' );
+		} else {
+			bb_delete_option( 'akismet_key' );
+		}
+	}
+
+	bb_option_form_element( 'akismet_key', array(
+		'title' => __( 'WordPress.com API Key' ),
+		'attributes' => array( 'maxlength' => 12 ),
+		'after' => $after,
+		'note' => sprintf( __( 'If you don\'t have a WordPress.com API Key, you can get one at <a href="%s">WordPress.com</a>' ), 'http://wordpress.com/api-keys/' )
+	) );
+
+	bb_option_form_element( 'akismet_stats', array(
+		'title' => __( 'Enable stats page' ),
+		'type' => 'checkbox',
+		'options' => array(
+			1 => __( 'Create a page that shows spam statistics.' )
+		),
+		'note' => __( 'This page will be viewable by moderators or higher.' )
+	) );
+?>
+
+	</fieldset>
+	<fieldset class="submit">
+		<?php bb_nonce_field( 'options-akismet-update' ); ?>
+		<input type="hidden" name="action" value="update-akismet-settings" />
+		<input class="submit" type="submit" name="submit" value="<?php _e('Save Changes') ?>" />
+	</fieldset>
+</form>
+<?php
+}
+
+function bb_ksd_configuration_page_add()
+{
+	bb_admin_add_submenu( __( 'Akismet' ), 'use_keys', 'bb_ksd_configuration_page', 'options-general.php' );
+}
+add_action( 'bb_admin_menu_generator', 'bb_ksd_configuration_page_add' );
+
+function bb_ksd_configuration_page_process()
+{
+	if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && $_POST['action'] == 'update-akismet-settings') {
+		bb_check_admin_referer( 'options-akismet-update' );
+
+		$goback = remove_query_arg( array( 'invalid-akismet', 'updated-akismet' ), wp_get_referer() );
+
+		if ( !isset( $_POST['akismet_stats'] ) ) {
+			$_POST['akismet_stats'] = false;
+		}
+
+		if ( true === (bool) $_POST['akismet_stats'] ) {
+			bb_update_option( 'akismet_stats', 1 );
+		} else {
+			bb_delete_option( 'akismet_stats' );
+		}
+
+		if ( $_POST['akismet_key'] ) {
+			$value = stripslashes_deep( trim( $_POST['akismet_key'] ) );
+			if ( $value ) {
+				if ( bb_akismet_verify_key( $value ) ) {
+					bb_update_option( 'akismet_key', $value );
+				} else {
+					$goback = add_query_arg( 'invalid-akismet', 'true', $goback );
+					bb_safe_redirect( $goback );
+					exit;
+				}
+			} else {
+				bb_delete_option( 'akismet_key' );
+			}
+		} else {
+			bb_delete_option( 'akismet_key' );
+		}
+
+		$goback = add_query_arg( 'updated-akismet', 'true', $goback );
+		bb_safe_redirect( $goback );
+		exit;
+	}
+
+	if ( !empty( $_GET['updated-akismet'] ) ) {
+		bb_admin_notice( __( '<strong>Settings saved.</strong>' ) );
+	}
+
+	if ( !empty( $_GET['invalid-akismet'] ) ) {
+		bb_admin_notice( __( '<strong>The key you attempted to enter is invalid. Reverting to previous setting.</strong>' ), 'error' );
+	}
+
+	global $bb_admin_body_class;
+	$bb_admin_body_class = ' bb-admin-settings';
+}
+add_action( 'bb_ksd_configuration_page_pre_head', 'bb_ksd_configuration_page_process' );
+
+// Bail here if no key is set
+if ( !bb_get_option( 'akismet_key' ) ) {
+	return;
+}
+
+function bb_ksd_stats_script()
+{
+?>
+<style>
+	#bb-ksd-stats-frame {
+		-moz-box-shadow: 0 0 15px rgb(255, 255, 255);
+		-webkit-box-shadow: 0 0 15px rgb(255, 255, 255);
+		box-shadow: 0 0 15px rgb(255, 255, 255);
+		margin-top: 16px;
+		width: 100%;
+		height: 700px;
+		border-width: 0;
+	}
+</style>
+<script type="text/javascript">
+	function resizeIframe() {
+		var height = document.documentElement.clientHeight;
+		height -= document.getElementById('bb-ksd-stats-frame').offsetTop;
+		height -= 60;
+		document.getElementById('bb-ksd-stats-frame').style.height = height +"px";
+	};
+	function resizeIframeInit() {
+		document.getElementById('bb-ksd-stats-frame').onload = resizeIframe;
+		window.onresize = resizeIframe;
+	}
+	addLoadEvent(resizeIframeInit);
+</script>
+<?php
+}
+
+function bb_ksd_stats_display_pre_head()
+{
+	if ( !bb_get_option( 'akismet_stats' ) ) {
+		return;
+	}
+	add_action( 'bb_admin_head', 'bb_ksd_stats_script' );
+}
+add_action( 'bb_ksd_stats_display_pre_head', 'bb_ksd_stats_display_pre_head' );
+
+function bb_ksd_stats_display()
+{
+	$site = urlencode( bb_get_uri( null, null, BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_AKISMET ) );
+	$url = "http://".bb_get_option( 'akismet_key' ).".web.akismet.com/1.0/user-stats.php?blog={$site}&amp;type=forum";
+?>
+	<iframe src="<?php echo $url; ?>" id="bb-ksd-stats-frame"></iframe>
+<?php
+}
+
+function bb_ksd_stats_page()
+{
+	if ( !bb_get_option( 'akismet_stats' ) ) {
+		return;
+	}
+	if ( function_exists( 'bb_admin_add_submenu' ) ) {
+		bb_admin_add_submenu( __( 'Akismet Stats' ), 'moderate', 'bb_ksd_stats_display', 'index.php' );
+	}
+}
+add_action( 'bb_admin_menu_generator', 'bb_ksd_stats_page' );
+
+function bb_ksd_submit( $submit, $type = false )
+{
+	global $bb_ksd_api_host;
+	global $bb_ksd_api_port;
+
+	switch ( $type ) {
+		case 'ham':
+		case 'spam':
+			$path = '/1.1/submit-' . $type;
+
+			$bb_post = bb_get_post( $submit );
+			if ( !$bb_post ) {
+				return;
+			}
+			$user = bb_get_user( $bb_post->poster_id );
+			if ( bb_is_trusted_user( $user->ID ) ) {
+				return;
+			}
+
+			$_submit = array(
+				'blog' => bb_get_uri( null, null, BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_AKISMET ),
+				'user_ip' => $bb_post->poster_ip,
+				'permalink' => get_topic_link( $bb_post->topic_id ), // First page
+				'comment_type' => 'forum',
+				'comment_author' => get_user_name( $user->ID ),
+				'comment_author_email' =>  bb_get_user_email( $user->ID ),
+				'comment_author_url' => get_user_link( $user->ID ),
+				'comment_content' => $bb_post->post_text,
+				'comment_date_gmt' => $bb_post->post_time
+			);
+			break;
+
+		case 'hammer':
+		case 'spammer':
+			$path = '/1.1/submit-' . substr( $type, 0, -3 );
+
+			$user = bb_get_user( $submit );
+			if ( !$user ) {
+				return;
+			}
+			if ( bb_is_trusted_user( $user->ID ) ) {
+				return;
+			}
+
+			$_submit = array(
+				'blog' => bb_get_uri( null, null, BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_AKISMET ),
+				'permalink' => get_user_profile_link( $user->ID ),
+				'comment_type' => 'profile',
+				'comment_author' => get_user_name( $user->ID ),
+				'comment_author_email' =>  bb_get_user_email( $user->ID ),
+				'comment_author_url' => get_user_link( $user->ID ),
+				'comment_content' => $user->occ . ' ' . $user->interests,
+				'comment_date_gmt' => $user->user_registered
+			);
+			break;
+
+		default:
+			if ( bb_is_trusted_user( bb_get_current_user() ) ) {
+				return;
+			}
+
+			$path = '/1.1/comment-check';
+
+			$_submit = array(
+				'blog' => bb_get_uri( null, null, BB_URI_CONTEXT_TEXT + BB_URI_CONTEXT_AKISMET ),
+				'user_ip' => preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] ),
+				'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+				'referrer' => $_SERVER['HTTP_REFERER'],
+				'comment_type' => isset($_POST['topic_id']) ? 'forum' : 'profile',
+				'comment_author' => bb_get_current_user_info( 'name' ),
+				'comment_author_email' => bb_get_current_user_info( 'email' ),
+				'comment_author_url' => bb_get_current_user_info( 'url' ),
+				'comment_content' => $submit
+			);
+			if ( isset( $_POST['topic_id'] ) ) {
+				$_submit['permalink'] = get_topic_link( $_POST['topic_id'] ); // First page
+			}
+			break;
+	}
+
+	$query_string = '';
+	foreach ( $_submit as $key => $data ) {
+		$query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&';
+	}
+	return bb_ksd_http_post( $query_string, $bb_ksd_api_host, $path, $bb_ksd_api_port );
+}
+
+function bb_ksd_submit_ham( $post_id )
+{
+	bb_ksd_submit( $post_id, 'ham' );
+}
+
+function bb_ksd_submit_spam( $post_id )
+{
+	bb_ksd_submit( $post_id, 'spam' );
+}
+
+function bb_ksd_check_post( $post_text )
+{
+	global $bb_ksd_pre_post_status, $bb_ksd_pre_post;
+
+	$bb_ksd_pre_post = $post_text;
+
+	return $post_text;
+}
+add_action( 'pre_post', 'bb_ksd_check_post', 1 );
+
+function bb_ksd_check_profile( $user_id )
+{
+	global $bb_current_user, $user_obj;
+	$bb_current_id = bb_get_current_user_info( 'id' );
+	bb_set_current_user( $user_id );
+	if ( $bb_current_id && $bb_current_id != $user_id ) {
+		if ( $user_obj->data->is_bozo && !$bb_current_user->data->is_bozo ) {
+			bb_ksd_submit( $user_id, 'hammer' );
+		}
+		if ( !$user_obj->data->is_bozo && $bb_current_user->data->is_bozo ) {
+			bb_ksd_submit( $user_id, 'spammer' );
+		}
+	} else {
+		$response = bb_ksd_submit( $bb_current_user->data->occ . ' ' . $bb_current_user->data->interests );
+		if ( 'true' == $response[1] && function_exists( 'bb_bozon' ) ) {
+			bb_bozon( bb_get_current_user_info( 'id' ) );
+		}
+	}
+	bb_set_current_user( (int) $bb_current_id );
+}
+add_action( 'register_user', 'bb_ksd_check_profile', 1);
+add_action( 'profile_edited', 'bb_ksd_check_profile', 1);
+
+function bb_ksd_new_post( $post_id )
+{
+	global $bb_ksd_pre_post_status;
+	if ( '2' != $bb_ksd_pre_post_status ) {
+		return;
+	}
+	$bb_post = bb_get_post( $post_id );
+	$topic = get_topic( $bb_post->topic_id );
+	if ( 0 == $topic->topic_posts ) {
+		bb_delete_topic( $topic->topic_id, 2 );
+	}
+}
+add_filter( 'bb_new_post', 'bb_ksd_new_post' );
+
+function bb_akismet_delete_old()
+{
+	// Delete old every 20
+	$n = mt_rand( 1, 20 );
+	if ( $n % 20 ) {
+		return;
+	}
+	global $bbdb;
+	$now = bb_current_time( 'mysql' );
+	$posts = (array) $bbdb->get_col( $bbdb->prepare(
+		"SELECT post_id FROM $bbdb->posts WHERE DATE_SUB(%s, INTERVAL 15 DAY) > post_time AND post_status = '2'",
+		$now
+	) );
+	foreach ( $posts as $post ) {
+		bb_delete_post( $post, 1 );
+	}
+}
+
+function bb_ksd_pre_post_status( $post_status )
+{
+	global $bb_current_user, $bb_ksd_pre_post_status, $bb_ksd_pre_post;
+
+	// Don't filter content from users with a trusted role
+	if ( in_array( $bb_current_user->roles[0], bb_trusted_roles() ) ) {
+		return $post_status;
+	}
+
+	$response = bb_ksd_submit( $bb_ksd_pre_post );
+	if ( 'true' == $response[1] ) {
+		$bb_ksd_pre_post_status = '2';
+		return $bb_ksd_pre_post_status;
+	}
+	return $post_status;
+}
+add_filter( 'pre_post_status', 'bb_ksd_pre_post_status' );
+
+function bb_ksd_delete_post( $post_id, $new_status, $old_status )
+{
+	// Don't report post deletion
+	if ( 1 == $new_status ) {
+		return;
+	}
+	// Don't report no change in post status
+	if ( $new_status == $old_status ) {
+		return;
+	}
+	// It's being marked as spam, so report it
+	if ( 2 == $new_status ) {
+		bb_ksd_submit_spam( $post_id );
+		return;
+	}
+	// It's not spam (and not being deleted), so it's ham now
+	if ( 2 == $old_status ) {
+		bb_ksd_submit_ham( $post_id );
+		return;
+	}
+}
+add_action( 'bb_delete_post', 'bb_ksd_delete_post', 10, 3);
+
+function bb_ksd_post_delete_link( $parts, $args )
+{
+	if ( !bb_current_user_can( 'moderate' ) ) {
+		return $parts;
+	}
+	$bb_post = bb_get_post( get_post_id( $args['post_id'] ) );
+
+	if ( 2 == $bb_post->post_status ) {
+		$query = array(
+			'id'     => $bb_post->post_id,
+			'status' => 0,
+			'view'   => 'all'
+		);
+		$display = __('Not Spam');
+	} else {
+		$query = array(
+			'id'     => $bb_post->post_id,
+			'status' => 2
+		);
+		$display = __('Spam');
+	}
+	$uri = bb_get_uri( 'bb-admin/delete-post.php', $query, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_ADMIN );
+	$uri = esc_url( bb_nonce_url( $uri, 'delete-post_' . $bb_post->post_id ) );
+	if ( !is_array( $parts ) ) {
+		$parts = array();
+		$before = '';
+		$after = '';
+	} else {
+		$before = $args['last_each']['before'];
+		$after = $args['last_each']['after'];
+	}
+
+	// Make sure that the last tag in $before gets a class (if it's there)
+	if ( preg_match( '/.*(<[^>]+>)[^<]*/', $before, $_node ) ) {
+		if ( preg_match( '/class=(\'|")(.*)\1/U', $_node[1], $_class ) ) {
+			$before = str_replace( $_class[0], 'class=' . $_class[1] . 'before-post-spam-link ' . $_class[2] . $_class[1], $before );
+		} else {
+			$before = preg_replace( '/(.*)<([a-z0-9_-]+)(\s?)([^>]*)>([^<]*)/i', '$1<$2 class="before-post-spam-link"$3$4>$5', $before, 1 );
+		}
+	}
+
+	$parts[] = $before . '<a class="post-spam-link" href="' . $uri . '" >' . $display . '</a>' . $after;
+	return $parts;
+}
+add_filter( 'bb_post_admin', 'bb_ksd_post_delete_link', 10, 2 );
+
+function bb_ksd_bulk_post_actions( &$bulk_actions, &$post_query ) {
+	$status = $post_query->get( 'post_status' );
+
+	$bulk_actions['unspam'] = __( 'Not Spam' );
+	$bulk_actions['spam'] = __( 'Mark as Spam' );
+
+	if ( 2 == $status )
+		unset( $bulk_actions['undelete'], $bulk_actions['spam'] );
+	elseif ( is_numeric( $status ) )
+		unset( $bulk_actions['unspam'] );
+}
+
+add_action( 'bulk_post_actions', 'bb_ksd_bulk_post_actions', 10, 2 );
+
+function bb_ksd_bulk_post__action( $query_vars, $post_ids, $action ) {
+	$count = 0;
+
+	switch ( $action ) {
+	case 'spam' :
+		foreach ( $post_ids as $post_id ) {
+			$count += (int) (bool) bb_delete_post( $post_id, 2 );
+		}
+		return array( 'message' => 'spammed', 'count' => $count );
+	case 'unspam' :
+		foreach ( $post_ids as $post_id ) {
+			$count += (int) (bool) bb_delete_post( $post_id, 0 );
+		}
+		return array( 'message' => 'unspammed-normal', 'count' => $count );
+	}
+}
+
+add_action( 'bulk_post__spam', 'bb_ksd_bulk_post__action', 10, 3 );
+add_action( 'bulk_post__unspam', 'bb_ksd_bulk_post__action', 10, 3 );
+
+function bb_ksd_add_post_status_to_forms( $stati, $type )
+{
+	if ( 'post' === $type ) {
+		$stati['2'] = __( 'Spam' );
+	}
+	return $stati;
+}
+add_filter( 'bb_query_form_post_status', 'bb_ksd_add_post_status_to_forms', 10, 2 );
+
+function bb_ksd_post_del_class( $classes, $post_id, $post )
+{
+	if ( '2' === (string) $post->post_status ) {
+		if ( $classes ) {
+			return $classes . ' spam';
+		}
+		return 'spam';
+	}
+	return $classes;
+}
+add_filter( 'post_del_class', 'bb_ksd_post_del_class', 10, 3 );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/bozo.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/bozo.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/bozo.php	(revision 5057)
@@ -0,0 +1,378 @@
+<?php
+/*
+Plugin Name: Bozo Users
+Plugin URI: http://bbpress.org/
+Description: Allows moderators to mark certain users as a "bozo". Bozo users can post, but their content is only visible to themselves.
+Author: Michael Adams
+Version: 1.1
+Author URI: http://blogwaffe.com/
+*/
+
+function bb_bozo_posts( $where ) {
+	if ( !$id = bb_get_current_user_info( 'id' ) )
+		return $where;
+
+	return preg_replace(
+		'/(\w+\.)?post_status = ["\']?0["\']?/',
+		"( \\1post_status = 0 OR \\1post_status > 1 AND \\1poster_id = '$id' )",
+	$where);
+}
+
+function bb_bozo_topics( $where ) {
+	if ( !$id = bb_get_current_user_info( 'id' ) )
+		return $where;
+
+	return preg_replace(
+		'/(\w+\.)?topic_status = ["\']?0["\']?/',
+		"( \\1topic_status = 0 OR \\1topic_status > 1 AND \\1topic_poster = '$id' )",
+	$where);
+}
+
+// Gets those users with the bozo bit.  Does not grab users who have been bozoed on a specific topic.
+// NOT bbdb::prepared
+function bb_get_bozos( $page = 1 ) {
+	global $bbdb, $bb_last_countable_query;
+	$page = (int) $page;
+	$limit = (int) bb_get_option('page_topics');
+	if ( 1 < $page )
+		$limit = ($limit * ($page - 1)) . ", $limit";
+	$bb_last_countable_query = "SELECT user_id FROM $bbdb->usermeta WHERE meta_key='is_bozo' AND meta_value='1' ORDER BY umeta_id DESC LIMIT $limit";
+	if ( $ids = (array) $bbdb->get_col( $bb_last_countable_query ) )
+		bb_cache_users( $ids );
+	return $ids;
+}
+
+function bb_current_user_is_bozo( $topic_id = false ) {
+	global $bb_current_user;
+	if ( bb_current_user_can('browse_deleted') && 'all' == @$_GET['view'] )
+		return false;
+	$is_bozo = isset($bb_current_user->data->is_bozo) && $bb_current_user->data->is_bozo;
+	if ( !$topic_id || $is_bozo )
+		return $is_bozo;
+
+	global $topic;
+	$topic = get_topic( $topic_id );
+	$id = bb_get_current_user_info( 'id' );
+	return isset($topic->bozos[$id]) && $topic->bozos[$id];
+}
+
+function bb_bozo_pre_permalink() {
+	if ( bb_is_topic() )
+		add_filter( 'get_topic_where', 'bb_bozo_topics' );
+}
+
+function bb_bozo_post_permalink() {
+	if ( bb_is_topic() )
+		remove_filter( 'get_topic_where', 'bb_bozo_topics' );
+}
+
+function bb_bozo_latest_filter() {
+	global $bb_current_user;
+	if ( isset($bb_current_user->data->bozo_topics) && $bb_current_user->data->bozo_topics )
+		add_filter( 'get_latest_topics_where', 'bb_bozo_topics' );
+}
+
+function bb_bozo_topic_db_filter() {
+	global $topic, $topic_id;
+	if ( bb_current_user_is_bozo( $topic->topic_id ? $topic->topic_id : $topic_id ) ) {
+		add_filter( 'get_thread_where', 'bb_bozo_posts' );
+		add_filter( 'get_thread_post_ids_where', 'bb_bozo_posts' );
+	}
+}
+
+function bb_bozo_profile_db_filter() {
+	global $user;
+	if ( bb_get_current_user_info( 'id' ) == $user->ID && @is_array($user->bozo_topics) )
+		add_filter( 'get_recent_user_replies_where', 'bb_bozo_posts' );
+}
+
+function bb_bozo_recount_topics() {
+	global $bbdb;
+	global $messages;
+	if ( isset($_POST['topic-bozo-posts']) && 1 == $_POST['topic-bozo-posts'] ):
+		$old = (array) $bbdb->get_col("SELECT object_id FROM $bbdb->meta WHERE object_type = 'bb_topic' AND meta_key = 'bozos'");
+		$old = array_flip($old);
+		$messages[] = __('Counted the number of bozo posts in each topic');
+		if ( $topics = (array) $bbdb->get_col("SELECT topic_id, poster_id, COUNT(post_id) FROM $bbdb->posts WHERE post_status > 1 GROUP BY topic_id, poster_id") ) :
+			$unique_topics = array_unique($topics);
+			$posters = (array) $bbdb->get_col('', 1);
+			$counts = (array) $bbdb->get_col('', 2);
+			foreach ($unique_topics as $i):
+				$bozos = array();
+				$indices = array_keys($topics, $i);
+				foreach ( $indices as $index )
+					$bozos[(int) $posters[$index]] = (int) $counts[$index]; 
+				if ( $bozos ) :
+					bb_update_topicmeta( $i, 'bozos', $bozos );
+					unset($indices, $index, $old[$i]);
+				endif;
+			endforeach;
+			unset($topics, $i, $counts, $posters, $bozos);
+		endif;
+		if ( $old ) :
+			$old = join(',', array_map('intval', array_flip($old)));
+			$bbdb->query("DELETE FROM $bbdb->meta WHERE object_type = 'bb_topic' AND object_id IN ($old) AND meta_key = 'bozos'");
+		endif;
+	endif;
+}
+
+function bb_bozo_recount_users() {
+	global $bbdb;
+	global $messages;
+	if ( isset($_POST['topics-replied-with-bozos']) && 1 == $_POST['topics-replied-with-bozos'] ) :
+		$messages[] = __('Counted each bozo user&#039;s total posts as well as the total topics to which they have replied');
+		if ( $users = (array) $bbdb->get_col("SELECT ID FROM $bbdb->users") ) :
+			$no_bozos = array();
+			$bozo_mkey = $bbdb->prefix . 'bozo_topics';
+			foreach ( $users as $user ) :
+				$topics_replied = (int) $bbdb->get_var( $bbdb->prepare(
+					"SELECT COUNT(DISTINCT topic_id) FROM $bbdb->posts WHERE post_status = 0 AND poster_id = %d",
+					$user
+				) );
+				bb_update_usermeta( $user, $bbdb->prefix. 'topics_replied', $topics_replied );
+				$bozo_keys = (array) $bbdb->get_col( $bbdb->prepare(
+					"SELECT topic_id, COUNT(post_id) FROM $bbdb->posts WHERE post_status > 1 AND poster_id = %d GROUP BY topic_id",
+					$user
+				) );
+				$bozo_values = (array) $bbdb->get_col('', 1);
+				if ( $c = count($bozo_keys) ) :
+					for ( $i=0; $i < $c; $i++ )
+						$bozo_topics[(int) $bozo_keys[$i]] = (int) $bozo_values[$i];
+					bb_update_usermeta( $user, $bozo_mkey, $bozo_topics );
+				else :
+					$no_bozos[] = (int) $user;
+				endif;
+			endforeach;
+			if ( $no_bozos ) :
+				$no_bozos = join(',', $no_bozos);
+				$bbdb->query( $bbdb->prepare(
+					"DELETE FROM $bbdb->usermeta WHERE user_id IN ($no_bozos) AND meta_key = %s",
+					$bozo_mkey
+				) );
+			endif;
+			unset($users, $user, $topics_replied, $bozo_keys, $bozo_values, $bozo_topics);
+		endif;
+	endif;
+}
+
+function bb_bozo_post_del_class( $classes, $post_id, $post )
+{
+	if ( 1 < $post->post_status && bb_current_user_can('browse_deleted') ) {
+		if ( $classes ) {
+			return $classes . ' bozo';
+		}
+		return 'bozo';
+	}
+	return $classes;
+}
+
+function bb_bozo_add_recount_list() {
+	global $recount_list;
+	$recount_list[21] = array('topics-replied-with-bozos', __('Count each bozo user&#039;s total posts as well as the total topics to which they have replied'), 'bb_bozo_recount_users');
+	$recount_list[22] = array('topic-bozo-posts', __('Count the number of bozo posts in each topic'), 'bb_bozo_recount_topics');
+	return;
+}
+
+function bb_bozo_topic_pages_add( $add ) {
+	global $topic;
+	if ( isset($_GET['view']) && 'all' == $_GET['view'] && bb_current_user_can('browse_deleted') ) :
+		$add += @array_sum($topic->bozos);
+	endif;
+	if ( bb_current_user_is_bozo( $topic->topic_id ) )
+		$add += $topic->bozos[bb_get_current_user_info( 'id' )];
+	return $add;
+}
+
+function bb_bozo_get_topic_posts( $topic_posts ) {
+	global $topic;
+	if ( bb_current_user_is_bozo( $topic->topic_id ) )
+		$topic_posts += $topic->bozos[bb_get_current_user_info( 'id' )];
+	return $topic_posts;
+}
+
+function bb_bozo_new_post( $post_id ) {
+	$bb_post = bb_get_post( $post_id );
+	if ( 1 < $bb_post->post_status )
+		bb_bozon( $bb_post->poster_id, $bb_post->topic_id );
+	$topic = get_topic( $bb_post->topic_id, false );
+	if ( 0 == $topic->topic_posts )
+		bb_delete_topic( $topic->topic_id, 2 );
+}
+
+function bb_bozo_pre_post_status( $status, $post_id, $topic_id ) {
+	if ( !$post_id && bb_current_user_is_bozo() )
+		$status = 2;
+	elseif ( bb_current_user_is_bozo( $topic_id ) )
+		$status = 2;
+	return $status;
+}
+
+function bb_bozo_delete_post( $post_id, $new_status, $old_status ) {
+	$bb_post = bb_get_post( $post_id );
+	if ( 1 < $new_status && 2 > $old_status )
+		bb_bozon( $bb_post->poster_id, $bb_post->topic_id );
+	elseif ( 2 > $new_status && 1 < $old_status )
+		bb_fermion( $bb_post->poster_id, $bb_post->topic_id );
+}
+
+function bb_bozon( $user_id, $topic_id = 0 ) {
+	global $bbdb;
+
+	$user_id = (int) $user_id;
+	$topic_id = (int) $topic_id;
+
+	if ( !$topic_id )
+		bb_update_usermeta( $user_id, 'is_bozo', 1 );
+	else {
+		$topic = get_topic( $topic_id );
+		$user = bb_get_user( $user_id );
+
+		$bozo_topics_key = $bbdb->prefix . 'bozo_topics';
+
+		if ( isset($topic->bozos[$user_id]) )
+			$topic->bozos[$user_id]++;
+		elseif ( is_array($topic->bozos) )
+			$topic->bozos[$user_id] = 1;
+		else
+			$topic->bozos = array($user_id => 1);
+		bb_update_topicmeta( $topic_id, 'bozos', $topic->bozos );
+		
+		if ( isset($user->{$bozo_topics_key}[$topic_id]) )
+			$user->{$bozo_topics_key}[$topic_id]++;
+		elseif ( is_array($user->bozo_topics) )
+			$user->{$bozo_topics_key}[$topic_id] = 1;
+		else
+			$user->$bozo_topics_key = array($topic_id => 1);
+		bb_update_usermeta( $user_id, $bozo_topics_key, $user->$bozo_topics_key );
+	}
+}
+
+function bb_fermion( $user_id, $topic_id = 0 ) {
+	global $bbdb;
+
+	$user_id = (int) $user_id;
+	$topic_id = (int) $topic_id;
+
+	if ( !$topic_id )
+		bb_delete_usermeta( $user_id, 'is_bozo' );
+	else {
+		$topic = get_topic( $topic_id );
+		$user = bb_get_user( $user_id );
+
+		$bozo_topics_key = $bbdb->prefix . 'bozo_topics';
+
+		if ( --$topic->bozos[$user_id] < 1 )
+			unset($topic->bozos[$user_id]);
+		bb_update_topicmeta( $topic_id, 'bozos', $topic->bozos );
+		
+		if ( --$user->{$bozo_topics_key}[$topic_id] < 1 )
+			unset($user->{$bozo_topics_key}[$topic_id]);
+		bb_update_usermeta( $user_id, $bozo_topics_key, $user->$bozo_topics_key );
+	}
+}
+
+function bb_bozo_profile_admin_keys( $a ) {
+	$a['is_bozo'] = array(
+		0,							// Required
+		__('This user is a bozo'),	// Label
+		'checkbox',					// Type
+		'1',						// Value
+		''							// Default when not set
+	);
+	return $a;
+}
+
+
+
+function bb_bozo_get_bozo_user_ids()
+{
+	global $bbdb;
+	$sql = "SELECT `user_id` FROM `$bbdb->usermeta` WHERE `meta_key` = 'is_bozo' AND `meta_value` = '1';";
+	$user_ids = $bbdb->get_col( $sql, 0 );
+	if ( is_wp_error( $user_ids ) || empty( $user_ids ) ) {
+		return false;
+	}
+	return $user_ids;
+}
+
+function bb_bozo_user_search_description( $description, $h2_search, $h2_role, $user_search_object )
+{
+	if ( is_array( $user_search_object->roles ) && in_array( 'bozo', $user_search_object->roles ) ) {
+		return sprintf( '%1$s%2$s that are bozos', $h2_search, $h2_role );
+	}
+	return $description;
+}
+add_filter( 'bb_user_search_description', 'bb_bozo_user_search_description', 10, 4 );
+
+function bb_bozo_user_search_form_add_inputs( $r, $user_search_object )
+{
+	$checked = '';
+	if ( is_array( $user_search_object->roles ) && in_array( 'bozo', $user_search_object->roles ) ) {
+		$checked = ' checked="checked"';
+	}
+	
+	$r .= "\t" . '<div>' . "\n";
+	$r .= "\t\t" . '<label for="userbozo">' . __('Bozos only') . '</label>' . "\n";
+	$r .= "\t\t" . '<div>' . "\n";
+	$r .= "\t\t\t" . '<input class="checkbox-input" type="checkbox" name="userrole[]" id="userbozo" value="bozo"' . $checked . ' />' . "\n";
+	$r .= "\t\t" . '</div>' . "\n";
+	$r .= "\t" . '</div>' . "\n";
+
+	return $r;
+}
+add_filter( 'bb_user_search_form_inputs', 'bb_bozo_user_search_form_add_inputs', 10, 2 );
+
+function bb_bozo_user_search_role_user_ids( $role_user_ids, $roles, $args )
+{
+	if ( !in_array( 'bozo', $roles ) ) {
+		return $role_user_ids;
+	}
+
+	$bozo_user_ids = bb_bozo_get_bozo_user_ids();
+
+	if ( 1 === count( $roles ) ) {
+		return $bozo_user_ids;
+	}
+
+	global $bbdb;
+	$role_meta_key = $bbdb->escape( $bbdb->prefix . 'capabilities' );
+	$role_sql_terms = array();
+	foreach ( $roles as $role ) {
+		if ( 'bozo' === $role ) {
+			continue;
+		}
+		$role_sql_terms[] = "`meta_value` LIKE '%" . $bbdb->escape( like_escape( $role ) ) . "%'";
+	}
+	$role_sql_terms = join( ' OR ', $role_sql_terms );
+	$role_sql = "SELECT `user_id` FROM `$bbdb->usermeta` WHERE `meta_key` = '$role_meta_key' AND ($role_sql_terms);";
+	$role_user_ids = $bbdb->get_col( $role_sql, 0 );
+	if ( is_wp_error( $role_user_ids ) || empty( $role_user_ids ) ) {
+		return array();
+	}
+
+	return array_intersect( $bozo_user_ids, $role_user_ids );
+}
+add_filter( 'bb_user_search_role_user_ids', 'bb_bozo_user_search_role_user_ids', 10, 3 );
+
+
+
+
+add_filter( 'pre_post_status', 'bb_bozo_pre_post_status', 5, 3 );
+add_action( 'bb_new_post', 'bb_bozo_new_post', 5 );
+add_action( 'bb_delete_post', 'bb_bozo_delete_post', 5, 3 );
+
+add_action( 'pre_permalink', 'bb_bozo_pre_permalink' );
+add_action( 'post_permalink', 'bb_bozo_post_permalink' );
+add_action( 'bb_index.php_pre_db', 'bb_bozo_latest_filter' );
+add_action( 'bb_forum.php_pre_db', 'bb_bozo_latest_filter' );
+add_action( 'bb_topic.php_pre_db', 'bb_bozo_topic_db_filter' );
+add_action( 'bb_profile.php_pre_db', 'bb_bozo_profile_db_filter' );
+
+add_action( 'bb_recount_list', 'bb_bozo_add_recount_list' );
+add_action( 'topic_pages_add', 'bb_bozo_topic_pages_add' );
+
+add_filter( 'post_del_class', 'bb_bozo_post_del_class', 10, 3 );
+add_filter( 'get_topic_posts', 'bb_bozo_get_topic_posts' );
+
+add_filter( 'get_profile_admin_keys', 'bb_bozo_profile_admin_keys' );
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/hello.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/hello.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/hello.php	(revision 5057)
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @package Hello_Louie
+ * @author Matt Mullenweg
+ * @version 1.0
+ */
+/*
+Plugin Name: Hello Louie
+Plugin URI: http://bbpress.org/
+Description: A tip of the hat to the venerable Hello Dolly plugin from WordPress. When activated you will randomly see a lyric from Frank Sinatra's 1964 rendition of "Hello Dolly" in the upper right of your admin screen on every page. This version includes a rewritten second verse as a tribute to Louis Armstrong.
+Author: Matt Mullenweg
+Version: 1.0
+Author URI: http://ma.tt/
+*/
+
+function hello_louie_get_lyric() {
+	/** These are the lyrics to Frank Sinatra's version of "Hello Dolly" from the album "It Might as Well Be Swing" */
+	$lyrics = "Hello Dolly
+Well hello Dolly
+It's so nice to have you back where you belong
+You're lookin' swell, Dolly
+We can tell, Dolly
+You're still glowin', you're still crowin'
+You're still goin' strong
+We feel the room swayin'
+'Cause the band's playin'
+One of your old favorite songs from way back when
+Take her wrap, fellas
+Find her an empty lap, fellas
+Dolly'll never go away
+Dolly'll never go away
+Hello Satch
+This is Francis, Louie
+It's so nice to see you back where you belong
+You're back on top, Louie
+Never stop, Louie
+You're still singin', you're still swingin'
+You're still goin' strong
+You get the room swayin'
+When you start in playin'
+One of your great songs, or songs from way back when
+Blow your horn, Louie
+Sing up a great big song, Louie
+Promise you won't go away
+Promise you won't go away
+Promise you won't go away again, ooh yeah";
+
+	// Here we split it into lines
+	$lyrics = explode("\n", $lyrics);
+
+	// And then randomly choose a line
+	return wptexturize( $lyrics[ mt_rand(0, count($lyrics) - 1) ] );
+}
+
+// This just echoes the chosen line, we'll position it later
+function hello_louie() {
+	$chosen = hello_louie_get_lyric();
+	echo "<p id='dolly'>$chosen</p>";
+}
+
+// Now we set that function up to execute when the admin_footer action is called
+add_action('bb_admin_footer', 'hello_louie');
+
+// We need some CSS to position the paragraph
+function louie_css() {
+	// This makes sure that the positioning is also good for right-to-left languages
+	global $bb_locale;
+	$x = ( isset( $bb_locale->text_direction ) && 'rtl' == $bb_locale->text_direction ) ? 'left' : 'right';
+
+	echo "
+	<style type='text/css'>
+	#dolly {
+		position: absolute;
+		top: 4.4em;
+		margin: 0;
+		padding: 0;
+		$x: 15px;
+		font-size: 11px;
+	}
+	</style>
+	";
+}
+
+add_action('bb_admin_head', 'louie_css');
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/readme.txt
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/readme.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-plugins/readme.txt	(revision 5057)
@@ -0,0 +1,3 @@
+This directory is reserved for plugins distributed with the bbPress core package.
+
+If you wish to install more plugins, you should create a new directory called "my-plugins" in the base directory of your bbPress installation and install them there, not here.
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-post.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-post.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-post.php	(revision 5057)
@@ -0,0 +1,91 @@
+<?php
+require( './bb-load.php' );
+
+if ( bb_is_login_required() )
+	bb_auth( 'logged_in' );
+
+bb_check_post_flood();
+
+if ( !$post_content = trim( $_POST['post_content'] ) )
+	bb_die( __( 'You need to actually submit some content!' ) );
+
+$post_author = $post_email = $post_url = '';
+
+if ( !bb_is_user_logged_in() ) {
+	if ( bb_is_login_required() ) {
+		bb_die( __( 'You are not allowed to post.  Are you logged in?' ) );
+	} else {
+		if ( !$post_author = sanitize_user( trim( $_POST['author'] ) ) )
+			bb_die( __( 'You need to submit your name!' ) );
+		elseif ( !$post_email = sanitize_email( trim( $_POST['email'] ) ) )
+			bb_die( __( 'You need to submit a valid email address!' ) );
+
+		if ( !empty( $_POST['url'] ) )
+			$post_url = esc_url( trim( $_POST['url'] ) );
+	}
+}
+
+
+
+if ( isset($_POST['topic']) && $forum_id = (int) $_POST['forum_id'] ) {
+	if ( bb_is_login_required() && ! bb_current_user_can('write_posts') )
+		bb_die(__('You are not allowed to post.  Are you logged in?'));
+
+	if ( bb_is_login_required() && ! bb_current_user_can( 'write_topic', $forum_id ) )
+		bb_die(__('You are not allowed to write new topics.'));
+
+	bb_check_admin_referer( 'create-topic' );
+
+	$topic = trim( $_POST['topic'] );
+	$tags  = trim( $_POST['tags']  );
+
+	if ('' == $topic)
+		bb_die(__('Please enter a topic title'));
+
+	$args = array();
+
+	if ( isset( $post_author ) )
+		$args['topic_poster_name'] = $args['topic_last_poster_name'] = $post_author;
+
+	$topic_id = bb_new_topic( $topic, $forum_id, $tags, $args );
+
+} elseif ( isset($_POST['topic_id'] ) ) {
+	$topic_id = (int) $_POST['topic_id'];
+	bb_check_admin_referer( 'create-post_' . $topic_id );
+}
+
+if ( bb_is_login_required() && ! bb_current_user_can( 'write_post', $topic_id ) )
+	bb_die(__('You are not allowed to post.  Are you logged in?'));
+
+if ( !topic_is_open( $topic_id ) )
+	bb_die(__('This topic has been closed'));
+
+$post_data = array(
+	'post_text' => stripslashes($_POST['post_content']),
+	'topic_id' => $topic_id,
+);
+
+foreach( array('post_author', 'post_email', 'post_url') as $field ) {
+	if ( ! empty( $$field ) ) {
+		$post_data[$field] = $$field;
+	}
+}
+
+$post_id = bb_insert_post($post_data);
+
+$tags  = trim( $_POST['tags']  );
+bb_add_topic_tags( $topic_id, $tags );
+
+$topic = get_topic( $topic_id, false );
+$link = get_post_link($post_id);
+
+if ( $topic->topic_posts )
+	$link = add_query_arg( 'replies', $topic->topic_posts, $link );
+
+// This action used to be bb_post.php, changed to avoid conflict in bb_load_template()
+do_action( 'bb-post.php', $post_id );
+if ($post_id)
+	wp_redirect( $link );
+else
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+exit;
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-reset-password.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-reset-password.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-reset-password.php	(revision 5057)
@@ -0,0 +1,24 @@
+<?php
+require('./bb-load.php');
+
+$error = false;
+
+if ( $_POST ) {
+	$action = 'send_key';
+	$user_login = sanitize_user( $_POST['user_login'], true );
+	if ( empty( $user_login ) ) {
+		$error = __('No username specified');
+	} else {
+		$send_key_result = bb_reset_email( $user_login );
+		if ( is_wp_error( $send_key_result ) )
+			$error = $send_key_result->get_error_message();
+	}
+} elseif ( isset( $_GET['key'] ) ) {
+	$action = 'reset_password';
+	$reset_pasword_result = bb_reset_password( $_GET['key'] );
+	if ( is_wp_error( $reset_pasword_result ) )
+		$error = $reset_pasword_result->get_error_message();
+}
+
+bb_load_template( 'password-reset.php', array('action', 'error') );
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-settings.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-settings.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-settings.php	(revision 5057)
@@ -0,0 +1,1242 @@
+<?php
+/**
+ * Used to setup and fix common variables and include
+ * the bbPress and BackPress procedural and class libraries.
+ *
+ * You should not have to change this file, all configuration
+ * should be possible in bb-config.php
+ *
+ * @package bbPress
+ */
+
+
+
+// Die if called directly
+if ( !defined( 'BB_PATH' ) ) {
+	die( 'This file cannot be called directly.' );
+}
+
+// Set default timezone in PHP 5.
+if ( function_exists( 'date_default_timezone_set' ) )
+        date_default_timezone_set( 'UTC' );
+
+/**
+ * bb_unregister_GLOBALS() - Turn register globals off
+ *
+ * @access private
+ * @return null Will return null if register_globals PHP directive was disabled
+ */
+function bb_unregister_GLOBALS()
+{
+	if ( !ini_get( 'register_globals' ) ) {
+		return;
+	}
+
+	if ( isset($_REQUEST['GLOBALS']) ) {
+		die( 'GLOBALS overwrite attempt detected' );
+	}
+
+	// Variables that shouldn't be unset
+	$noUnset = array( 'GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES', 'bb_table_prefix', 'bb' );
+
+	$input = array_merge( $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset( $_SESSION ) && is_array( $_SESSION ) ? $_SESSION : array() );
+	foreach ( $input as $k => $v ) {
+		if ( !in_array( $k, $noUnset ) && isset( $GLOBALS[$k] ) ) {
+			$GLOBALS[$k] = NULL;
+			unset( $GLOBALS[$k] );
+		}
+	}
+}
+bb_unregister_GLOBALS();
+
+
+
+/**
+ * Let bbPress know what we are up to at the moment
+ */
+
+/**
+ * Whether the current script is in the admin area or not
+ */
+if ( !defined( 'BB_IS_ADMIN' ) ) {
+	define( 'BB_IS_ADMIN', false );
+} elseif ( BB_IS_ADMIN ) {
+	$bb_hardcoded = (array) $bb;
+}
+
+/**
+ * Whether the current script is part of the installation process or not
+ * @since 1.0
+ */
+if ( !defined( 'BB_INSTALLING' ) ) {
+	define( 'BB_INSTALLING', false );
+}
+
+/**
+ * Whether to load deprecated routines, constants and functions
+ * @since 1.0
+ */
+if ( !defined( 'BB_LOAD_DEPRECATED' ) ) {
+	define( 'BB_LOAD_DEPRECATED', true );
+}
+
+
+
+/**
+ * Remove filters and action that have been set by an included WordPress install
+ */
+if ( defined( 'ABSPATH' ) ) {
+	$wp_filter = array();
+	$wp_actions = array();
+	$merged_filters = array();
+	$wp_current_filter = array();
+}
+
+
+
+/**
+ * Define include paths and load core BackPress libraries
+ */
+
+/**
+ * The full path to the BackPress libraries
+ */
+if ( !defined( 'BACKPRESS_PATH' ) ) {
+	define( 'BACKPRESS_PATH', BB_PATH . BB_INC . 'backpress/' );
+}
+
+// Load logging class
+require_once( BACKPRESS_PATH . 'class.bp-log.php' );
+$bb_log = new BP_Log();
+if ( defined( 'BB_LOG_LEVEL' ) ) {
+	$bb_log->set_level( BB_LOG_LEVEL );
+}
+if ( defined( 'BB_LOG_TYPE' ) ) {
+	$bb_log->set_type( BB_LOG_TYPE );
+}
+if ( defined( 'BB_LOG_FILENAME' ) ) {
+	$bb_log->set_filename( BB_LOG_FILENAME );
+}
+$bb_log->notice('Logging started');
+
+// Load core BackPress functions
+require_once( BACKPRESS_PATH . 'functions.core.php' );
+require_once( BACKPRESS_PATH . 'functions.compat.php' );
+require_once( BACKPRESS_PATH . 'functions.formatting.php' );
+
+// WP_Error
+if ( !class_exists( 'WP_Error' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-error.php' );
+}
+
+
+
+/**
+ * Set up database parameters based on config and initialise
+ */
+
+/**
+ * Define the full path to the database class
+ */
+if ( !defined( 'BB_DATABASE_CLASS_INCLUDE' ) ) {
+	define( 'BB_DATABASE_CLASS_INCLUDE', BACKPRESS_PATH . 'class.bpdb-multi.php' );
+}
+
+/**
+ * Define the name of the database class
+ */
+if ( !defined( 'BB_DATABASE_CLASS' ) ) {
+	define( 'BB_DATABASE_CLASS', 'BPDB_Multi' );
+}
+
+if ( in_array( BB_DATABASE_CLASS, array( 'BPDB', 'BPDB_Multi' ) ) ) {
+	/**
+	 * Define BackPress Database errors if not already done - no localisation at this stage
+	 */
+	if ( !defined( 'BPDB__CONNECT_ERROR_MESSAGE' ) ) {
+		define( 'BPDB__CONNECT_ERROR_MESSAGE', 'ERROR: Could not establish a database connection' );
+	}
+	if ( !defined( 'BPDB__CONNECT_ERROR_MESSAGE' ) ) {
+		define( 'BPDB__SELECT_ERROR_MESSAGE', 'ERROR: Can\'t select database.' );
+	}
+	if ( !defined( 'BPDB__ERROR_STRING' ) ) {
+		define( 'BPDB__ERROR_STRING', 'ERROR: bbPress database error - "%s" for query "%s" via caller "%s"' );
+	}
+	if ( !defined( 'BPDB__ERROR_HTML' ) ) {
+		define( 'BPDB__ERROR_HTML', '<div id="error"><p class="bpdberror"><strong>Database error:</strong> [%s]<br /><code>%s</code><br />Caller: %s</p></div>' );
+	}
+	if ( !defined( 'BPDB__DB_VERSION_ERROR' ) ) {
+		define( 'BPDB__DB_VERSION_ERROR', 'ERROR: bbPress requires MySQL 4.0.0 or higher' );
+	}
+	if ( !defined( 'BPDB__PHP_EXTENSION_MISSING' ) ) {
+		define( 'BPDB__PHP_EXTENSION_MISSING', 'ERROR: bbPress requires The MySQL PHP extension' );
+	}
+}
+
+// Load the database class
+if ( BB_DATABASE_CLASS_INCLUDE ) {
+	require_once( BB_DATABASE_CLASS_INCLUDE );
+}
+
+// Die if there is no database table prefix
+if ( !$bb_table_prefix ) {
+	die( 'You must specify a table prefix in your <code>bb-config.php</code> file.' );
+}
+
+// Setup the global database connection
+$bbdb_class = BB_DATABASE_CLASS;
+$bbdb =& new $bbdb_class( array(
+	'name' => BBDB_NAME,
+	'user' => BBDB_USER,
+	'password' => BBDB_PASSWORD,
+	'host' => BBDB_HOST,
+	'charset' => defined( 'BBDB_CHARSET' ) ? BBDB_CHARSET : false,
+	'collate' => defined( 'BBDB_COLLATE' ) ? BBDB_COLLATE : false
+) );
+unset( $bbdb_class );
+
+/**
+ * bbPress tables
+ */
+$bbdb->tables = array(
+	'forums'             => false,
+	'meta'               => false,
+	'posts'              => false,
+	'tagged'             => false, // Deprecated
+	'tags'               => false, // Deprecated
+	'terms'              => false,
+	'term_relationships' => false,
+	'term_taxonomy'      => false,
+	'topics'             => false,
+	'topicmeta'          => false, // Deprecated
+	'users'              => false,
+	'usermeta'           => false
+);
+
+// Set the prefix on the tables
+if ( is_wp_error( $bbdb->set_prefix( $bb_table_prefix ) ) ) {
+	die( 'Your table prefix may only contain letters, numbers and underscores.' );
+}
+
+// Set a site id if there isn't one already
+if ( !isset( $bb->site_id ) ) {
+	$bb->site_id = 1;
+}
+
+
+
+/**
+ * Load core bbPress libraries
+ */
+
+require_once( BB_PATH . BB_INC . 'functions.bb-core.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-forums.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-topics.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-posts.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-topic-tags.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-users.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-meta.php' );
+require_once( BB_PATH . BB_INC . 'class.bb-query.php' );
+require_once( BB_PATH . BB_INC . 'class.bb-walker.php' );
+
+
+
+/**
+ * Load API and object handling BackPress libraries
+ */
+
+// Plugin API
+if ( !function_exists( 'add_filter' ) ) {
+	require_once( BACKPRESS_PATH . 'functions.plugin-api.php' );
+}
+
+// Shortcodes API
+if ( !function_exists( 'add_shortcode' ) ) {
+	require_once( BACKPRESS_PATH . 'functions.shortcodes.php' );
+} else {
+	remove_all_shortcodes();
+}
+
+
+
+/**
+ * Define the full path to the object cache functions include
+ */
+$_internal_object_cache_functions_include = BACKPRESS_PATH . 'loader.wp-object-cache.php';
+$_memcached_object_cache_functions_include = BACKPRESS_PATH . 'loader.wp-object-cache-memcached.php';
+if ( !defined( 'BB_OBJECT_CACHE_FUNCTIONS_INCLUDE' ) ) {
+	if ( defined( 'BB_OBJECT_CACHE_TYPE' ) && 'memcached' === BB_OBJECT_CACHE_TYPE ) {
+		define( 'BB_OBJECT_CACHE_FUNCTIONS_INCLUDE', $_memcached_object_cache_functions_include );
+	} else {
+		define( 'BB_OBJECT_CACHE_FUNCTIONS_INCLUDE', $_internal_object_cache_functions_include );
+	}
+}
+
+// See if a caching class is already loaded (by WordPress)
+if ( function_exists( 'wp_cache_init' ) ) {
+	if ( isset( $_wp_using_ext_object_cache ) ) {
+		$_bb_using_ext_object_cache = $_wp_using_ext_object_cache;
+	} else {
+		$_bb_using_ext_object_cache = false;
+	}
+} elseif ( BB_OBJECT_CACHE_FUNCTIONS_INCLUDE ) {
+	// Load the object cache class
+	require_once( BB_OBJECT_CACHE_FUNCTIONS_INCLUDE );
+	if ( BB_OBJECT_CACHE_FUNCTIONS_INCLUDE === $_internal_object_cache_functions_include ) {
+		$_bb_using_ext_object_cache = false;
+	} else {
+		$_bb_using_ext_object_cache = true;
+	}
+}
+unset( $_internal_object_cache_functions_include );
+
+// Instantiate the $wp_object_cache object using wp_cache_init()
+if ( function_exists( 'wp_cache_init' ) ) {
+	// Clear WordPress cache if it exists already - maybe should save and re-load?
+	unset( $wp_object_cache );
+	wp_cache_init();
+	if ( function_exists( 'wp_cache_add_global_groups' ) ) {
+		wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'useremail', 'usernicename' ) );
+	}
+}
+
+
+
+/**
+ * Load mapping class for BackPress to store options
+ */
+require_once( BB_PATH . BB_INC . 'class.bp-options.php' );
+require_once( BACKPRESS_PATH . 'functions.bp-options.php' );
+
+
+
+/**
+ * Load WP_Http class
+ */
+if ( !class_exists( 'WP_Http' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-http.php' );
+}
+
+
+
+/**
+ * Determine language settings and load i10n libraries as required
+ */
+
+/**
+ * The full path to the directory containing language files
+ */
+if ( !defined( 'BB_LANG_DIR' ) ) {
+	if ( BB_LOAD_DEPRECATED && defined( 'BBLANGDIR' ) ) {
+		// User has set old constant
+		bb_log_deprecated( 'constant', 'BBLANGDIR', 'BB_LANG_DIR' );
+		define( 'BB_LANG_DIR', BBLANGDIR );
+	} else {
+		define( 'BB_LANG_DIR', BB_PATH . 'my-languages/' ); // absolute path with trailing slash
+	}
+}
+
+/**
+ * The language in which to display bbPress
+ */
+if ( BB_LOAD_DEPRECATED && !defined( 'BB_LANG' ) && defined( 'BBLANG' ) && '' != BBLANG ) {
+	// User has set old constant
+	bb_log_deprecated( 'constant', 'BBLANG', 'BB_LANG' );
+	define( 'BB_LANG', BBLANG );
+}
+if ( !class_exists( 'MO' ) ) {
+	require_once( BACKPRESS_PATH . 'pomo/mo.php' );
+}
+
+// Is WordPress loaded
+if ( !defined( 'BB_IS_WP_LOADED') ) {
+	define( 'BB_IS_WP_LOADED', defined( 'DB_NAME' ) );
+}
+
+// Only load this if WordPress isn't loaded
+if ( !BB_IS_WP_LOADED ) {
+	require_once( BACKPRESS_PATH . 'functions.kses.php' );
+}
+require_once( BB_PATH . BB_INC . 'functions.bb-l10n.php' );
+
+
+
+/**
+ * Routines related to installation
+ */
+
+// Load BB_CHANNELS_INCLUDE if it exists, must be done before the install is completed
+if ( defined( 'BB_CHANNELS_INCLUDE' ) && file_exists( BB_CHANNELS_INCLUDE ) && !is_dir( BB_CHANNELS_INCLUDE ) ) {
+	require_once( BB_CHANNELS_INCLUDE );
+}
+
+// If there is no forum table in the database then redirect to the installer
+if ( !BB_INSTALLING && !bb_is_installed() ) {
+	$link = preg_replace( '|(/bb-admin)?/[^/]+?$|', '/', $_SERVER['PHP_SELF'] ) . 'bb-admin/install.php';
+	require_once( BB_PATH . BB_INC . 'functions.bb-pluggable.php' );
+	wp_redirect( $link );
+	die();
+}
+
+// Setup some variables in the $bb class if they don't exist - some of these are deprecated
+if ( BB_LOAD_DEPRECATED ) {
+	foreach ( array( 'use_cache' => false, 'debug' => false ) as $o => $oo ) {
+		if ( !isset( $bb->$o ) ) {
+			$bb->$o = $oo;
+		} else {
+			bb_log_deprecated( 'variable', '$bb->' . $o );
+		}
+	}
+	unset( $o, $oo );
+}
+
+// Disable plugins during installation
+if ( BB_INSTALLING ) {
+	foreach ( array('active_plugins') as $i ) {
+		$bb->$i = false;
+	}
+	unset( $i );
+}
+
+
+
+/**
+ * Load additional bbPress libraries
+ */
+
+require_once( BB_PATH . BB_INC . 'functions.bb-formatting.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-template.php' );
+require_once( BB_PATH . BB_INC . 'functions.bb-capabilities.php' );
+require_once( BB_PATH . BB_INC . 'class.bb-pingbacks.php' );
+
+
+
+// Cache options from the database
+if ( !isset( $bb->load_options ) ) {
+	$bb->load_options = true;
+}
+if ( $bb->load_options ) {
+	$bbdb->suppress_errors();
+	bb_cache_all_options();
+	$bbdb->suppress_errors( false );
+}
+
+/**
+ * Set the URI and derivitaves
+ */
+if ( $bb->uri = bb_get_option( 'uri' ) ) {
+	$bb->uri = rtrim( trim( $bb->uri ), " \t\n\r\0\x0B/" ) . '/';
+	
+	if ( preg_match( '@^(https?://[^/]+)((?:/.*)*/{1,1})$@i', $bb->uri, $matches ) ) {
+		// Used when setting up cookie domain
+		$bb->domain = $matches[1];
+		// Used when setting up cookie paths
+		$bb->path = $matches[2];
+	}
+	unset( $matches );
+} elseif ( BB_LOAD_DEPRECATED ) {
+	// Backwards compatibility
+	// These were never set in the database
+	if ( isset( $bb->domain ) ) {
+		bb_log_deprecated( 'variable', '$bb->domain', '$bb->uri' );
+		$bb->domain = rtrim( trim( $bb->domain ), " \t\n\r\0\x0B/" );
+	}
+	if ( isset( $bb->path ) ) {
+		bb_log_deprecated( 'variable', '$bb->path', '$bb->uri' );
+		$bb->path = trim( $bb->path );
+		if ( $bb->path != '/' ) $bb->path = '/' . trim( $bb->path, " \t\n\r\0\x0B/" ) . '/';
+	}
+	// We need both to build a uri
+	if ( $bb->domain && $bb->path ) {
+		$bb->uri = $bb->domain . $bb->path;
+	}
+}
+
+// Die if no URI
+if ( !BB_INSTALLING && !$bb->uri ) {
+	bb_die( __( 'Could not determine site URI' ) );
+}
+
+/**
+ * BB_FORCE_SSL_USER_FORMS - Whether to force use of ssl on user forms like login, registration and profile editing
+ */
+if ( !defined( 'BB_FORCE_SSL_USER_FORMS' ) ) {
+	define( 'BB_FORCE_SSL_USER_FORMS', false );
+}
+force_ssl_login( BB_FORCE_SSL_USER_FORMS );
+
+/**
+ * BB_FORCE_SSL_ADMIN - Whether to force use of ssl in the admin area
+ */
+if ( !defined( 'BB_FORCE_SSL_ADMIN' ) ) {
+	define( 'BB_FORCE_SSL_ADMIN', false );
+}
+force_ssl_admin( BB_FORCE_SSL_ADMIN );
+
+// Load default filters
+require_once( BB_PATH . BB_INC . 'defaults.bb-filters.php' );
+
+// Load default scripts
+require_once( BB_PATH . BB_INC . 'functions.bb-script-loader.php' );
+
+/* Check if the globals have been sanitized by WordPress or not (else there would be extra slashes while deep integration) */
+if ( !function_exists( 'wp_magic_quotes' ) ) {
+	// Sanitise external input
+	$_GET    = bb_global_sanitize( $_GET );
+	$_POST   = bb_global_sanitize( $_POST );
+	$_COOKIE = bb_global_sanitize( $_COOKIE, false );
+	$_SERVER = bb_global_sanitize( $_SERVER );
+}
+
+
+/**
+ * Define theme and plugin constants
+ */
+
+/**
+ * Full path to the location of the core plugins directory
+ */
+define( 'BB_CORE_PLUGIN_DIR', BB_PATH . 'bb-plugins/' );
+
+/**
+ * Full URL of the core plugins directory
+ */
+define( 'BB_CORE_PLUGIN_URL', $bb->uri . 'bb-plugins/' );
+
+/**
+ * Full path to the location of the core themes directory
+ */
+define( 'BB_CORE_THEME_DIR', BB_PATH . 'bb-templates/' );
+
+/**
+ * Full URL of the core themes directory
+ */
+define( 'BB_CORE_THEME_URL', $bb->uri . 'bb-templates/' );
+
+/**
+ * The default theme
+ */
+define( 'BB_DEFAULT_THEME', 'core#kakumei' );
+
+/**
+ * Full path to the location of the default theme directory
+ */
+define( 'BB_DEFAULT_THEME_DIR', BB_CORE_THEME_DIR . 'kakumei/' );
+
+/**
+ * Full URL of the default theme directory
+ */
+define( 'BB_DEFAULT_THEME_URL', BB_CORE_THEME_URL . 'kakumei/' );
+
+/**
+ * Full path to the location of the user plugins directory
+ */
+if ( !defined( 'BB_PLUGIN_DIR' ) ) {
+	if ( BB_LOAD_DEPRECATED && defined( 'BBPLUGINDIR' ) ) {
+		// User has set old constant
+		bb_log_deprecated( 'constant', 'BBPLUGINDIR', 'BB_PLUGIN_DIR' );
+		define( 'BB_PLUGIN_DIR', BBPLUGINDIR );
+	} else {
+		define( 'BB_PLUGIN_DIR', BB_PATH . 'my-plugins/' );
+	}
+}
+
+/**
+ * Full URL of the user plugins directory
+ */
+if ( !defined( 'BB_PLUGIN_URL' ) ) {
+	if ( BB_LOAD_DEPRECATED && defined( 'BBPLUGINURL' ) ) {
+		// User has set old constant
+		bb_log_deprecated( 'constant', 'BBPLUGINURL', 'BB_PLUGIN_URL' );
+		define( 'BB_PLUGIN_URL', BBPLUGINURL );
+	} else {
+		define( 'BB_PLUGIN_URL', $bb->uri . 'my-plugins/' );
+	}
+}
+
+/**
+ * Full path to the location of the user themes directory
+ */
+if ( !defined( 'BB_THEME_DIR' ) ) {
+	if ( BB_LOAD_DEPRECATED && defined( 'BBTHEMEDIR' ) ) {
+		// User has set old constant
+		bb_log_deprecated( 'constant', 'BBTHEMEDIR', 'BB_THEME_DIR' );
+		define( 'BB_THEME_DIR', BBTHEMEDIR );
+	} else {
+		define( 'BB_THEME_DIR', BB_PATH . 'my-templates/' );
+	}
+}
+
+/**
+ * Full URL of the user themes directory
+ */
+if ( !defined( 'BB_THEME_URL' ) ) {
+	if ( BB_LOAD_DEPRECATED && defined( 'BBTHEMEURL' ) ) {
+		// User has set old constant
+		bb_log_deprecated( 'constant', 'BBTHEMEURL', 'BB_THEME_URL' );
+		define( 'BB_THEME_URL', BBTHEMEURL );
+	} else {
+		define( 'BB_THEME_URL', $bb->uri . 'my-templates/' );
+	}
+}
+
+/**
+ * Look-up arrays provide easier access to arbitrary plugin and theme locations
+ */
+$_default_plugin_locations = array(
+	'core' => array(
+		'dir' => BB_CORE_PLUGIN_DIR,
+		'url' => BB_CORE_PLUGIN_URL,
+		'cap' => 'manage_plugins'
+	),
+	'user' => array(
+		'dir' => BB_PLUGIN_DIR,
+		'url' => BB_PLUGIN_URL,
+		'cap' => 'manage_plugins'
+	)
+);
+
+if ( isset( $bb->plugin_locations ) && is_array( $bb->plugin_locations ) ) {
+	$bb->plugin_locations = array_merge( $_default_plugin_locations, $bb->plugin_locations );
+} else {
+	$bb->plugin_locations = $_default_plugin_locations;
+}
+
+// Don't accept a plugin location called "all". Unlikely, but really not desirable.
+if ( isset( $bb->plugin_locations['all'] ) ) {
+	unset( $bb->plugin_locations['all'] );
+}
+
+$_default_theme_locations = array(
+	'core' => array(
+		'dir' => BB_CORE_THEME_DIR,
+		'url' => BB_CORE_THEME_URL,
+		'cap' => 'manage_themes'
+	),
+	'user' => array(
+		'dir' => BB_THEME_DIR,
+		'url' => BB_THEME_URL,
+		'cap' => 'manage_themes'
+	)
+);
+
+if ( isset( $bb->theme_locations ) && is_array( $bb->theme_locations ) ) {
+	$bb->theme_locations = array_merge( $_default_theme_locations, $bb->theme_locations );
+} else {
+	$bb->theme_locations = $_default_theme_locations;
+}
+
+
+
+/**
+ * Add custom tables if present
+ */
+
+// Resolve the various ways custom user tables might be setup
+bb_set_custom_user_tables();
+
+// Add custom databases if required
+if ( isset( $bb->custom_databases ) ) {
+	foreach ( $bb->custom_databases as $connection => $database ) {
+		$bbdb->add_db_server( $connection, $database );
+	}
+}
+unset( $connection, $database );
+
+// Add custom tables if required
+if ( isset( $bb->custom_tables ) ) {
+	$bbdb->tables = array_merge( $bbdb->tables, $bb->custom_tables );
+	if ( is_wp_error( $bbdb->set_prefix( $bbdb->prefix ) ) ) {
+		die( __( 'Your user table prefix may only contain letters, numbers and underscores.' ) );
+	}
+}
+
+
+
+/**
+ * Sort out cookies so they work with WordPress (if required)
+ * Note that database integration is no longer a pre-requisite for cookie integration
+ */
+
+$bb->wp_siteurl = bb_get_option( 'wp_siteurl' );
+if ( $bb->wp_siteurl ) {
+	$bb->wp_siteurl = rtrim( trim( $bb->wp_siteurl ), " \t\n\r\0\x0B/" );
+}
+
+$bb->wp_home = bb_get_option( 'wp_home' );
+if ( $bb->wp_home ) {
+	$bb->wp_home = rtrim( trim( $bb->wp_home ), " \t\n\r\0\x0B/" );
+}
+
+$bb->wp_cookies_integrated = false;
+$bb->cookiedomain = bb_get_option( 'cookiedomain' );
+$bb->cookiepath = bb_get_option( 'cookiepath' );
+$bb->wordpress_mu_primary_blog_id = bb_get_option( 'wordpress_mu_primary_blog_id' );
+
+if ( $bb->wp_siteurl && $bb->wp_home ) {
+	if ( $bb->cookiedomain ) {
+		$bb->wp_cookies_integrated = true;
+	} else {
+		$cookiedomain = bb_get_common_domains( $bb->uri, $bb->wp_home );
+		if ( bb_match_domains( $bb->uri, $bb->wp_home ) ) {
+			if ( $bb->wordpress_mu_primary_blog_id ) {
+				$bb->cookiedomain = '.' . $cookiedomain;
+			}
+			if ( !$bb->cookiepath ) {
+				$bb->cookiepath = bb_get_common_paths( $bb->uri, $bb->wp_home );
+			}
+			$bb->wp_cookies_integrated = true;
+		} elseif ( $cookiedomain && strpos( $cookiedomain, '.' ) !== false ) {
+			$bb->cookiedomain = '.' . $cookiedomain;
+			if ( !$bb->cookiepath ) {
+				$bb->cookiepath = bb_get_common_paths( $bb->uri, $bb->wp_home );
+			}
+			$bb->wp_cookies_integrated = true;
+		}
+		unset( $cookiedomain );
+	}
+}
+
+define( 'BB_HASH', $bb->wp_cookies_integrated ? md5( $bb->wp_siteurl ) : md5( $bb->uri ) );
+
+if ( BB_LOAD_DEPRECATED ) {
+	// Deprecated setting
+	$bb->usercookie = bb_get_option( 'usercookie' );
+	if ( !$bb->usercookie ) {
+		$bb->usercookie = ( $bb->wp_cookies_integrated ? 'wordpressuser_' : 'bb_user_' ) . BB_HASH;
+	} else {
+		bb_log_deprecated( 'variable', '$bb->usercookie' );
+	}
+
+	// Deprecated setting
+	$bb->passcookie = bb_get_option( 'passcookie' );
+	if ( !$bb->passcookie ) {
+		$bb->passcookie = ( $bb->wp_cookies_integrated ? 'wordpresspass_' : 'bb_pass_' ) . BB_HASH;
+	} else {
+		bb_log_deprecated( 'variable', '$bb->passcookie' );
+	}
+}
+
+$bb->authcookie = bb_get_option( 'authcookie' );
+if ( !$bb->authcookie ) {
+	$bb->authcookie = ( $bb->wp_cookies_integrated ? 'wordpress_' : 'bbpress_' ) . BB_HASH;
+}
+
+$bb->secure_auth_cookie = bb_get_option( 'secure_auth_cookie' );
+if ( !$bb->secure_auth_cookie ) {
+	$bb->secure_auth_cookie = ( $bb->wp_cookies_integrated ? 'wordpress_sec_' : 'bbpress_sec_' ) . BB_HASH;
+}
+
+$bb->logged_in_cookie = bb_get_option( 'logged_in_cookie' );
+if ( !$bb->logged_in_cookie ) {
+	$bb->logged_in_cookie = ( $bb->wp_cookies_integrated ? 'wordpress_logged_in_' : 'bbpress_logged_in_' ) . BB_HASH;
+}
+
+// Cookie path was set before integration logic above
+if ( !$bb->cookiepath ) {
+	$bb->cookiepath = $bb->wp_cookies_integrated ? preg_replace( '|https?://[^/]+|i', '', $bb->wp_home ) : $bb->path;
+}
+$bb->cookiepath = rtrim( trim( $bb->cookiepath ), " \t\n\r\0\x0B/" ) . '/';
+
+$bb->admin_cookie_path = bb_get_option( 'admin_cookie_path' );
+if ( !$bb->admin_cookie_path ) {
+	$bb->admin_cookie_path = $bb->path . 'bb-admin';
+}
+if ( '/' !== $bb->admin_cookie_path = trim( $bb->admin_cookie_path ) ) {
+	$bb->admin_cookie_path = rtrim( $bb->admin_cookie_path, " \t\n\r\0\x0B/" );
+}
+
+if ( BB_LOAD_DEPRECATED ) {
+	$_plugin_cookie_paths = bb_get_option( 'plugin_cookie_paths' );
+
+	// Deprecated settings
+	if ( $_plugin_cookie_paths ) {
+		if ( isset( $_plugin_cookie_paths['core'] ) && $_plugin_cookie_paths['core'] ) {
+			$bb->core_plugins_cookie_path = $_plugin_cookie_paths['core'];
+		}
+		if ( isset( $_plugin_cookie_paths['user'] ) && $_plugin_cookie_paths['user'] ) {
+			$bb->user_plugins_cookie_path = $_plugin_cookie_paths['user'];
+		}
+	} else {
+		if ( $bb->core_plugins_cookie_path = bb_get_option( 'core_plugins_cookie_path' ) ) {
+			bb_log_deprecated( 'variable', '$bb->core_plugins_cookie_path', '$bb->plugin_cookie_paths[\'core\']' );
+		}
+		if ( $bb->user_plugins_cookie_path = bb_get_option( 'user_plugins_cookie_path' ) ) {
+			bb_log_deprecated( 'variable', '$bb->core_plugins_cookie_path', '$bb->plugin_cookie_paths[\'user\']' );
+		}
+	}
+
+	if ( !$bb->core_plugins_cookie_path && isset( $bb->plugin_locations['core']['url'] ) && $bb->plugin_locations['core']['url'] ) {
+		$bb->core_plugins_cookie_path = preg_replace( '|https?://[^/]+|i', '', $bb->plugin_locations['core']['url'] );
+	}
+	$bb->core_plugins_cookie_path = rtrim( trim( $bb->core_plugins_cookie_path ), " \t\n\r\0\x0B/" );
+
+	if ( !$bb->user_plugins_cookie_path && isset( $bb->plugin_locations['user']['url'] ) && $bb->plugin_locations['user']['url'] ) {
+		$bb->user_plugins_cookie_path = preg_replace( '|https?://[^/]+|i', '', $bb->plugin_locations['user']['url'] );
+	}
+	$bb->user_plugins_cookie_path = rtrim( trim( $bb->user_plugins_cookie_path ), " \t\n\r\0\x0B/" );
+
+	if ( !$_plugin_cookie_paths ) {
+		$bb->plugin_cookie_paths = array();
+	}
+	if ( !isset( $_plugin_cookie_paths['core'] ) ) {
+		$bb->plugin_cookie_paths['core'] = $bb->core_plugins_cookie_path;
+	}
+	if ( !isset( $_plugin_cookie_paths['user'] ) ) {
+		$bb->plugin_cookie_paths['user'] = $bb->user_plugins_cookie_path;
+	}
+}
+
+$bb->sitecookiepath = bb_get_option( 'sitecookiepath' );
+$_bb_sitecookiepath = $bb->sitecookiepath;
+if ( !$bb->sitecookiepath && $bb->wp_cookies_integrated ) {
+	$bb->sitecookiepath = preg_replace( '|https?://[^/]+|i', '', $bb->wp_siteurl );
+	$_bb_sitecookiepath = $bb->sitecookiepath;
+}
+$bb->sitecookiepath = rtrim( trim( $bb->sitecookiepath ), " \t\n\r\0\x0B/" ) . '/';
+
+$bb->wp_admin_cookie_path = bb_get_option( 'wp_admin_cookie_path' );
+if ( !$bb->wp_admin_cookie_path && $bb->wp_cookies_integrated ) {
+	if ( $bb->wordpress_mu_primary_blog_id ) {
+		$bb->wp_admin_cookie_path = $_bb_sitecookiepath;
+	} else {
+		$bb->wp_admin_cookie_path = $_bb_sitecookiepath . '/wp-admin';
+	}
+}
+if ( '/' !== $bb->wp_admin_cookie_path = trim( $bb->wp_admin_cookie_path ) ) {
+	$bb->wp_admin_cookie_path = rtrim( $bb->wp_admin_cookie_path, " \t\n\r\0\x0B/" );
+}
+
+$bb->wp_plugins_cookie_path = bb_get_option( 'wp_plugins_cookie_path' );
+if ( !$bb->wp_plugins_cookie_path && $bb->wp_cookies_integrated ) {
+	// This is a best guess only, should be manually set to match WP_PLUGIN_URL
+	$bb->wp_plugins_cookie_path = $_bb_sitecookiepath . '/wp-content/plugins';
+}
+if ( '/' !== $bb->wp_plugins_cookie_path = trim( $bb->wp_plugins_cookie_path ) ) {
+	$bb->wp_plugins_cookie_path = rtrim( $bb->wp_plugins_cookie_path, " \t\n\r\0\x0B/" );
+}
+unset( $_bb_sitecookiepath );
+
+/**
+ * Should be exactly the same as the default value of the KEYS in bb-config-sample.php
+ * @since 1.0
+ */
+$bb_default_secret_key = 'put your unique phrase here';
+
+
+
+/**
+ * Initialise localisation
+ */
+
+// Load the default text localization domain.
+bb_load_default_textdomain();
+
+// Pull in locale data after loading text domain.
+require_once( BB_PATH . BB_INC . 'class.bb-locale.php' );
+
+/**
+ * Localisation object
+ */
+$bb_locale = new BB_Locale();
+
+
+
+/**
+ * Remaining BackPress
+ */
+
+// WP_Pass
+if ( !class_exists( 'WP_Pass' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-pass.php' );
+}
+
+// WP_Users
+if ( !class_exists( 'WP_Users' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-users.php' );
+	$wp_users_object = new WP_Users( $bbdb );
+}
+
+if ( !class_exists( 'BP_Roles' ) ) {
+	require_once( BACKPRESS_PATH . 'class.bp-roles.php' );
+}
+
+/**
+ * BP_Roles object
+ */
+$wp_roles = new BP_Roles( $bbdb );
+
+// BP_User
+if ( !class_exists( 'BP_User' ) ) {
+	require_once( BACKPRESS_PATH . 'class.bp-user.php' );
+}
+
+// WP_Auth
+if ( !class_exists( 'WP_Auth' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-auth.php' );
+
+	$cookies = array();
+
+	$cookies['logged_in'][] = array(
+		'domain' => $bb->cookiedomain,
+		'path' => $bb->cookiepath,
+		'name' => $bb->logged_in_cookie
+	);
+
+	if ( $bb->sitecookiepath && $bb->cookiepath != $bb->sitecookiepath ) {
+		$cookies['logged_in'][] = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $bb->sitecookiepath,
+			'name' => $bb->logged_in_cookie
+		);
+	}
+
+	$cookies['auth'][] = array(
+		'domain' => $bb->cookiedomain,
+		'path' => $bb->admin_cookie_path,
+		'name' => $bb->authcookie
+	);
+
+	$cookies['secure_auth'][] = array(
+		'domain' => $bb->cookiedomain,
+		'path' => $bb->admin_cookie_path,
+		'name' => $bb->secure_auth_cookie,
+		'secure' => true
+	);
+
+	$_plugin_cookie_paths = bb_get_option( 'plugin_cookie_paths' );
+	foreach ( $bb->plugin_locations as $_name => $_data ) {
+		if ( isset( $_data['cookie_path'] ) && !empty( $_data['cookie_path'] ) ) {
+			$_cookie_path = $_data['cookie_path'];
+		} elseif ( !$_plugin_cookie_paths || !isset( $_plugin_cookie_paths[$_name] ) || !$_plugin_cookie_paths[$_name] ) {
+			$_cookie_path = preg_replace( '|https?://[^/]+|i', '', $_data['url'] );
+		} else {
+			$_cookie_path = $_plugin_cookie_paths[$_name];
+		}
+		if ( '/' !== $_cookie_path = trim( $_cookie_path ) ) {
+			$_cookie_path = rtrim( $_cookie_path, " \t\n\r\0\x0B/" );
+		}
+
+		if ( !$_cookie_path ) {
+			continue;
+		}
+
+		$_auth = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $_cookie_path,
+			'name' => $bb->authcookie
+		);
+
+		if ( !in_array( $_auth, $cookies['auth'] ) ) {
+			$cookies['auth'][] = $_auth;
+		}
+
+		$_secure_auth = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $_cookie_path,
+			'name' => $bb->secure_auth_cookie,
+			'secure' => true
+		);
+
+		if ( !in_array( $_secure_auth, $cookies['secure_auth'] ) ) {
+			$cookies['secure_auth'][] = $_secure_auth;
+		}
+	}
+	unset( $_plugin_cookie_paths, $_type, $_data, $_cookie_path, $_auth, $_secure_auth );
+
+	if ( $bb->wp_admin_cookie_path ) {
+		$cookies['auth'][] = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $bb->wp_admin_cookie_path,
+			'name' => $bb->authcookie
+		);
+
+		$cookies['secure_auth'][] = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $bb->wp_admin_cookie_path,
+			'name' => $bb->secure_auth_cookie,
+			'secure' => true
+		);
+	}
+
+	if ( $bb->wp_plugins_cookie_path ) {
+		$cookies['auth'][] = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $bb->wp_plugins_cookie_path,
+			'name' => $bb->authcookie
+		);
+
+		$cookies['secure_auth'][] = array(
+			'domain' => $bb->cookiedomain,
+			'path' => $bb->wp_plugins_cookie_path,
+			'name' => $bb->secure_auth_cookie,
+			'secure' => true
+		);
+	}
+
+	/**
+	 * The current cookie version
+	 *
+	 * Version 1 is for WordPress >= 2.6 and < 2.8
+	 * Version 2 is for Wordpress >= 2.8
+	 */
+	if ( !defined( 'WP_AUTH_COOKIE_VERSION' ) ) {
+		define( 'WP_AUTH_COOKIE_VERSION', 2 );
+	}
+
+	/**
+	 * WP_Auth object
+	 */
+	$wp_auth_object = new WP_Auth( $bbdb, $wp_users_object, $cookies );
+
+	unset( $cookies );
+}
+
+/**
+ * Current user object
+ */
+$bb_current_user =& $wp_auth_object->current;
+
+// WP_Scripts/WP_Styles
+if ( !class_exists( 'WP_Dependencies' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-dependencies.php' );
+}
+
+if ( !class_exists( 'WP_Scripts' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-scripts.php' );
+	require_once( BACKPRESS_PATH . 'functions.wp-scripts.php' );
+} else {
+	unset( $wp_scripts );
+}
+
+if ( !class_exists( 'WP_Styles' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-styles.php' );
+	require_once( BACKPRESS_PATH . 'functions.wp-styles.php' );
+} else {
+	unset( $wp_styles );
+}
+
+
+
+// WP_Taxonomy
+if ( !class_exists( 'WP_Taxonomy' ) ) {
+	require_once( BACKPRESS_PATH . 'class.wp-taxonomy.php' );
+}
+if ( !class_exists( 'BB_Taxonomy' ) ) {
+	require_once( BB_PATH . BB_INC . 'class.bb-taxonomy.php' );
+}
+if ( !isset( $wp_taxonomy_object ) ) {
+	// Clean slate
+	$wp_taxonomy_object = new BB_Taxonomy( $bbdb );
+} elseif ( !is_a( $wp_taxonomy_object, 'BB_Taxonomy' ) ) {
+	// exists, but it's not good enough, translate it
+
+	// preserve the references
+	$tax =& $wp_taxonomy_object->taxonomies;
+	$wp_taxonomy_object = new BB_Taxonomy( $bbdb );
+	$wp_taxonomy_object->taxonomies =& $tax;
+	unset( $tax );
+}
+
+$wp_taxonomy_object->register_taxonomy( 'bb_topic_tag', 'bb_topic' );
+
+$wp_taxonomy_object->register_taxonomy( 'bb_subscribe', 'bb_user' );
+
+do_action( 'bb_options_loaded' );
+
+
+
+/**
+ * Load deprecated constants and functions
+ */
+
+// Skip loading of deprecated stuff unless specifically requested
+if ( BB_LOAD_DEPRECATED ) {
+	/**
+	 * Define deprecated constants for plugin compatibility
+	 * $deprecated_constants below is a complete array of old constants and their replacements
+	 */
+	$deprecated_constants = array(
+		'BBPATH'      => 'BB_PATH',
+		'BBINC'       => 'BB_INC',
+		'BBLANG'      => 'BB_LANG',
+		'BBLANGDIR'   => 'BB_LANG_DIR',
+		'BBPLUGINDIR' => 'BB_PLUGIN_DIR',
+		'BBPLUGINURL' => 'BB_PLUGIN_URL',
+		'BBTHEMEDIR'  => 'BB_THEME_DIR',
+		'BBTHEMEURL'  => 'BB_THEME_URL',
+		'BBHASH'      => 'BB_HASH'
+	);
+	foreach ( $deprecated_constants as $old => $new ) {
+		// only define if new one is defined
+		if ( !defined( $old ) && defined( $new ) ) {
+			define( $old, constant( $new ) );
+		} elseif ( defined( $old ) ) {
+			bb_log_deprecated( 'constant', $old, $new );
+		}
+	}
+
+	$deprecated_constants = array(
+		'USER_BBDB_NAME'         => 'user_bbdb_name',
+		'USER_BBDB_USER'         => 'user_bbdb_user',
+		'USER_BBDB_PASSWORD'     => 'user_bbdb_password',
+		'USER_BBDB_HOST'         => 'user_bbdb_host',
+		'USER_BBDB_CHARSET'      => 'user_bbdb_charset',
+		'CUSTOM_USER_TABLE'      => 'custom_user_table',
+		'CUSTOM_USER_META_TABLE' => 'custom_user_meta_table',
+	);
+	foreach ( $deprecated_constants as $old => $new ) {
+		if ( !defined( $old ) ) {
+			define( $old, $bb->$new );
+		} else {
+			bb_log_deprecated( 'constant', $old, '$bb->' . $new );
+		}
+	}
+	unset($deprecated_constants, $old, $new);
+
+	/**
+	 * Load deprecated functions
+	 */
+	require_once( BB_PATH . BB_INC . 'functions.bb-deprecated.php' );
+
+	/**
+	 * Old cache global object for backwards compatibility
+	 */
+	$bb_cache = new BB_Cache();
+}
+
+
+
+/**
+ * Load active template functions.php file
+ */
+$template_functions_include = bb_get_active_theme_directory() . 'functions.php';
+if ( file_exists( $template_functions_include ) ) {
+	require_once( $template_functions_include );
+}
+unset( $template_functions_include );
+
+
+
+/**
+ * Load Plugins
+ */
+
+// Skip plugin loading in "safe" mode
+if ( $bb->plugin_locations && ( !isset( $bb->safemode ) || $bb->safemode !== true ) ) {
+	// Autoloaded "underscore" plugins
+	foreach ( $bb->plugin_locations as $_name => $_data ) {
+		foreach ( bb_glob( $_data['dir'] . '_*.php' ) as $_plugin ) {
+			require_once( $_plugin );
+		}
+		unset( $_plugin );
+	}
+	unset( $_name, $_data );
+	do_action( 'bb_underscore_plugins_loaded' );
+
+	// Normal plugins
+	if ( $_plugins = bb_get_option( 'active_plugins' ) ) {
+		foreach ( (array) $_plugins as $_plugin ) {
+			if ( !preg_match( '/^([a-z0-9_-]+)#((?:[a-z0-9\/\\_-]+.)+)(php)$/i', $_plugin, $_matches ) ) {
+				// The plugin entry in the database is invalid
+				continue;
+			}
+
+			$_directory = $bb->plugin_locations[$_matches[1]]['dir'];
+			$_plugin = $_matches[2] . $_matches[3];
+
+			if ( !$_plugin ) {
+				// Not likely
+				continue;
+			}
+
+			if ( validate_file( $_plugin ) ) {
+				// $plugin has .., :, etc.
+				continue;
+			}
+
+			if ( !file_exists( $_directory . $_plugin ) ) {
+				// The plugin isn't there
+				continue;
+			}
+
+			require_once( $_directory . $_plugin );
+		}
+	}
+	unset( $_plugins, $_plugin, $_directory );
+	do_action( 'bb_plugins_loaded' );
+}
+
+require_once( BB_PATH . BB_INC . 'functions.bb-pluggable.php' );
+
+
+
+/**
+ * Reference to $wp_roles
+ */
+$bb_roles =& $wp_roles;
+do_action( 'bb_got_roles' );
+
+
+
+/**
+ * Create an API hook to run on shutdown
+ */
+
+function bb_shutdown_action_hook() {
+	do_action( 'bb_shutdown' );
+}
+register_shutdown_function( 'bb_shutdown_action_hook' );
+
+
+/**
+ * Get details of the current user
+ */
+
+bb_current_user();
+
+
+
+/**
+ * Initialise CRON
+ */
+
+if ( !function_exists( 'wp_schedule_single_event' ) ) {
+	require_once( BACKPRESS_PATH . 'functions.wp-cron.php' );
+}
+if ( ( !defined('DOING_CRON') || !DOING_CRON ) ) {
+	wp_cron();
+}
+
+
+
+/**
+ * The currently viewed page number
+ */
+$page = bb_get_uri_page();
+
+
+
+/**
+ * Initialisation complete API hook
+ */
+
+do_action( 'bb_init' );
+
+
+
+/**
+ * Block user if they deserve it
+ */
+
+if ( bb_is_user_logged_in() && bb_has_broken_pass() ) {
+	bb_block_current_user();
+}
+
+
+
+/**
+ * Send HTTP headers
+ */
+
+bb_send_headers();
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/functions.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/functions.php	(revision 5057)
@@ -0,0 +1,6 @@
+<?php
+/* Functions for Kakumei Blue Theme */
+
+/* The below actions are removed because we add our own checkbox (check post-form.php and edit-form.php) */
+remove_action( 'post_form', 'bb_user_subscribe_checkbox' );
+remove_action( 'edit_form', 'bb_user_subscribe_checkbox' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style-rtl.css	(revision 5057)
@@ -0,0 +1,1 @@
+@import url('../kakumei/style-rtl.css');
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei-blue/style.css	(revision 5057)
@@ -0,0 +1,31 @@
+/*
+Theme Name: Kakumei Blue
+Theme URI: http://bbpress.org/
+Description: The "revolutionized" new face of bbPress... in blue.
+Version: 1.0
+Author: <a href="http://avalonstar.com">Bryan Veloso</a> with updates by <a href="http://unlettered.org">Sam Bauers</a>
+Author URI: 
+*/
+
+
+@import url('../kakumei/style.css');
+
+a { color: #32689b; text-decoration: none; }
+a:hover { color: #001364; }
+#header { background-image: url('images/page_header_bblogo.png'); }
+#header div.search input.submit:hover { color: #001364; }
+#header div.search input.submit:active { border-color: #001364; }
+form.login input.submit:hover { color: #001364; }
+form.login input.submit:active { border-color: #001364; }
+input#tagformsub:hover { color: #001364; }
+input#tagformsub:active { border-color: #001364; }
+.sticky { background: #adb9e1; }
+#thread li.pingback { background-color: #d8dcf2; border-color: #d8dcf2; }
+#thread li.pingback .threadpost { background-color: #d8dcf2; }
+#topic-move input { color: #32689b; }
+#topic-move input:hover { color: #001364; }
+#latest tr:hover, #forumlist tr:hover, #favorites tr:hover { background: #d8dcf2; }
+#profile-menu li a:hover { background: #d8dcf2; }
+a.prev.page-numbers:hover, a.next.page-numbers:hover { color: #001364; }
+p.submit input:hover { color: #001364; }
+p.submit input:active { border-color: #001364; }
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/404.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/404.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/404.php	(revision 5057)
@@ -0,0 +1,9 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Page not found!'); ?></div>
+
+<h2 id="http404"><?php _e('Page not found!'); ?></h2>
+
+<p><?php _e('I\'m sorry, but there is nothing at this URL.'); ?></p>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-form.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-form.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-form.php	(revision 5057)
@@ -0,0 +1,33 @@
+<?php if ( !bb_get_user( get_post_author_id() ) ) : ?>
+
+	<?php bb_load_template( 'post-form-anonymous.php' ); ?>
+
+<?php endif; ?>
+
+<?php if ( $topic_title ) : ?>
+<p role="main">
+	<label for="topic"><?php _e( 'Topic:' ); ?><br />
+		<input name="topic" type="text" id="topic" size="50" maxlength="80" tabindex="31" value="<?php echo esc_attr( get_topic_title() ); ?>" />
+	</label>
+</p>
+<?php endif; do_action( 'edit_form_pre_post' ); ?>
+
+<p>
+	<label for="post_content"><?php _e( 'Post:' ); ?><br />
+		<textarea name="post_content" cols="50" rows="8" tabindex="32" id="post_content"><?php echo apply_filters( 'edit_text', get_post_text() ); ?></textarea>
+	</label>
+</p>
+
+<?php if ( bb_get_user( get_post_author_id() ) && bb_is_subscriptions_active() ) : ?>
+<p id="post-form-subscription-container" class="left">
+	<?php bb_user_subscribe_checkbox( 'tab=33' ); ?>
+</p>
+<?php endif; ?>
+
+<p class="submit">
+	<input type="submit" name="Submit" value="<?php esc_attr_e( 'Edit Post &raquo;' ); ?>" tabindex="34" />
+	<input type="hidden" name="post_id" value="<?php post_id(); ?>" />
+	<input type="hidden" name="topic_id" value="<?php topic_id(); ?>" />
+</p>
+
+<p><?php _e( 'Allowed markup:' ); ?> <code><?php allowed_markup(); ?></code>. <br /><?php _e( 'Put code in between <code>`backticks`</code>.' ); ?></p>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-post.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-post.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/edit-post.php	(revision 5057)
@@ -0,0 +1,6 @@
+<?php bb_get_header(); ?>
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Edit Post'); ?></div>
+
+<?php edit_form(); ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/favorites.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/favorites.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/favorites.php	(revision 5057)
@@ -0,0 +1,54 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <a href="<?php user_profile_link( $user_id ); ?>"><?php echo get_user_display_name( $user_id ); ?></a> &raquo; <?php _e('Favorites'); ?></div>
+
+<h2 id="userlogin" role="main"><?php echo get_user_display_name( $user->ID ); ?> <small>(<?php echo get_user_name( $user->ID ); ?>)</small> <?php _e( 'favorites' ); ?><?php if ( $topics ) printf( __( ' - %d' ), $favorites_total ); ?></h2>
+
+<p><?php _e( 'Favorites allow members to create a custom <abbr title="Really Simple Syndication">RSS</abbr> feed which pulls recent replies to the topics they specify.' ); ?></p>
+<?php if ( bb_current_user_can( 'edit_favorites_of', $user_id ) ) : ?>
+<p><?php _e( 'To add topics to your list of favorites, just click the "Add to Favorites" link found on that topic&#8217;s page.' ); ?></p>
+<?php endif; ?>
+
+<?php if ( $topics ) : ?>
+
+<table id="favorites">
+<tr>
+	<th><?php _e('Topic'); ?></th>
+	<th><?php _e('Posts'); ?></th>
+	<!-- <th><?php _e('Voices'); ?></th> -->
+	<th><?php _e('Last Poster'); ?></th>
+	<th><?php _e('Freshness'); ?></th>
+<?php if ( bb_current_user_can( 'edit_favorites_of', $user_id ) ) : ?>
+	<th><?php _e('Remove'); ?></th>
+<?php endif; ?>
+</tr>
+
+<?php foreach ( $topics as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+<?php if ( bb_current_user_can( 'edit_favorites_of', $user_id ) ) : ?>
+	<td class="num">[<?php user_favorites_link('', array('mid'=>'&times;'), $user_id); ?>]</td>
+<?php endif; ?>
+</tr>
+<?php endforeach; ?>
+</table>
+
+<p class="rss-link"><a href="<?php favorites_rss_link( $user_id ); ?>" class="rss-link"><?php _e('<abbr title="Really Simple Syndication">RSS</abbr> feed for these favorites'); ?></a></p>
+
+<?php favorites_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+
+<?php else: if ( $user_id == bb_get_current_user_info( 'id' ) ) : ?>
+
+<p><?php _e('You currently have no favorites.'); ?></p>
+
+<?php else : ?>
+
+<p><?php echo get_user_name( $user_id ); ?> <?php _e('currently has no favorites.'); ?></p>
+
+<?php endif; endif; ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/footer.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/footer.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/footer.php	(revision 5057)
@@ -0,0 +1,22 @@
+		</div>
+	</div>
+	<div id="footer" role="contentinfo">
+		<p><?php printf(__('%1$s is proudly powered by <a href="%2$s">bbPress</a>.'), bb_option('name'), "http://bbpress.org") ?></p>
+
+		<!-- If you like showing off the fact that your server rocks -->
+		<!-- <p class="showoff">
+<?php
+global $bbdb;
+printf(
+__( 'This page generated in %s seconds, using %d queries.' ),
+bb_number_format_i18n( bb_timer_stop(), 2 ),
+bb_number_format_i18n( $bbdb->num_queries )
+);
+?>
+		</p> -->
+	</div>
+
+<?php do_action('bb_foot'); ?>
+
+</body>
+</html>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/forum.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/forum.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/forum.php	(revision 5057)
@@ -0,0 +1,67 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a><?php bb_forum_bread_crumb(); ?></div>
+
+<?php if ( $topics || $stickies ) : ?>
+
+<table id="latest" role="main">
+<tr>
+	<th><?php _e('Topic'); ?> &#8212; <?php bb_new_topic_link(); ?></th>
+	<th><?php _e('Posts'); ?></th>
+	<!-- <th><?php _e('Voices'); ?></th> -->
+	<th><?php _e('Last Poster'); ?></th>
+	<th><?php _e('Freshness'); ?></th>
+</tr>
+
+<?php if ( $stickies ) : foreach ( $stickies as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <big><a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></big><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; ?>
+
+<?php if ( $topics ) : foreach ( $topics as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; ?>
+</table>
+<p class="rss-link"><a href="<?php bb_forum_posts_rss_link(); ?>" class="rss-link"><?php _e('<abbr title="Really Simple Syndication">RSS</abbr> feed for this forum'); ?></a></p>
+<?php forum_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+<?php endif; ?>
+
+<?php if ( bb_forums( $forum_id ) ) : ?>
+<h2><?php _e('Subforums'); ?></h2>
+<table id="forumlist">
+
+<tr>
+	<th><?php _e('Main Theme'); ?></th>
+	<th><?php _e('Topics'); ?></th>
+	<th><?php _e('Posts'); ?></th>
+</tr>
+
+<?php while ( bb_forum() ) : ?>
+<?php if (bb_get_forum_is_category()) : ?>
+<tr<?php bb_forum_class('bb-category'); ?>>
+	<td colspan="3"><?php bb_forum_pad( '<div class="nest">' ); ?><a href="<?php forum_link(); ?>"><?php forum_name(); ?></a><?php forum_description( array( 'before' => '<small> &#8211; ', 'after' => '</small>' ) ); ?><?php bb_forum_pad( '</div>' ); ?></td>
+</tr>
+<?php continue; endif; ?>
+<tr<?php bb_forum_class(); ?>>
+	<td><?php bb_forum_pad( '<div class="nest">' ); ?><a href="<?php forum_link(); ?>"><?php forum_name(); ?></a><?php forum_description( array( 'before' => '<small> &#8211; ', 'after' => '</small>' ) ); ?><?php bb_forum_pad( '</div>' ); ?></td>
+	<td class="num"><?php forum_topics(); ?></td>
+	<td class="num"><?php forum_posts(); ?></td>
+</tr>
+<?php endwhile; ?>
+</table>
+<?php endif; ?>
+
+<?php post_form(); ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/front-page.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/front-page.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/front-page.php	(revision 5057)
@@ -0,0 +1,90 @@
+<?php bb_get_header(); ?>
+
+<?php if ( $forums ) : ?>
+
+<div id="hottags" role="main">
+<h2><?php _e('Hot Tags'); ?></h2>
+<p class="frontpageheatmap"><?php bb_tag_heat_map(); ?></p>
+</div>
+
+<div id="discussions">
+<?php if ( $topics || $super_stickies ) : ?>
+
+<h2><?php _e('Latest Discussions'); ?></h2>
+
+<table id="latest">
+<tr>
+	<th><?php _e('Topic'); ?> &#8212; <?php bb_new_topic_link(); ?></th>
+	<th><?php _e('Posts'); ?></th>
+	<!-- <th><?php _e('Voices'); ?></th> -->
+	<th><?php _e('Last Poster'); ?></th>
+	<th><?php _e('Freshness'); ?></th>
+</tr>
+
+<?php if ( $super_stickies ) : foreach ( $super_stickies as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <big><a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></big><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; // $super_stickies ?>
+
+<?php if ( $topics ) : foreach ( $topics as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; // $topics ?>
+</table>
+<?php bb_latest_topics_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+<?php endif; // $topics or $super_stickies ?>
+
+<?php if ( bb_forums() ) : ?>
+<h2><?php _e('Forums'); ?></h2>
+<table id="forumlist">
+
+<tr>
+	<th><?php _e('Main Theme'); ?></th>
+	<th><?php _e('Topics'); ?></th>
+	<th><?php _e('Posts'); ?></th>
+</tr>
+<?php while ( bb_forum() ) : ?>
+<?php if (bb_get_forum_is_category()) : ?>
+<tr<?php bb_forum_class('bb-category'); ?>>
+	<td colspan="3"><?php bb_forum_pad( '<div class="nest">' ); ?><a href="<?php forum_link(); ?>"><?php forum_name(); ?></a><?php forum_description( array( 'before' => '<small> &#8211; ', 'after' => '</small>' ) ); ?><?php bb_forum_pad( '</div>' ); ?></td>
+</tr>
+<?php continue; endif; ?>
+<tr<?php bb_forum_class(); ?>>
+	<td><?php bb_forum_pad( '<div class="nest">' ); ?><a href="<?php forum_link(); ?>"><?php forum_name(); ?></a><?php forum_description( array( 'before' => '<small> &#8211; ', 'after' => '</small>' ) ); ?><?php bb_forum_pad( '</div>' ); ?></td>
+	<td class="num"><?php forum_topics(); ?></td>
+	<td class="num"><?php forum_posts(); ?></td>
+</tr>
+<?php endwhile; ?>
+</table>
+<?php endif; // bb_forums() ?>
+
+<?php if ( bb_is_user_logged_in() ) : ?>
+<div id="viewdiv">
+<h2><?php _e('Views'); ?></h2>
+<ul id="views">
+<?php foreach ( bb_get_views() as $the_view => $title ) : ?>
+<li class="view"><a href="<?php view_link( $the_view ); ?>"><?php view_name( $the_view ); ?></a></li>
+<?php endforeach; ?>
+</ul>
+</div>
+<?php endif; // bb_is_user_logged_in() ?>
+
+</div>
+
+<?php else : // $forums ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Add New Topic'); ?></div>
+
+<?php post_form(); endif; // $forums ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/functions.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/functions.php	(revision 5057)
@@ -0,0 +1,6 @@
+<?php
+/* Functions for Kakumei Theme */
+
+/* The below actions are removed because we add our own checkbox (check post-form.php and edit-form.php) */
+remove_action( 'post_form', 'bb_user_subscribe_checkbox' );
+remove_action( 'edit_form', 'bb_user_subscribe_checkbox' );
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/header.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/header.php	(revision 5057)
@@ -0,0 +1,40 @@
+<?php
+$_head_profile_attr = '';
+if ( bb_is_profile() ) {
+	global $self;
+	if ( !$self ) {
+		$_head_profile_attr = ' profile="http://www.w3.org/2006/03/hcard"';
+	}
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"<?php bb_language_attributes( '1.1' ); ?>>
+<head<?php echo $_head_profile_attr; ?>>
+	<meta http-equiv="X-UA-Compatible" content="IE=8" />
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title><?php bb_title() ?></title>
+	<link rel="stylesheet" href="<?php bb_stylesheet_uri(); ?>" type="text/css" />
+<?php if ( 'rtl' == bb_get_option( 'text_direction' ) ) : ?>
+	<link rel="stylesheet" href="<?php bb_stylesheet_uri( 'rtl' ); ?>" type="text/css" />
+<?php endif; ?>
+
+<?php bb_feed_head(); ?>
+
+<?php bb_head(); ?>
+
+</head>
+<body id="<?php bb_location(); ?>">
+	<div id="wrapper">
+		<div id="header" role="banner">
+			<h1><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a></h1>
+			<?php if ( bb_get_option('description') ) : ?><p class="description"><?php bb_option('description'); ?></p><?php endif; ?>
+
+<?php if ( !in_array( bb_get_location(), array( 'login-page', 'register-page' ) ) ) login_form(); ?>
+
+			<div class="search">
+<?php search_form(); ?>
+			</div>
+		</div>
+		<div id="main">
+
+<?php if ( bb_is_profile() ) profile_menu(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/logged-in.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/logged-in.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/logged-in.php	(revision 5057)
@@ -0,0 +1,5 @@
+<p class="login">
+	<?php printf(__('Welcome, %1$s'), bb_get_profile_link(bb_get_current_user_info( 'name' )));?>
+	<?php bb_admin_link( 'before= | ' );?>
+	| <?php bb_logout_link(); ?>
+</p>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login-form.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login-form.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login-form.php	(revision 5057)
@@ -0,0 +1,33 @@
+<form class="login" method="post" action="<?php bb_uri( 'bb-login.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_USER_FORMS ); ?>">
+	<p>
+		<?php
+	printf(
+		__( '<a href="%1$s">Register</a> or log in - <a href="%2$s">lost password?</a>' ),
+		bb_get_uri( 'register.php', null, BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_USER_FORMS ),
+		bb_get_uri( 'bb-login.php', array( 'action' => 'lostpassword' ), BB_URI_CONTEXT_A_HREF + BB_URI_CONTEXT_BB_USER_FORMS )
+	);
+	?>
+
+	</p>
+	<div>
+		<label>
+			<?php _e('Username'); ?><br />
+			<input name="log" type="text" id="quick_user_login" size="13" maxlength="40" value="<?php if (!is_bool($user_login)) echo $user_login; ?>" tabindex="10" />
+		</label>
+		<label>
+			<?php _e( 'Password' ); ?><br />
+			<input name="pwd" type="password" id="quick_password" size="13" maxlength="40" tabindex="11" />
+		</label>
+		<input name="redirect_to" type="hidden" value="<?php echo $re; ?>" />
+		<?php wp_referer_field(); ?>
+
+		<input type="submit" name="Submit" class="submit" value="<?php echo esc_attr__( 'Log in &raquo;' ); ?>" tabindex="13" />
+	</div>
+	<div class="remember">
+		<label>
+			<input name="rememberme" type="checkbox" id="quick_remember" value="1" tabindex="12"<?php echo $remember_checked; ?> />
+			<?php _e('Remember me'); ?>
+
+		</label>
+	</div>
+</form>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/login.php	(revision 5057)
@@ -0,0 +1,75 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Log in'); ?></div>
+
+<h2 id="userlogin" role="main"><?php !empty( $user_login ) ? _e('Log in Failed') : _e('Log in') ; ?></h2>
+
+<form method="post" action="<?php bb_uri('bb-login.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_USER_FORMS); ?>">
+<fieldset>
+<table>
+<?php
+	$user_login_error = $bb_login_error->get_error_message( 'user_login' );
+	$user_email_error = $bb_login_error->get_error_message( 'user_email' );
+	$user_password_error = $bb_login_error->get_error_message( 'password' );
+?>
+	<tr valign="top" class="form-field <?php if ( $user_login_error || $user_email_error ) echo ' form-invalid error'; ?>">
+		<th scope="row">
+			<label for="user_login"><?php _e('Username'); ?></label>
+			<?php if ( $user_login_error ) echo "<em>$user_login_error</em>"; ?>
+			<?php if ( $user_email_error ) echo "<em>$user_email_error</em>"; ?>
+		</th>
+		<td>
+			<input name="log" id="user_login" type="text" value="<?php echo $user_login; ?>" />
+		</td>
+	</tr>
+	<tr valign="top" class="form-field <?php if ( $user_password_error ) echo 'form-invalid error'; ?>">
+		<th scope="row">
+			<label for="password"><?php _e('Password'); ?></label>
+			<?php if ( $user_password_error ) echo "<em>$user_password_error</em>"; ?>
+		</th>
+		<td>
+			<input name="pwd" id="password" type="password" />
+		</td>
+	</tr>
+
+	<tr valign="top" class="form-field">
+		<th scope="row"><label for="remember"><?php _e('Remember me'); ?></label></th>
+		<td><input name="rememberme" type="checkbox" id="remember" value="1"<?php echo $remember_checked; ?> /></td>
+	</tr>
+	<tr>
+		<th scope="row">&nbsp;</th>
+		<td>
+			<input name="redirect_to" type="hidden" value="<?php echo $redirect_to; ?>" />
+			<input type="submit" value="<?php echo esc_attr( !empty( $user_login ) ? __( 'Try Again &raquo;' ): __( 'Log in &raquo;' ) ); ?>" />
+			<?php wp_referer_field(); ?>
+		</td>
+	</tr>
+</table>
+
+</fieldset>
+</form>
+
+<h2 id="passwordrecovery"><?php _e( 'Password Recovery' ); ?></h2>
+<form method="post" action="<?php bb_uri('bb-reset-password.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_USER_FORMS); ?>">
+<fieldset>
+	<p><?php _e('To recover your password, enter your information below.'); ?></p>
+	<table>
+		<tr valign="top" class="form-field">
+			<th scope="row">
+				<label for="user_login_reset_password"><?php _e( 'Username' ); ?></label>
+			</th>
+			<td>
+				<input name="user_login" id="user_login_reset_password" type="text" value="<?php echo $user_login; ?>" />
+			</td>
+		</tr>
+		<tr valign="top">
+			<th scope="row"></th>
+			<td>
+				<input type="submit" value="<?php echo esc_attr__( 'Recover Password &raquo;' ); ?>" />
+			</td>
+		</tr>
+	</table>
+</fieldset>
+</form>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/password-reset.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/password-reset.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/password-reset.php	(revision 5057)
@@ -0,0 +1,20 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Log in'); ?></div>
+
+<h2 role="main"><?php _e('Password Reset'); ?></h2>
+
+<?php if ( $error ) : ?>
+<p class="notice error"><?php echo $error; ?></p>
+<?php else : ?>
+<?php switch ( $action ) : ?>
+<?php case ( 'send_key' ) : ?>
+<p class="notice"><?php _e('An email has been sent to the address we have on file for you. If you don&#8217;t get anything within a few minutes, or your email has changed, you may want to get in touch with the webmaster or forum administrator here.'); ?></p>
+<?php break; ?>
+<?php case ( 'reset_password' ) : ?>
+<p class="notice"><?php _e('Your password has been reset and a new one has been mailed to you.'); ?></p>
+<?php break; ?>
+<?php endswitch; ?>
+<?php endif; ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form-anonymous.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form-anonymous.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form-anonymous.php	(revision 5057)
@@ -0,0 +1,31 @@
+<?php
+		// Setup $current_poster varaible on post edit
+		if ( bb_is_topic_edit() ) :
+			foreach( array( 'post_author', 'post_email', 'post_url' ) as $post_author_meta )
+				$current_poster[$post_author_meta] = bb_get_post_meta( $post_author_meta, $post_id );
+
+		// Shift $current_poster values from cookie
+		else :
+			$current_poster               = bb_get_current_poster();
+			$current_poster['post_email'] = $current_poster['post_author_email'];
+			$current_poster['post_url']   = $current_poster['post_author_url'];
+		endif;
+?>
+
+	<p id="post-form-author-container">
+		<label for="author"><?php _e( 'Author' ); ?>
+			<input type="text" name="author" id="author" size="50" tabindex="30" aria-required="true" value="<?php echo esc_attr( $current_poster['post_author'] ); ?>" />
+		</label>
+	</p>
+
+	<p id="post-form-email-container">
+		<label for="email"><?php _e( 'Email' ); ?>
+			<input type="text" name="email" id="email" size="50" tabindex="31" aria-required="true" value="<?php echo esc_attr( $current_poster['post_email'] ); ?>" />
+		</label>
+	</p>
+
+	<p id="post-form-url-container">
+		<label for="url"><?php _e( 'Website' ); ?>
+			<input type="text" name="url" id="url" size="50" tabindex="32" value="<?php echo esc_attr( $current_poster['post_url'] ); ?>" />
+		</label>
+	</p>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post-form.php	(revision 5057)
@@ -0,0 +1,43 @@
+<?php if ( !bb_is_topic() ) : ?>
+<p id="post-form-title-container">
+	<label for="topic"><?php _e( 'Title' ); ?>
+		<input name="topic" type="text" id="topic" size="50" maxlength="100" tabindex="34" />
+	</label>
+</p>
+<?php endif; do_action( 'post_form_pre_post' ); ?>
+
+<p id="post-form-post-container">
+	<label for="post_content"><?php _e( 'Post' ); ?>
+		<textarea name="post_content" cols="50" rows="8" id="post_content" tabindex="35"></textarea>
+	</label>
+</p>
+
+<?php if ( bb_is_user_logged_in() ) : /* Display Tags box to only logged in users */ ?>
+<p id="post-form-tags-container">
+	<label for="tags-input"><?php _e( 'Tags (comma separated)' ); ?>
+		<input id="tags-input" name="tags" type="text" size="50" maxlength="100" value="<?php bb_tag_name(); ?>" tabindex="36" />
+	</label>
+</p>
+<?php endif; ?>
+
+<?php if ( bb_is_tag() || bb_is_front() ) : ?>
+<p id="post-form-forum-container">
+	<label for="forum-id"><?php _e( 'Forum' ); ?>
+		<?php bb_new_topic_forum_dropdown( 'tab=37' ); ?>
+	</label>
+</p>
+<?php endif; ?>
+
+<?php if ( bb_is_user_logged_in() && bb_is_subscriptions_active() ) : ?>
+<p id="post-form-subscription-container" class="left">
+	<?php bb_user_subscribe_checkbox( 'tab=38' ); ?>
+</p>
+<?php endif; ?>
+
+<p id="post-form-submit-container" class="submit">
+	<input type="submit" id="postformsub" name="Submit" value="<?php esc_attr_e( 'Send Post &raquo;' ); ?>" tabindex="39" />
+</p>
+
+<div class="clear"></div>
+
+<p id="post-form-allowed-container" class="allowed"><?php _e( 'Allowed markup:' ); ?> <code><?php allowed_markup(); ?></code>. <br /><?php _e( 'You can also put code in between backtick ( <code>`</code> ) characters.' ); ?></p>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/post.php	(revision 5057)
@@ -0,0 +1,13 @@
+		<div id="position-<?php post_position(); ?>">
+			<div class="threadauthor">
+				<?php post_author_avatar_link(); ?>
+				<p>
+					<strong><?php post_author_link(); ?></strong><br />
+					<small><?php post_author_title_link(); ?></small>
+				</p>
+			</div>
+			<div class="threadpost">
+				<div class="post"><?php post_text(); ?></div>
+				<div class="poststuff"><?php printf( __('Posted %s ago'), bb_get_post_time() ); ?> <a href="<?php post_anchor_link(); ?>">#</a> <?php bb_post_admin(); ?></div>
+			</div>
+		</div>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-base.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-base.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-base.php	(revision 5057)
@@ -0,0 +1,8 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <a href="<?php user_profile_link( $user_id ); ?>"><?php echo get_user_display_name( $user_id ); ?></a> &raquo; <?php echo $profile_page_title; ?></div>
+<h2 role="main"><?php echo get_user_name( $user->ID ); ?></h2>
+
+<?php bb_profile_base_content(); ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-edit.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile-edit.php	(revision 5057)
@@ -0,0 +1,51 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb">
+	<a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <a href="<?php user_profile_link( $user_id ); ?>"><?php echo get_user_display_name( $user_id ); ?></a> &raquo; <?php _e( 'Edit Profile' ); ?>
+</div>
+
+<h2 id="userlogin" role="main">
+	<?php echo get_user_display_name( $user->ID ); ?> <small>(<?php echo get_user_name( $user->ID ); ?>)</small>
+</h2>
+
+<form method="post" action="<?php profile_tab_link( $user->ID, 'edit', BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_USER_FORMS ); ?>">
+
+	<fieldset>
+		<legend><?php _e( 'Profile Info' ); ?></legend>
+		<?php bb_profile_data_form(); ?>
+	</fieldset>
+
+<?php if ( bb_current_user_can( 'edit_users' ) ) : ?>
+	<fieldset>
+		<legend><?php _e( 'Administration' ); ?></legend>
+		<?php bb_profile_admin_form(); ?>
+	</fieldset>
+<?php endif; ?>
+
+<?php
+if ( !( array_key_exists( 'keymaster', $user->capabilities ) && !bb_current_user_can( 'keep_gate' ) ) ) :
+	if ( bb_current_user_can( 'change_user_password', $user->ID ) ) :
+?>
+	<fieldset>
+		<legend><?php _e( 'Password' ); ?></legend>
+		<p><?php _e( 'To change your password, enter a new password twice below:' ); ?></p>
+		<?php bb_profile_password_form(); ?>
+	</fieldset>
+<?php endif; endif; ?>
+
+	<p class="submit right">
+		<input type="submit" name="Submit" value="<?php echo esc_attr__( 'Update Profile &raquo;' ); ?>" />
+	</p>
+
+</form>
+
+<form method="post" action="<?php profile_tab_link( $user->ID, 'edit' );  ?>">
+
+	<p class="submit left">
+		<?php bb_nonce_field( 'edit-profile_' . $user->ID ); ?>
+		<?php user_delete_button(); ?>
+	</p>
+
+</form>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/profile.php	(revision 5057)
@@ -0,0 +1,87 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <a href="<?php user_profile_link( $user_id ); ?>"><?php echo get_user_display_name( $user_id ); ?></a> &raquo; <?php _e('Profile') ?></div>
+
+<div class="vcard" role="main">
+
+<?php if ( $avatar = bb_get_avatar( $user->ID ) ) : ?>
+<div id="useravatar"><?php echo $avatar; ?></div>
+<?php unset($avatar); endif; ?>
+<h2 id="userlogin"><span class="fn"><?php echo get_user_display_name( $user->ID ); ?></span> <small>(<span class="nickname"><?php echo get_user_name( $user->ID ); ?></span>)</small></h2>
+
+<?php if ( $updated ) : ?>
+<div class="notice">
+<p><?php _e('Profile updated'); ?>. <a href="<?php profile_tab_link( $user_id, 'edit' ); ?>"><?php _e('Edit again &raquo;'); ?></a></p>
+</div>
+<?php elseif ( $user_id == bb_get_current_user_info( 'id' ) ) : ?>
+<p>
+<?php _e('This is how your profile appears to a logged in member.'); ?>
+
+<?php if (bb_current_user_can( 'edit_user', $user->ID )) : ?>
+<?php printf(__('You may <a href="%1$s">edit this information</a>.'), esc_attr( get_profile_tab_link( $user_id, 'edit' ) ) ); ?>
+<?php endif; ?>
+</p>
+
+<?php if (bb_current_user_can( 'edit_favorites_of', $user->ID )) : ?>
+<p><?php printf(__('You can also <a href="%1$s">manage your favorites</a> and subscribe to your favorites&#8217; <a href="%2$s"><abbr title="Really Simple Syndication">RSS</abbr> feed</a>.'), esc_attr( get_favorites_link() ), esc_attr( get_favorites_rss_link() )); ?></p>
+<?php endif; ?>
+<?php endif; ?>
+
+<?php bb_profile_data(); ?>
+
+</div>
+
+<h3 id="useractivity"><?php _e('User Activity') ?></h3>
+
+<div id="user-replies" class="user-recent"><h4><?php _e('Recent Replies'); ?></h4>
+<?php if ( $posts ) : ?>
+<ol>
+<?php foreach ($posts as $bb_post) : $topic = get_topic( $bb_post->topic_id ) ?>
+<li<?php alt_class('replies'); ?>>
+	<a href="<?php topic_link(); ?>"><?php topic_title(); ?></a> -
+	<?php if ( $user->ID == bb_get_current_user_info( 'id' ) ) printf(__('You last replied: <a href="%1$s">%2$s ago</a>'), get_post_link(), bb_get_post_time()); else printf(__('User last replied: <a href="%1$s">%2$s ago</a>'), get_post_link(), bb_get_post_time()); ?> |
+
+	<span class="freshness"><?php
+		if ( bb_get_post_time( 'timestamp' ) < get_topic_time( 'timestamp' ) )
+			printf(__('Most recent reply: <a href="%1$s">%2$s ago</a>'), get_topic_last_post_link(),  get_topic_time());
+		else
+			_e('No replies since');
+	?></span>
+</li>
+<?php endforeach; ?>
+</ol>
+<?php else : if ( $page ) : ?>
+<p><?php _e('No more replies.') ?></p>
+<?php else : ?>
+<p><?php _e('No replies yet.') ?></p>
+<?php endif; endif; ?>
+</div>
+
+<div id="user-threads" class="user-recent">
+<h4><?php _e('Topics Started') ?></h4>
+<?php if ( $topics ) : ?>
+<ol>
+<?php foreach ($topics as $topic) : ?>
+<li<?php alt_class('topics'); ?>>
+	<a href="<?php topic_link(); ?>"><?php topic_title(); ?></a> -
+	<?php printf(__('Started: %s ago'), get_topic_start_time()); ?> |
+
+	<span class="freshness"><?php
+		if ( get_topic_start_time( 'timestamp' ) < get_topic_time( 'timestamp' ) )
+			printf(__('Most recent reply: <a href="%1$s">%2$s ago</a>'), get_topic_last_post_link(), get_topic_time());
+		else
+			_e('No replies since');
+	?></span>
+</li>
+<?php endforeach; ?>
+</ol>
+<?php else : if ( $page ) : ?>
+<p><?php _e('No more topics posted.') ?></p>
+<?php else : ?>
+<p><?php _e('No topics posted yet.') ?></p>
+<?php endif; endif;?>
+</div>
+
+<?php profile_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register-success.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register-success.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register-success.php	(revision 5057)
@@ -0,0 +1,9 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Register'); ?></div>
+
+<h2 id="register" role="main"><?php _e('Great!'); ?></h2>
+
+<p><?php printf(__('Your registration as <strong>%s</strong> was successful. Within a few minutes you should receive an email with your password.'), $user_login) ?></p>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/register.php	(revision 5057)
@@ -0,0 +1,83 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Register'); ?></div>
+
+<h2 id="register" role="main"><?php _e('Registration'); ?></h2>
+
+<?php if ( !bb_is_user_logged_in() ) : ?>
+
+<form method="post" action="<?php bb_uri('register.php', null, BB_URI_CONTEXT_FORM_ACTION + BB_URI_CONTEXT_BB_USER_FORMS); ?>">
+
+<fieldset>
+<legend><?php _e('Profile Information'); ?></legend>
+
+<p><?php _e("Your password will be emailed to the address you provide."); ?></p>
+
+<?php
+
+$user_login_error = $bb_register_error->get_error_message( 'user_login' );
+
+?>
+
+<table width="100%">
+	<tr class="form-field form-required required<?php if ( $user_login_error ) echo ' form-invalid error'; ?>">
+		<th scope="row">
+			<label for="user_login"><?php _e('Username'); ?></label>
+			<?php if ( $user_login_error ) echo "<em>$user_login_error</em>"; ?>
+		</th>
+		<td>
+			<input name="user_login" type="text" id="user_login" size="30" maxlength="30" value="<?php echo $user_login; ?>" />
+		</td>
+	</tr>
+
+<?php
+
+if ( is_array($profile_info_keys) ) :
+	foreach ( $profile_info_keys as $key => $label ) :
+		$class = 'form-field';
+		if ( $label[0] ) {
+			$class .= ' form-required required';
+		}
+		if ( $profile_info_key_error = $bb_register_error->get_error_message( $key ) )
+			$class .= ' form-invalid error';
+
+?>
+
+	<tr class="<?php echo $class; ?>">
+		<th scope="row">
+			<label for="<?php echo $key; ?>"><?php echo $label[1]; ?></label>
+			<?php if ( $profile_info_key_error ) echo "<em>$profile_info_key_error</em>"; ?>
+		</th>
+		<td>
+			<input name="<?php echo $key; ?>" type="text" id="<?php echo $key; ?>" size="30" maxlength="140" value="<?php echo $$key; ?>" />
+		</td>
+	</tr>
+
+<?php
+
+	endforeach; // profile_info_keys
+endif; // profile_info_keys
+
+?>
+
+</table>
+
+<p class="required-message"><?php _e('These items are <span class="required">required</span>.') ?></p>
+
+</fieldset>
+
+<?php do_action('extra_profile_info', $user); ?>
+
+<p class="submit">
+	<input type="submit" name="Submit" value="<?php echo esc_attr__( 'Register &raquo;' ); ?>" />
+</p>
+
+</form>
+
+<?php else : ?>
+
+<p><?php _e('You&#8217;re already logged in, why do you need to register?'); ?></p>
+
+<?php endif; ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/rss2.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/rss2.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/rss2.php	(revision 5057)
@@ -0,0 +1,35 @@
+<?php
+header( 'Content-Type: text/xml; charset=UTF-8' );
+echo '<' . '?xml version="1.0" encoding="UTF-8"?' . '>' . "\n";
+bb_generator( 'comment' );
+?>
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:atom="http://www.w3.org/2005/Atom"
+	<?php do_action( 'bb_rss2_ns'); ?>
+	>
+	<channel>
+		<title><?php echo $title; ?></title>
+		<link><?php echo $link; ?></link>
+		<description><?php echo $description; ?></description>
+		<language><?php echo esc_html( bb_get_option( 'language' ) ); ?></language>
+		<pubDate><?php echo gmdate( 'D, d M Y H:i:s +0000' ); ?></pubDate>
+		<?php bb_generator( 'rss2' ); ?>
+		<?php do_action( 'bb_rss2_head' ); ?>
+		<atom:link href="<?php echo $link_self; ?>" rel="self" type="application/rss+xml" />
+
+<?php foreach ( (array) $posts as $bb_post ) : ?>
+		<item>
+			<title><?php post_author(); ?> <?php _e( 'on' ); ?> "<?php topic_title( $bb_post->topic_id ); ?>"</title>
+			<link><?php post_link(); ?></link>
+			<pubDate><?php bb_post_time( array( 'format' => 'D, d M Y H:i:s +0000', 'localize' => false ) ); ?></pubDate>
+			<dc:creator><?php post_author(); ?></dc:creator>
+			<guid isPermaLink="false"><?php post_id(); ?>@<?php bb_uri(); ?></guid>
+			<description><![CDATA[<?php post_text(); ?>]]></description>
+			<?php do_action( 'bb_rss2_item' ); ?>
+		</item>
+<?php endforeach; ?>
+
+	</channel>
+</rss>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search-form.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search-form.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search-form.php	(revision 5057)
@@ -0,0 +1,7 @@
+<form class="search-form" role="search" action="<?php bb_uri('search.php', null, BB_URI_CONTEXT_FORM_ACTION); ?>" method="get">
+	<p>
+		<label class="hidden" for="q"><?php _e('Search:'); ?></label>
+		<input class="text" type="text" size="14" maxlength="100" name="q" id="q" />
+		<input class="submit" type="submit" value="<?php echo esc_attr__( 'Search &raquo;' ); ?>" />
+	</p>
+</form>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/search.php	(revision 5057)
@@ -0,0 +1,55 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Search')?></div>
+<?php bb_topic_search_form(); ?>
+
+<?php if ( !empty ( $q ) ) : ?>
+<h3 id="search-for"><?php printf( __( 'Search for %s' ), '&#8220;' . esc_html( $q ) . '&#8221;' ); ?></h3>
+<?php endif; ?>
+
+<?php if ( $recent ) : ?>
+<div id="results-recent" class="search-results">
+	<h4><?php _e( 'Recent Posts' )?></h4>
+	<ol>
+<?php foreach ( $recent as $bb_post ) : ?>
+		<li<?php alt_class( 'recent' ); ?>>
+			<a class="result" href="<?php post_link(); ?>"><?php echo bb_show_topic_context( $q, get_topic_title( $bb_post->topic_id ) ); ?></a>
+			<span class="freshness"><?php printf( __( 'By <a href="%1$s">%2$s</a> on %3$s'), get_user_profile_link( $bb_post->poster_id ), get_post_author(), bb_datetime_format_i18n( bb_get_post_time( array( 'format' => 'timestamp' ) ) ) ); ?></span>
+			<p><?php echo bb_show_context( $q, $bb_post->post_text ); ?></p>
+		</li>
+<?php endforeach; ?>
+	</ol>
+</div>
+<?php endif; ?>
+
+<?php if ( $relevant ) : ?>
+<div id="results-relevant" class="search-results">
+	<h4><?php _e( 'Relevant Topics' )?></h4>
+	<ol>
+<?php foreach ( $relevant as $topic ) : ?>
+<?php $bb_post = bb_get_first_post( $topic ); ?>
+		<li<?php alt_class( 'relevant' ); ?>>
+			<a class="result" href="<?php post_link(); ?>"><?php echo bb_show_topic_context( $q, get_topic_title() ); ?></a>
+			<span class="freshness"><?php printf( __( 'By <a href="%1$s">%2$s</a> on %3$s' ), get_user_profile_link( $topic->topic_poster ), get_topic_author(), bb_datetime_format_i18n( get_topic_start_time( array( 'format' => 'timestamp' ) ) ) ); ?></span>
+			<p><?php echo bb_show_context( $q, $bb_post->post_text ); ?></p>
+		</li>
+<?php endforeach; ?>
+	</ol>
+</div>
+<?php endif; ?>
+
+<?php bb_search_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?> 
+
+<?php if ( $q && !$recent && !$relevant ) : ?>
+<p><?php printf( __( 'Your search %s did not return any results. Here are some suggestions:' ), '&#8220;<em>' . esc_html( $q ) . '</em>&#8221;' ); ?></p>
+<ul id="search-suggestions">
+	<li><?php _e( 'Make sure all words are spelled correctly' ); ?></li>
+	<li><?php _e( 'Try different keywords' ); ?></li>
+	<li><?php _e( 'Try more general keywords' ); ?></li>
+</ul>
+<?php endif; ?>
+
+<br />
+<p><?php printf( __( 'You may also try your <a href="%s">search at Google</a>.' ), 'http://google.com/search?q=site:' . bb_get_uri( null, null, BB_URI_CONTEXT_TEXT ) . urlencode( ' ' . $q ) ); ?></p>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/stats.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/stats.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/stats.php	(revision 5057)
@@ -0,0 +1,26 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option( 'name' ); ?></a> &raquo; <?php _e( 'Statistics' ); ?></div>
+
+<dl role="main" class="left">
+	<dt><?php _e( 'Registered Users' ); ?></dt>
+	<dd><strong><?php bb_total_users(); ?></strong></dd>
+	<dt><?php _e( 'Posts' ); ?></dt>
+	<dd><strong><?php total_posts(); ?></strong></dd>
+<?php do_action( 'bb_stats_left' ); ?>
+</dl>
+
+<div class="right">
+<?php if ( $popular ) : ?>
+	<h3><?php _e( 'Most Popular Topics' ); ?></h3>
+	<ol>
+<?php foreach ( $popular as $topic ) : ?>
+		<li><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a> &#8212; <?php printf( _n( '%s post', '%s posts', get_topic_posts() ), bb_number_format_i18n( get_topic_posts() ) ); ?></li>
+<?php endforeach; ?>
+	</ol>
+<?php endif; ?>
+
+<?php do_action( 'bb_stats_right' ); ?>
+</div>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style-rtl.css	(revision 5057)
@@ -0,0 +1,189 @@
+/* Globals 
+=================================== */
+
+body { font-family: Tahoma, 'Trebuchet MS', 'Lucida Grande', Verdana, Tahoma, Arial; }
+
+pre, code { font-family: Tahoma, Monaco, 'Courier New', monospace; }
+
+.left { float: right; }
+
+.right { float: left; }
+
+/* Structure
+=================================== */
+
+#header { background-position: -43px bottom; }
+
+#header h1 {
+	font-family: Tahoma, Georgia;
+	text-align: left;
+	left: 53px;
+	right: auto;
+}
+
+#header div.search {
+	float: left;
+}
+
+#header p.description {
+	font-family: Tahoma, Georgia;
+	text-align: left;
+	left: 53px;
+	right: auto;
+}
+
+#footer {
+	font-family: Tahoma, Georgia;
+	text-align: left;
+}
+
+/* Login Form
+=================================== */
+
+.login { 
+	right: 0;
+	left: auto;
+}
+
+.login label {
+	float: right;
+	padding-left: 10px;
+	padding-right: 0;
+}
+
+form.login input.submit { float: right; }
+
+
+/* Front Page
+=================================== */
+
+#front-page #hottags {
+	right: 0;
+	left: auto;
+	width: 150px;
+}
+
+#front-page #discussions {
+	margin-right: 170px;
+	margin-left: 0;
+}
+
+#front-page #discussions ul { padding: 0 14px 0 0; }
+
+/* Topic Page
+=================================== */
+
+.infobox ul { margin: 10px 20px 10px 0; }
+
+#topic-info {
+	float: right;
+	padding: 0 0 0 1em;
+}
+
+#topic-tags {
+	border-right: 1px solid #ccc;
+	border-left: none;
+	float: left;
+	padding: 0 1em 0 0;
+}
+
+#thread li ol, #thread li ul {
+	margin-right: 40px;
+	margin-left: 0;
+}
+
+.threadauthor { float: right; }
+
+.threadauthor small { font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; }
+
+.threadpost { margin: 0 140px 0 0; }
+
+#thread .post blockquote {
+	margin: 0 4ex 0 0;
+	padding: 0 1ex 0 0;
+	border-right: 5px solid #ccc;
+	border-left: none;
+}
+
+.poststuff { font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; }
+
+.num, #forumlist small { font-family: Tahoma, Verdana,Arial,Helvetica,sans-serif; }
+
+#manage-tags li { float: right; }
+
+p.submit { text-align: left; }
+
+.rss-link {
+	background-position: 100% 50%;
+}
+
+/* Other
+=================================== */
+
+#front-search { float: left; }
+
+#forumlist tr td div.nest {
+	padding-right: 2.5ex;
+	padding-left: 0;
+}
+
+#latest th, #forumlist th, #favorites th {
+	text-align: right;
+	font-family: Tahoma, Verdana,Arial,Helvetica,sans-serif;
+}
+
+/* Profile Page
+=================================== */
+
+#profile-menu {
+	left: 0;
+	right: auto;
+}
+
+#profile-menu li {
+	margin-left: 0;
+	margin-right: 3px;
+}
+
+.user-recent ol { margin: 5px 28px 0 0; }
+
+/* Search
+=================================== */
+
+#topic-search-form fieldset {
+	padding: 10px 10px 10px 0;
+}
+
+#topic-search-form div label {
+	float: right;
+	text-align: left;
+	padding-right: 0;
+	padding-left: 1em;
+}
+
+#topic-search-form div div {
+	float: right;
+}
+
+.search-results ol { margin: 5px 28px 0 0; }
+
+/* Profile Edit
+=================================== */
+
+#login-page fieldset,
+#register-page fieldset,
+#profile-page fieldset {
+	padding: 10px 10px 10px 0;
+}
+
+#login-page fieldset table,
+#register-page fieldset table,
+#profile-page fieldset table {
+	text-align: right;
+}
+
+#login-page fieldset table th,
+#register-page fieldset table th,
+#profile-page fieldset table th {
+	text-align: left;
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style.css
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/style.css	(revision 5057)
@@ -0,0 +1,1030 @@
+/*
+Theme Name: Kakumei
+Theme URI: http://bbpress.org/
+Description: The "revolutionized" new face of bbPress.
+Version: 1.0
+Author: <a href="http://avalonstar.com">Bryan Veloso</a> with updates by <a href="http://unlettered.org">Sam Bauers</a>
+Author URI: 
+*/
+
+/* Globals 
+=================================== */
+
+* { margin: 0; padding: 0; }
+
+body {
+	margin-bottom: 50px;
+	background-color: #fff;
+	font: 62.5% 'Trebuchet MS', 'Lucida Grande', Verdana, Tahoma, Arial;
+}
+
+a { color: #2e6e15; text-decoration: none; }
+a:hover { color: #006400; }
+
+.alt { background-color: #fff; }
+
+pre, code { font: 1.0em Monaco, 'Courier New', monospace; }
+
+pre, p { margin-bottom: 1.0em; }
+
+.left { float: left; }
+
+.right { float: right; }
+
+.clear { clear: both; }
+
+.delete:hover {
+	background-color: #c00;
+	color: #fff;
+}
+
+h1 { font-size: 2em; }
+
+h2 { font-size: 1.3em; }
+
+fieldset {
+	border-width: 0;
+	padding: 0;
+	margin: 0;
+}
+
+img.avatar { border: 1px solid #ddd; }
+
+.bozo { background-color: #eeee88; }
+
+.alt.bozo { background-color: #ffff99; }
+
+.deleted { background-color: #ee8888; }
+
+.alt.deleted { background-color: #ff9999; }
+
+/* Structure
+=================================== */
+
+#wrapper {
+	background: #fff url('images/page_header_tile.png') repeat-x 0px -15px;
+}
+
+#header {
+	background: url('images/page_header_bblogo.png') no-repeat bottom right; /* Remove to get rid of bb emblem. */
+	margin: 0 auto 10px;
+	width: 760px;
+	height: 116px;
+	position: relative;
+}
+
+#header h1 {
+	font-family: Georgia;
+	font-style: italic;
+	overflow: auto;
+	position: absolute;
+	display: block;
+	color: #444;
+	text-align: right;
+	letter-spacing: -1px;
+	right: 53px;
+	bottom: 26px;
+	padding: 6px;
+	z-index: 2;
+}
+
+#header p.description {
+	font-size: 1.2em;
+	font-family: Georgia;
+	font-style: italic;
+	overflow: auto;
+	position: absolute;
+	display: block;
+	color: #666;
+	text-align: right;
+	right: 53px;
+	bottom: 13px;
+	padding: 6px;
+	z-index: 2;
+}
+
+#header p { margin-bottom: 0; }
+
+#header h1 a { color: #555; text-decoration: none; }
+#header h1 a:hover { color: #666; }
+
+#header div.search {
+	float: right;
+	padding: 4px;
+	background-color: #aaa;
+	-moz-border-radius-bottomleft: 4px;
+	-khtml-border-bottom-left-radius: 4px;
+	-webkit-border-bottom-left-radius: 4px;
+	border-bottom-left-radius: 4px;
+	-moz-border-radius-bottomright: 4px;
+	-khtml-border-bottom-right-radius: 4px;
+	-webkit-border-bottom-right-radius: 4px;
+	border-bottom-right-radius: 4px;
+}
+
+#header div.search input {
+	border: 1px solid #999;
+	background-color: #fdfdfd;
+	padding: 2px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
+#header div.search input.submit {
+	background-color: #ccc;
+	line-height: 15px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 2px 4px;
+}
+
+#header div.search input.submit:hover {
+	background-color: #ddd;
+	color: rgb(0, 100, 0);
+}
+
+#header div.search input.submit:active {
+	border-color: rgb(0, 100, 0);
+}
+
+#main {
+	font-size: 1.2em;
+	width: 760px;
+	margin: 0 auto 25px;
+	position: relative;
+}
+
+.bbcrumb {
+	text-transform: uppercase;
+	font-size: 0.9em;
+	letter-spacing: 1px;
+	padding: 5px 0 20px;
+	font-weight: bold;
+}
+
+#footer {
+	font-family: Georgia;
+	font-style: italic;
+	border-top: 1px solid #ccc;
+	margin: auto;
+	color: #666;
+	font-size: 1.0em;
+	padding-top: 10px;
+	clear: both;
+	text-align: right;
+	width: 760px;
+}
+
+#footer p.showoff {
+	color: #888;
+}
+
+.notice {
+	border: 1px solid #4c9545;
+	background-color: #abd8a2;
+	color: #4c9545;
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 10px 15px;
+	margin: 0 0 1.1em;
+}
+
+.notice.error {
+	border-color: #852424;
+	background-color: #ca8a8a;
+	color: #5d2424;
+}
+
+.notice p { margin-bottom: 0; }
+
+/* Login Form
+=================================== */
+
+.login {
+	position: absolute;
+	bottom: 31px;
+	left: 0;
+	font-weight: normal;
+	color: #444;
+	width: 100%;
+	z-index: 1;
+	font-size: 12px;
+}
+
+form.login {
+	bottom: 15px;
+}
+
+.login p { padding: 0 0 8px; }
+
+.login label {
+	display: block;
+	float: left;
+	padding-right: 10px;
+	line-height: 14px;
+}
+
+form.login input {
+	border: 1px solid #999;
+	padding: 2px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	background-color:#fff;
+}
+
+form.login input.submit {
+	background-color: #ccc;
+	float: left;
+	margin-top: 14px;
+	margin-bottom: -10px;
+	display: block;
+	line-height: 15px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 2px 4px;
+}
+
+form.login input.submit:hover {
+	background-color: #ddd;
+	color: rgb(0, 100, 0);
+}
+
+form.login input.submit:active {
+	border-color: rgb(0, 100, 0);
+}
+
+div.remember label {
+	clear: both;
+	padding: 0;
+	margin: 6px 0 0 0;
+	line-height: 12px;
+}
+
+form.login input#remember {
+	border-width: 0;
+	padding: 0;
+	margin: 0;
+	vertical-align: baseline;
+}
+
+/* Front Page
+=================================== */
+
+#front-page #hottags {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 150px;
+	overflow: hidden;
+}
+
+#front-page #discussions {
+	margin-left: 170px;
+	width: 590px;
+}
+
+.frontpageheatmap {
+	font-weight: normal;
+	line-height: 30px;
+	padding-bottom: 10px;
+}
+
+#front-page #main h2, #forum-page #main h2, h2.post-form, #userlogin, #currentfavorites, #register, #passwordrecovery {
+	color: #555;
+	border-bottom: 1px solid #ddd;
+	margin: 0 0 10px;
+	padding: 0 0 5px;
+}
+
+#front-page #discussions ul { padding: 0 0 0 14px; }
+
+.sticky { background-color: #a1d29a; }
+
+/* Page navigation
+=================================== */
+
+a.page-numbers,
+span.page-numbers {
+	border-right: 1px solid #bbbbbb;
+	border-bottom: 1px solid #bbbbbb;
+	padding: 4px 4px 2px 5px;
+	margin-left: 5px;
+	background-image: url('images/page-links-background.gif');
+	background-repeat: no-repeat;
+	background-position: 0 0;
+}
+
+span.page-numbers.current {
+	color: #ffffff;
+	background-position: 0 -100px;
+}
+
+#latest a.page-numbers {
+	font-size: 0.8em;
+	padding: 3px 3px 1px 4px;
+	margin-left: 3px;
+}
+
+a.prev.page-numbers,
+a.next.page-numbers,
+span.page-numbers.dots {
+	border-width: 0;
+	padding: 0 4px;
+	background-image: none;
+}
+
+span.page-numbers.dots {
+	padding: 0;
+}
+
+a.page-numbers:hover {
+	background-position: 0 -100px;
+}
+
+a.prev.page-numbers:hover,
+a.next.page-numbers:hover {
+	color: #006400;
+}
+
+/* Topic Page
+=================================== */
+
+.infobox {
+	border: 1px solid #ccc;
+	border-width: 1px 0;
+	padding: 1em 0;
+}
+
+.infobox ul {
+	margin: 10px 0 10px 20px;
+	padding: 0;
+	list-style-type: disc;
+}
+
+.infobox ul li { padding-bottom: 3px; }
+
+#topic-info {
+	float: left;
+	padding: 0 1em 0 0;
+}
+
+#topic-tags {
+	border-left: 1px solid #ccc;
+	float: right;
+	padding: 0 0 0 1em;
+}
+
+#tag-form p {
+	margin-bottom: 0;
+}
+
+input#tag {
+	border: 1px solid #999;
+	padding: 2px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
+input#tagformsub {
+	border: 1px solid #999;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	line-height: 15px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 2px 4px;
+}
+
+input#tagformsub:hover {
+	background-color: #ddd;
+	color: rgb(0, 100, 0);
+}
+
+input#tagformsub:active {
+	border-color: rgb(0, 100, 0);
+}
+
+.nav {
+	margin: 15px 0;
+	padding: 12px 0;
+	text-align: center;
+}
+
+#thread {
+	list-style: none;
+	margin: 2em 0 0 0;
+	padding: 0;
+}
+
+#thread li {
+	line-height: 1.5em;
+	clear: both;
+	/* Hack to force padding on .threadauthor on IE */
+	border-top: 1px solid #fff;
+}
+
+#thread li ol, #thread li ul { margin-left: 40px; }
+
+#thread li ol li, #thread li ul li { padding: 0; }
+
+.threadauthor {
+	float: left;
+	padding: 1em 1em 0 1em;
+	width: 120px;
+}
+
+.threadauthor p { margin: 0; }
+
+.threadauthor small { font: 11px Verdana, Arial, Helvetica, sans-serif; }
+
+.threadpost {
+	padding: 1.5em 1em;
+	margin-left: 140px;
+	background-color: #eee;
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+}
+
+#thread li.alt .threadpost { background-color: transparent; }
+
+#thread li.deleted { background-color: transparent; }
+
+#thread li.deleted .threadpost { background-color: #ee8888; }
+
+#thread li.alt.deleted .threadpost { background-color: #ff9999; }
+
+#thread li .post-undelete-link, #thread li.deleted .post-delete-link { display: none; }
+#thread li.deleted .post-undelete-link { display: inline; }
+
+#thread li.deleted .before-post-delete-link, #thread li .before-post-undelete-link { display: none; }
+#thread li.deleted span.before-post-undelete-link { display: inline; }
+#thread li.deleted div.before-post-undelete-link { display: block; }
+#thread li.deleted li.before-post-undelete-link { display: block; }
+
+#thread li.pingback {
+	margin-left: 140px;
+	margin-top: 2px;
+	margin-bottom: 2px;
+	background-color: #e4f3e1;
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	border: 0.2em solid #e4f3e1;
+}
+
+#thread li.pingback.alt {
+	background-color: #fff;
+}
+
+#thread li.pingback .threadpost {
+	background-color: #e4f3e1;
+}
+
+#thread li.pingback.alt .threadpost {
+	background-color: #fff;
+}
+
+#thread li.pingback .threadauthor {
+	float: none;
+	padding: 0.9em 0.9em 0;
+	width: auto;
+}
+
+#thread li.pingback .threadauthor strong {
+	font-weight: normal;
+}
+
+#thread li.pingback .threadauthor small {
+	color: #333;
+}
+
+#thread li.pingback .threadpost {
+	margin-left: 0;
+	padding: 0.5em 0.9em 1.5em;
+}
+
+#thread li.pingback .post {
+	font-style: italic;
+	color: #333;
+	margin: 0 0 0 2.4em;
+	padding: 0 0 0 0.8em;
+	border-left: 3px dotted #ccc;
+}
+
+#thread .post {
+	_height: 90px; /* Hack to fix broken .alt coloring in IE6 */
+}
+
+#thread .post blockquote {
+	margin: 0 0 0 2.4em;
+	padding: 0 0 0 0.8em;
+	border-left: 3px solid #ccc;
+}
+
+#thread .post li { clear:none; }
+
+.poststuff {
+	clear:both;
+	_clear: none; /* Hack to fix broken .alt coloring in IE6 */
+	border-top: 1px dotted #ccc;
+	margin: 10px 0 0;
+	padding: 5px 0 0;
+	font: 10px Verdana, Arial, Helvetica, sans-serif;
+	text-transform: uppercase;
+}
+
+.num, #forumlist small {
+	font: 11px Verdana,Arial,Helvetica,sans-serif;
+	text-align: center;
+	white-space: nowrap;
+}
+
+h2.post-form {
+	border-bottom-width: 0;
+	margin-bottom: 2px;
+}
+
+.postform {
+	background-color: #f0f0f0;
+	padding: 1em;
+	margin-bottom: 1em;
+	-moz-border-radius: 6px;
+	-khtml-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+}
+
+.postform textarea {
+	height: 12em;
+	margin: 5px 0;
+	padding: 5px;
+	width: 724px;
+	max-width: 724px;
+	border: 1px solid #ccc;
+	display: block;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	background-color:#fff;
+}
+
+.postform label { display: block; }
+
+.postform #author,
+.postform #email,
+.postform #topic,
+.postform #tags-input,
+.postform #url {
+	margin: 5px 0;
+	padding: 5px;
+	width: 724px;
+	border: 1px solid #ccc;
+	display: block;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	background-color:#fff;
+}
+
+.postform p#post-form-forum-container label span {
+	font-weight: bold;
+	font-style: italic;
+}
+
+#manage-tags {
+	margin: 10px 0;
+	list-style: none;
+}
+
+#manage-tags li {
+	float: left;
+	width: 200px;
+	padding-bottom: 10px;
+}
+
+p.submit { text-align: right; }
+
+p.allowed { margin-bottom: 0; }
+
+.topiclink { display: block; }
+
+.topictitle {
+	font-size: 26px;
+	font-weight: normal;
+	display: inline;
+}
+
+p.rss-link {
+	text-align: right;
+}
+
+a.rss-link {
+	padding: 6px 0 6px 20px;
+	background-image: url('images/feed-icon-16x16.gif');
+	background-repeat: no-repeat;
+	background-position: 0 50%;
+}
+
+#topic-move,
+#topic-move fieldset,
+#topic-move fieldset div {
+	display: inline;
+}
+
+#topic-move input {
+	font-family: inherit;
+	background: none;
+	border: none;
+	cursor: pointer;
+	color: #2e6e15;
+	font-size: 1em;
+}
+
+#topic-move input:hover { color: #006400; }
+
+/* Other
+=================================== */
+
+#content .frontpageheatmap a {
+	font-weight: normal;
+	text-decoration: none;
+}
+
+#content .infobox li { margin-bottom: 2px; }
+
+#content .nav a {
+	border: 1px solid #ccc;
+	font-weight: normal;
+}
+#content .nav a:hover {
+	border: 1px solid #999;
+}
+#content a:visited {
+	font-weight: normal;
+}
+
+#front-search {
+	float: right;
+	margin-top: -8px;
+}
+
+#latest td, #forumlist td, #favorites td { padding: 5px 10px; }
+
+#forumlist tr td div.nest {
+	padding-left: 2.5ex;
+}
+
+#latest tr:hover, #forumlist tr:hover, #favorites tr:hover { background-color: #e4f3e1; }
+
+#latest th, #forumlist th, #favorites th {
+	text-align: left;
+	background-color: rgb(102, 102, 102);
+	font: 11px Verdana,Arial,Helvetica,sans-serif;
+	font-weight: normal;
+	padding: 5px 9px;
+	color: rgb(255, 255, 255);
+}
+
+#latest th a, #forumlist th a, #favorites th a {
+	color: rgb(200, 200, 200);
+	font-style: italic;
+}
+
+#latest th a:hover, #forumlist th a:hover, #favorites th a:hover {
+	color: rgb(255, 255, 255);
+}
+
+tr.bb-category td {
+	background-color: #ddd;
+}
+
+#latest, #forumlist, #favorites {
+	background-color: #f7f7f7;
+	margin-bottom: 3em;
+	width: 100%;
+}
+
+#latest, #forumlist {
+	margin-top: -0.9em;
+}
+
+#discussions .nav {
+	margin-top: -2em;
+	margin-bottom: 1em;
+}
+
+/* Profile Page
+=================================== */
+
+#profile-menu {
+	list-style: none;
+	position: absolute;
+	right: 0;
+}
+
+#profile-menu li {
+	display: inline;
+	margin-left: 3px;
+}
+
+#profile-menu li a {
+	font-size: 1.1em;
+	background-color: #ddd;
+	padding: 4px 7px;
+	border-top: 3px double #9e9e9e;
+	position: relative;
+	top: -10px;
+	-moz-border-radius-bottomleft: 6px;
+	-khtml-border-bottom-left-radius: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	border-bottom-left-radius: 6px;
+	-moz-border-radius-bottomright: 6px;
+	-khtml-border-bottom-right-radius: 6px;
+	-webkit-border-bottom-right-radius: 6px;
+	border-bottom-right-radius: 6px;
+}
+
+#profile-menu li.current a {
+	background-color: #bbb;
+	border-top: 1px solid #9e9e9e;
+}
+
+#profile-menu li a:hover {
+	background-color: #e4f3e1;
+}
+
+#useravatar { margin-bottom: 1em; }
+
+#useravatar img { display: block; border-width: 3px; border-style: double; }
+
+#userinfo { margin-top: 10px; }
+
+#userinfo dt { font-weight: bold; }
+
+#userinfo dd { margin: 0 0 5px; } 
+
+#useractivity { margin: 15px 0 5px; }
+
+.user-recent { margin: 0 0 10px; }
+
+.user-recent ol { margin: 5px 0 0 28px; }
+
+.user-recent ol li { margin: 0 0 3px; }
+
+/* Search
+=================================== */
+
+#topic-search-form fieldset {
+	border-top: 3px double #ccc;
+	border-bottom: 1px solid #ccc;
+	border-left: none;
+	border-right: none;
+	padding: 10px 0 10px 10px;
+	margin-bottom: 15px;
+	background-color: #f6f6f6;
+}
+
+#topic-search-form div {
+	clear: both;
+}
+
+#topic-search-form div label {
+	display: block;
+	float: left;
+	padding: 5px;
+	text-align: right;
+	width: 20%;
+	vertical-align: top;
+	padding-right: 1em;
+	font-weight: bold;
+}
+
+#topic-search-form div div {
+	display: block;
+	float: left;
+	clear: none;
+	padding: 5px;
+}
+
+#topic-search-form div div input {
+	width: 280px;
+	border: 1px solid #ccc;
+	padding: 2px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
+#search-for { margin: 15px 0 5px; }
+
+ul#search-suggestions { margin-left: 25px; }
+
+.search-results { margin: 0 0 10px; }
+
+.search-results ol { margin: 5px 0 0 28px; }
+
+.search-results ol li { border-bottom: 1px solid #ccc; padding: 2px 5px 0 5px; min-height: 50px; }
+
+.search-results ol li.alt { background-color: #f8f8f8; }
+
+.search-results ol li:hover { background-color: #e4f3e1; }
+
+.search-results ol li a.result { font-size: 15px; }
+
+.search-results ol li span.freshness { float: right; font-style: italic; }
+
+/* Login, Register, Profile Edit
+=================================== */
+
+#login-page fieldset,
+#register-page fieldset,
+#profile-page fieldset {
+	border-top: 3px double #ccc;
+	border-bottom: 1px solid #ccc;
+	border-left: none;
+	border-right: none;
+	padding: 10px 0 10px 10px;
+	margin-bottom: 15px;
+	background-color: #f6f6f6;
+}
+
+#login-page legend,
+#register-page legend,
+#profile-page legend {
+	font-weight: bold;
+	padding: 0 15px;
+}
+
+#login-page fieldset table,
+#register-page fieldset table,
+#profile-page fieldset table {
+	text-align: left;
+	margin: 0 15px;
+	width: 95%;
+	border-collapse: collapse;
+}
+
+#login-page fieldset table th,
+#register-page fieldset table th,
+#profile-page fieldset table th {
+	padding: 5px;
+	text-align: right;
+	width: 20%;
+	vertical-align: top;
+	padding-right: 1em;
+}
+
+#login-page fieldset table tr.error th em,
+#register-page fieldset table tr.error th em,
+#profile-page fieldset table tr.error th em {
+	position: absolute;
+	/* fieldset:padding-left + table:margin-left + table:width + td:padding-left + input:width + input:padding-right */
+	left: 458px; /* 10 + 15 + 20% * ( 95% * (760 - 10)  ) + 5 + 280 + 5 */
+	margin-left: 1em; /* th:padding-right = 1em */
+	color: red;
+	font-style: normal;
+}
+
+#login-page fieldset table td,
+#register-page fieldset table td,
+#profile-page fieldset table td {
+	padding: 5px;
+}
+
+#login-page fieldset table td p,
+#register-page fieldset table td p,
+#profile-page fieldset table td p{
+	margin: 5px 0;
+}
+
+#login-page fieldset input[type=text],
+#register-page fieldset input[type=text],
+#profile-page fieldset input[type=text],
+#login-page fieldset input[type=password],
+#profile-page fieldset input[type=password] {
+	width: 280px;
+	border: 1px solid #ccc;
+	padding: 2px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	background-color:#fff;
+}
+
+#login-page fieldset p,
+#register-page fieldset p,
+#profile-page fieldset p {
+	font-size: 11px;
+	margin: 10px 16px;
+}
+
+#login-page tr.form-required label:before,
+#register-page tr.form-required label:before,
+#profile-page tr.form-required label:before,
+p.required-message:before {
+	content: ' * ';
+	color: red;
+	vertical-align: 10%;
+}
+
+.form-invalid {
+	background-color: #ffebe8 !important;
+}
+
+.form-invalid input {
+	padding: 1px;
+	border: 1px solid #c00 !important;
+}
+
+.hidden {
+	display: none;
+}
+
+#pass-strength-result {
+	padding: 2px;
+	text-align: center;
+	width: 280px;
+	border: 1px solid #ccc;
+	background-color: #e3e3e3;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
+#pass-strength-result.bad {
+	background-color: #ffeff7;
+	border-color: #c69;
+}
+
+#pass-strength-result.good {
+	background-color: #effff4;
+	border-color: #66cc87;
+}
+
+#pass-strength-result.short {
+	background-color: #e3e3e3;
+}
+
+#pass-strength-result.strong {
+	background-color: #59ef86;
+	border-color: #319f52;
+}
+
+p.submit input {
+	background-color: #ccc;
+	border: 1px solid #999;
+	padding: 2px 4px;
+	line-height: 14px;
+	font-size: 12px;
+	margin: 0;
+	line-height: 15px;
+	-moz-border-radius: 4px;
+	-khtml-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+}
+
+p.submit input:hover {
+	background-color: #ddd;
+	color: rgb(0, 100, 0);
+}
+
+p.submit input:active {
+	border-color: rgb(0, 100, 0);
+}
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tag-single.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tag-single.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tag-single.php	(revision 5057)
@@ -0,0 +1,41 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <a href="<?php bb_tag_page_link(); ?>"><?php _e('Tags'); ?></a> &raquo; <?php bb_tag_name(); ?></div>
+
+<?php do_action('tag_above_table'); ?>
+
+<?php if ( $topics ) : ?>
+
+<table id="latest" role="main">
+<tr>
+	<th><?php _e('Topic'); ?> &#8212; <?php bb_new_topic_link(); ?></th>
+	<th><?php _e('Posts'); ?></th>
+	<!-- <th><?php _e('Voices'); ?></th> -->
+	<th><?php _e('Last Poster'); ?></th>
+	<th><?php _e('Freshness'); ?></th>
+</tr>
+
+<?php foreach ( $topics as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; ?>
+</table>
+
+<p class="rss-link"><a href="<?php bb_tag_posts_rss_link(); ?>" class="rss-link"><?php _e('<abbr title="Really Simple Syndication">RSS</abbr> link for this tag') ?></a></p>
+
+<?php tag_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+
+<?php endif; ?>
+
+<?php post_form(); ?>
+
+<?php do_action('tag_below_table'); ?>
+
+<?php manage_tags_forms(); ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tags.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tags.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/tags.php	(revision 5057)
@@ -0,0 +1,11 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php _e('Tags'); ?></div>
+
+<p role="main"><?php _e('This is a collection of tags that are currently popular on the forums.'); ?></p>
+
+<div id="hottags">
+<?php bb_tag_heat_map( 9, 38, 'pt', 80 ); ?>
+</div>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic-tags.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic-tags.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic-tags.php	(revision 5057)
@@ -0,0 +1,16 @@
+<div id="topic-tags">
+<p><?php _e('Tags:'); ?></p>
+
+<?php if ( bb_get_topic_tags() ) : ?>
+
+<?php bb_list_tags(); ?>
+
+<?php else : ?>
+
+<p><?php printf(__('No <a href="%s">tags</a> yet.'), bb_get_tag_page_link() ); ?></p>
+
+<?php endif; ?>
+
+<?php tag_form(); ?>
+
+</div>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/topic.php	(revision 5057)
@@ -0,0 +1,57 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a><?php bb_forum_bread_crumb(); ?></div>
+<div class="infobox" role="main">
+
+<div id="topic-info">
+<span id="topic_labels"><?php bb_topic_labels(); ?></span>
+<h2<?php topic_class( 'topictitle' ); ?>><?php topic_title(); ?></h2>
+<span id="topic_posts">(<?php topic_posts_link(); ?>)</span>
+<span id="topic_voices">(<?php printf( _n( '%s voice', '%s voices', bb_get_topic_voices() ), bb_get_topic_voices() ); ?>)</span>
+
+<ul class="topicmeta">
+	<li><?php printf(__('Started %1$s ago by %2$s'), get_topic_start_time(), get_topic_author()) ?></li>
+<?php if ( 1 < get_topic_posts() ) : ?>
+	<li><?php printf(__('<a href="%1$s">Latest reply</a> from %2$s'), esc_attr( get_topic_last_post_link() ), get_topic_last_poster()) ?></li>
+<?php endif; ?>
+<?php if ( bb_is_user_logged_in() ) : ?>
+	<li<?php echo $class;?> id="favorite-toggle"><?php user_favorites_link(); ?></li>
+<?php endif; do_action('topicmeta'); ?>
+</ul>
+</div>
+
+<?php topic_tags(); ?>
+
+<div style="clear:both;"></div>
+</div>
+<?php do_action('under_title'); ?>
+<?php if ($posts) : ?>
+<?php topic_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+<div id="ajax-response"></div>
+<ol id="thread" class="list:post">
+
+<?php foreach ($posts as $bb_post) : $del_class = post_del_class(); ?>
+	<li id="post-<?php post_id(); ?>"<?php alt_class('post', $del_class); ?>>
+<?php bb_post_template(); ?>
+	</li>
+<?php endforeach; ?>
+
+</ol>
+<div class="clearit"><br style=" clear: both;" /></div>
+<p class="rss-link"><a href="<?php topic_rss_link(); ?>" class="rss-link"><?php _e('<abbr title="Really Simple Syndication">RSS</abbr> feed for this topic') ?></a></p>
+<?php topic_pages( array( 'before' => '<div class="nav">', 'after' => '</div>' ) ); ?>
+<?php endif; ?>
+<?php if ( topic_is_open( $bb_post->topic_id ) ) : ?>
+<?php post_form(); ?>
+<?php else : ?>
+<h2><?php _e('Topic Closed') ?></h2>
+<p><?php _e('This topic has been closed to new replies.') ?></p>
+<?php endif; ?>
+<?php if ( bb_current_user_can( 'delete_topic', get_topic_id() ) || bb_current_user_can( 'close_topic', get_topic_id() ) || bb_current_user_can( 'stick_topic', get_topic_id() ) || bb_current_user_can( 'move_topic', get_topic_id() ) ) : ?>
+
+<div class="admin">
+<?php bb_topic_admin(); ?>
+</div>
+
+<?php endif; ?>
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/view.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/view.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/kakumei/view.php	(revision 5057)
@@ -0,0 +1,44 @@
+<?php bb_get_header(); ?>
+
+<div class="bbcrumb"><a href="<?php bb_uri(); ?>"><?php bb_option('name'); ?></a> &raquo; <?php view_name(); ?></div>
+
+<?php if ( $topics || $stickies ) : ?>
+
+<table id="latest" role="main">
+<tr>
+	<th><?php _e('Topic'); ?></th>
+	<th><?php _e('Posts'); ?></th>
+	<!-- <th><?php _e('Voices'); ?></th> -->
+	<th><?php _e('Last Poster'); ?></th>
+	<th><?php _e('Freshness'); ?></th>
+</tr>
+
+<?php if ( $stickies ) : foreach ( $stickies as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <big><a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></big></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; ?>
+
+<?php if ( $topics ) : foreach ( $topics as $topic ) : ?>
+<tr<?php topic_class(); ?>>
+	<td><?php bb_topic_labels(); ?> <a href="<?php topic_link(); ?>"><?php topic_title(); ?></a><?php topic_page_links(); ?></td>
+	<td class="num"><?php topic_posts(); ?></td>
+	<!-- <td class="num"><?php bb_topic_voices(); ?></td> -->
+	<td class="num"><?php topic_last_poster(); ?></td>
+	<td class="num"><a href="<?php topic_last_post_link(); ?>" title="<?php topic_time(array('format'=>'datetime')); ?>"><?php topic_time(); ?></a></td>
+</tr>
+<?php endforeach; endif; ?>
+</table>
+
+<p class="rss-link"><a href="<?php bb_view_rss_link(); ?>" class="rss-link"><?php _e('<abbr title="Really Simple Syndication">RSS</abbr> feed for this view'); ?></a></p>
+
+<div class="nav">
+<?php view_pages(); ?>
+</div>
+<?php endif; ?>
+
+<?php bb_get_footer(); ?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/readme.txt
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/readme.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/bb-templates/readme.txt	(revision 5057)
@@ -0,0 +1,3 @@
+This directory is reserved for themes distributed with the bbPress core package.
+
+If you wish to install more themes, you should create a new directory called "my-templates" in the base directory of your bbPress installation and install them there, not here.
Index: /tags/1.5-beta-3/bp-forums/bbpress/edit.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/edit.php	(revision 5057)
@@ -0,0 +1,28 @@
+<?php
+require('./bb-load.php');
+
+bb_auth('logged_in');
+
+$post_id = (int) $_GET['id'];
+
+$bb_post  = bb_get_post( $post_id );
+
+if ( !$bb_post || !bb_current_user_can( 'edit_post', $post_id ) ) {
+	wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+	die();
+}
+
+if ( 0 != $bb_post->post_status && 'all' == $_GET['view'] ) // We're trying to edit a deleted post
+	add_filter('bb_is_first_where', 'bb_no_where');
+
+$topic = get_topic( $bb_post->topic_id );
+
+if ( bb_is_first( $bb_post->post_id ) && bb_current_user_can( 'edit_topic', $topic->topic_id ) ) 
+	$topic_title = $topic->topic_title;
+else 
+	$topic_title = false;
+
+
+bb_load_template( 'edit-post.php', array('topic_title') );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/favorites.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/favorites.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/favorites.php	(revision 5057)
@@ -0,0 +1,48 @@
+<?php
+require_once('./bb-load.php');
+
+if ( isset( $_GET['fav'] ) && isset( $_GET['topic_id'] ) ) {
+	bb_auth( 'logged_in' );
+
+	if ( !bb_current_user_can( 'edit_favorites_of', $user_id ) ) {
+		bb_die( __( 'You cannot edit those favorites. How did you get here?' ) );
+	}
+
+	$fav = (int) $_GET['fav'];
+	$topic_id = (int) $_GET['topic_id'];
+
+	bb_check_admin_referer( 'toggle-favorite_' . $topic_id );
+
+	$topic = get_topic( $topic_id );
+	if ( !$topic || 0 != $topic->topic_status ) {
+		exit;
+	}
+
+	if ( $fav ) {
+		bb_add_user_favorite( $user_id, $topic_id );
+	} else {
+		bb_remove_user_favorite( $user_id, $topic_id );
+	}
+
+	$ref = wp_get_referer();
+	if ( false !== strpos( $ref, bb_get_uri( null, null, BB_URI_CONTEXT_TEXT ) ) ) {
+		bb_safe_redirect( $ref );
+	} else {
+		wp_redirect( get_topic_link( $topic_id ) );
+	}
+	exit;
+}
+
+if ( !bb_is_profile() ) {
+	$sendto = get_profile_tab_link( $user->ID, 'favorites' );
+	wp_redirect( $sendto );
+	exit;
+}
+
+if ( $topics = get_user_favorites( $user->ID, true ) ) {
+	bb_cache_last_posts( $topics );
+}
+
+$favorites_total = isset( $user->favorites ) ? count( explode( ',', $user->favorites ) ) : 0;
+
+bb_load_template( 'favorites.php', array( 'favorites_total', 'self' ) );
Index: /tags/1.5-beta-3/bp-forums/bbpress/forum.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/forum.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/forum.php	(revision 5057)
@@ -0,0 +1,26 @@
+<?php
+
+require_once('./bb-load.php');
+
+$forum_id = 0;
+
+bb_repermalink();
+
+if ( !$forum )
+	bb_die(__('Forum not found.'));
+
+$bb_db_override = false;
+do_action( 'bb_forum.php_pre_db', $forum_id );
+
+if ( !$bb_db_override ) :
+	if ( $topics = get_latest_topics( $forum_id, $page ) ) {
+		bb_cache_last_posts( $topics );
+	}
+	if ( $stickies = get_sticky_topics( $forum_id, $page ) ) {
+		bb_cache_last_posts( $stickies );
+	}
+endif;
+
+bb_load_template( 'forum.php', array('bb_db_override', 'stickies'), $forum_id );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/index.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/index.php	(revision 5057)
@@ -0,0 +1,24 @@
+<?php
+
+require('./bb-load.php');
+
+bb_repermalink();
+
+$bb_db_override = false;
+do_action( 'bb_index.php_pre_db' );
+
+if ( isset($_GET['new']) && '1' == $_GET['new'] ) :
+	$forums = false;
+elseif ( !$bb_db_override ) :
+	$forums = bb_get_forums(); // Comment to hide forums
+	if ( $topics = get_latest_topics( false, $page ) ) {
+		bb_cache_last_posts( $topics );
+	}
+	if ( $super_stickies = get_sticky_topics() ) {
+		bb_cache_last_posts( $super_stickies );
+	}
+endif;
+
+bb_load_template( 'front-page.php', array('bb_db_override', 'super_stickies') );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/license.txt
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/license.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/license.txt	(revision 5057)
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
Index: /tags/1.5-beta-3/bp-forums/bbpress/page.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/page.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/page.php	(revision 5057)
@@ -0,0 +1,3 @@
+<?php
+include('./index.php');
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/profile-base.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/profile-base.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/profile-base.php	(revision 5057)
@@ -0,0 +1,19 @@
+<?php
+require_once('./bb-load.php');
+
+$user_id = bb_get_current_user_info( 'id' );
+
+if ( !bb_is_profile() ) {
+	$sendto = get_profile_tab_link( $user_id, 'edit' );
+	wp_redirect( $sendto );
+	exit;
+}
+
+do_action($self . '_pre_head');
+
+
+if ( is_callable($self) )
+	bb_load_template( 'profile-base.php', array('self'), $user_id );
+
+exit;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/profile-edit.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/profile-edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/profile-edit.php	(revision 5057)
@@ -0,0 +1,179 @@
+<?php
+require_once( './bb-load.php' );
+
+// Redirect if we require SSL and it isn't
+bb_ssl_redirect();
+
+// Authenticate against the "logged_in" cookie
+bb_auth( 'logged_in' );
+
+// Check that the current user can do this, if not kick them to the front page
+if ( !bb_current_user_can( 'edit_user', $user_id ) ) {
+	$sendto = bb_get_uri( null, null, BB_URI_CONTEXT_HEADER );
+	wp_redirect( $sendto );
+	exit;
+}
+
+// Store the current user id
+$bb_current_id = bb_get_current_user_info( 'id' );
+
+// I don't know how this would ever get triggered
+if ( !bb_is_profile() ) {
+	$sendto = get_profile_tab_link( $bb_current_id, 'edit' );
+	wp_redirect( $sendto );
+	exit;
+}
+
+// Set some low capabilities if the current user has none
+if ( !isset( $user->capabilities ) ) {
+	$user->capabilities = array( 'inactive' => true );
+}
+
+// Store the profile info keys
+$profile_info_keys = bb_get_profile_info_keys( 'profile-edit' );
+
+// Store additional keys if the current user has access to them
+if ( bb_current_user_can('edit_users') ) {
+	$profile_admin_keys = bb_get_profile_admin_keys( 'profile-edit' );
+	$assignable_caps = bb_get_assignable_caps();
+}
+
+// Instantiate the error object
+$errors = new WP_Error;
+
+if ( 'post' == strtolower($_SERVER['REQUEST_METHOD']) ) {
+	$_POST = stripslashes_deep( $_POST );
+	bb_check_admin_referer( 'edit-profile_' . $user_id );
+
+	// Fix the URL before sanitizing it
+	$user_url = bb_fix_link( $_POST['user_url'] );
+
+	// Sanitize the profile info keys and check for missing required data
+	foreach ( $profile_info_keys as $key => $label ) {
+		$$key = apply_filters( 'sanitize_profile_info', $_POST[$key], $key, $_POST[$key] );
+		if ( !$$key && $label[0] == 1 ) {
+			$errors->add( $key, sprintf( __( '%s is required.' ), esc_html( $label[1] ) ) );
+			$$key = false;
+		}
+	}
+
+	// Find out if we have a valid email address
+	if ( isset( $user_email ) && !$user_email = is_email( $user_email ) ) {
+		$errors->add( 'user_email', __( 'Invalid email address' ), array( 'data' => $_POST['user_email'] ) );
+	}
+
+	// Deal with errors for users who can edit others data
+	if ( bb_current_user_can('edit_users') ) {
+		// Get the user object
+		$user_obj = new BP_User( $user->ID );
+		
+		// If we are deleting just do it and redirect
+		if ( isset( $_POST['delete-user'] ) && $_POST['delete-user'] && $bb_current_id != $user->ID ) {
+			if ( !bb_current_user_can( 'keep_gate' ) && 'keymaster' == $user_obj->roles[0] ) { /* Only a keymaster can delete another keymaster */
+				$errors->add( 'delete', __( 'You can not delete this user!' ) );
+			} else {
+				bb_delete_user( $user->ID );
+				wp_redirect( bb_get_uri(null, null, BB_URI_CONTEXT_HEADER) );
+				exit;
+			}
+		}
+
+		// Store the new role
+		$role = $_POST['role'];
+
+		// Deal with errors with the role
+		if ( !isset($wp_roles->role_objects[$role]) ) {
+			$errors->add( 'role', __( 'Invalid Role' ) );
+		} elseif ( !bb_current_user_can( 'keep_gate' ) && ( 'keymaster' == $role || 'keymaster' == $user_obj->roles[0] ) ) {
+			$errors->add( 'role', __( 'You are not the Gate Keeper.' ) );
+		} elseif ( 'keymaster' == $user_obj->roles[0] && 'keymaster' != $role && $bb_current_id == $user->ID ) {
+			$errors->add( 'role', __( 'You are Keymaster, so you may not demote yourself.' ) );
+		}
+
+		// Sanitize the profile admin keys and check for missing required data
+		foreach ( $profile_admin_keys as $key => $label ) {
+			if ( isset( $$key ) )
+				continue;
+
+			$$key = apply_filters( 'sanitize_profile_admin', $_POST[$key], $key, $_POST[$key] );
+			if ( !$$key && $label[0] == 1 ) {
+				$errors->add( $key, sprintf( __( '%s is required.' ), esc_html( $label[1] ) ) );
+				$$key = false;
+			}
+		}
+
+		// Create variable for the requested roles
+		foreach ( $assignable_caps as $cap => $label ) {
+			if ( isset($$cap) )
+				continue;
+
+			$$cap = ( isset($_POST[$cap]) && $_POST[$cap] ) ? 1 : 0;
+		}
+	}
+
+	// Deal with errors generated from the password form
+	if ( bb_current_user_can( 'change_user_password', $user->ID ) ) {
+		if ( ( !empty($_POST['pass1']) || !empty($_POST['pass2']) ) && $_POST['pass1'] !== $_POST['pass2'] ) {
+			$errors->add( 'pass', __( 'You must enter the same password twice.' ) );
+		} elseif( !empty($_POST['pass1']) && !bb_current_user_can( 'change_user_password', $user->ID ) ) {
+			$errors->add( 'pass', __( "You are not allowed to change this user's password." ) );
+		}
+	}
+
+	// If there are no errors then update the records
+	if ( !$errors->get_error_codes() ) {
+		do_action('before_profile_edited', $user->ID);
+		
+		if ( bb_current_user_can( 'edit_user', $user->ID ) ) {
+			// All these are always set at this point
+			bb_update_user( $user->ID, $user_email, $user_url, $display_name );
+
+			// Add user meta data
+			foreach( $profile_info_keys as $key => $label ) {
+				if ( 'display_name' == $key || 'ID' == $key || strpos($key, 'user_') === 0 )
+					continue;
+				if ( $$key != '' || isset($user->$key) )
+					bb_update_usermeta( $user->ID, $key, $$key );
+			}
+		}
+
+		if ( bb_current_user_can( 'edit_users' ) ) {
+			if ( !array_key_exists($role, $user->capabilities) ) {
+				$user_obj->set_role($role); // Only support one role for now
+				if ( 'blocked' == $role && 'blocked' != $old_role )
+					bb_break_password( $user->ID );
+				elseif ( 'blocked' != $role && array_key_exists( 'blocked', $user->capabilities ) )
+					bb_fix_password( $user->ID );
+			}
+			foreach( $profile_admin_keys as $key => $label )
+				if ( $$key != ''  || isset($user->$key) )
+					bb_update_usermeta( $user->ID, $key, $$key );
+			foreach( $assignable_caps as $cap => $label ) {
+				if ( ( !$already = array_key_exists($cap, $user->capabilities) ) && $$cap) {
+					$user_obj->add_cap($cap);
+				} elseif ( !$$cap && $already ) {
+					$user_obj->remove_cap($cap);
+				}
+			}
+		}
+
+		if ( bb_current_user_can( 'change_user_password', $user->ID ) && !empty($_POST['pass1']) ) {
+			$_POST['pass1'] = addslashes($_POST['pass1']);
+			bb_update_user_password( $user->ID, $_POST['pass1'] );
+
+			if ( bb_get_current_user_info( 'ID' ) == $user->ID ) {
+				bb_clear_auth_cookie();
+				bb_set_auth_cookie( $user->ID );
+			}
+		}
+		
+		do_action('profile_edited', $user->ID);
+
+		wp_redirect( add_query_arg( 'updated', 'true', get_user_profile_link( $user->ID ) ) );
+		exit;
+	}
+}
+
+bb_load_template( 'profile-edit.php', array('profile_info_keys', 'profile_admin_keys', 'assignable_caps', 'user_email', 'bb_roles', 'errors', 'self') );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/profile.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/profile.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/profile.php	(revision 5057)
@@ -0,0 +1,31 @@
+<?php
+require_once('./bb-load.php');
+
+bb_repermalink(); // The magic happens here.
+
+if ( $self ) {
+	if ( strpos($self, '.php') !== false ) {
+		require($self);
+	} else {
+		require( BB_PATH . 'profile-base.php' );
+	}
+	return;
+}
+
+$reg_time = bb_gmtstrtotime( $user->user_registered );
+$profile_info_keys = bb_get_profile_info_keys();
+
+if ( !isset( $_GET['updated'] ) )
+	$updated = false;
+else
+	$updated = true;
+
+do_action( 'bb_profile.php_pre_db', $user_id );
+
+if ( isset($user->is_bozo) && $user->is_bozo && $user->ID != bb_get_current_user_info( 'id' ) && !bb_current_user_can( 'moderate' ) )
+	$profile_info_keys = array();
+
+$posts = bb_get_recent_user_replies( $user_id );
+$topics = get_recent_user_threads( $user_id );
+
+bb_load_template( 'profile.php', array('reg_time', 'profile_info_keys', 'updated', 'threads'), $user_id );
Index: /tags/1.5-beta-3/bp-forums/bbpress/readme.txt
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/readme.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/readme.txt	(revision 5057)
@@ -0,0 +1,22 @@
+bbPress is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+bbPress is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License version 2 for more
+details.
+
+You should have received a copy of the GNU General Public License in a file
+named "license.txt" along with this program; if not, you may request a copy by
+writing to:
+
+	Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor
+	Boston, MA
+	02110-1301 USA
+
+The license is also available for viewing on the world wide web at:
+
+	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
Index: /tags/1.5-beta-3/bp-forums/bbpress/register.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/register.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/register.php	(revision 5057)
@@ -0,0 +1,72 @@
+<?php
+require('./bb-load.php');
+
+bb_ssl_redirect();
+
+$profile_info_keys = bb_get_profile_info_keys();
+
+unset($profile_info_keys['first_name']);
+unset($profile_info_keys['last_name']);
+unset($profile_info_keys['display_name']);
+
+$user_login = '';
+$user_safe = true;
+
+$bb_register_error = new WP_Error;
+
+$_globals = array('profile_info_keys', 'user_safe', 'user_login', 'user_email', 'user_url', 'bad_input', 'bb_register_error');
+$_globals = array_merge($_globals, array_keys($profile_info_keys));
+
+if ( $_POST && 'post' == strtolower($_SERVER['REQUEST_METHOD']) ) {
+	$_POST = stripslashes_deep( $_POST );
+	$_POST['user_login'] = trim( $_POST['user_login'] );
+	$user_login = sanitize_user( $_POST['user_login'], true );
+	if ( $user_login !== $_POST['user_login'] ) {
+		$bad_input = true;
+		if ( $user_login ) {
+			$bb_register_error->add( 'user_login', sprintf( __( '%s is an invalid username. How\'s this one?' ), esc_html( $_POST['user_login'] ) ) );
+		} else {
+			$bb_register_error->add( 'user_login', sprintf( __( '%s is an invalid username.' ), esc_html( $_POST['user_login'] ) ) );
+		}
+	}
+
+	foreach ( $profile_info_keys as $key => $label ) {
+		if ( is_string($$key) )
+			$$key = esc_attr( $$key );
+		elseif ( is_null($$key) )
+			$$key = esc_attr( $_POST[$key] );
+
+		if ( !$$key && $label[0] == 1 ) {
+			$bad_input = true;
+			$$key = false;
+			$bb_register_error->add( $key, sprintf( __( '%s is required' ), $label[1] ) );
+		}
+	}
+
+	if ( !$bad_input ) {
+		$user_id = bb_new_user( $user_login, $_POST['user_email'], $_POST['user_url'] );
+		if ( is_wp_error( $user_id ) ) { // error
+			foreach ( $user_id->get_error_codes() as $code )
+				$bb_register_error->add( $code, $user_id->get_error_message( $code ) );
+			if ( $bb_register_error->get_error_message( 'user_login' ) )
+				$user_safe = false;
+		} elseif ( $user_id ) { // success
+			foreach( $profile_info_keys as $key => $label )
+				if ( strpos($key, 'user_') !== 0 && $$key !== '' )
+					bb_update_usermeta( $user_id, $key, $$key );
+			do_action('register_user', $user_id);
+
+			bb_load_template( 'register-success.php', $_globals );
+			exit;	
+		} // else failure
+	}
+}
+
+if ( isset( $_GET['user'] ) )
+	$user_login = sanitize_user( $_GET['user'], true ) ;
+elseif ( isset( $_POST['user_login'] ) && !is_string($user_login) )
+	$user_login = '';
+
+bb_load_template( 'register.php', $_globals );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/rss.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/rss.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/rss.php	(revision 5057)
@@ -0,0 +1,199 @@
+<?php
+require('./bb-load.php');
+
+// Determine the type of feed and the id of the object
+if ( isset($_GET['view']) || bb_get_path() == 'view' ) {
+	
+	// View
+	$feed = 'view';
+	$feed_id = isset($_GET['view']) ? $_GET['view'] : bb_get_path(2);
+	
+} elseif ( isset($_GET['topic']) || bb_get_path() == 'topic' ) {
+	
+	// Topic
+	$feed = 'topic';
+	$topic = get_topic(isset($_GET['topic']) ? $_GET['topic'] : bb_get_path(2));
+	$feed_id = $topic->topic_id;
+	
+} elseif ( isset($_GET['profile']) || bb_get_path() == 'profile' ) {
+	
+	// Profile
+	$feed = 'profile';
+	$feed_id = isset($_GET['profile']) ? $_GET['profile'] : bb_get_path(2);
+	
+} elseif ( isset($_GET['tag']) || bb_get_path() == 'tags' ) {
+	
+	if ( isset($_GET['topics']) || bb_get_path(3) == 'topics' ) {
+		// Tag recent topics
+		$feed = 'tag-topics';
+	} else {
+		// Tag recent posts
+		$feed = 'tag-posts';
+	}
+	$feed_id = isset($_GET['tag']) ? $_GET['tag'] : bb_get_path(2);
+	
+} elseif ( isset($_GET['forum']) || bb_get_path() == 'forum' ) {
+	
+	if ( isset($_GET['topics']) || bb_get_path(3) == 'topics' ) {
+		// Forum recent topics
+		$feed = 'forum-topics';
+	} else {
+		// Forum recent posts
+		$feed = 'forum-posts';
+	}
+	$forum = bb_get_forum(isset($_GET['forum']) ? $_GET['forum'] : bb_get_path(2));
+	$feed_id = $forum->forum_id;
+	
+} elseif ( isset($_GET['topics']) || bb_get_path() == 'topics' ) {
+	
+	// Recent topics
+	$feed = 'all-topics';
+	
+} else {
+	
+	// Recent posts
+	$feed = 'all-posts';
+	
+}
+
+// Initialise the override variable
+$bb_db_override = false;
+do_action( 'bb_rss.php_pre_db' );
+
+if ( !$bb_db_override ) {
+	
+	// Get the posts and the title for the given feed
+	switch ($feed) {
+		case 'view':
+			if ( !isset($bb_views[$feed_id]) )
+				die();
+			if ( !$bb_views[$feed_id]['feed'] )
+				die();
+			if ( !$topics_object = new BB_Query( 'topic', $bb_views[$feed_id]['query'], "bb_view_$feed_id" ) )
+				die();
+			
+			$topics = $topics_object->results;
+			
+			$posts = array();
+			foreach ( (array) $topics as $topic ) {
+				$posts[] = bb_get_first_post($topic->topic_id);
+			}
+			
+			$title = sprintf( __( '%1$s &raquo; View: %2$s' ), bb_get_option( 'name' ), $bb_views[$feed_id]['title'] );
+			$link = get_view_link($feed_id);
+			$link_self = bb_get_view_rss_link($feed_id);
+			break;
+		
+		case 'topic':
+			if ( !$topic = get_topic ( $feed_id ) )
+				die();
+			if ( !$posts = get_thread( $feed_id, 0, 1 ) )
+				die(); /* Should die here, as the topic posts aren't there, so the topic is most probably deleted/empty */
+			$title = sprintf( __( '%1$s &raquo; Topic: %2$s' ), bb_get_option( 'name' ), get_topic_title() );
+			$link = get_topic_link($feed_id);
+			$link_self = get_topic_rss_link($feed_id);
+			break;
+		
+		case 'profile':
+			if ( bb_get_option( 'mod_rewrite' ) === 'slugs') {
+				if ( !$user = bb_get_user_by_nicename( $feed_id ) )
+					$user = bb_get_user( $feed_id );
+			} else {
+ 	                        if ( !$user = bb_get_user( $feed_id ) )
+				        $user = bb_get_user_by_nicename( $feed_id ); 
+			}
+			if ( !$user ) {
+				die();
+			}
+			$posts = get_user_favorites( $user->ID );
+			
+			$title = sprintf( __( '%1$s &raquo; User Favorites: %2$s' ), bb_get_option( 'name' ), $user->user_nicename );
+			$link = get_user_profile_link($feed_id);
+			$link_self = get_favorites_rss_link($feed_id);
+			break;
+		
+		case 'tag-topics':
+			if ( !$tag = bb_get_tag( $feed_id ) )
+				die();
+			$topics = get_tagged_topics( array( 'tag_id' => $tag->tag_id, 'page' => 0 ) );
+			
+			$posts = array();
+			foreach ( (array) $topics as $topic ) {
+				$posts[] = bb_get_first_post($topic->topic_id);
+			}
+			
+			$title = sprintf( __( '%1$s &raquo; Tag: %2$s - Recent Topics' ), bb_get_option( 'name' ), bb_get_tag_name() );
+			$link = bb_get_tag_link($feed_id);
+			$link_self = bb_get_tag_topics_rss_link($feed_id);
+			break;
+		
+		case 'tag-posts':
+			if ( !$tag = bb_get_tag( $feed_id ) )
+				die();
+			$posts = get_tagged_topic_posts( array( 'tag_id' => $tag->tag_id, 'page' => 0 ) );
+			$title = sprintf( __( '%1$s &raquo; Tag: %2$s - Recent Posts' ), bb_get_option( 'name' ), bb_get_tag_name() );
+			$link = bb_get_tag_link($feed_id);
+			$link_self = bb_get_tag_posts_rss_link($feed_id);
+			break;
+		
+		case 'forum-topics':
+			$topics = get_latest_topics( $feed_id );
+			
+			$posts = array();
+			foreach ( (array) $topics as $topic) {
+				$posts[] = bb_get_first_post($topic->topic_id);
+			}
+			
+			$title = sprintf( __( '%1$s &raquo; Forum: %2$s - Recent Topics' ), bb_get_option( 'name' ), get_forum_name( $feed_id ) );
+			$link = get_forum_link($feed_id);
+			$link_self = bb_get_forum_topics_rss_link($feed_id);
+			break;
+		
+		case 'forum-posts':
+			$posts = bb_get_latest_forum_posts( $feed_id );
+			$title = sprintf( __( '%1$s &raquo; Forum: %2$s - Recent Posts' ), bb_get_option( 'name' ), get_forum_name( $feed_id ) );
+			$link = get_forum_link($feed_id);
+			$link_self = bb_get_forum_posts_rss_link($feed_id);
+			break;
+		
+		// Get just the first post from the latest topics
+		case 'all-topics':
+			$topics = get_latest_topics();
+			
+			$posts = array();
+			foreach ( (array) $topics as $topic ) {
+				$posts[] = bb_get_first_post($topic->topic_id);
+			}
+			
+			$title = sprintf( __( '%1$s &raquo; Recent Topics' ), bb_get_option( 'name' ) );
+			$link = bb_get_uri();
+			$link_self = bb_get_topics_rss_link();
+			break;
+		
+		// Get latest posts by default
+		case 'all-posts':
+		default:
+			$posts = bb_get_latest_posts( 35 );
+			$title = sprintf( __( '%1$s &raquo; Recent Posts' ), bb_get_option( 'name' ) );
+			$link = bb_get_uri();
+			$link_self = bb_get_posts_rss_link();
+			break;
+	}
+}
+
+if ( !$posts ) /* We do typecasting in the template, but all themes don't have that! */
+	$posts = array();
+else /* Only send 304 if there are posts */
+	bb_send_304( $posts[0]->post_time );
+
+if (!$description = bb_get_option( 'description' ) ) {
+	$description = $title;
+}
+$title = apply_filters( 'bb_title_rss', $title, $feed );
+$description = apply_filters( 'bb_description_rss', $description, $feed );
+$posts = apply_filters( 'bb_posts_rss', $posts, $feed );
+$link_self = apply_filters( 'bb_link_self_rss', $link_self, $feed );
+
+bb_load_template( 'rss2.php', array('bb_db_override', 'title', 'description', 'link', 'link_self'), $feed );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/search.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/search.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/search.php	(revision 5057)
@@ -0,0 +1,43 @@
+<?php
+require_once( './bb-load.php' );
+
+if ( !$q = trim( @$_GET['search'] ) )
+	$q = trim( @$_GET['q'] );
+
+$bb_query_form = new BB_Query_Form;
+
+if ( $q = stripslashes( $q ) ) {
+	$per_page = ceil( (int) bb_get_option( 'page_topics' ) / 2 );
+	
+	/* Recent */
+	add_filter( 'bb_recent_search_fields',   create_function( '$f', 'return $f . ", MAX(post_time) AS post_time";' ) );
+	add_filter( 'bb_recent_search_group_by', create_function( '', 'return "t.topic_id";' ) );
+	$bb_query_form->BB_Query_Form( 'post', array(), array( 'order_by' => 'p.post_time', 'count' => true, 'per_page' => $per_page, 'post_status' => 0, 'topic_status' => 0, 'post_text' => $q, 'forum_id', 'tag', 'topic_author', 'post_author' ), 'bb_recent_search' );
+	$recent = $bb_query_form->results;
+	$recent_count = $recent ? $bb_query_form->found_rows : 0;
+	
+	/* Relevant */
+	$bb_query_form->BB_Query_Form( 'topic', array( 'search' => $q ), array( 'per_page' => $per_page, 'count' => true, 'post_status' => 0, 'topic_status' => 0, 'search', 'forum_id', 'tag', 'topic_author', 'post_author' ), 'bb_relevant_search' );
+	$relevant = $bb_query_form->results;
+	$relevant_count = $relevant ? $bb_query_form->found_rows : 0;
+	
+	$search_count = max( $recent_count, $relevant_count );
+	
+	$q = $bb_query_form->get( 'search' );
+}
+
+do_action( 'do_search', $q );
+
+// Cache topics
+// NOT bbdb::prepared
+if ( $recent ) :
+	$topic_ids = array();
+	foreach ($recent as $bb_post) {
+		$topic_ids[] = (int) $bb_post->topic_id;
+	}
+	$topic_ids = join($topic_ids);
+	if ( $topics = $bbdb->get_results("SELECT * FROM $bbdb->topics WHERE topic_id IN ($topic_ids)") )
+		$topics = bb_append_meta( $topics, 'topic' );
+endif;
+
+bb_load_template( 'search.php', array( 'q', 'recent', 'relevant', 'per_page', 'search_count' ), $q );
Index: /tags/1.5-beta-3/bp-forums/bbpress/statistics.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/statistics.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/statistics.php	(revision 5057)
@@ -0,0 +1,13 @@
+<?php
+
+require('./bb-load.php');
+
+require_once( BB_PATH . BB_INC . 'functions.bb-statistics.php' );
+
+$popular = get_popular_topics();
+
+$bb->static_title = __('Statistics');
+
+bb_load_template( 'stats.php', array('popular') );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/tag-add.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/tag-add.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/tag-add.php	(revision 5057)
@@ -0,0 +1,27 @@
+<?php
+
+require( './bb-load.php' );
+
+bb_auth( 'logged_in' );
+
+if ( !bb_is_user_logged_in() )
+	bb_die( __( 'You need to be logged in to add a tag.' ) );
+
+$topic_id = (int) @$_POST['id' ];
+$page     = (int) @$_POST['page'];
+$tag      =       @$_POST['tag'];
+$tag      =       stripslashes( $tag );
+
+bb_check_admin_referer( 'add-tag_' . $topic_id );
+
+if ( !$topic = get_topic ( $topic_id ) )
+	bb_die( __( 'Topic not found.' ) );
+
+if ( bb_add_topic_tags( $topic_id, $tag ) )
+	wp_redirect( get_topic_link( $topic_id, $page ) );
+else
+	bb_die( __( 'The tag was not added.  Either the tag name was invalid or the topic is closed.' ) );
+
+exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/tag-remove.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/tag-remove.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/tag-remove.php	(revision 5057)
@@ -0,0 +1,30 @@
+<?php
+
+require( './bb-load.php' );
+
+bb_auth( 'logged_in' );
+
+$tag_id   = (int) @$_GET['tag'];
+$user_id  = (int) @$_GET['user'];
+$topic_id = (int) @$_GET['topic'];
+
+bb_check_admin_referer( 'remove-tag_' . $tag_id . '|' . $topic_id );
+
+$tag   = bb_get_tag ( $tag_id );
+$topic = get_topic  ( $topic_id );
+$user  = bb_get_user( $user_id );
+
+if ( !$tag || !$topic )
+	bb_die( __( 'Invalid tag or topic.' ) );
+
+if ( false !== bb_remove_topic_tag( $tag_id, $user_id, $topic_id ) ) {
+	if ( !$redirect = wp_get_referer() )
+		$redirect = get_topic_link( $topic_id );
+	bb_safe_redirect( $redirect );
+} else {
+	bb_die( __( 'The tag was not removed.' ) );
+}
+
+exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/tags.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/tags.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/tags.php	(revision 5057)
@@ -0,0 +1,17 @@
+<?php
+require_once('./bb-load.php');
+
+bb_repermalink();
+
+if ( $tag_name && $tag ) :
+
+	if ( $topics = get_tagged_topics($tag->tag_id, $page) ) {
+		bb_cache_last_posts( $topics );
+	}
+
+	bb_load_template( 'tag-single.php', array('tag', 'tag_name', 'topics'), $tag->tag_id );
+else :
+
+	bb_load_template( 'tags.php' );
+endif;
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/topic.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/topic.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/topic.php	(revision 5057)
@@ -0,0 +1,49 @@
+<?php
+require_once('./bb-load.php');
+$topic_id = 0;
+
+$view_deleted = false;
+if ( bb_current_user_can('browse_deleted') && 'all' == @$_GET['view'] ) {
+	add_filter('get_topic_where', 'bb_no_where');
+	$view_deleted = true;
+}
+
+bb_repermalink();
+
+if ( !$topic )
+	bb_die(__('Topic not found.'));
+
+if ( $view_deleted ) {
+	add_filter('get_thread_where', create_function('', 'return "p.topic_id = ' . $topic_id . '";'));
+	add_filter('get_thread_post_ids', create_function('', 'return "p.topic_id = ' . $topic_id . '";'));
+	add_filter('post_edit_uri', 'bb_make_link_view_all');
+}
+
+$bb_db_override = false;
+do_action( 'bb_topic.php_pre_db', $topic_id );
+
+if ( !$bb_db_override ) :
+	$posts = get_thread( $topic_id, $page );
+	$forum = bb_get_forum ( $topic->forum_id );
+
+	$tags  = bb_get_topic_tags ( $topic_id );
+	if ( $tags && $bb_current_id = bb_get_current_user_info( 'id' ) ) {
+		$user_tags  = bb_get_user_tags   ( $topic_id, $bb_current_id );
+		$other_tags = bb_get_other_tags  ( $topic_id, $bb_current_id );
+		$public_tags = bb_get_public_tags( $topic_id );
+	} elseif ( is_array($tags) ) {
+		$user_tags  = false;
+		$other_tags = bb_get_public_tags( $topic_id );
+		$public_tags =& $other_tags;
+	} else {
+		$user_tags = $other_tags = $public_tags = false;
+	}
+
+	$list_start = ($page - 1) * bb_get_option('page_topics') + 1;
+
+	bb_post_author_cache($posts);
+endif;
+
+bb_load_template( 'topic.php', array('bb_db_override', 'user_tags', 'other_tags', 'list_start'), $topic_id );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/view.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/view.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/view.php	(revision 5057)
@@ -0,0 +1,28 @@
+<?php
+require_once('./bb-load.php');
+
+bb_repermalink();
+
+$view = bb_slug_sanitize($view);
+
+$sticky_count = $topic_count = 0;
+$stickies = $topics = $view_count = false;
+
+if ( isset($bb_views[$view]) ) {
+	if ( $bb_views[$view]['sticky'] ) {
+		$sticky_query = bb_view_query( $view, array('sticky' => '-no') ); // -no = yes
+		$stickies     = $sticky_query->results;
+		$sticky_count = $sticky_query->found_rows;
+	}
+	$topic_query = bb_view_query( $view, array('count' => true) );
+	$topics      = $topic_query->results;
+	$topic_count = $topic_query->found_rows;
+
+	$view_count = max($sticky_count, $topic_count);
+}
+
+do_action( 'bb_custom_view', $view, $page );
+
+bb_load_template( 'view.php', array('view_count', 'stickies'), $view );
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bbpress/xmlrpc.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bbpress/xmlrpc.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bbpress/xmlrpc.php	(revision 5057)
@@ -0,0 +1,4087 @@
+<?php
+/**
+ * XML-RPC protocol support for bbPress
+ *
+ * @since 1.0
+ * @package bbPress
+ */
+
+
+
+/**
+ * Whether this is an XML-RPC Request
+ *
+ * @since 1.0
+ * @var bool
+ */
+define( 'XMLRPC_REQUEST', true );
+
+// Get rid of cookies sent by some browser-embedded clients
+$_COOKIE = array();
+
+// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default
+if ( !isset( $HTTP_RAW_POST_DATA ) ) {
+	$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
+}
+
+// Fix for mozBlog and other cases where '<?xml' isn't on the very first line
+if ( isset( $HTTP_RAW_POST_DATA ) ) {
+	$HTTP_RAW_POST_DATA = trim( $HTTP_RAW_POST_DATA );
+}
+
+// Load bbPress
+require_once( './bb-load.php' );
+
+
+
+// If the service discovery data is requested then return it and exit
+if ( isset( $_GET['rsd'] ) ) {
+	header( 'Content-Type: text/xml; charset=UTF-8', true );
+	echo '<?xml version="1.0" encoding="UTF-8"?'.'>' . "\n";
+	echo '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">' . "\n";
+	echo '	<service>' . "\n";
+	echo '		<engineName>bbPress</engineName>' . "\n";
+	echo '		<engineLink>http://bbpress.org/</engineLink>' . "\n";
+	echo '		<homePageLink>' . bb_get_uri() . '</homePageLink>' . "\n";
+	echo '		<apis>' . "\n";
+	echo '			<api name="bbPress" blogID="1" preferred="true" apiLink="' . bb_get_uri( 'xmlrpc.php' ) . '" />' . "\n";
+	echo '		</apis>' . "\n";
+	echo '	</service>' . "\n";
+	echo '</rsd>' . "\n";
+	exit;
+}
+
+
+
+// Load the XML-RPC server/client classes
+require_once( BACKPRESS_PATH . '/class.ixr.php' );
+
+
+
+/**
+ * XML-RPC server class to allow for remote publishing
+ *
+ * @since 1.0
+ * @package bbPress
+ * @subpackage Publishing
+ * @uses class IXR_Server
+ */
+class BB_XMLRPC_Server extends IXR_Server
+{
+	/**
+	 * Stores the last error generated by the class
+	 *
+	 * @since 1.0
+	 * @var object|boolean An instance of the IXR_Error class or false if no error exists
+	 */
+	var $error = false;
+
+	/**
+	 * Site options which can be manipulated using XML-RPC
+	 *
+	 * @since 1.0
+	 * @var array
+	 */
+	var $site_options = array();
+
+	/**
+	 * Whether read-only methods require authentication
+	 *
+	 * @since 1.0
+	 * @var boolean
+	 **/
+	var $auth_readonly = false;
+
+	/**
+	 * Whether user switching is allowed
+	 *
+	 * @since 1.0
+	 * @var boolean
+	 **/
+	var $allow_user_switching = false;
+
+	/**
+	 * Initialises the XML-RPC server
+	 *
+	 * @since 1.0
+	 * @return void
+	 */
+	function BB_XMLRPC_Server()
+	{
+		// bbPress publishing API
+		if ( bb_get_option( 'enable_xmlrpc' ) ) {
+			$this->methods = array(
+				// - Demo
+				'demo.sayHello'         => 'this:sayHello',
+				'demo.addTwoNumbers'    => 'this:addTwoNumbers',
+				// - Forums
+				'bb.getForumCount'      => 'this:bb_getForumCount',
+				'bb.getForums'          => 'this:bb_getForums',
+				'bb.getForum'           => 'this:bb_getForum',
+				'bb.newForum'           => 'this:bb_newForum',
+				'bb.editForum'          => 'this:bb_editForum',
+				'bb.deleteForum'        => 'this:bb_deleteForum',
+				// - Topics
+				'bb.getTopicCount'      => 'this:bb_getTopicCount',
+				'bb.getTopics'          => 'this:bb_getTopics',
+				'bb.getTopic'           => 'this:bb_getTopic',
+				'bb.newTopic'           => 'this:bb_newTopic',
+				'bb.editTopic'          => 'this:bb_editTopic',
+				'bb.deleteTopic'        => 'this:bb_deleteTopic',        // Also undeletes
+				'bb.moveTopic'          => 'this:bb_moveTopic',
+				'bb.stickTopic'         => 'this:bb_stickTopic',         // Also unsticks
+				'bb.closeTopic'         => 'this:bb_closeTopic',         // Also opens
+				'bb.getTopicStatusList' => 'this:bb_getTopicStatusList',
+				// - Posts (replies)
+				'bb.getPostCount'      => 'this:bb_getPostCount',
+				'bb.getPosts'          => 'this:bb_getPosts',
+				'bb.getPost'           => 'this:bb_getPost',
+				'bb.newPost'           => 'this:bb_newPost',
+				'bb.editPost'          => 'this:bb_editPost',
+				'bb.deletePost'        => 'this:bb_deletePost',          // Also undeletes
+				'bb.getPostStatusList' => 'this:bb_getPostStatusList',
+				// - Topic Tags
+				'bb.getHotTopicTags'   => 'this:bb_getHotTopicTags',
+				'bb.getTopicTagCount'  => 'this:bb_getTopicTagCount',
+				'bb.getTopicTags'      => 'this:bb_getTopicTags',
+				'bb.getTopicTag'       => 'this:bb_getTopicTag',
+				'bb.addTopicTags'      => 'this:bb_addTopicTags',
+				'bb.removeTopicTags'   => 'this:bb_removeTopicTags',
+				'bb.renameTopicTag'    => 'this:bb_renameTopicTag',
+				'bb.mergeTopicTags'    => 'this:bb_mergeTopicTags',
+				'bb.destroyTopicTag'   => 'this:bb_destroyTopicTag',
+				// - Options
+				'bb.getOptions'        => 'this:bb_getOptions',
+				'bb.setOptions'        => 'this:bb_setOptions'
+			);
+		}
+
+		// Pingback
+		if ( bb_get_option( 'enable_pingback' ) ) {
+			$this->methods = array_merge( (array)$this->methods, array(
+				'pingback.ping' => 'this:pingback_ping',
+				'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks'
+			) );
+		}
+
+		// Tells read-only methods whether they require authentication or not
+		$this->auth_readonly = apply_filters( 'bb_xmlrpc_auth_readonly', $this->auth_readonly );
+
+		// Whether or not to allow user switching
+		$this->allow_user_switching = bb_get_option( 'bb_xmlrpc_allow_user_switching' );
+
+		$this->initialise_site_option_info();
+		$this->methods = apply_filters( 'bb_xmlrpc_methods', $this->methods );
+		$this->IXR_Server( $this->methods );
+	}
+
+
+
+	/**
+	 * Utility methods
+	 */
+
+	/**
+	 * Checks the user credentials supplied in the request to make sure they are valid
+	 *
+	 * @since 1.0
+	 * @return integer|boolean The user id if the user is valid, otherwise false
+	 * @param string $user_login The users login
+	 * @param string $user_pass The users password in plain text
+	 * @param string $capability The capability to check (optional)
+	 * @param string $message The message to pass back in the error if the capability check fails (optional)
+	 */
+	function authenticate( $user_login, $user_pass, $capability = 'read', $message = false )
+	{
+		if ( is_array( $user_login ) ) {
+			$auth_user_login = (string) $user_login[0];
+			$switch_user_login = (string) $user_login[1];
+		} else {
+			$auth_user_login = (string) $user_login;
+			$switch_user_login = false;
+		}
+		
+		// Check the login
+		$user = bb_check_login( $auth_user_login, $user_pass );
+		if ( !$user || is_wp_error( $user ) ) {
+			$this->error = new IXR_Error( 403, __( 'Authentication failed.' ) );
+			return false;
+		}
+
+		// Set the current user
+		$user = bb_set_current_user( $user->ID );
+
+		// Make sure they are allowed to do this
+		if ( !bb_current_user_can( $capability ) ) {
+			if ( !$message ) {
+				$message = __( 'You do not have permission to read this.' );
+			}
+			$this->error = new IXR_Error( 403, $message );
+			return false;
+		}
+
+		// Switch the user if requested and allowed
+		if ( $switch_user_login && $this->allow_user_switching && bb_current_user_can( 'edit_users' ) ) {
+			$user = $this->switch_user( $switch_user_login, $capability, $message );
+		}
+
+		return $user;
+	}
+
+	/**
+	 * Switches the currently active user for incognito actions
+	 *
+	 * @since 1.0
+	 * @return integer|boolean The user id if the user is valid, otherwise false
+	 * @param string $user_login The users login
+	 * @param string $capability The capability to check (optional)
+	 * @param string $message The message to pass back in the error if the capability check fails (optional)
+	 */
+	function switch_user( $user_login, $capability = 'read', $message = false )
+	{
+		// Just get the user, authentication has already been established by the 
+		$user = bb_get_user( $user_login, array( 'by' => 'login' ) );
+		if ( !$user || is_wp_error( $user ) ) {
+			$this->error = new IXR_Error( 400, __( 'User switching failed, the requested user does not exist.' ) );
+			return false;
+		}
+
+		// Set the current user
+		$user = bb_set_current_user( $user->ID );
+
+		// Make sure they are allowed to do this
+		if ( !bb_current_user_can( $capability ) ) {
+			if ( !$message ) {
+				$message = __( 'You do not have permission to read this.' );
+			}
+			$this->error = new IXR_Error( 403, $message );
+			return false;
+		}
+
+		return $user;
+	}
+
+	/**
+	 * Sanitises data from XML-RPC request parameters
+	 *
+	 * @since 1.0
+	 * @return mixed The sanitised variable, should come back with the same type
+	 * @param $array mixed The variable to be sanitised
+	 * @uses $bbdb BackPress database class instance
+	 */
+	function escape( &$array )
+	{
+		global $bbdb;
+
+		if ( !is_array( $array ) ) {
+			// Escape it
+			$array = $bbdb->escape( $array );
+		} elseif ( count( $array ) ) {
+			foreach ( (array) $array as $k => $v ) {
+				if ( is_array( $v ) ) {
+					// Recursively sanitize arrays
+					$this->escape( $array[$k] );
+				} elseif ( is_object( $v ) ) {
+					// Don't sanitise objects - shouldn't happen anyway
+				} else {
+					// Escape it
+					$array[$k] = $bbdb->escape( $v );
+				}
+			}
+		}
+
+		return $array;
+	}
+
+	/**
+	 * Prepares forum data for return in an XML-RPC object
+	 *
+	 * @since 1.0
+	 * @return array The prepared forum data
+	 * @param array|object The unprepared forum data
+	 **/
+	function prepare_forum( $forum )
+	{
+		// Cast to an array
+		$_forum = (array) $forum;
+		// Set the URI
+		$_forum['forum_uri'] = get_forum_link( $_forum['forum_id'] );
+		// Give this a definite value
+		if ( !isset( $_forum['forum_is_category'] ) ) {
+			$_forum['forum_is_category'] = 0;
+		}
+		// Allow plugins to modify the data
+		return apply_filters( 'bb_xmlrpc_prepare_forum', $_forum, (array) $forum );
+	}
+
+	/**
+	 * Prepares topic data for return in an XML-RPC object
+	 *
+	 * @since 1.0
+	 * @return array The prepared topic data
+	 * @param array|object The unprepared topic data
+	 **/
+	function prepare_topic( $topic )
+	{
+		// Cast to an array
+		$_topic = (array) $topic;
+		// Set the URI
+		$_topic['topic_uri'] = get_topic_link( $_topic['topic_id'] );
+		// Set readable times
+		$_topic['topic_start_time_since'] = bb_since( $_topic['topic_start_time'] );
+		$_topic['topic_time_since'] = bb_since( $_topic['topic_time'] );
+		// Set the display names
+		$_topic['topic_poster_display_name'] = get_user_display_name( $_topic['topic_poster'] );
+		$_topic['topic_last_poster_display_name'] = get_user_display_name( $_topic['topic_last_poster'] );
+		// Remove some sensitive user ids
+		unset(
+			$_topic['topic_poster'],
+			$_topic['topic_last_poster']
+		);
+		// Allow plugins to modify the data
+		return apply_filters( 'bb_xmlrpc_prepare_topic', $_topic, (array) $topic );
+	}
+
+	/**
+	 * Prepares post data for return in an XML-RPC object
+	 *
+	 * @since 1.0
+	 * @return array The prepared post data
+	 * @param array|object The unprepared post data
+	 **/
+	function prepare_post( $post )
+	{
+		// Cast to an array
+		$_post = (array) $post;
+		// Set the URI
+		$_post['post_uri'] = get_post_link( $_post['post_id'] );
+		// Set readable times
+		$_post['post_time_since'] = bb_since( $_post['post_time'] );
+		// Set the display names
+		$_post['poster_display_name'] = get_user_display_name( $_post['poster_id'] );
+		// Remove some sensitive data
+		unset(
+			$_post['poster_id'],
+			$_post['poster_ip'],
+			$_post['pingback_queued']
+		);
+		// Allow plugins to modify the data
+		return apply_filters( 'bb_xmlrpc_prepare_post', $_post, (array) $post );
+	}
+
+	/**
+	 * Prepares topic tag data for return in an XML-RPC object
+	 *
+	 * @since 1.0
+	 * @return array The prepared topic tag data
+	 * @param array|object The unprepared topic tag data
+	 **/
+	function prepare_topic_tag( $tag )
+	{
+		// Cast to an array
+		$_tag = (array) $tag;
+		// Set the URI
+		$_tag['topic_tag_uri'] = bb_get_tag_link( $tag );
+		// Consistent nomenclature
+		$_tag['topic_tag_name'] = (string) $_tag['name'];
+		$_tag['topic_tag_slug'] = (string) $_tag['slug'];
+		$_tag['topic_tag_count'] = (int) $_tag['count'];
+		// Remove some sensitive data
+		unset(
+			$_tag['object_id'],
+			$_tag['name'],
+			$_tag['slug'],
+			$_tag['count'],
+			$_tag['term_id'],
+			$_tag['term_group'],
+			$_tag['term_taxonomy_id'],
+			$_tag['taxonomy'],
+			$_tag['description'],
+			$_tag['parent'],
+			$_tag['count'],
+			$_tag['user_id'],
+			$_tag['tag_id'],
+			$_tag['tag'],
+			$_tag['raw_tag'],
+			$_tag['tag_count']
+		);
+		// Allow plugins to modify the data
+		return apply_filters( 'bb_xmlrpc_prepare_topic_tag', $_tag, (array) $tag );
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Demo XML-RPC methods
+	 */
+
+	/**
+	 * Hello world demo function for XML-RPC
+	 *
+	 * @since 1.0
+	 * @return string The phrase 'Hello!'
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 *
+	 * XML-RPC request to get a greeting
+	 * <methodCall>
+	 *     <methodName>demo.sayHello</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function sayHello( $args )
+	{
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly && !$this->authenticate( $username, $password ) ) {
+			return $this->error;
+		}
+
+		return 'Hello!';
+	}
+
+	/**
+	 * Adds two numbers together as a demo of XML-RPC
+	 *
+	 * @since 1.0
+	 * @return integer The sum of the two supplied numbers
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer $args[2] The first number to be added
+	 * @param integer $args[3] The second number to be added
+	 *
+	 * XML-RPC request to get the sum of two numbers
+	 * <methodCall>
+	 *     <methodName>demo.addTwoNumbers</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>5</int></value></param>
+	 *         <param><value><int>102</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function addTwoNumbers( $args )
+	{
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly && !$this->authenticate( $username, $password ) ) {
+			return $this->error;
+		}
+
+		$number1 = (int) $args[2];
+		$number2 = (int) $args[3];
+
+		return ( $number1 + $number2 );
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Forum XML-RPC methods
+	 */
+
+	/**
+	 * Returns a numerical count of forums
+	 *
+	 * @since 1.0
+	 * @return integer|object The number of forums when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The parent forum's id or slug (optional)
+	 * @param integer $args[3] The depth of child forums to retrieve (optional)
+	 *
+	 * XML-RPC request to get a count of all forums in the bbPress instance
+	 * <methodCall>
+	 *     <methodName>bb.getForumCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get a count of all child forums in the forum with id number 34
+	 * <methodCall>
+	 *     <methodName>bb.getForumCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get a count of all child forums in the forum with slug "first-forum"
+	 * <methodCall>
+	 *     <methodName>bb.getForumCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>first-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get a count of all child forums in the forum with id number 34 no more than 2 forums deep in the hierarchy
+	 * <methodCall>
+	 *     <methodName>bb.getForumCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *         <param><value><int>2</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getForumCount( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getForumCount' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForumCount' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Setup an array to store arguments to pass to bb_get_forums() function
+		$get_forums_args = array(
+			'child_of' => 0,
+			'hierarchical' => 0,
+			'depth' => 0
+		);
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[2] ) ? $args[2] : false;
+
+		if ( $forum_id ) {
+			// Check for bad data
+			if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+				return $this->error;
+			}
+			// Check the requested forum exists
+			if ( !$forum = bb_get_forum( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
+				return $this->error;
+			}
+			// Add the specific forum to the arguments
+			$get_forums_args['child_of'] = (int) $forum->forum_id;
+		}
+
+		// Can only be an integer
+		$depth = (int) $args[3];
+
+		if ( $depth > 0 ) {
+			// Add the depth to traverse to the arguments
+			$get_forums_args['depth'] = $depth;
+			// Only make it hierarchical if the depth > 1
+			if ( $depth > 1 ) {
+				$get_forums_args['hierarchical'] = 1;
+			}
+		}
+
+		// Get the forums. Return 0 when no forums exist
+		if ( !$forums = bb_get_forums( $get_forums_args ) ) {
+			$count = 0;
+		} else {
+			$count = count( $forums );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getForumCount' );
+
+		// Return a count of the forums
+		return $count;
+	}
+
+	/**
+	 * Returns details of multiple forums
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing details of all returned forums when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The parent forum's id or slug (optional)
+	 * @param integer $args[3] The depth of child forums to retrieve (optional)
+	 *
+	 * XML-RPC request to get all forums in the bbPress instance
+	 * <methodCall>
+	 *     <methodName>bb.getForums</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get all child forums in the forum with id number 34
+	 * <methodCall>
+	 *     <methodName>bb.getForums</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get all child forums in the forum with slug "first-forum"
+	 * <methodCall>
+	 *     <methodName>bb.getForums</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>first-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get all child forums in the forum with id number 34 no more than 2 forums deep in the hierarchy
+	 * <methodCall>
+	 *     <methodName>bb.getForums</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *         <param><value><int>2</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getForums( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getForums' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForums' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Setup an array to store arguments to pass to bb_get_forums() function
+		$get_forums_args = array(
+			'child_of' => 0,
+			'hierarchical' => 0,
+			'depth' => 0
+		);
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[2] ) ? $args[2] : false;
+
+		if ( $forum_id ) {
+			// Check for bad data
+			if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+				return $this->error;
+			}
+			// First check the requested forum exists
+			if ( !$forum = bb_get_forum( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
+				return $this->error;
+			}
+			// Add the specific forum to the arguments
+			$get_forums_args['child_of'] = (int) $forum->forum_id;
+		}
+
+		// Can only be an integer
+		$depth = (int) $args[3];
+
+		if ( $depth > 0 ) {
+			// Add the depth to traverse to to the arguments
+			$get_forums_args['depth'] = $depth;
+			// Only make it hierarchical if the depth > 1
+			if ( $depth > 1 ) {
+				$get_forums_args['hierarchical'] = 1;
+			}
+		}
+
+		// Get the forums. Return an error when no forums exist
+		if ( !$forums = bb_get_forums( $get_forums_args ) ) {
+			$this->error = new IXR_Error( 404, __( 'No forums found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_forums = array();
+		foreach ( $forums as $forum ) {
+			$_forums[] = $this->prepare_forum( $forum );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getForums' );
+
+		// Return the forums
+		return $_forums;
+	}
+
+	/**
+	 * Returns details of a forum
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing details of the returned forum when successfully executed or an IXR_Error object on failure
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The forum's id or slug
+	 *
+	 * XML-RPC request to get the forum with id number 34
+	 * <methodCall>
+	 *     <methodName>bb.getForum</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get the forum with slug "first-forum"
+	 * <methodCall>
+	 *     <methodName>bb.getForum</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>first-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getForum( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getForum' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForum' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested forum exists
+		if ( !$forum = bb_get_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 404, __( 'No forum found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$forum = $this->prepare_forum( $forum );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getForum' );
+
+		// Return the forums
+		return $forum;
+	}
+
+	/**
+	 * Creates a new forum
+	 *
+	 * @since 1.0
+	 * @return array|object The forum data when successfully created or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various settings in the new forum
+	 * @param string $args[2]['name'] The name of the forum
+	 * @param string $args[2]['description'] The description of the forum (optional)
+	 * @param integer|string $args[2]['parent_id'] The unique id of the parent forum for this forum (optional)
+	 * @param integer $args[2]['order'] The position of the forum in the forum list (optional)
+	 * @param integer $args[2]['is_category'] Whether the forum is simply a container category (optional)
+	 *
+	 * XML-RPC request to create a new sub-forum called "A new forum" inside the parent forum with id 2
+	 * <methodCall>
+	 *     <methodName>bb.newForum</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>name</name>
+	 *                 <value><string>A new forum</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>parent_id</name>
+	 *                 <value><integer>2</integer></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_newForum( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.newForum' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_forums', __( 'You do not have permission to manage forums.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.newForum' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Minimum requirement is a name for the new forum
+		if ( !isset( $structure['name'] ) || !$structure['name'] ) {
+			$this->error = new IXR_Error( 400, __( 'The forum name is invalid.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_new_forum()
+		$bb_new_forum_args = array(
+			'forum_name' => (string) $structure['name'],
+			'forum_desc' => (string) $structure['description'],
+			'forum_parent' => (int) $structure['parent_id'],
+			'forum_order' => (int) $structure['order'],
+			'forum_is_category' => (int) $structure['is_category']
+		);
+
+		// Remove empty settings so that changes to the defaults in bb_new_forum() are honoured
+		$bb_new_forum_args = array_filter( $bb_new_forum_args );
+
+		// Leave the require until the very end
+		require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+		// Create the forum
+		if ( !$forum_id = (int) bb_new_forum( $bb_new_forum_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The forum could not be created.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$forum = $this->prepare_forum( bb_get_forum( $forum_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.newForum' );
+
+		return $forum;
+	}
+
+	/**
+	 * Edits an existing forum
+	 *
+	 * @since 1.0
+	 * @return array|object The forum data when successfully edited or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various settings in the new forum, at least one must be specified
+	 * @param integer|string $args[2]['forum_id'] The unique id of the forum to be edited
+	 * @param string $args[2]['name'] The name of the forum (optional)
+	 * @param string $args[2]['slug'] The slug for the forum (optional)
+	 * @param string $args[2]['description'] The description of the forum (optional)
+	 * @param integer $args[2]['parent_id'] The unique id of the parent forum for this forum (optional)
+	 * @param integer $args[2]['order'] The position of the forum in the forum list (optional)
+	 * @param integer $args[2]['is_category'] Whether the forum is simply a container category (optional)
+	 *
+	 * XML-RPC request to edit a forum with id 11, changing the description
+	 * <methodCall>
+	 *     <methodName>bb.editForum</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>forum_id</name>
+	 *                 <value><integer>11</integer></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>description</name>
+	 *                 <value><string>This is a great forum for all sorts of reasons.</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_editForum( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.editForum' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_forums', __( 'You do not have permission to manage forums.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.editForum' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Can be numeric id or slug
+		$forum_id = isset( $structure['forum_id'] ) ? $structure['forum_id'] : false;
+
+		// Check for bad data
+		if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested forum exists
+		if ( !$forum = bb_get_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No forum found.' ) );
+			return $this->error;
+		}
+
+		// Cast the forum object as an array
+		$forum = (array) $forum;
+		// The forum id may have been a slug, so make sure it's an integer here
+		$forum_id = (int) $forum['forum_id'];
+
+		// Remove some unneeded indexes
+		unset( $forum['topics'] );
+		unset( $forum['posts'] );
+
+		// Add one if it isn't there
+		if ( !isset( $forum['forum_is_category'] ) ) {
+			$forum['forum_is_category'] = 0;
+		}
+
+		// Validate the name for the forum
+		if ( isset( $structure['name'] ) && !$structure['name'] ) {
+			$this->error = new IXR_Error( 400, __( 'The forum name is invalid.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_update_forum()
+		$bb_update_forum_args = array(
+			'forum_name' => $structure['name']
+		);
+
+		// Slug cannot be blank
+		if ( isset( $structure['slug'] ) && $structure['slug'] !== '' ) {
+			$bb_update_forum_args['forum_slug'] = $structure['slug'];
+		}
+
+		// Description can be nothing
+		if ( isset( $structure['description'] ) ) {
+			$bb_update_forum_args['forum_desc'] = $structure['description'];
+		}
+
+		// Parent forum ID must be an integer and it can be 0
+		if ( isset( $structure['parent_id'] ) && is_integer( $structure['parent_id'] ) ) {
+			$bb_update_forum_args['forum_parent'] = $structure['parent_id'];
+		}
+
+		// Order must be an integer and it can be 0
+		if ( isset( $structure['order'] ) && is_integer( $structure['order'] ) ) {
+			$bb_update_forum_args['forum_order'] = $structure['order'];
+		}
+
+		// Category flag must be an integer and it can be 0
+		if ( isset( $structure['is_category'] ) && is_integer( $structure['is_category'] ) ) {
+			$bb_update_forum_args['forum_is_category'] = $structure['is_category'];
+		}
+
+		// Merge the changes into the existing data for the forum
+		$bb_update_forum_args = wp_parse_args( $bb_update_forum_args, $forum );
+
+		// Leave the require until the very end
+		require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+		// Update the forum
+		if ( !bb_update_forum( $bb_update_forum_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The forum could not be edited.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$forum = $this->prepare_forum( bb_get_forum( $forum_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.editForum' );
+
+		return $forum;
+	}
+
+	/**
+	 * Deletes a forum
+	 *
+	 * @since 1.0
+	 * @return integer|object 1 when successfully deleted or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The unique id of the forum to be deleted
+	 *
+	 * XML-RPC request to delete a forum with the slug "naughty-forum"
+	 * <methodCall>
+	 *     <methodName>bb.deleteForum</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>naughty-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_deleteForum( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.deleteForum' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'delete_forums', __( 'You do not have permission to delete forums.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.deleteForum' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested forum exists
+		if ( !$forum = bb_get_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No forum found.' ) );
+			return $this->error;
+		}
+
+		// Cast the forum object as an array
+		$forum = (array) $forum;
+		// The forum id may have been a slug, so make sure it's an integer here
+		$forum_id = (int) $forum['forum_id'];
+
+		// Make sure they are allowed to delete this forum specifically
+		if ( !bb_current_user_can( 'delete_forum', $forum_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to delete this forum.' ) );
+			return $this->error;
+		}
+
+		// Leave the require until the very end
+		require_once( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+		// Delete the forum
+		if ( !bb_delete_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'The forum could not be deleted.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.deleteForum' );
+
+		return $result;
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Topic XML-RPC methods
+	 */
+
+	/**
+	 * Returns a numerical count of topics
+	 *
+	 * @since 1.0
+	 * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The forum id or slug (optional)
+	 *
+	 * XML-RPC request to get a count of all topics in the bbPress instance
+	 * <methodCall>
+	 *     <methodName>bb.getTopicCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get a count of all topics in the forum with id number 34
+	 * <methodCall>
+	 *     <methodName>bb.getTopicCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get a count of all topics in the forum with slug "first-forum"
+	 * <methodCall>
+	 *     <methodName>bb.getTopicCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>first-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopicCount( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopicCount' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicCount' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		if ( isset( $args[2] ) && $forum_id = $args[2] ) {
+			// Check for bad data
+			if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+				return $this->error;
+			}
+			// Check the requested forum exists
+			if ( !$forum = bb_get_forum( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
+				return $this->error;
+			}
+
+			// OK, let's trust the count in the forum table
+			$count = (int) $forum->topics;
+		} else {
+			// Get all forums
+			$forums = bb_get_forums();
+	
+			// Return an error when no forums exist
+			if ( !$forums ) {
+				$this->error = new IXR_Error( 400, __( 'No forums found.' ) );
+				return $this->error;
+			}
+
+			// Count the topics
+			$count = 0;
+			foreach ( $forums as $forum ) {
+				$count += (int) $forum->topics;
+			}
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopicCount' );
+
+		// Return the count of topics
+		return $count;
+	}
+
+	/**
+	 * Returns details of the latest topics
+	 *
+	 * @since 1.0
+	 * @return array|object The topics when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The forum id or slug (optional)
+	 * @param integer $args[3] The number of topics to return (optional)
+	 * @param integer $args[4] The number of the page to return (optional)
+	 *
+	 * XML-RPC request to get all topics in the bbPress instance
+	 * <methodCall>
+	 *     <methodName>bb.getTopics</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get all topics in the forum with id number 34
+	 * <methodCall>
+	 *     <methodName>bb.getTopics</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>34</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get topics 6 to 10 in the forum with slug "first-forum"
+	 * <methodCall>
+	 *     <methodName>bb.getTopics</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>first-forum</string></value></param>
+	 *         <param><value><int>5</int></value></param>
+	 *         <param><value><int>2</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopics( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopics' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopics' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Setup an array to store arguments to pass to get_topics() function
+		$get_topics_args = array(
+			'forum' => false,
+			'number' => false,
+			'page' => false
+		);
+
+		// Can be numeric id or slug
+		if ( isset( $args[2] ) && $forum_id = $args[2] ) {
+			// Check for bad data
+			if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+				return $this->error;
+			}
+			// Check the requested forum exists
+			if ( !$forum = bb_get_forum( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
+				return $this->error;
+			}
+
+			// The forum id may have been a slug, so make sure it's an integer here
+			$get_topics_args['forum'] = (int) $forum->forum_id;
+		}
+
+		// Can only be an integer
+		if ( isset( $args[3] ) && $number = (int) $args[3] ) {
+			$get_topics_args['number'] = $number;
+		}
+
+		// Can only be an integer
+		if ( isset( $args[4] ) && $page = (int) $args[4] ) {
+			$get_topics_args['page'] = $page;
+		}
+
+		// Get the topics
+		if ( !$topics = get_latest_topics( $get_topics_args ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topics found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_topics = array();
+		foreach ( $topics as $topic ) {
+			$_topics[] = $this->prepare_topic( $topic );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopics' );
+
+		// Return the topics
+		return $_topics;
+	}
+
+	/**
+	 * Returns details of a topic
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing details of the returned topic when successfully executed or an IXR_Error object on failure
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The topic's id or slug
+	 *
+	 * XML-RPC request to get the topic with id number 105
+	 * <methodCall>
+	 *     <methodName>bb.getTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>105</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get the topic with slug "cheesy-biscuits"
+	 * <methodCall>
+	 *     <methodName>bb.getTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>cheesy-biscuits</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$topic = $this->prepare_topic( $topic );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopic' );
+
+		// Return the topic
+		return $topic;
+	}
+
+	/**
+	 * Creates a new topic
+	 *
+	 * @since 1.0
+	 * @return array|object The topic data when successfully created or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various parameters in the new topic
+	 * @param string $args[2]['title'] The title of the topic
+	 * @param string $args[2]['text'] The text of the topic
+	 * @param integer|string $args[2]['forum_id'] The unique id of the forum which will contain this topic, slugs are OK to use too
+	 * @param string|array $args[2]['tags'] A comma delimited string or an array of tags to add to the topic (optional)
+	 *
+	 * XML-RPC request to create a new topic called "Insane monkeys" inside the forum with id 2
+	 * <methodCall>
+	 *     <methodName>bb.newTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>title</name>
+	 *                 <value><string>Insane monkeys</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>text</name>
+	 *                 <value><string>I just saw some insane monkeys eating bananas, did anyone else see that?</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>forum_id</name>
+	 *                 <value><integer>2</integer></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>tags</name>
+	 *                 <value><string>monkeys, bananas</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_newTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.newTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'write_topics', __( 'You do not have permission to write topics.' ) );
+
+		// Additionally they need to be able to write posts
+		if ( !$this->error && !bb_current_user_can( 'write_posts' ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to write posts.' ) );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.newTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Can be numeric id or slug
+		$forum_id = isset( $structure['forum_id'] ) ? $structure['forum_id'] : false;
+
+		// Check for bad data
+		if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested forum exists
+		if ( !$forum = bb_get_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No forum found.' ) );
+			return $this->error;
+		}
+
+		// The forum id may have been a slug, so make sure it's an integer here
+		$forum_id = (int) $forum->forum_id;
+
+		// Make sure they are allowed to write topics to this forum
+		if ( !bb_current_user_can( 'write_topic', $forum_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to write topics to this forum.' ) );
+			return $this->error;
+		}
+
+		// The topic requires a title
+		if ( !isset( $structure['title'] ) || !$structure['title'] ) {
+			$this->error = new IXR_Error( 400, __( 'The topic title is invalid.' ) );
+			return $this->error;
+		}
+
+		// The topic requires text
+		if ( !isset( $structure['text'] ) || !$structure['text'] ) {
+			$this->error = new IXR_Error( 400, __( 'The topic text is invalid.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_insert_topic()
+		$bb_insert_topic_args = array(
+			'topic_title' => (string) $structure['title'],
+			'forum_id' => $forum_id,
+			'tags' => (string) trim( $structure['tags'] )
+		);
+
+		// Remove empty settings so that changes to the defaults in bb_insert_topic() are honoured
+		$bb_insert_topic_args = array_filter( $bb_insert_topic_args );
+
+		// Create the topic
+		if ( !$topic_id = bb_insert_topic( $bb_insert_topic_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The topic could not be created.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_insert_post()
+		$bb_insert_post_args = array(
+			'topic_id' => (int) $topic_id,
+			'post_text' => (string) $structure['text']
+		);
+
+		// Create the post
+		if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The post could not be created.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$topic = $this->prepare_topic( get_topic( $topic_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.newTopic' );
+
+		return $topic;
+	}
+
+	/**
+	 * Edits an existing topic
+	 *
+	 * @since 1.0
+	 * @return array|object The topic data when successfully edited or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various parameters in the edited topic
+	 * @param integer|string $args[2]['topic_id'] The topic's id or slug
+	 * @param string $args[2]['title'] The title of the topic
+	 * @param string $args[2]['text'] The text of the topic
+	 *
+	 * XML-RPC request to edit the title of a topic with the slug "insane-monkeys"
+	 * <methodCall>
+	 *     <methodName>bb.editTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>topic_id</name>
+	 *                 <value><string>insane-monkeys</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>title</name>
+	 *                 <value><string>Very insane monkeys</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_editTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.editTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'edit_topics', __( 'You do not have permission to edit topics.' ) );
+
+		// Additionally they need to be able to edit posts
+		if ( !$this->error && !bb_current_user_can( 'edit_posts' ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to edit posts.' ) );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.editTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Can be numeric id or slug
+		$topic_id = isset( $structure['topic_id'] ) ? $structure['topic_id'] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to edit this topic
+		if ( !bb_current_user_can( 'edit_topic', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to edit this topic.' ) );
+			return $this->error;
+		}
+
+		// Get the first post in the topic (that's where the content is)
+		if ( !$post = bb_get_first_post( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No posts found.' ) );
+			return $this->error;
+		}
+
+		$post_id = (int) $post->post_id;
+
+		// Make sure they are allowed to edit this post
+		if ( !bb_current_user_can( 'edit_post', $post_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to edit this post.' ) );
+			return $this->error;
+		}
+
+		// The topic requires a title
+		if ( isset( $structure['title'] ) && !$structure['title'] ) {
+			$this->error = new IXR_Error( 400, __( 'The topic title is invalid.' ) );
+			return $this->error;
+		}
+
+		// The topic requires text
+		if ( isset( $structure['text'] ) && !$structure['text'] ) {
+			$this->error = new IXR_Error( 400, __( 'The topic text is invalid.' ) );
+			return $this->error;
+		}
+
+		if ( $structure['title'] ) {
+			if ( !bb_insert_topic( array( 'topic_title' => (string) $structure['title'], 'topic_id' => $topic_id ) ) ) {
+				$this->error = new IXR_Error( 500, __( 'The topic could not be edited.' ) );
+				return $this->error;
+			}
+		}
+
+		if ( $structure['text'] ) {
+			if ( !bb_insert_post( array( 'post_text' => (string) $structure['text'], 'post_id' => $post_id, 'topic_id'=> $topic_id ) ) ) {
+				$this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
+				return $this->error;
+			}
+		}
+
+		// Only include "safe" data in the array
+		$topic = $this->prepare_topic( get_topic( $topic_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.editTopic' );
+
+		return $topic;
+	}
+
+	/**
+	 * Deletes a topic
+	 *
+	 * @since 1.0
+	 * @return integer|object 0 if already changed, 1 when successfully changed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The unique id of the topic to be deleted
+	 * @param integer $args[3] 1 deletes the topic, 0 undeletes the topic
+	 *
+	 * XML-RPC request to delete a topic with id of 34
+	 * <methodCall>
+	 *     <methodName>bb.deleteTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><integer>34</integer></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_deleteTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.deleteTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'delete_topics', __( 'You do not have permission to delete topics.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.deleteTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		$delete = isset( $args[3] ) ? (int) $args[3] : 1;
+
+		// Don't do anything if already set that way
+		if ( $delete === (int) $topic->topic_status ) {
+			return 0;
+		}
+
+		// Make sure they are allowed to delete this topic
+		if ( !bb_current_user_can( 'delete_topic', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to delete this topic.' ) );
+			return $this->error;
+		}
+
+		// Delete the topic
+		if ( !bb_delete_topic( $topic_id, $delete ) ) {
+			$this->error = new IXR_Error( 500, __( 'The topic could not be deleted.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.deleteTopic' );
+
+		return $result;
+	}
+
+	/**
+	 * Moves a topic to a different forum
+	 *
+	 * @since 1.0
+	 * @return integer|object the forum id where the topic lives after the method is called or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The unique id of the topic to be moved
+	 * @param integer|string $args[3] The unique id of the forum to be moved to
+	 *
+	 * XML-RPC request to move the topic with id of 34 to forum with slug of "better-forum"
+	 * <methodCall>
+	 *     <methodName>bb.moveTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><integer>34</integer></value></param>
+	 *         <param><value><string>better-forum</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_moveTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.moveTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'move_topics', __( 'You do not have permission to move topics.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.moveTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[3] ) ? $args[3] : false;
+
+		// Check for bad data
+		if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$forum = bb_get_forum( $forum_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No forum found.' ) );
+			return $this->error;
+		}
+
+		// The forum id may have been a slug, so make sure it's an integer here
+		$forum_id = (int) $forum->forum_id;
+
+		// Only move it if it isn't already there
+		if ( $forum_id !== (int) $topic->forum_id ) {
+			// Make sure they are allowed to move this topic specifically to this forum
+			if ( !bb_current_user_can( 'move_topic', $topic_id, $forum_id ) ) {
+				$this->error = new IXR_Error( 403, __( 'You are not allowed to move this topic to this forum.' ) );
+				return $this->error;
+			}
+
+			// Move the topic
+			if ( !bb_move_topic( $topic_id, $forum_id ) ) {
+				$this->error = new IXR_Error( 500, __( 'The topic could not be moved.' ) );
+				return $this->error;
+			}
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.moveTopic' );
+
+		return $forum_id;
+	}
+
+	/**
+	 * Sticks a topic to the top of a forum or the front page
+	 *
+	 * @since 1.0
+	 * @return integer|object 0 if it is already stuck to the desired location, 1 when successfully stuck or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The unique id of the topic to be stuck
+	 * @param integer $args[3] 0 unsticks, 1 sticks, 2 sticks to front (optional)
+	 *
+	 * XML-RPC request to stick the topic with id of 34 to the front page
+	 * <methodCall>
+	 *     <methodName>bb.stickTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><integer>34</integer></value></param>
+	 *         <param><value><integer>1</integer></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_stickTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.stickTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'stick_topics', __( 'You do not have permission to stick topics.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.stickTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to stick this topic
+		if ( !bb_current_user_can( 'stick_topic', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to stick this topic.' ) );
+			return $this->error;
+		}
+
+		// Stick to where?
+		$where = isset( $args[3] ) ? (int) $args[3] : 1;
+
+		// Forget it if it's already there
+		if ( $where === (int) $topic->topic_sticky ) {
+			return 0;
+		}
+
+		// Stick the topic
+		if ( !bb_stick_topic( $topic_id, $where ) ) {
+			$this->error = new IXR_Error( 500, __( 'The topic could not be stuck.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.stickTopic' );
+
+		return $result;
+	}
+
+
+
+	/**
+	 * Closes a topic
+	 *
+	 * @since 1.0
+	 * @return integer|object 0 when already changed, 1 when successfully changed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The unique id of the topic to be closed
+	 * @param integer $args[2] 0 closes, 1 opens (optional)
+	 *
+	 * XML-RPC request to close the topic with slug of "really-old-topic"
+	 * <methodCall>
+	 *     <methodName>bb.closeTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>really-old-topic</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to open the topic with slug of "really-old-topic"
+	 * <methodCall>
+	 *     <methodName>bb.closeTopic</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>really-old-topic</string></value></param>
+	 *         <param><value><integer>1</integer></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_closeTopic( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.closeTopic' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'close_topics', __( 'You do not have permission to close topics.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.closeTopic' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to close this topic
+		if ( !bb_current_user_can( 'close_topic', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to close this topic.' ) );
+			return $this->error;
+		}
+
+		// Open or close?
+		$close = isset( $args[3] ) ? (int) $args[3] : 0;
+
+		// Forget it if it's already matching
+		if ( $close === (int) $topic->topic_open ) {
+			return 0;
+		}
+
+		// Close the topic
+		if ( !$close && !bb_close_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'The topic could not be closed.' ) );
+			return $this->error;
+		}
+
+		// Open the topic
+		if ( $close && !bb_open_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'The topic could not be opened.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.closeTopic' );
+
+		return $result;
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Post XML-RPC methods
+	 */
+
+	/**
+	 * Returns a numerical count of posts
+	 *
+	 * @since 1.0
+	 * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The topic id or slug
+	 *
+	 * XML-RPC request to get a count of all posts in the topic with slug "countable-topic"
+	 * <methodCall>
+	 *     <methodName>bb.getPostCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>countable-topic</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getPostCount( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getPostCount' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPostCount' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// OK, let's trust the count in the topic table
+		$count = $topic->topic_posts;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getPostCount' );
+
+		// Return the count of posts
+		return $count;
+	}
+
+	/**
+	 * Returns details of the posts in a given topic
+	 *
+	 * @since 1.0
+	 * @return array|object The posts when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The topic id or slug
+	 * @param integer $args[3] The number of posts to return (optional)
+	 * @param integer $args[4] The number of the page to return (optional)
+	 *
+	 * XML-RPC request to get all posts in the topic with id number 53
+	 * <methodCall>
+	 *     <methodName>bb.getPosts</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>53</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get the latest 5 posts in the topic with id number 341
+	 * <methodCall>
+	 *     <methodName>bb.getPosts</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>341</int></value></param>
+	 *         <param><value><int>5</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get posts 11 to 20 in the topic with slug "long-topic"
+	 * <methodCall>
+	 *     <methodName>bb.getPosts</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>long-topic</string></value></param>
+	 *         <param><value><int>10</int></value></param>
+	 *         <param><value><int>2</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getPosts( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getPosts' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPosts' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Setup an array to store arguments to pass to get_thread() function
+		$get_thread_args = array();
+
+		// Can only be an integer
+		if ( isset( $args[3] ) && $per_page = (int) $args[3] ) {
+			$get_thread_args['per_page'] = $per_page;
+		}
+
+		// Can only be an integer
+		if ( isset( $args[4] ) && $page = (int) $args[4] ) {
+			$get_thread_args['page'] = $page;
+		}
+
+		// Get the posts
+		if ( !$posts = get_thread( $topic_id, $get_thread_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'No posts found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_posts = array();
+		foreach ( $posts as $post ) {
+			$_posts[] = $this->prepare_post( $post );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getPosts' );
+
+		// Return the posts
+		return $_posts;
+	}
+
+	/**
+	 * Returns details of a post
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing details of the returned post when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer $args[2] The post's id
+	 *
+	 * XML-RPC request to get the post with id number 32
+	 * <methodCall>
+	 *     <methodName>bb.getPost</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>32</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getPost( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getPost' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPost' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$post_id = isset( $args[2] ) ? (int) $args[2] : false;
+
+		// Check for bad data
+		if ( !$post_id ) {
+			$this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested post exists
+		if ( !$post = bb_get_post( $post_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No post found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_post = $this->prepare_post( $post );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getPost' );
+
+		// Return the post
+		return $_post;
+	}
+
+	/**
+	 * Creates a new post in a given topic
+	 *
+	 * @since 1.0
+	 * @return array|object The post data when successfully created or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various parameters in the new topic
+	 * @param string $args[2]['text'] The text of the topic
+	 * @param integer|string $args[2]['topic_id'] The unique id of the topic which will contain this topic, slugs are OK to use too
+	 *
+	 * XML-RPC request to create a new post in the topic with slug "totally-worth-it"
+	 * <methodCall>
+	 *     <methodName>bb.newPost</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>text</name>
+	 *                 <value><string>I agree, it is totally worth it.</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>topic_id</name>
+	 *                 <value><string>totally-worth-it</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_newPost( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.newPost' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'write_posts', __( 'You do not have permission to write posts.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.newPost' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The post data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Can be numeric id or slug
+		$topic_id = isset( $structure['topic_id'] ) ? $structure['topic_id'] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to write posts to this topic
+		if ( !bb_current_user_can( 'write_post', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to write posts to this topic.' ) );
+			return $this->error;
+		}
+
+		// The post requires text
+		if ( !isset( $structure['text'] ) || !$structure['text'] ) {
+			$this->error = new IXR_Error( 400, __( 'The post text is invalid.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_insert_post()
+		$bb_insert_post_args = array(
+			'topic_id' => $topic_id,
+			'post_text' => (string) $structure['text']
+		);
+
+		// Create the post
+		if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The post could not be created.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$post = $this->prepare_forum( bb_get_post( $post_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.newPost' );
+
+		return $post;
+	}
+
+	/**
+	 * Edits an existing post
+	 *
+	 * @since 1.0
+	 * @return array|object The post data when successfully edited or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The values for the various parameters in the new topic
+	 * @param integer $args[2]['post_id'] The unique id of the post
+	 * @param string $args[2]['text'] The text of the topic
+	 *
+	 * XML-RPC request to edit the text of the post with an id of 452
+	 * <methodCall>
+	 *     <methodName>bb.editPost</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>post_id</name>
+	 *                 <value><int>452</int></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>text</name>
+	 *                 <value><string>For now I will withhold my opinion.</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_editPost( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.editPost' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'edit_posts', __( 'You do not have permission to edit posts.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.editPost' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The post data is invalid.' ) );
+			return $this->error;
+		}
+
+		$structure = (array) $args[2];
+
+		// Can be numeric id or slug
+		$post_id = isset( $structure['post_id'] ) ? (int) $structure['post_id'] : false;
+
+		// Check for bad data
+		if ( !$post_id ) {
+			$this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$post = bb_get_post( $post_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No post found.' ) );
+			return $this->error;
+		}
+
+		// Re-assign the post id
+		$post_id = (int) $post->post_id;
+
+		// Make sure they are allowed to edit this post
+		if ( !bb_current_user_can( 'edit_post', $post_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to edit this post.' ) );
+			return $this->error;
+		}
+
+		// The post requires text
+		if ( !isset( $structure['text'] ) || !$structure['text'] ) {
+			$this->error = new IXR_Error( 400, __( 'The post text is invalid.' ) );
+			return $this->error;
+		}
+
+		// Inject structure into an array suitable for bb_insert_post()
+		$bb_insert_post_args = array(
+			'post_id' => $post_id,
+			'post_text' => (string) $structure['text']
+		);
+
+		// Create the post
+		if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
+			$this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$post = $this->prepare_forum( bb_get_post( $post_id ) );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.editPost' );
+
+		return $post;
+	}
+
+	/**
+	 * Deletes an existing post
+	 *
+	 * @since 1.0
+	 * @return integer|object 1 when successfully deleted, 0 when already deleted or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The unique id of the post
+	 * @param array $args[3] 1 deletes the post, 0 undeletes the post (optional)
+	 *
+	 * XML-RPC request to delete the post with an id of 4301
+	 * <methodCall>
+	 *     <methodName>bb.editPost</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>4301</int></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_deletePost( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.deletePost' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'delete_posts', __( 'You do not have permission to delete posts.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.deletePost' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$post_id = isset( $args[2] ) ? (int) $args[2] : false;
+
+		// Check for bad data
+		if ( !$post_id ) {
+			$this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$post = bb_get_post( $post_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No post found.' ) );
+			return $this->error;
+		}
+
+		// Re-assign the post id
+		$post_id = (int) $post->post_id;
+
+		// Make sure they are allowed to delete this post
+		if ( !bb_current_user_can( 'delete_post', $post_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to delete this post.' ) );
+			return $this->error;
+		}
+
+		$status = isset( $args[3] ) ? (int) $args[3] : 1;
+
+		if ( $status === (int) $post->post_status ) {
+			return 0;
+		}
+
+		// Delete the post
+		if ( !$post_id = bb_delete_post( $post_id, $status ) ) {
+			$this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.deletePost' );
+
+		return $result;
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Topic Tag XML-RPC methods
+	 */
+
+	/**
+	 * Returns the hot tags in order of hotness in a given forum or all hot tags
+	 *
+	 * @since 1.0
+	 * @return integer|object The tag data when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer $args[2] The number of tags to return (optional)
+	 * @param integer|string $args[3] The forum id or slug (optional)
+	 *
+	 * XML-RPC request to get the 20 hottest tags in the forum with slug "hawtness"
+	 * <methodCall>
+	 *     <methodName>bb.getTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>20</int></value></param>
+	 *         <param><value><string>hawtness</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getHotTopicTags( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getHotTopicTags' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getHotTopicTags' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Must be a number
+		$per_page = isset( $args[2] ) ? (integer) $args[2] : false;
+
+		// Can be numeric id or slug
+		$forum_id = isset( $args[3] ) ? $args[3] : false;
+
+		if ( $forum_id ) {
+			// Check for bad data
+			if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
+				return $this->error;
+			}
+
+			// Check the requested forum exists
+			if ( !$forum = bb_get_forum( $forum_id ) ) {
+				$this->error = new IXR_Error( 404, __( 'No forum found.' ) );
+				return $this->error;
+			}
+
+			global $bbdb;
+			$topic_ids = $bbdb->get_col( $bbdb->prepare( "SELECT topic_id FROM `" . $bbdb->topics . "` WHERE `topic_status` = 0 AND `topic_open` = 1 AND `tag_count` > 0 AND `forum_id` = %s;", $forum_id ) );
+
+			if ( !count( $topic_ids ) ) {
+				$this->error = new IXR_Error( 400, __( 'No topics found.' ) );
+				return $this->error;
+			}
+
+			global $wp_taxonomy_object;
+			$tags = $wp_taxonomy_object->get_object_terms( $topic_ids, 'bb_topic_tag', array( 'fields' => 'all_with_object_id', 'orderby' => 'count', 'order' => 'DESC' ) );
+
+			if ( !$tags || is_wp_error( $tags ) ) {
+				$this->error = new IXR_Error( 500, __( 'Could not retrieve hot topic tags.' ) );
+				return $this->error;
+			}
+			if ( !count( $tags ) ) {
+				$this->error = new IXR_Error( 500, __( 'No hot topic tags found.' ) );
+				return $this->error;
+			}
+			global $bb_log;
+			$bb_log->debug($tags);
+
+			for ( $i = 0; isset( $tags[$i] ); $i++ ) {
+				_bb_make_tag_compat( $tags[$i] );
+			}
+			$bb_log->debug($tags);
+
+			// Only include "safe" data in the array
+			$_tags = array();
+			foreach ( $tags as $tag ) {
+				$_tag = $this->prepare_topic_tag( $tag );
+				if ( !in_array( $_tag, $_tags ) ) {
+					$_tags[] = $_tag;
+				}
+			}
+
+			if ( $per_page ) {
+				$_tags = array_slice( $_tags, 0, $per_page );
+			}
+		} else {
+			if ( !$tags = bb_get_top_tags( array( 'get' => 'all', 'number' => $per_page ) ) ) {
+				$this->error = new IXR_Error( 500, __( 'No hot topic tags found.' ) );
+				return $this->error;
+			}
+
+			// Only include "safe" data in the array
+			$_tags = array();
+			foreach ( $tags as $tag ) {
+				$_tags[] = $this->prepare_topic_tag( $tag );
+			}
+		}
+
+		do_action( 'bb_xmlrpc_call', 'bb.getHotTopicTags' );
+
+		return $_tags;
+	}
+
+	/**
+	 * Returns a numerical count of tags in a given topic or all tags
+	 *
+	 * @since 1.0
+	 * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The topic id or slug (optional)
+	 *
+	 * XML-RPC request to get a count of all tags in the topic with slug "woot-frist-topic"
+	 * <methodCall>
+	 *     <methodName>bb.getTopicTagCount</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>woot-frist-topic</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopicTagCount( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopicTagCount' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTagCount' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( $topic_id ) {
+			if ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+				return $this->error;
+			}
+
+			// Check the requested topic exists
+			if ( !$topic = get_topic( $topic_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+				return $this->error;
+			}
+
+			// The topic id may have been a slug, so make sure it's an integer here
+			$topic_id = (int) $topic->topic_id;
+
+			// Now get the tags
+			if ( !$tags = bb_get_topic_tags( $topic_id ) ) {
+				$tags = array();
+			}
+
+			// Count the tags
+			$count = count( $tags );
+		} else {
+			global $wp_taxonomy_object;
+			$count = $wp_taxonomy_object->count_terms( 'bb_topic_tag' );
+			if ( is_wp_error( $count ) ) {
+				$this->error = new IXR_Error( 500, __( 'Could not get a count of all topic tags.' ) );
+				return $this->error;
+			}
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTagCount' );
+
+		// Return the count of tags
+		return $count;
+	}
+
+	/**
+	 * Returns the tags in a given topic or all tags
+	 *
+	 * @since 1.0
+	 * @return integer|object The tag data when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param integer|string $args[2] The topic id or slug (optional)
+	 *
+	 * XML-RPC request to get all tags in the topic with slug "woot-frist-topic"
+	 * <methodCall>
+	 *     <methodName>bb.getTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>woot-frist-topic</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopicTags( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopicTags' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTags' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( $topic_id ) {
+			if ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+				return $this->error;
+			}
+
+			// Check the requested topic exists
+			if ( !$topic = get_topic( $topic_id ) ) {
+				$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+				return $this->error;
+			}
+
+			// The topic id may have been a slug, so make sure it's an integer here
+			$topic_id = (int) $topic->topic_id;
+
+			// Now get the tags
+			if ( !$tags = bb_get_topic_tags( $topic_id ) ) {
+				$this->error = new IXR_Error( 500, __( 'No topic tags found.' ) );
+				return $this->error;
+			}
+		} else {
+			global $wp_taxonomy_object;
+			$tags = $wp_taxonomy_object->get_terms( 'bb_topic_tag', array( 'get' => 'all' ) );
+			if ( is_wp_error( $tags ) ) {
+				$this->error = new IXR_Error( 500, __( 'Could not retrieve all topic tags.' ) );
+				return $this->error;
+			}
+			for ( $i = 0; isset( $tags[$i] ); $i++ ) {
+				_bb_make_tag_compat( $tags[$i] );
+			}
+		}
+
+		// Only include "safe" data in the array
+		$_tags = array();
+		foreach ( $tags as $tag ) {
+			$_tags[] = $this->prepare_topic_tag( $tag );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTags' );
+
+		// Return the tags
+		return $_tags;
+	}
+
+	/**
+	 * Returns the topics which are tagged with the given tag
+	 *
+	 * @since 1.0
+	 * @return integer|object The topic data when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string $args[2] The tag name or slug
+	 * @param integer $args[3] The number of topics to return (optional)
+	 * @param integer $args[4] The number of the page to return (optional)
+	 *
+	 * XML-RPC request to get the latest 10 topics tagged with the tag "apples"
+	 * <methodCall>
+	 *     <methodName>bb.getTopicTag</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>apples</string></value></param>
+	 *         <param><value><string>10</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getTopicTag( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getTopicTag' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTag' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can only be a string
+		$tag_id = isset( $args[2] ) ? (string) $args[2] : false;
+
+		// Check for bad data
+		if ( !$tag_id ) {
+			$this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$tag = bb_get_tag( $tag_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No tag found.' ) );
+			return $this->error;
+		}
+
+		// Get the numeric tag id
+		$tag_id = (int) $tag->tag_id;
+
+		// Setup an array to store arguments to pass to get_tagged_topics() function
+		$get_topics_args = array(
+			'tag_id' => false,
+			'number' => false,
+			'page' => false
+		);
+
+		// Can only be an integer
+		if ( isset( $args[3] ) && $number = (int) $args[3] ) {
+			$get_topics_args['number'] = $number;
+		}
+
+		// Can only be an integer
+		if ( isset( $args[4] ) && $page = (int) $args[4] ) {
+			$get_topics_args['page'] = $page;
+		}
+
+		// Now get the topics
+		if ( !$topics = get_tagged_topics( $tag_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'No topics found.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_topics = array();
+		foreach ( $topics as $topic ) {
+			$_topics[] = $this->prepare_topic( $topic );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTag' );
+
+		// Return the topics
+		return $_topics;
+	}
+
+	/**
+	 * Adds the specified tags to the specified topic
+	 *
+	 * @since 1.0
+	 * @return array|object The tags which were added when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string|integer $args[2] The topic id or slug
+	 * @param string|array $args[3] The tags to add to the topic
+	 *
+	 * XML-RPC request to add the tag "banana" to the topic with id 219
+	 * <methodCall>
+	 *     <methodName>bb.addTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><string>banana</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to add the tags "banana" and "man" to the topic with id 219
+	 * <methodCall>
+	 *     <methodName>bb.addTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><string>banana, man</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to add the tags "banana" and "man" to the topic with id 219 using an array
+	 * <methodCall>
+	 *     <methodName>bb.addTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><array>
+	 *             <data><value><string>banana</string></value></data>
+	 *             <data><value><string>man</string></value></data>
+	 *         </array></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_addTopicTags( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.addTopicTags' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'edit_tags', __( 'You do not have permission to edit tags.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.addTopicTags' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to add tags to this topic
+		if ( !bb_current_user_can( 'add_tag_to', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to add tags to this topic.' ) );
+			return $this->error;
+		}
+
+		$tags = isset( $args[3] ) ? $args[3] : false;
+
+		// Check for bad data
+		if ( !$tags || ( !is_string( $tags ) && !is_array( $tags ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The tag data is invalid.' ) );
+			return $this->error;
+		}
+
+		// Add the tags
+		if ( !$tag_ids = bb_add_topic_tags( $topic_id, $tags ) ) {
+			$this->error = new IXR_Error( 500, __( 'The tags could not be added.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$_tags = array();
+		foreach ( $tag_ids as $tag_id ) {
+			$_tags[] = $this->prepare_topic_tag( bb_get_tag( $tag_id ) );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.addTopicTags' );
+
+		// Return the tags which were added as an array
+		return $_tags;
+	}
+
+	/**
+	 * Removes the specified tags from the specified topic
+	 *
+	 * @since 1.0
+	 * @return integer|object 1 when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string|integer $args[2] The topic id or slug
+	 * @param string|array $args[3] The tags to remove from the topic
+	 *
+	 * XML-RPC request to remove the tag "banana" to the topic with id 219
+	 * <methodCall>
+	 *     <methodName>bb.removeTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><string>banana</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to remove the tags "banana" and "man" to the topic with id 219
+	 * <methodCall>
+	 *     <methodName>bb.removeTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><string>banana, man</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to remove the tags "banana" and "man" to the topic with id 219 using an array
+	 * <methodCall>
+	 *     <methodName>bb.removeTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><int>219</int></value></param>
+	 *         <param><value><array>
+	 *             <data><value><string>banana</string></value></data>
+	 *             <data><value><string>man</string></value></data>
+	 *         </array></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_removeTopicTags( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.removeTopicTags' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'edit_tags', __( 'You do not have permission to edit tags.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.removeTopicTags' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can be numeric id or slug
+		$topic_id = isset( $args[2] ) ? $args[2] : false;
+
+		// Check for bad data
+		if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested topic exists
+		if ( !$topic = get_topic( $topic_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No topic found.' ) );
+			return $this->error;
+		}
+
+		// The topic id may have been a slug, so make sure it's an integer here
+		$topic_id = (int) $topic->topic_id;
+
+		// Make sure they are allowed to add tags to this topic
+		if ( !bb_current_user_can( 'add_tag_to', $topic_id ) ) {
+			$this->error = new IXR_Error( 403, __( 'You do not have permission to remove tags from this topic.' ) );
+			return $this->error;
+		}
+
+		$tags = isset( $args[3] ) ? $args[3] : false;
+
+		// Check for bad data
+		if ( !$tags || ( !is_string( $tags ) && !is_array( $tags ) ) ) {
+			$this->error = new IXR_Error( 400, __( 'The tag data is invalid.' ) );
+			return $this->error;
+		}
+
+		// Add the tags
+		if ( !bb_remove_topic_tags( $topic_id, $tags ) ) {
+			$this->error = new IXR_Error( 500, __( 'The tags could not be removed.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.removeTopicTags' );
+
+		// Return the result
+		return $result;
+	}
+
+	/**
+	 * Renames the specified tag to a new tag name
+	 *
+	 * @since 1.0
+	 * @return array|object The tag data when successfully renamed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string $args[2] The tag name or slug
+	 * @param string $args[3] The new tag name (slug is auto-generated)
+	 *
+	 * XML-RPC request to rename the tag "banana" to "bananas"
+	 * <methodCall>
+	 *     <methodName>bb.renameTopicTag</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>banana</string></value></param>
+	 *         <param><value><string>bananas</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_renameTopicTag( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.renameTopicTag' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.renameTopicTag' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can only be a string
+		$tag_id = isset( $args[2] ) ? (string) $args[2] : false;
+
+		// Check for bad data
+		if ( !$tag_id ) {
+			$this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested tag exists
+		if ( !$tag = bb_get_tag( $tag_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No tag found.' ) );
+			return $this->error;
+		}
+
+		// Get the numeric tag id
+		$tag_id = (int) $tag->tag_id;
+
+		// Can only be a string
+		$tag_name = isset( $args[3] ) ? (string) $args[3] : false;
+
+		// Check for bad data
+		if ( !$tag_name || $tag_name == $tag->tag_name ) {
+			$this->error = new IXR_Error( 400, __( 'The tag name is invalid.' ) );
+			return $this->error;
+		}
+
+		// Rename the tag
+		if ( !$new_tag = bb_rename_tag( $tag_id, $tag_name ) ) {
+			$this->error = new IXR_Error( 500, __( 'The tag could not be renamed.' ) );
+			return $this->error;
+		}
+
+		// Only include "safe" data in the array
+		$new_tag = $this->prepare_topic_tag( $new_tag );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.renameTopicTag' );
+
+		// Return the tag
+		return $new_tag;
+	}
+
+	/**
+	 * Merges the specified tags
+	 *
+	 * @since 1.0
+	 * @return array|object The tag data when successfully merged or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string $args[2] The old tag name or slug to be destroyed
+	 * @param string $args[3] The new tag name or slug where the old tag will be merged to
+	 *
+	 * XML-RPC request to merge the tag "banana" into the tag "apple"
+	 * <methodCall>
+	 *     <methodName>bb.mergeTopicTags</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>banana</string></value></param>
+	 *         <param><value><string>apple</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_mergeTopicTags( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.mergeTopicTags' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.mergeTopicTags' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can only be strings
+		$old_tag_id = isset( $args[2] ) ? (string) $args[2] : false;
+		$new_tag_id = isset( $args[3] ) ? (string) $args[3] : false;
+
+		// Check for bad data
+		if ( !$old_tag_id ) {
+			$this->error = new IXR_Error( 400, __( 'The old tag id is invalid.' ) );
+			return $this->error;
+		}
+		if ( !$new_tag_id ) {
+			$this->error = new IXR_Error( 400, __( 'The new tag id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested tags exist
+		if ( !$old_tag = bb_get_tag( $old_tag_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No old tag found.' ) );
+			return $this->error;
+		}
+		if ( !$new_tag = bb_get_tag( $new_tag_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No new tag found.' ) );
+			return $this->error;
+		}
+
+		// Get the numeric tag ids
+		$old_tag_id = (int) $old_tag->tag_id;
+		$new_tag_id = (int) $new_tag->tag_id;
+
+		// Rename the tag
+		if ( !$result = bb_rename_tag( $old_tag_id, $new_tag_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'The tags could not be merged.' ) );
+			return $this->error;
+		}
+
+		// Get the merged tag
+		$new_tag = bb_get_tag( $new_tag_id );
+
+		// Only include "safe" data in the array
+		$new_tag = $this->prepare_topic_tag( $new_tag );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.mergeTopicTags' );
+
+		// Return the tag
+		return $new_tag;
+	}
+
+	/**
+	 * Destroys the specified tag
+	 *
+	 * @since 1.0
+	 * @return integer|object 1 when successfully deleted or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param string $args[2] The tag name or slug to be destroyed
+	 *
+	 * XML-RPC request to destroy the tag "banana"
+	 * <methodCall>
+	 *     <methodName>bb.destroyTopicTag</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><string>banana</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_destroyTopicTag( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.destroyTopicTag' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.destroyTopicTag' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Can only be a string
+		$tag_id = isset( $args[2] ) ? (string) $args[2] : false;
+
+		// Check for bad data
+		if ( !$tag_id ) {
+			$this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
+			return $this->error;
+		}
+
+		// Check the requested tag exists
+		if ( !$tag = bb_get_tag( $tag_id ) ) {
+			$this->error = new IXR_Error( 400, __( 'No tag found.' ) );
+			return $this->error;
+		}
+
+		// Get the numeric tag id
+		$tag_id = (int) $tag->tag_id;
+
+		// Destroy the tag
+		if ( !$result = bb_destroy_tag( $tag_id ) ) {
+			$this->error = new IXR_Error( 500, __( 'The tag could not be destroyed.' ) );
+			return $this->error;
+		}
+
+		$result = 1;
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.destroyTopicTag' );
+
+		// Return the tag
+		return $result;
+	}
+
+
+
+	/**
+	 * bbPress publishing API - Options XML-RPC methods
+	 */
+
+	/**
+	 * Initialises site options which can be manipulated using XML-RPC
+	 *
+	 * @since 1.0
+	 * @return void
+	 */
+	function initialise_site_option_info()
+	{
+		$this->site_options = array(
+			// Read only options
+			'software_name'		=> array(
+				'desc'			=> __( 'Software Name' ),
+				'readonly'		=> true,
+				'value'			=> 'bbPress'
+			),
+			'software_version'	=> array(
+				'desc'			=> __( 'Software Version' ),
+				'readonly'		=> true,
+				'option'		=> 'version'
+			),
+			'site_url'			=> array(
+				'desc'			=> __( 'Site URL' ),
+				'readonly'		=> true,
+				'option'		=> 'uri'
+			),
+
+			// Updatable options
+			'site_name'		=> array(
+				'desc'			=> __( 'Site Name' ),
+				'readonly'		=> false,
+				'option'		=> 'name'
+			),
+			'site_description'	=> array(
+				'desc'			=> __( 'Site Description' ),
+				'readonly'		=> false,
+				'option'		=> 'description'
+			),
+			'time_zone'			=> array(
+				'desc'			=> __( 'Time Zone' ),
+				'readonly'		=> false,
+				'option'		=> 'gmt_offset'
+			),
+			'datetime_format'	=> array(
+				'desc'			=> __( 'Date/Time Format' ),
+				'readonly'		=> false,
+				'option'		=> 'datetime_format'
+			),
+			'date_format'		=> array(
+				'desc'			=> __( 'Date Format' ),
+				'readonly'		=> false,
+				'option'		=> 'date_format'
+			)
+		);
+
+		$this->site_options = apply_filters( 'xmlrpc_site_options', $this->site_options );
+	}
+
+	/**
+	 * Compiles site options into an array suitable to be passed back through the XML-RPC server
+	 *
+	 * @since 1.0
+	 * @return array The site options in an array
+	 * @param array $options An array of options to fetch and return
+	 */
+	function _getOptions( $options )
+	{
+		$data = array();
+		foreach ( $options as $option ) {
+			if ( array_key_exists( $option, $this->site_options ) ) {
+				$data[$option] = $this->site_options[$option];
+
+				// Is the value static or dynamic?
+				if ( isset( $data[$option]['option'] ) ) {
+					$data[$option]['value'] = bb_get_option( $data[$option]['option'] );
+					unset( $data[$option]['option'] );
+				}
+			}
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Gets the specified site options
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing the specified options when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The options to be retrieved, when omitted the method returns all options (optional)
+	 *
+	 * XML-RPC request to get all site options
+	 * <methodCall>
+	 *     <methodName>bb.getOptions</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 *
+	 * XML-RPC request to get the site name and site description
+	 * <methodCall>
+	 *     <methodName>bb.getOptions</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><array>
+	 *             <data><value><string>site_name</string></value></data>
+	 *             <data><value><string>site_description</string></value></data>
+	 *         </array></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_getOptions( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.getOptions' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		if ( $this->auth_readonly ) {
+			$user = $this->authenticate( $username, $password );
+		}
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.getOptions' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// If there are parameters supplied then make sure they are in an array
+		$options = isset( $args[2] ) ? (array) $args[2] : false;
+
+		// If no specific options where asked for, return all of them
+		if ( !$options || !count( $options ) ) {
+			$options = array_keys( $this->site_options );
+		}
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.getOptions' );
+
+		return $this->_getOptions( $options );
+	}
+
+	/**
+	 * Sets the specified site options to the specified values
+	 *
+	 * @since 1.0
+	 * @return array|object An array containing the specified options when successfully executed or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The username for authentication
+	 * @param string $args[1] The password for authentication
+	 * @param array $args[2] The options to be updated along with the new value of the option
+	 *
+	 * XML-RPC request to set the site name and site description
+	 * <methodCall>
+	 *     <methodName>bb.setOptions</methodName>
+	 *     <params>
+	 *         <param><value><string>joeblow</string></value></param>
+	 *         <param><value><string>123password</string></value></param>
+	 *         <param><value><struct>
+	 *             <member>
+	 *                 <name>site_name</name>
+	 *                 <value><string>Awesome forums</string></value>
+	 *             </member>
+	 *             <member>
+	 *                 <name>site_description</name>
+	 *                 <value><string>My totally awesome forums will kick your butt</string></value>
+	 *             </member>
+	 *         </struct></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function bb_setOptions( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'bb.setOptions' );
+
+		// Escape args
+		$this->escape( $args );
+
+		// Get the login credentials
+		$username = $args[0];
+		$password = (string) $args[1];
+
+		// Check the user is valid
+		$user = $this->authenticate( $username, $password, 'manage_options', __( 'You are not allowed to manage options.' ) );
+
+		do_action( 'bb_xmlrpc_call_authenticated', 'bb.setOptions' );
+
+		// If an error was raised by authentication or by an action then return it
+		if ( $this->error ) {
+			return $this->error;
+		}
+
+		// Make sure there is something for us to do
+		if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
+			$this->error = new IXR_Error( 400, __( 'The options data is invalid.' ) );
+			return $this->error;
+		}
+
+		$options = (array) $args[2];
+
+		// Update the requested options
+		foreach( $options as $o_name => $o_value ) {
+			$option_names[] = $o_name;
+
+			// If there is no value set skip it
+			if ( empty( $o_value ) ) {
+				continue;
+			}
+
+			// If the option doesn't exist skip it
+			if ( !array_key_exists( $o_name, $this->site_options ) ) {
+				continue;
+			}
+
+			// If the option is readonly skip it
+			if ( $this->site_options[$o_name]['readonly'] == true ) {
+				continue;
+			}
+
+			// Everything is good, update the option
+			bb_update_option( $this->site_options[$o_name]['option'], $o_value );
+		}
+
+		$_options = $this->_getOptions( $option_names );
+
+		do_action( 'bb_xmlrpc_call_return', 'bb.setOptions' );
+
+		// Now return the updated values
+		return $_options;
+	}
+
+
+
+	/**
+	 * Pingback XML-RPC methods
+	 */
+
+	/**
+	 * Processes pingback requests
+	 *
+	 * @since 1.0
+	 * @link http://www.hixie.ch/specs/pingback/pingback
+	 * @return string|object A message of success or an IXR_Error object on failure
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The full URI of the post where the pingback is being sent from
+	 * @param string $args[1] The full URI of the post where the pingback is being sent to
+	 *
+	 * XML-RPC request to register a pingback
+	 * <methodCall>
+	 *     <methodName>pingback.ping</methodName>
+	 *     <params>
+	 *         <param><value><string>http://example.org/2008/09/post-containing-a-link/</string></value></param>
+	 *         <param><value><string>http://example.com/2008/08/post-being-linked-to/</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function pingback_ping( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'pingback.ping' );
+
+		$this->escape( $args );
+
+		// No particular need to sanitise
+		$link_from = (string) $args[0];
+		$link_to   = (string) $args[1];
+
+		// Tidy up ampersands in the URLs
+		$link_from = str_replace( '&amp;', '&', $link_from );
+		$link_to   = str_replace( '&amp;', '&', $link_to );
+		$link_to   = str_replace( '&', '&amp;', $link_to );
+
+		// Check if the topic linked to is in our site - a little more strict than WordPress, doesn't pull out the www if added
+		if ( !bb_match_domains( $link_to, bb_get_uri() ) ) {
+			// These are not the droids you are looking for
+			$this->error = new IXR_Error( 0, __( 'This is not the site you are trying to pingback.' ) );
+			return $this->error;
+		}
+
+		// Get the topic
+		if ( $topic_to = bb_get_topic_from_uri( $link_to ) ) {
+			// Topics shouldn't ping themselves
+			if ( $topic_from = bb_get_topic_from_uri( $link_from ) ) {
+				if ( $topic_from->topic_id === $topic_to->topic_id ) {
+					$this->error = new IXR_Error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) );
+					return $this->error;
+				}
+			}
+		} else {
+			$this->error = new IXR_Error ( 33, __( 'The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.' ) );
+			return $this->error;
+		}
+
+		// Let's check that the remote site didn't already pingback this entry
+		$query = new BB_Query( 'post', array( 'topic_id' => $topic_to->topic_id, 'append_meta' => true ), 'get_thread' );
+		$posts_to = $query->results;
+		unset( $query );
+
+		// Make sure we have some posts in the topic, this error should never happen really
+		if ( !$posts_to || !is_array( $posts_to ) || !count( $posts_to ) ) {
+			$this->error = new IXR_Error( 0, __( 'The specified target topic does not contain any posts.' ) );
+			return $this->error;
+		}
+
+		// Check if we already have a pingback from this URL
+		foreach ( $posts_to as $post ) {
+			if ( isset( $post->pingback_uri ) && trim( $post->pingback_uri ) === trim( $link_from ) ) {
+				$this->error = new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
+				return $this->error;
+			}
+		}
+		unset( $posts_to, $post );
+
+		// Give time for the server sending the pingback to finish publishing it's post
+		sleep(1);
+
+		// Let's check the remote site for valid URL and content
+		$link_from_source = wp_remote_fopen( $link_from );
+		if ( !$link_from_source ) {
+			$this->error = new IXR_Error( 16, __( 'The source URL does not exist.' ) );
+			return $this->error;
+		}
+
+		// Allow plugins to filter here
+		$link_from_source = apply_filters( 'bb_pre_remote_source', $link_from_source, $link_to );
+
+		// Work around bug in strip_tags()
+		$link_from_source = str_replace( '<!DOC', '<DOC', $link_from_source );
+
+		// Normalize spaces
+		$link_from_source = preg_replace( '/[\s\r\n\t]+/', ' ', $link_from_source );
+
+		// Turn certain elements to double line returns
+		$link_from_source = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $link_from_source );
+
+		// Find the title of the page
+		preg_match( '|<title>([^<]*?)</title>|is', $link_from_source, $link_from_title );
+		$link_from_title = $link_from_title[1];
+		if ( empty( $link_from_title ) ) {
+			$this->error = new IXR_Error( 32, __( 'We cannot find a title on that page.' ) );
+			return $this->error;
+		}
+
+		// Strip out all tags except anchors
+		$link_from_source = strip_tags( $link_from_source, '<a>' ); // just keep the tag we need
+
+		// Split the source into paragraphs
+		$link_from_paragraphs = explode( "\n\n", $link_from_source );
+
+		// Prepare the link to search for in preg_match() once here
+		$preg_target = preg_quote( $link_to );
+
+		// Loop through the paragraphs looking for the context for the url
+		foreach ( $link_from_paragraphs as $link_from_paragraph ) {
+			// The url exists
+			if ( strpos( $link_from_paragraph, $link_to ) !== false ) {
+				// But is it in an anchor tag
+				preg_match(
+					"|<a[^>]+?" . $preg_target . "[^>]*>([^>]+?)</a>|",
+					$link_from_paragraph,
+					$context
+				);
+				// If the URL isn't in an anchor tag, keep looking
+				if ( empty( $context ) ) {
+					continue;
+				}
+
+				// We're going to use this fake tag to mark the context in a bit
+				// the marker is needed in case the link text appears more than once in the paragraph
+				$excerpt = preg_replace( '|\</?wpcontext\>|', '', $link_from_paragraph );
+
+				// Prevent really long link text
+				if ( strlen( $context[1] ) > 100 ) {
+					$context[1] = substr( $context[1], 0, 100 ) . '...';
+				}
+
+				// Set up the marker around the context
+				$marker = '<wpcontext>' . $context[1] . '</wpcontext>';
+				// Swap out the link for our marker
+				$excerpt = str_replace( $context[0], $marker, $excerpt );
+				// Strip all tags except for our context marker
+				$excerpt = trim( strip_tags( $excerpt, '<wpcontext>' ) );
+				// Make the marker safe for use in regexp
+				$preg_marker = preg_quote( $marker );
+				// Reduce the excerpt to only include 100 characters on either side of the link
+				$excerpt = preg_replace( "|.*?\s(.{0,100}" . $preg_marker . "{0,100})\s.*|s", '$1', $excerpt );
+				// Strip tags again, to remove the marker wrapper
+				$excerpt = strip_tags( $excerpt );
+				break;
+			}
+		}
+
+		 // Make sure the link to the target was found in the excerpt
+		if ( empty( $context ) ) {
+			$this->error = new IXR_Error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
+			return $this->error;
+		}
+
+		// Add whacky prefix and suffix to the excerpt and sanitize
+		$excerpt = '[...] ' . esc_html( $excerpt ) . ' [...]';
+		$this->escape( $excerpt );
+
+		// Build an array of post data to insert then insert a new post
+		$postdata = array(
+			'topic_id' => $topic_to->topic_id,
+			'post_text' => $excerpt,
+			'poster_id' => 0,
+		);
+		if ( !$post_ID = bb_insert_post( $postdata ) ) {
+			$this->error = new IXR_Error( 0, __( 'The pingback could not be added.' ) );
+			return $this->error;
+		}
+
+		// Add meta to let us know where the pingback came from
+		$link_from = str_replace( '&', '&amp;', $link_from );
+		$this->escape( $link_from );
+		bb_update_postmeta( $post_ID, 'pingback_uri', $link_from );
+
+		// Add the title to meta
+		$this->escape( $link_from_title );
+		bb_update_postmeta( $post_ID, 'pingback_title', $link_from_title );
+
+		// Action for plugins and what not
+		do_action( 'bb_pingback_post', $post_ID );
+
+		// Return success message, complete with emoticon
+		return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $link_from, $link_to );
+	}
+
+
+
+	/**
+	 * Returns an array of URLs that pingbacked the given URL
+	 *
+	 * @since 1.0
+	 * @link http://www.aquarionics.com/misc/archives/blogite/0198.html
+	 * @return array The array of URLs that pingbacked the given topic
+	 * @param array $args Arguments passed by the XML-RPC call
+	 * @param string $args[0] The full URI of the post where the pingback is being sent from
+	 * @param string $args[1] The full URI of the post where the pingback is being sent to
+	 *
+	 * XML-RPC request to get all pingbacks on a topic
+	 * <methodCall>
+	 *     <methodName>pingback.ping</methodName>
+	 *     <params>
+	 *         <param><value><string>http://example.com/2008/08/post-tobe-queried/</string></value></param>
+	 *     </params>
+	 * </methodCall>
+	 */
+	function pingback_extensions_getPingbacks( $args )
+	{
+		do_action( 'bb_xmlrpc_call', 'pingback.extensions.getPingbacks' );
+
+		$this->escape( $args );
+
+		// Don't accept arrays of arguments
+		if ( is_array( $args ) ) {
+			$this->error = new IXR_Error( 404, __( 'The requested method only accepts one parameter.' ) );
+			return $this->error;
+		} else {
+			$url = (string) $args;
+		}
+
+		// Tidy up ampersands in the URI
+		$url = str_replace( '&amp;', '&', $url );
+		$url = str_replace( '&', '&amp;', $url );
+
+		// Check if the URI is in our site
+		if ( !bb_match_domains( $url, bb_get_uri() ) ) {
+			// These are not the droids you are looking for
+			$this->error = new IXR_Error( 0, __( 'The specified target URL is not on this domain.' ) );
+			return $this->error;
+		}
+
+		// Make sure the specified URI is in fact associated with a topic
+		if ( !$topic = bb_get_topic_from_uri( $url ) ) {
+			$this->error = new IXR_Error( 33, __( 'The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.' ) );
+			return $this->error;
+		}
+
+		// Grab the posts from the topic
+		$query = new BB_Query( 'post', array( 'topic_id' => $topic_to->topic_id, 'append_meta' => true ), 'get_thread' );
+		$posts_to = $query->results;
+		unset( $query );
+
+		// Check for pingbacks in the post meta data
+		$pingbacks = array();
+		foreach ( $posts_to as $post ) {
+			if ( isset( $post->pingback_uri ) ) {
+				$pingbacks[] = $post->pingback_uri;
+			}
+		}
+		unset( $post );
+
+		// This will return an empty array on failure
+		return $pingbacks;
+	}
+}
+
+
+
+/**
+ * Initialises the XML-RPC server
+ *
+ * @since 1.0
+ * @var object The instance of the XML-RPC server class
+ */
+$bb_xmlrpc_server = new BB_XMLRPC_Server();
Index: /tags/1.5-beta-3/bp-forums/bp-forums-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-actions.php	(revision 5057)
@@ -0,0 +1,5 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-admin.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-admin.php	(revision 5057)
@@ -0,0 +1,297 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_forums_add_admin_menu() {
+	global $bp;
+
+	if ( !is_super_admin() )
+		return false;
+
+	// Add the administration tab under the "Site Admin" tab for site administrators
+	$hook = add_submenu_page( 'bp-general-settings', __( 'Forums Setup', 'buddypress' ), __( 'Forums Setup', 'buddypress' ), 'manage_options', 'bb-forums-setup', "bp_forums_bbpress_admin" );
+	add_action( "admin_print_styles-$hook", 'bp_core_add_admin_menu_styles' );
+}
+add_action( bp_core_admin_hook(), 'bp_forums_add_admin_menu' );
+
+function bp_forums_bbpress_admin() {
+	global $bp;
+
+	$action = bp_get_admin_url( 'admin.php?page=bb-forums-setup&reinstall=1' );
+	?>
+
+	<div class="wrap">
+		<?php screen_icon( 'buddypress' ); ?>
+
+		<h2 class="nav-tab-wrapper">
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-general-settings'                 ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Components', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-page-settings'                    ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Pages', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bp-settings'                         ), 'admin.php' ) ); ?>" class="nav-tab"><?php _e( 'Settings', 'buddypress' ); ?></a>
+			<a href="<?php bp_admin_url( add_query_arg( array( 'page' => 'bb-forums-setup'                     ), 'admin.php' ) ); ?>" class="nav-tab nav-tab-active"><?php _e( 'Forum Setup', 'buddypress' ); ?></a>
+
+			<?php do_action( 'bp_admin_tabs' ); ?>
+		</h2>
+
+		<?php if ( isset( $_POST['submit'] ) ) : ?>
+
+			<div id="message" class="updated fade">
+				<p><?php _e( 'Settings Saved.', 'buddypress' ) ?></p>
+			</div>
+
+		<?php endif; ?>
+
+		<?php
+
+		if ( isset( $_REQUEST['reinstall'] ) || !bp_forums_is_installed_correctly() ) :
+
+			bp_forums_bbpress_install_wizard();
+
+		else : ?>
+
+			<p><?php printf( __( 'bbPress forum integration in BuddyPress has been set up correctly. If you are having problems you can <a href="%s" title="Reinstall bbPress">re-install</a>.', 'buddypress' ), $action ); ?>
+			<p><?php _e( 'NOTE: The forums directory will only work if your bbPress tables are in the same database as your WordPress tables. If you are not using an existing bbPress install you can ignore this message.', 'buddypress' ); ?></p>
+
+		<?php endif; ?>
+
+	</div>
+<?php
+}
+
+function bp_forums_bbpress_install_wizard() {
+	$post_url = network_admin_url( 'admin.php?page=bb-forums-setup' );
+
+	$step = isset( $_REQUEST['step'] ) ? $_REQUEST['step'] : '';
+
+	switch( $step ) {
+		case 'existing':
+			if ( 1 == (int)$_REQUEST['doinstall'] ) {
+				if ( !bp_forums_configure_existing_install() ) {
+					_e( 'The bb-config.php file was not found at that location, please try again.', 'buddypress' );
+				} else {
+					?>
+					<h3><?php _e( 'Forums were set up correctly using your existing bbPress install!', 'buddypress' ) ?></h3>
+					<p><?php _e( 'BuddyPress will now use its internal copy of bbPress to run the forums on your site. If you wish, you can remove your old bbPress installation files, as long as you keep the bb-config.php file in the same location.', 'buddypress' ) ?></p><?php
+				}
+			} else { ?>
+
+					<form action="" method="post">
+						<h3><?php _e( 'Existing bbPress Installation', 'buddypress' ) ?></h3>
+						<p><?php _e( "BuddyPress can make use of your existing bbPress install. Just provide the location of your <code>bb-config.php</code> file, and BuddyPress will do the rest.", 'buddypress' ) ?></p>
+						<p><label><code>bb-config.php</code> file location:</label><br /><input style="width: 50%" type="text" name="bbconfigloc" id="bbconfigloc" value="<?php echo str_replace( 'buddypress', '', $_SERVER['DOCUMENT_ROOT'] ) ?>" /></p>
+						<p><input type="submit" class="button-primary" value="<?php _e( 'Complete Installation', 'buddypress' ) ?>" /></p>
+						<input type="hidden" name="step" value="existing" />
+						<input type="hidden" name="doinstall" value="1" />
+						<?php wp_nonce_field( 'bp_forums_existing_install_init' ) ?>
+					</form>
+
+				<?php
+			}
+		break;
+
+		case 'new':
+			if ( isset( $_REQUEST['doinstall'] ) && 1 == (int)$_REQUEST['doinstall'] ) {
+				$result = bp_forums_bbpress_install();
+
+				switch ( $result ) {
+					case 1:
+						_e( '<p>All done! Configuration settings have been saved to the file <code>bb-config.php</code> in the root of your WordPress install.</p>', 'buddypress' );
+						break;
+					default:
+						// Just write the contents to screen
+						_e( '<p>A configuration file could not be created. No problem, but you will need to save the text shown below into a file named <code>bb-config.php</code> in the root directory of your WordPress installation before you can start using the forum functionality.</p>', 'buddypress' ); ?>
+
+						<textarea style="display:block; margin-top: 30px; width: 80%;" rows="50"><?php echo htmlspecialchars( $result ); ?></textarea>
+
+					<?php
+						break;
+				}
+			} else { ?>
+
+				<h3><?php _e( 'New bbPress Installation', 'buddypress' ) ?></h3>
+				<p><?php _e( "You've decided to set up a new installation of bbPress for forum management in BuddyPress. This is very simple and is usually just a one click
+				process. When you're ready, hit the link below.", 'buddypress' ) ?></p>
+				<p><a class="button-primary" href="<?php echo wp_nonce_url( $post_url . '&step=new&doinstall=1', 'bp_forums_new_install_init' ) ?>"><?php _e( 'Complete Installation', 'buddypress' ) ?></a></p>
+
+				<?php
+			}
+		break;
+
+		default:
+			if ( !file_exists( BP_PLUGIN_DIR . '/bp-forums/bbpress/' ) ) { ?>
+
+				<div id="message" class="error">
+					<p><?php printf( __( 'bbPress files were not found. To install the forums component you must download a copy of bbPress and make sure it is in the folder: "%s"', 'buddypress' ), 'wp-content/plugins/buddypress/bp-forums/bbpress/' ) ?></p>
+				</div>
+
+			<?php } else { ?>
+
+				<p><?php _e( 'Forums in BuddyPress make use of a bbPress installation to function. You can choose to either let BuddyPress set up a new bbPress install, or use an already existing bbPress install. Please choose one of the options below.', 'buddypress' ) ?></p>
+
+				<a class="button" href="<?php echo $post_url . '&step=new' ?>"><?php _e( 'Set up a new bbPress installation', 'buddypress' ) ?></a> &nbsp;
+				<a class="button" href="<?php echo $post_url . '&step=existing' ?>"><?php _e( 'Use an existing bbPress installation', 'buddypress' ) ?></a>
+
+			<?php }
+		break;
+	}
+}
+
+function bp_forums_configure_existing_install() {
+	global $wpdb, $bbdb;
+
+	check_admin_referer( 'bp_forums_existing_install_init' );
+
+	// Sanitize $_REQUEST['bbconfigloc']
+	$_REQUEST['bbconfigloc'] = apply_filters( 'bp_forums_bbconfig_location', $_REQUEST['bbconfigloc'] );
+
+	if ( false === strpos( $_REQUEST['bbconfigloc'], 'bb-config.php' ) ) {
+		if ( '/' != substr( $_REQUEST['bbconfigloc'], -1, 1 ) )
+			$_REQUEST['bbconfigloc'] .= '/';
+
+		$_REQUEST['bbconfigloc'] .= 'bb-config.php';
+	}
+
+	bp_update_option( 'bb-config-location', $_REQUEST['bbconfigloc'] );
+
+	if ( !file_exists( $_REQUEST['bbconfigloc'] ) )
+		return false;
+
+	return true;
+}
+
+function bp_forums_bbpress_install() {
+	global $wpdb, $bbdb, $bp;
+
+	check_admin_referer( 'bp_forums_new_install_init' );
+
+	// Create the bb-config.php file
+	$initial_write = bp_forums_bbpress_write(
+		BP_PLUGIN_DIR . '/bp-forums/bbpress/bb-config-sample.php',
+		ABSPATH . 'bb-config.php',
+		array(
+			"define( 'BBDB_NAME',"  => array( "'bbpress'",                     	"'" . DB_NAME . "'" ),
+			"define( 'BBDB_USER',"  => array( "'username'",                    	"'" . DB_USER . "'" ),
+			"define( 'BBDB_PASSWO"  => array( "'password'",                    	"'" . DB_PASSWORD . "'" ),
+			"define( 'BBDB_HOST',"  => array( "'localhost'",                   	"'" . DB_HOST . "'" ),
+			"define( 'BBDB_CHARSE"  => array( "'utf8'",                        	"'" . DB_CHARSET . "'" ),
+			"define( 'BBDB_COLLAT"  => array( "''",                            	"'" . DB_COLLATE . "'" ),
+			"define( 'BB_AUTH_KEY"  => array( "'put your unique phrase here'",  "'" . addslashes( AUTH_KEY ) . "'" ),
+			"define( 'BB_SECURE_A"  => array( "'put your unique phrase here'",  "'" . addslashes( SECURE_AUTH_KEY ) . "'" ),
+			"define( 'BB_LOGGED_I"  => array( "'put your unique phrase here'",  "'" . addslashes( LOGGED_IN_KEY ) . "'" ),
+			"define( 'BB_NONCE_KE"  => array( "'put your unique phrase here'",  "'" . addslashes( NONCE_KEY ) . "'" ),
+			"\$bb_table_prefix = '" => array( "'bb_'",                          "'" . $bp->table_prefix . "bb_'" ),
+			"define( 'BB_LANG', '"  => array( "''",                             "'" . get_locale() . "'" )
+		)
+	);
+
+	// Add the custom user and usermeta entries to the config file
+	if ( $initial_write == 1 )
+		$file = file_get_contents( ABSPATH . 'bb-config.php' );
+	else
+		$file = &$initial_write;
+
+	$file = trim( $file );
+	if ( '?>' == substr( $file, -2, 2 ) )
+		$file = substr( $file, 0, -2 );
+
+	$file .= "\n" .   '$bb->custom_user_table = \'' . $wpdb->users . '\';';
+	$file .= "\n" .   '$bb->custom_user_meta_table = \'' . $wpdb->usermeta . '\';';
+	$file .= "\n\n" . '$bb->uri = \'' . BP_PLUGIN_URL . '/bp-forums/bbpress/\';';
+	$file .= "\n" .   '$bb->name = \'' . get_blog_option( bp_get_root_blog_id(), 'blogname' ) . ' ' . __( 'Forums', 'buddypress' ) . '\';';
+
+	if ( is_multisite() )
+		$file .= "\n" .   '$bb->wordpress_mu_primary_blog_id = ' . bp_get_root_blog_id() . ';';
+
+	if ( defined( 'AUTH_SALT' ) )
+		$file .= "\n\n" . 'define(\'BB_AUTH_SALT\', \'' . addslashes( AUTH_SALT ) . '\');';
+
+	if ( defined( 'LOGGED_IN_SALT' ) )
+		$file .= "\n" .   'define(\'BB_LOGGED_IN_SALT\', \'' . addslashes( LOGGED_IN_SALT ) . '\');';
+
+	if ( defined( 'SECURE_AUTH_SALT' ) )
+		$file .= "\n" .   'define(\'BB_SECURE_AUTH_SALT\', \'' . addslashes( SECURE_AUTH_SALT ) . '\');';
+
+	$file .= "\n\n" . 'define(\'WP_AUTH_COOKIE_VERSION\', 2);';
+	$file .= "\n\n" . '?>';
+
+	if ( $initial_write == 1 ) {
+		$file_handle = fopen( ABSPATH . 'bb-config.php', 'w' );
+		fwrite( $file_handle, $file );
+		fclose( $file_handle );
+	} else {
+		$initial_write = $file;
+	}
+
+	bp_update_option( 'bb-config-location', ABSPATH . 'bb-config.php' );
+	return $initial_write;
+}
+
+function bp_forums_bbpress_write( $file_source, $file_target, $alterations ) {
+
+	if ( !$file_source || !file_exists( $file_source ) || !is_file( $file_source ) )
+		return -1;
+
+	if ( !$file_target )
+		$file_target = $file_source;
+
+	if ( !$alterations || !is_array( $alterations ) )
+		return -2;
+
+	// Get the existing lines in the file
+	$lines = file( $file_source );
+
+	// Initialise an array to store the modified lines
+	$modified_lines = array();
+
+	// Loop through the lines and modify them
+	foreach ( (array)$lines as $line ) {
+		if ( isset( $alterations[substr( $line, 0, 20 )] ) ) {
+			$alteration = $alterations[substr( $line, 0, 20 )];
+			$modified_lines[] = str_replace( $alteration[0], $alteration[1], $line );
+		} else {
+			$modified_lines[] = $line;
+		}
+	}
+
+	$writable = true;
+	if ( file_exists( $file_target ) ) {
+		if ( !is_writable( $file_target ) ) {
+			$writable = false;
+		}
+	} else {
+		$dir_target = dirname( $file_target );
+
+		if ( file_exists( $dir_target ) ) {
+			if ( !is_writable( $dir_target ) || !is_dir( $dir_target ) ) {
+				$writable = false;
+			}
+		} else {
+			$writable = false;
+		}
+	}
+
+	if ( empty( $writable ) )
+		return trim( join( null, $modified_lines ) );
+
+	// Open the file for writing - rewrites the whole file
+	$file_handle = fopen( $file_target, 'w' );
+
+	// Write lines one by one to avoid OS specific newline hassles
+	foreach ( (array)$modified_lines as $modified_line ) {
+		if ( false !== strpos( $modified_line, '?>' ) ) {
+			$modified_line = '?>';
+		}
+		fwrite( $file_handle, $modified_line );
+		if ( $modified_line == '?>' ) {
+			break;
+		}
+	}
+
+	// Close the config file
+	fclose( $file_handle );
+
+	@chmod( $file_target, 0666 );
+
+	return 1;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-bbpress-sa.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-bbpress-sa.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-bbpress-sa.php	(revision 5057)
@@ -0,0 +1,434 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_forums_load_bbpress() {
+	global $bp, $wpdb, $wp_roles, $current_user, $wp_users_object;
+	global $bb, $bbdb, $bb_table_prefix, $bb_current_user;
+	global $bb_roles, $wp_taxonomy_object, $bb_queries;
+
+	// Return if we've already run this function.
+	if ( is_object( $bbdb ) )
+		return;
+
+	if ( !bp_forums_is_installed_correctly() )
+		return false;
+
+	define( 'BB_PATH', BP_PLUGIN_DIR . '/bp-forums/bbpress/' );
+	define( 'BACKPRESS_PATH', BP_PLUGIN_DIR . '/bp-forums/bbpress/bb-includes/backpress/' );
+	define( 'BB_URL', BP_PLUGIN_URL . '/bp-forums/bbpress/' );
+	define( 'BB_INC', 'bb-includes/' );
+
+	require( BB_PATH . BB_INC . 'class.bb-query.php' );
+	require( BB_PATH . BB_INC . 'class.bb-walker.php' );
+
+	require( BB_PATH . BB_INC . 'functions.bb-core.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-forums.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-topics.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-posts.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-topic-tags.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-capabilities.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-meta.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-pluggable.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-formatting.php' );
+	require( BB_PATH . BB_INC . 'functions.bb-template.php' );
+
+	require( BACKPRESS_PATH . 'class.wp-taxonomy.php' );
+	require( BB_PATH . BB_INC . 'class.bb-taxonomy.php' );
+
+	require( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
+
+	$bb = new stdClass();
+	require( $bp->forums->bbconfig );
+
+	// Setup the global database connection
+	$bbdb = new BPDB ( BBDB_USER, BBDB_PASSWORD, BBDB_NAME, BBDB_HOST );
+
+	// Set the table names
+	$bbdb->forums             = $bb_table_prefix . 'forums';
+	$bbdb->meta               = $bb_table_prefix . 'meta';
+	$bbdb->posts              = $bb_table_prefix . 'posts';
+	$bbdb->terms              = $bb_table_prefix . 'terms';
+	$bbdb->term_relationships = $bb_table_prefix . 'term_relationships';
+	$bbdb->term_taxonomy      = $bb_table_prefix . 'term_taxonomy';
+	$bbdb->topics             = $bb_table_prefix . 'topics';
+
+	if ( isset( $bb->custom_user_table ) )
+		$bbdb->users = $bb->custom_user_table;
+	else
+		$bbdb->users = $wpdb->users;
+
+	if ( isset( $bb->custom_user_meta_table ) )
+		$bbdb->usermeta = $bb->custom_user_meta_table;
+	else
+		$bbdb->usermeta = $wpdb->usermeta;
+
+	$bbdb->prefix = $bb_table_prefix;
+
+	define( 'BB_INSTALLING', false );
+
+	if ( is_object( $wp_roles ) ) {
+		$bb_roles = $wp_roles;
+		bb_init_roles( $bb_roles );
+	}
+
+	do_action( 'bb_got_roles' );
+	do_action( 'bb_init'      );
+	do_action( 'init_roles'   );
+
+	$bb_current_user = $current_user;
+	$wp_users_object = new BP_Forums_BB_Auth;
+
+	if ( !isset( $wp_taxonomy_object ) )
+		$wp_taxonomy_object = new BB_Taxonomy( $bbdb );
+
+	$wp_taxonomy_object->register_taxonomy( 'bb_topic_tag', 'bb_topic' );
+
+	// Set a site id if there isn't one already
+	if ( !isset( $bb->site_id ) )
+		$bb->site_id = bp_get_root_blog_id();
+
+	// Check if the tables are installed, if not, install them
+	if ( !$tables_installed = (boolean) $bbdb->get_results( 'DESCRIBE `' . $bbdb->forums . '`;', ARRAY_A ) ) {
+		require( BB_PATH . 'bb-admin/includes/defaults.bb-schema.php' );
+
+		// Backticks and "IF NOT EXISTS" break the dbDelta function.
+		bp_bb_dbDelta( str_replace( ' IF NOT EXISTS', '', str_replace( '`', '', $bb_queries ) ) );
+
+		require( BB_PATH . 'bb-admin/includes/functions.bb-upgrade.php' );
+		bb_update_db_version();
+
+		// Set the site admins as the keymasters
+		$site_admins = get_site_option( 'site_admins', array('admin') );
+		foreach ( (array)$site_admins as $site_admin )
+			update_user_meta( bp_core_get_userid( $site_admin ), $bb_table_prefix . 'capabilities', array( 'keymaster' => true ) );
+
+		// Create the first forum.
+		bb_new_forum( array( 'forum_name' => 'Default Forum' ) );
+
+		// Set the site URI
+		bb_update_option( 'uri', BB_URL );
+	}
+
+	register_shutdown_function( create_function( '', 'do_action("bb_shutdown");' ) );
+}
+add_action( 'bbpress_init', 'bp_forums_load_bbpress' );
+
+/* WP to bbP wrapper functions */
+function bb_get_current_user() { global $current_user; return $current_user; }
+function bb_get_user( $user_id ) { return get_userdata( $user_id ); }
+function bb_cache_users( $users ) {}
+
+/**
+ * bbPress needs this class for its usermeta manipulation.
+ */
+class BP_Forums_BB_Auth {
+	function update_meta( $args = '' ) {
+		$defaults = array( 'id' => 0, 'meta_key' => null, 'meta_value' => null, 'meta_table' => 'usermeta', 'meta_field' => 'user_id', 'cache_group' => 'users' );
+		$args = wp_parse_args( $args, $defaults );
+		extract( $args, EXTR_SKIP );
+
+		return update_user_meta( $id, $meta_key, $meta_value );
+	}
+}
+
+/**
+ * bbPress needs the DB class to be BPDB, but we want to use WPDB, so we can
+ * extend it and use this.
+ */
+class BPDB extends WPDB {
+	var $db_servers = array();
+
+	function BPDB( $dbuser, $dbpassword, $dbname, $dbhost ) {
+		$this->__construct( $dbuser, $dbpassword, $dbname, $dbhost );
+	}
+
+	function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
+		parent::__construct( $dbuser, $dbpassword, $dbname, $dbhost );
+
+		$args = func_get_args();
+		$args = call_user_func_array( array( &$this, 'init' ), $args );
+
+		if ( $args['host'] )
+			$this->db_servers['dbh_global'] = $args;
+	}
+
+	/**
+	 * Determine if a database supports a particular feature.
+	 *
+	 * Overriden here to work around differences between bbPress', and WordPress', implementation differences.
+	 * In particular, when BuddyPress tries to run bbPress' SQL installation script, the collation check always
+	 * failed. The capability is long supported by WordPress' minimum required MySQL version, so this is safe.
+	 */
+	function has_cap( $db_cap, $_table_name='' ) {
+		if ( 'collation' == $db_cap )
+			return true;
+
+		return parent::has_cap( $db_cap );
+	}
+
+	/**
+	 * Initialises the class variables based on provided arguments.
+	 * Based on, and taken from, the BackPress class in turn taken from the 1.0 branch of bbPress.
+	 */
+	function init( $args ) {
+		if ( 4 == func_num_args() ) {
+			$args = array(
+				'user'     => $args,
+				'password' => func_get_arg( 1 ),
+				'name'     => func_get_arg( 2 ),
+				'host'     => func_get_arg( 3 ),
+				'charset'  => defined( 'BBDB_CHARSET' ) ? BBDB_CHARSET : false,
+				'collate'  => defined( 'BBDB_COLLATE' ) ? BBDB_COLLATE : false,
+			);
+		}
+
+		$defaults = array(
+			'user'     => false,
+			'password' => false,
+			'name'     => false,
+			'host'     => 'localhost',
+			'charset'  => false,
+			'collate'  => false,
+			'errors'   => false
+		);
+
+		return wp_parse_args( $args, $defaults );
+	}
+
+	function escape_deep( $data ) {
+		if ( is_array( $data ) ) {
+			foreach ( (array) $data as $k => $v ) {
+				if ( is_array( $v ) ) {
+					$data[$k] = $this->_escape( $v );
+				} else {
+					$data[$k] = $this->_real_escape( $v );
+				}
+			}
+		} else {
+			$data = $this->_real_escape( $data );
+		}
+
+		return $data;
+	}
+}
+
+/* BBPress needs this function to convert vars */
+function backpress_convert_object( &$object, $output ) {
+	if ( is_array( $object ) ) {
+		foreach ( array_keys( $object ) as $key )
+			backpress_convert_object( $object[$key], $output );
+	} else {
+		switch ( $output ) {
+			case OBJECT  : break;
+			case ARRAY_A : $object = get_object_vars($object); break;
+			case ARRAY_N : $object = array_values(get_object_vars($object)); break;
+		}
+	}
+}
+
+/**
+ * Copied from wp-admin/includes/upgrade.php, this will take care of creating
+ * the bbPress stand-alone tables without loading a conflicting WP Admin.
+ *
+ * @param unknown_type $queries
+ * @param unknown_type $execute
+ * @return unknown
+ */
+function bp_bb_dbDelta($queries, $execute = true) {
+	global $wpdb;
+
+	// Separate individual queries into an array
+	if ( !is_array($queries) ) {
+		$queries = explode( ';', $queries );
+		if ('' == $queries[count($queries) - 1]) array_pop($queries);
+	}
+
+	$cqueries = array(); // Creation Queries
+	$iqueries = array(); // Insertion Queries
+	$for_update = array();
+
+	// Create a tablename index for an array ($cqueries) of queries
+	foreach($queries as $qry) {
+		if (preg_match("|CREATE TABLE ([^ ]*)|", $qry, $matches)) {
+			$cqueries[trim( strtolower($matches[1]), '`' )] = $qry;
+			$for_update[$matches[1]] = 'Created table '.$matches[1];
+		} else if (preg_match("|CREATE DATABASE ([^ ]*)|", $qry, $matches)) {
+			array_unshift($cqueries, $qry);
+		} else if (preg_match("|INSERT INTO ([^ ]*)|", $qry, $matches)) {
+			$iqueries[] = $qry;
+		} else if (preg_match("|UPDATE ([^ ]*)|", $qry, $matches)) {
+			$iqueries[] = $qry;
+		} else {
+			// Unrecognized query type
+		}
+	}
+
+	// Check to see which tables and fields exist
+	if ($tables = $wpdb->get_col('SHOW TABLES;')) {
+		// For every table in the database
+		foreach ($tables as $table) {
+			// Upgrade global tables only for the main site. Don't upgrade at all if DO_NOT_UPGRADE_GLOBAL_TABLES is defined.
+			if ( in_array($table, $wpdb->tables('global')) && ( !is_main_site() || defined('DO_NOT_UPGRADE_GLOBAL_TABLES') ) )
+				continue;
+
+			// If a table query exists for the database table...
+			if ( array_key_exists(strtolower($table), $cqueries) ) {
+				// Clear the field and index arrays
+				$cfields = $indices = array();
+				// Get all of the field names in the query from between the parens
+				preg_match("|\((.*)\)|ms", $cqueries[strtolower($table)], $match2);
+				$qryline = trim($match2[1]);
+
+				// Separate field lines into an array
+				$flds = explode("\n", $qryline);
+
+				//echo "<hr/><pre>\n".print_r(strtolower($table), true).":\n".print_r($cqueries, true)."</pre><hr/>";
+
+				// For every field line specified in the query
+				foreach ($flds as $fld) {
+					// Extract the field name
+					preg_match("|^([^ ]*)|", trim($fld), $fvals);
+					$fieldname = trim( $fvals[1], '`' );
+
+					// Verify the found field name
+					$validfield = true;
+					switch (strtolower($fieldname)) {
+					case '':
+					case 'primary':
+					case 'index':
+					case 'fulltext':
+					case 'unique':
+					case 'key':
+						$validfield = false;
+						$indices[] = trim(trim($fld), ", \n");
+						break;
+					}
+					$fld = trim($fld);
+
+					// If it's a valid field, add it to the field array
+					if ($validfield) {
+						$cfields[strtolower($fieldname)] = trim($fld, ", \n");
+					}
+				}
+
+				// Fetch the table column structure from the database
+				$tablefields = $wpdb->get_results("DESCRIBE {$table};");
+
+				// For every field in the table
+				foreach ($tablefields as $tablefield) {
+					// If the table field exists in the field array...
+					if (array_key_exists(strtolower($tablefield->Field), $cfields)) {
+						// Get the field type from the query
+						preg_match("|".$tablefield->Field." ([^ ]*( unsigned)?)|i", $cfields[strtolower($tablefield->Field)], $matches);
+						$fieldtype = $matches[1];
+
+						// Is actual field type different from the field type in query?
+						if ($tablefield->Type != $fieldtype) {
+							// Add a query to change the column type
+							$cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN {$tablefield->Field} " . $cfields[strtolower($tablefield->Field)];
+							$for_update[$table.'.'.$tablefield->Field] = "Changed type of {$table}.{$tablefield->Field} from {$tablefield->Type} to {$fieldtype}";
+						}
+
+						// Get the default value from the array
+							//echo "{$cfields[strtolower($tablefield->Field)]}<br>";
+						if (preg_match("| DEFAULT '(.*)'|i", $cfields[strtolower($tablefield->Field)], $matches)) {
+							$default_value = $matches[1];
+							if ($tablefield->Default != $default_value) {
+								// Add a query to change the column's default value
+								$cqueries[] = "ALTER TABLE {$table} ALTER COLUMN {$tablefield->Field} SET DEFAULT '{$default_value}'";
+								$for_update[$table.'.'.$tablefield->Field] = "Changed default value of {$table}.{$tablefield->Field} from {$tablefield->Default} to {$default_value}";
+							}
+						}
+
+						// Remove the field from the array (so it's not added)
+						unset($cfields[strtolower($tablefield->Field)]);
+					} else {
+						// This field exists in the table, but not in the creation queries?
+					}
+				}
+
+				// For every remaining field specified for the table
+				foreach ($cfields as $fieldname => $fielddef) {
+					// Push a query line into $cqueries that adds the field to that table
+					$cqueries[] = "ALTER TABLE {$table} ADD COLUMN $fielddef";
+					$for_update[$table.'.'.$fieldname] = 'Added column '.$table.'.'.$fieldname;
+				}
+
+				// Index stuff goes here
+				// Fetch the table index structure from the database
+				$tableindices = $wpdb->get_results("SHOW INDEX FROM {$table};");
+
+				if ($tableindices) {
+					// Clear the index array
+					unset($index_ary);
+
+					// For every index in the table
+					foreach ($tableindices as $tableindex) {
+						// Add the index to the index data array
+						$keyname = $tableindex->Key_name;
+						$index_ary[$keyname]['columns'][] = array('fieldname' => $tableindex->Column_name, 'subpart' => $tableindex->Sub_part);
+						$index_ary[$keyname]['unique'] = ($tableindex->Non_unique == 0)?true:false;
+					}
+
+					// For each actual index in the index array
+					foreach ($index_ary as $index_name => $index_data) {
+						// Build a create string to compare to the query
+						$index_string = '';
+						if ($index_name == 'PRIMARY') {
+							$index_string .= 'PRIMARY ';
+						} else if($index_data['unique']) {
+							$index_string .= 'UNIQUE ';
+						}
+						$index_string .= 'KEY ';
+						if ($index_name != 'PRIMARY') {
+							$index_string .= $index_name;
+						}
+						$index_columns = '';
+						// For each column in the index
+						foreach ($index_data['columns'] as $column_data) {
+							if ($index_columns != '') $index_columns .= ',';
+							// Add the field to the column list string
+							$index_columns .= $column_data['fieldname'];
+							if ($column_data['subpart'] != '') {
+								$index_columns .= '('.$column_data['subpart'].')';
+							}
+						}
+						// Add the column list to the index create string
+						$index_string .= ' ('.$index_columns.')';
+						if (!(($aindex = array_search($index_string, $indices)) === false)) {
+							unset($indices[$aindex]);
+							//echo "<pre style=\"border:1px solid #ccc;margin-top:5px;\">{$table}:<br />Found index:".$index_string."</pre>\n";
+						}
+						//else echo "<pre style=\"border:1px solid #ccc;margin-top:5px;\">{$table}:<br /><b>Did not find index:</b>".$index_string."<br />".print_r($indices, true)."</pre>\n";
+					}
+				}
+
+				// For every remaining index specified for the table
+				foreach ( (array) $indices as $index ) {
+					// Push a query line into $cqueries that adds the index to that table
+					$cqueries[] = "ALTER TABLE {$table} ADD $index";
+					$for_update[$table.'.'.$fieldname] = 'Added index '.$table.' '.$index;
+				}
+
+				// Remove the original table creation query from processing
+				unset($cqueries[strtolower($table)]);
+				unset($for_update[strtolower($table)]);
+			} else {
+				// This table exists in the database, but not in the creation queries?
+			}
+		}
+	}
+
+	$allqueries = array_merge($cqueries, $iqueries);
+	if ($execute) {
+		foreach ($allqueries as $query) {
+			//echo "<pre style=\"border:1px solid #ccc;margin-top:5px;\">".print_r($query, true)."</pre>\n";
+			$wpdb->query($query);
+		}
+	}
+
+	return $for_update;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-bbpress.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-bbpress.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-bbpress.php	(revision 5057)
@@ -0,0 +1,8 @@
+<?php
+/**
+ * Placeholder for bbPress plugin bridge
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-filters.php	(revision 5057)
@@ -0,0 +1,206 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Apply WordPress defined filters */
+add_filter( 'bp_forums_bbconfig_location', 'wp_filter_kses', 1 );
+add_filter( 'bp_forums_bbconfig_location', 'esc_attr', 1 );
+
+add_filter( 'bp_get_the_topic_title', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'bp_forums_filter_kses', 1 );
+add_filter( 'bp_get_the_topic_post_content', 'bp_forums_filter_kses', 1 );
+
+add_filter( 'bp_get_the_topic_title', 'force_balance_tags' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'force_balance_tags' );
+add_filter( 'bp_get_the_topic_post_content', 'force_balance_tags' );
+
+add_filter( 'bp_get_the_topic_title', 'wptexturize' );
+add_filter( 'bp_get_the_topic_poster_name', 'wptexturize' );
+add_filter( 'bp_get_the_topic_last_poster_name', 'wptexturize' );
+add_filter( 'bp_get_the_topic_post_content', 'wptexturize' );
+add_filter( 'bp_get_the_topic_post_poster_name', 'wptexturize' );
+
+add_filter( 'bp_get_the_topic_title', 'convert_smilies' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'convert_smilies' );
+add_filter( 'bp_get_the_topic_post_content', 'convert_smilies' );
+
+add_filter( 'bp_get_the_topic_title', 'convert_chars' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'convert_chars' );
+add_filter( 'bp_get_the_topic_post_content', 'convert_chars' );
+
+add_filter( 'bp_get_the_topic_post_content', 'wpautop' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'wpautop' );
+
+add_filter( 'bp_get_the_topic_post_content', 'stripslashes_deep' );
+add_filter( 'bp_get_the_topic_title', 'stripslashes_deep' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'stripslashes_deep' );
+add_filter( 'bp_get_the_topic_poster_name', 'stripslashes_deep' );
+add_filter( 'bp_get_the_topic_last_poster_name', 'stripslashes_deep' );
+add_filter( 'bp_get_the_topic_object_name', 'stripslashes_deep' );
+
+add_filter( 'bp_get_the_topic_post_content', 'make_clickable', 9 );
+
+add_filter( 'bp_get_forum_topic_count_for_user', 'bp_core_number_format' );
+add_filter( 'bp_get_forum_topic_count', 'bp_core_number_format' );
+
+add_filter( 'bp_get_the_topic_title', 'bp_forums_make_nofollow_filter' );
+add_filter( 'bp_get_the_topic_latest_post_excerpt', 'bp_forums_make_nofollow_filter' );
+add_filter( 'bp_get_the_topic_post_content', 'bp_forums_make_nofollow_filter' );
+
+function bp_forums_filter_kses( $content ) {
+	global $allowedtags;
+
+	$forums_allowedtags = $allowedtags;
+	$forums_allowedtags['span'] = array();
+	$forums_allowedtags['span']['class'] = array();
+	$forums_allowedtags['div'] = array();
+	$forums_allowedtags['div']['class'] = array();
+	$forums_allowedtags['div']['id'] = array();
+	$forums_allowedtags['a']['class'] = array();
+	$forums_allowedtags['img'] = array();
+	$forums_allowedtags['br'] = array();
+	$forums_allowedtags['p'] = array();
+	$forums_allowedtags['img']['src'] = array();
+	$forums_allowedtags['img']['alt'] = array();
+	$forums_allowedtags['img']['class'] = array();
+	$forums_allowedtags['img']['width'] = array();
+	$forums_allowedtags['img']['height'] = array();
+	$forums_allowedtags['img']['class'] = array();
+	$forums_allowedtags['img']['id'] = array();
+	$forums_allowedtags['code'] = array();
+	$forums_allowedtags['blockquote'] = array();
+
+	$forums_allowedtags = apply_filters( 'bp_forums_allowed_tags', $forums_allowedtags );
+	return wp_kses( $content, $forums_allowedtags );
+}
+
+function bp_forums_filter_tag_link( $link, $tag, $page, $context ) {
+	global $bp;
+
+	return apply_filters( 'bp_forums_filter_tag_link', bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/tag/' . $tag . '/' );
+}
+add_filter( 'bb_get_tag_link', 'bp_forums_filter_tag_link', 10, 4);
+
+function bp_forums_make_nofollow_filter( $text ) {
+	return preg_replace_callback( '|<a (.+?)>|i', 'bp_forums_make_nofollow_filter_callback', $text );
+}
+	function bp_forums_make_nofollow_filter_callback( $matches ) {
+		$text = $matches[1];
+		$text = str_replace( array( ' rel="nofollow"', " rel='nofollow'"), '', $text );
+		return "<a $text rel=\"nofollow\">";
+	}
+
+/**
+ * bp_forums_add_forum_topic_to_page_title( $title )
+ *
+ * Append forum topic to page title
+ *
+ * @global object $bp
+ * @param string $title New page title; see bp_modify_page_title()
+ * @param string $title Original page title
+ * @param string $sep How to separate the various items within the page title.
+ * @param string $seplocation Direction to display title
+ * @return string
+ * @see bp_modify_page_title()
+ */
+function bp_forums_add_forum_topic_to_page_title( $title, $original_title, $sep, $seplocation  ) {
+	global $bp;
+
+	if ( bp_is_current_action( 'forum' ) && bp_is_action_variable( 'topic', 0 ) )
+		if ( bp_has_forum_topic_posts() )
+			$title .= bp_get_the_topic_title() . " $sep ";
+
+	return $title;
+}
+add_filter( 'bp_modify_page_title', 'bp_forums_add_forum_topic_to_page_title', 9, 4 );
+
+/**
+ * bp_forums_strip_mentions_on_post_edit( $title )
+ *
+ * Removes the anchor tag autogenerated for at-mentions when forum topics and posts are edited.
+ * Prevents embedded anchor tags.
+ *
+ * @global object $bp
+ * @param string $content
+ * @return string $content
+ */
+function bp_forums_strip_mentions_on_post_edit( $content ) {
+	global $bp;
+
+	$content = htmlspecialchars_decode( $content );
+
+	$pattern = "|<a href=&#039;" . bp_get_root_domain() . "/" . bp_get_members_root_slug() . "/[A-Za-z0-9-_\.]+/&#039; rel=&#039;nofollow&#039;>(@[A-Za-z0-9-_\.@]+)</a>|";
+
+	$content = preg_replace( $pattern, "$1", $content );
+
+	return $content;
+}
+add_filter( 'bp_get_the_topic_post_edit_text', 'bp_forums_strip_mentions_on_post_edit' );
+add_filter( 'bp_get_the_topic_text', 'bp_forums_strip_mentions_on_post_edit' );
+
+/**
+ * "REPLIED TO" SQL FILTERS
+ */
+
+/**
+ * Filters the get_topics_distinct portion of the Forums sql when on a user's Replied To page.
+ *
+ * This filter is added in bp_has_forum_topics()
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @global object $wpdb The WordPress database global
+ * @param string $sql
+ * @return string $sql
+ */
+function bp_forums_add_replied_distinct_sql( $sql ) {
+	global $wpdb;
+
+	$sql = $wpdb->prepare( "DISTINCT t.topic_id, " );
+
+	return $sql;
+}
+
+/**
+ * Filters the get_topics_join portion of the Forums sql when on a user's Replied To page.
+ *
+ * This filter is added in bp_has_forum_topics()
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @global object $bbdb The bbPress database global
+ * @global object $wpdb The WordPress database global
+ * @param string $sql
+ * @return string $sql
+ */
+function bp_forums_add_replied_join_sql( $sql ) {
+	global $bbdb, $wpdb;
+
+	$sql .= $wpdb->prepare( " LEFT JOIN $bbdb->posts p ON p.topic_id = t.topic_id " );
+
+	return $sql;
+}
+
+/**
+ * Filters the get_topics_where portion of the Forums sql when on a user's Replied To page.
+ *
+ * This filter is added in bp_has_forum_topics()
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @global object $wpdb The WordPress database global
+ * @param string $sql
+ * @return string $sql
+ */
+function bp_forums_add_replied_where_sql( $sql ) {
+	global $wpdb;
+
+	$sql .= $wpdb->prepare( " AND p.poster_id = %s ", bp_displayed_user_id() );
+
+	return $sql;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-functions.php	(revision 5057)
@@ -0,0 +1,617 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_forums_is_installed_correctly() {
+	global $bp;
+
+	if ( isset( $bp->forums->bbconfig ) && is_file( $bp->forums->bbconfig ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * Checks $bp pages global and looks for directory page
+ *
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @return bool True if set, False if empty
+ */
+function bp_forums_has_directory() {
+	global $bp;
+
+	return (bool) !empty( $bp->pages->forums->id );
+}
+
+/** Forum Functions ***********************************************************/
+
+function bp_forums_get_forum( $forum_id ) {
+	do_action( 'bbpress_init' );
+	return bb_get_forum( $forum_id );
+}
+
+function bp_forums_new_forum( $args = '' ) {
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'forum_name'        => '',
+		'forum_desc'        => '',
+		'forum_parent_id'   => bp_forums_parent_forum_id(),
+		'forum_order'       => false,
+		'forum_is_category' => 0
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bb_new_forum( array( 'forum_name' => stripslashes( $forum_name ), 'forum_desc' => stripslashes( $forum_desc ), 'forum_parent' => $forum_parent_id, 'forum_order' => $forum_order, 'forum_is_category' => $forum_is_category ) );
+}
+
+function bp_forums_update_forum( $args = '' ) {
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'forum_id'          => '',
+		'forum_name'        => '',
+		'forum_desc'        => '',
+		'forum_slug'        => '',
+		'forum_parent_id'   => bp_forums_parent_forum_id(),
+		'forum_order'       => false,
+		'forum_is_category' => 0
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bb_update_forum( array( 'forum_id' => (int)$forum_id, 'forum_name' => stripslashes( $forum_name ), 'forum_desc' => stripslashes( $forum_desc ), 'forum_slug' => stripslashes( $forum_slug ), 'forum_parent' => $forum_parent_id, 'forum_order' => $forum_order, 'forum_is_category' => $forum_is_category ) );
+}
+
+/** Topic Functions ***********************************************************/
+
+function bp_forums_get_forum_topics( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'type'          => 'newest',
+		'forum_id'      => false,
+		'user_id'       => false,
+		'page'          => 1,
+		'per_page'      => 15,
+		'offset'        => false,
+		'number'        => false,
+		'exclude'       => false,
+		'show_stickies' => 'all',
+		'filter'        => false // if $type = tag then filter is the tag name, otherwise it's terms to search on.
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( class_exists( 'BB_Query' ) ) {
+		switch ( $type ) {
+			case 'newest':
+				$query = new BB_Query( 'topic', array( 'forum_id' => $forum_id, 'topic_author_id' => $user_id, 'per_page' => $per_page, 'page' => $page, 'number' => $per_page, 'exclude' => $exclude, 'topic_title' => $filter, 'sticky' => $show_stickies, 'offset' => $offset, 'number' => $number ), 'get_latest_topics' );
+				$topics =& $query->results;
+				break;
+
+			case 'popular':
+				$query = new BB_Query( 'topic', array( 'forum_id' => $forum_id, 'topic_author_id' => $user_id, 'per_page' => $per_page, 'page' => $page, 'order_by' => 't.topic_posts', 'topic_title' => $filter, 'sticky' => $show_stickies ) );
+				$topics =& $query->results;
+				break;
+
+			case 'unreplied':
+				$query = new BB_Query( 'topic', array( 'forum_id' => $forum_id, 'topic_author_id' => $user_id, 'post_count' => 1, 'per_page' => $per_page, 'page' => $page, 'order_by' => 't.topic_time', 'topic_title' => $filter, 'sticky' => $show_stickies ) );
+				$topics =& $query->results;
+				break;
+
+			case 'tags':
+				$query = new BB_Query( 'topic', array( 'forum_id' => $forum_id, 'topic_author_id' => $user_id, 'tag' => $filter, 'per_page' => $per_page, 'page' => $page, 'order_by' => 't.topic_time', 'sticky' => $show_stickies ) );
+				$topics =& $query->results;
+				break;
+		}
+	} else {
+		$topics = array();
+	}
+
+	return apply_filters_ref_array( 'bp_forums_get_forum_topics', array( &$topics, &$r ) );
+}
+
+function bp_forums_get_topic_details( $topic_id ) {
+	do_action( 'bbpress_init' );
+
+	$query = new BB_Query( 'topic', 'topic_id=' . $topic_id . '&page=1' /* Page override so bbPress doesn't use the URI */ );
+
+	return $query->results[0];
+}
+
+function bp_forums_get_topic_id_from_slug( $topic_slug ) {
+	do_action( 'bbpress_init' );
+
+	if ( empty( $topic_slug ) )
+		return false;
+
+	return bb_get_id_from_slug( 'topic', $topic_slug );
+}
+
+function bp_forums_new_topic( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_title'            => '',
+		'topic_slug'             => '',
+		'topic_text'             => '',
+		'topic_poster'           => $bp->loggedin_user->id,       // accepts ids
+		'topic_poster_name'      => $bp->loggedin_user->fullname, // accept names
+		'topic_last_poster'      => $bp->loggedin_user->id,       // accepts ids
+		'topic_last_poster_name' => $bp->loggedin_user->fullname, // accept names
+		'topic_start_time'       => bp_core_current_time(),
+		'topic_time'             => bp_core_current_time(),
+		'topic_open'             => 1,
+		'topic_tags'             => false, // accepts array or comma delim
+		'forum_id'               => 0      // accepts ids or slugs
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	$topic_title = strip_tags( $topic_title );
+
+	if ( empty( $topic_title ) || !strlen( trim( $topic_title ) ) )
+		return false;
+
+	if ( empty( $topic_slug ) )
+		$topic_slug = sanitize_title( $topic_title );
+
+	if ( !$topic_id = bb_insert_topic( array( 'topic_title' => stripslashes( $topic_title ), 'topic_slug' => $topic_slug, 'topic_poster' => $topic_poster, 'topic_poster_name' => $topic_poster_name, 'topic_last_poster' => $topic_last_poster, 'topic_last_poster_name' => $topic_last_poster_name, 'topic_start_time' => $topic_start_time, 'topic_time' => $topic_time, 'topic_open' => $topic_open, 'forum_id' => (int)$forum_id, 'tags' => $topic_tags ) ) )
+		return false;
+
+	// Now insert the first post.
+	if ( !bp_forums_insert_post( array( 'topic_id' => $topic_id, 'post_text' => $topic_text, 'post_time' => $topic_time, 'poster_id' => $topic_poster ) ) )
+		return false;
+
+	do_action( 'bp_forums_new_topic', $topic_id );
+
+	return $topic_id;
+}
+
+function bp_forums_update_topic( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_id'    => false,
+		'topic_title' => '',
+		'topic_text'  => '',
+		'topic_tags'  => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	// bb_insert_topic() will append tags, but not remove them. So we remove all existing tags.
+	bb_remove_topic_tags( $topic_id );
+
+	if ( !$topic_id = bb_insert_topic( array( 'topic_id' => $topic_id, 'topic_title' => stripslashes( $topic_title ), 'tags' => $topic_tags ) ) )
+		return false;
+
+	if ( !$post = bb_get_first_post( $topic_id ) )
+		return false;
+
+	// Update the first post
+	if ( !$post = bp_forums_insert_post( array( 'post_id' => $post->post_id, 'topic_id' => $topic_id, 'post_text' => $topic_text, 'post_time' => $post->post_time, 'poster_id' => $post->poster_id, 'poster_ip' => $post->poster_ip, 'post_status' => $post->post_status, 'post_position' => $post->post_position ) ) )
+		return false;
+
+	return bp_forums_get_topic_details( $topic_id );
+}
+
+function bp_forums_sticky_topic( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_id' => false,
+		'mode'     => 'stick' // stick/unstick
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( 'stick' == $mode )
+		return bb_stick_topic( $topic_id );
+	else if ( 'unstick' == $mode )
+		return bb_unstick_topic( $topic_id );
+
+	return false;
+}
+
+function bp_forums_openclose_topic( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_id' => false,
+		'mode'     => 'close' // stick/unstick
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( 'close' == $mode )
+		return bb_close_topic( $topic_id );
+	else if ( 'open' == $mode )
+		return bb_open_topic( $topic_id );
+
+	return false;
+}
+
+function bp_forums_delete_topic( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_id' => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bb_delete_topic( $topic_id, 1 );
+}
+
+function bp_forums_total_topic_count() {
+	global $bbdb;
+
+	do_action( 'bbpress_init' );
+
+	if ( isset( $bbdb ) ) {
+		if ( bp_is_active( 'groups' ) ) {
+			$groups_table_sql = groups_add_forum_tables_sql();
+			$groups_where_sql = groups_add_forum_where_sql( "t.topic_status = 0" );
+		} else {
+			$groups_table_sql = '';
+			$groups_where_sql = "t.topic_status = 0";
+		}
+		$count = $bbdb->get_results( $bbdb->prepare( "SELECT t.topic_id FROM {$bbdb->topics} AS t {$groups_table_sql} WHERE {$groups_where_sql}" ) );
+		$count = count( (array)$count );
+	} else {
+		$count = 0;
+	}
+
+	return apply_filters( 'bp_forums_total_topic_count', $count );
+}
+
+function bp_forums_total_topic_count_for_user( $user_id = 0 ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	if ( !$user_id )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	if ( class_exists( 'BB_Query' ) ) {
+		$query = new BB_Query( 'topic', array( 'topic_author_id' => $user_id, 'page' => 1, 'per_page' => -1, 'count' => true ) );
+		$count = $query->count;
+		$query = null;
+	} else {
+		$count = 0;
+	}
+
+	return $count;
+}
+
+/**
+ * Return the total number of topics replied to by a given user
+ *
+ * Uses an unfortunate technique to count unique topics, due to limitations in BB_Query.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param int $user_id Defaults to displayed user, then to logged-in user
+ * @return int $count
+ */
+function bp_forums_total_replied_count_for_user( $user_id = 0 ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	if ( !$user_id )
+		$user_id = ( bp_displayed_user_id() ) ? bp_displayed_user_id() : bp_loggedin_user_id();
+
+	if ( !$user_id )
+		return 0;
+
+	if ( class_exists( 'BB_Query' ) ) {
+		$query = new BB_Query( 'post', array( 'post_author_id' => $user_id, 'page' => 1, 'per_page' => -1, 'count' => true ) );
+
+		// Count the unique topics. No better way to do this in the bbPress query API
+		$topics = array();
+		foreach( $query->results as $result ) {
+			if ( !in_array( $result->topic_id, $topics ) )
+				$topics[] = $result->topic_id;
+		}
+		$count = count( $topics );
+		$query = null;
+	} else {
+		$count = 0;
+	}
+
+	return apply_filters( 'bp_forums_total_replied_count_for_user', $count, $user_id );
+}
+
+function bp_forums_get_topic_extras( $topics ) {
+	global $bp, $wpdb, $bbdb;
+
+	if ( empty( $topics ) )
+		return $topics;
+
+	// Get the topic ids
+	foreach ( (array)$topics as $topic ) $topic_ids[] = $topic->topic_id;
+	$topic_ids = $wpdb->escape( join( ',', (array)$topic_ids ) );
+
+	// Fetch the topic's last poster details
+	$poster_details = $wpdb->get_results( $wpdb->prepare( "SELECT t.topic_id, t.topic_last_poster, u.user_login, u.user_nicename, u.user_email, u.display_name FROM {$wpdb->users} u, {$bbdb->topics} t WHERE u.ID = t.topic_last_poster AND t.topic_id IN ( {$topic_ids} )" ) );
+	for ( $i = 0, $count = count( $topics ); $i < $count; ++$i ) {
+		foreach ( (array)$poster_details as $poster ) {
+			if ( $poster->topic_id == $topics[$i]->topic_id ) {
+				$topics[$i]->topic_last_poster_email       = $poster->user_email;
+				$topics[$i]->topic_last_poster_nicename    = $poster->user_nicename;
+				$topics[$i]->topic_last_poster_login       = $poster->user_login;
+				$topics[$i]->topic_last_poster_displayname = $poster->display_name;
+			}
+		}
+	}
+
+	// Fetch fullname for the topic's last poster
+	if ( bp_is_active( 'xprofile' ) ) {
+		$poster_names = $wpdb->get_results( $wpdb->prepare( "SELECT t.topic_id, pd.value FROM {$bp->profile->table_name_data} pd, {$bbdb->topics} t WHERE pd.user_id = t.topic_last_poster AND pd.field_id = 1 AND t.topic_id IN ( {$topic_ids} )" ) );
+		for ( $i = 0, $count = count( $topics ); $i < $count; ++$i ) {
+			foreach ( (array)$poster_names as $name ) {
+				if ( $name->topic_id == $topics[$i]->topic_id )
+					$topics[$i]->topic_last_poster_displayname = $name->value;
+			}
+		}
+	}
+
+	return $topics;
+}
+
+/** Post Functions ************************************************************/
+
+function bp_forums_get_topic_posts( $args = '' ) {
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'topic_id' => false,
+		'page'     => 1,
+		'per_page' => 15,
+		'order'    => 'ASC'
+	);
+
+	$args  = wp_parse_args( $args, $defaults );
+	$query = new BB_Query( 'post', $args, 'get_thread' );
+
+	return bp_forums_get_post_extras( $query->results );
+}
+
+function bp_forums_get_post( $post_id ) {
+	do_action( 'bbpress_init' );
+	return bb_get_post( $post_id );
+}
+
+function bp_forums_delete_post( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'post_id' => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bb_delete_post( $post_id, 1 );
+}
+
+function bp_forums_insert_post( $args = '' ) {
+	global $bp;
+
+	do_action( 'bbpress_init' );
+
+	$defaults = array(
+		'post_id'       => false,
+		'topic_id'      => false,
+		'post_text'     => '',
+		'post_time'     => bp_core_current_time(),
+		'poster_id'     => $bp->loggedin_user->id, // accepts ids or names
+		'poster_ip'     => $_SERVER['REMOTE_ADDR'],
+		'post_status'   => 0, // use bb_delete_post() instead
+		'post_position' => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( !$post = bp_forums_get_post( $post_id ) )
+		$post_id = false;
+
+	if ( !isset( $topic_id ) )
+		$topic_id = $post->topic_id;
+
+	if ( empty( $post_text ) )
+		$post_text = $post->post_text;
+
+	if ( !isset( $post_time ) )
+		$post_time = $post->post_time;
+
+	if ( !isset( $post_position ) )
+		$post_position = $post->post_position;
+
+	$post_id = bb_insert_post( array( 'post_id' => $post_id, 'topic_id' => $topic_id, 'post_text' => stripslashes( trim( $post_text ) ), 'post_time' => $post_time, 'poster_id' => $poster_id, 'poster_ip' => $poster_ip, 'post_status' => $post_status, 'post_position' => $post_position ) );
+
+	if ( !empty( $post_id ) )
+		do_action( 'bp_forums_new_post', $post_id );
+
+	return $post_id;
+}
+
+function bp_forums_get_post_extras( $posts ) {
+	global $bp, $wpdb;
+
+	if ( empty( $posts ) )
+		return $posts;
+
+	// Get the user ids
+	foreach ( (array)$posts as $post ) $user_ids[] = $post->poster_id;
+	$user_ids = $wpdb->escape( join( ',', (array)$user_ids ) );
+
+	// Fetch the poster's user_email, user_nicename and user_login
+	$poster_details = $wpdb->get_results( $wpdb->prepare( "SELECT u.ID as user_id, u.user_login, u.user_nicename, u.user_email, u.display_name FROM {$wpdb->users} u WHERE u.ID IN ( {$user_ids} )" ) );
+
+	for ( $i = 0, $count = count( $posts ); $i < $count; ++$i ) {
+		foreach ( (array)$poster_details as $poster ) {
+			if ( $poster->user_id == $posts[$i]->poster_id ) {
+				$posts[$i]->poster_email    = $poster->user_email;
+				$posts[$i]->poster_login    = $poster->user_nicename;
+				$posts[$i]->poster_nicename = $poster->user_login;
+				$posts[$i]->poster_name     = $poster->display_name;
+			}
+		}
+	}
+
+	// Fetch fullname for each poster.
+	if ( bp_is_active( 'xprofile' ) ) {
+		$poster_names = $wpdb->get_results( $wpdb->prepare( "SELECT pd.user_id, pd.value FROM {$bp->profile->table_name_data} pd WHERE pd.user_id IN ( {$user_ids} )" ) );
+		for ( $i = 0, $count = count( $posts ); $i < $count; ++$i ) {
+			foreach ( (array)$poster_names as $name ) {
+				if ( isset( $topics[$i] ) && $name->user_id == $topics[$i]->user_id )
+				$posts[$i]->poster_name = $poster->value;
+			}
+		}
+	}
+
+	return apply_filters( 'bp_forums_get_post_extras', $posts );
+}
+
+function bp_forums_get_forum_topicpost_count( $forum_id ) {
+	global $wpdb, $bbdb;
+
+	do_action( 'bbpress_init' );
+
+	// Need to find a bbPress function that does this
+	return $wpdb->get_results( $wpdb->prepare( "SELECT topics, posts from {$bbdb->forums} WHERE forum_id = %d", $forum_id ) );
+}
+
+function bp_forums_filter_caps( $allcaps ) {
+	global $bp, $wp_roles, $bb_table_prefix;
+
+	if ( !isset( $bp->loggedin_user->id ) )
+		return $allcaps;
+
+	$bb_cap = get_user_meta( $bp->loggedin_user->id, $bb_table_prefix . 'capabilities', true );
+
+	if ( empty( $bb_cap ) )
+		return $allcaps;
+
+	$bb_cap = array_keys($bb_cap);
+	$bb_cap = $wp_roles->get_role( $bb_cap[0] );
+	$bb_cap = $bb_cap->capabilities;
+
+	return array_merge( (array) $allcaps, (array) $bb_cap );
+}
+add_filter( 'user_has_cap', 'bp_forums_filter_caps' );
+
+/**
+ * Returns the parent forum id for the bbPress abstraction layer
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return int
+ */
+function bp_forums_parent_forum_id() {
+	return apply_filters( 'bp_forums_parent_forum_id', BP_FORUMS_PARENT_FORUM_ID );
+}
+
+/**
+ * Should sticky topics be broken out of regular topic order on forum directories?
+ *
+ * Defaults to false. Define BP_FORUMS_ENABLE_GLOBAL_DIRECTORY_STICKIES, or filter
+ * bp_forums_enable_global_directory_stickies, to change this behavior.
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return bool True if stickies should be displayed at the top of the global directory, false
+ *    otherwise.
+ */
+function bp_forums_enable_global_directory_stickies() {
+	return apply_filters( 'bp_forums_enable_global_directory_stickies', defined( 'BP_FORUMS_ENABLE_GLOBAL_DIRECTORY_STICKIES' ) && BP_FORUMS_ENABLE_GLOBAL_DIRECTORY_STICKIES );
+}
+
+
+/********************************************************************************
+ * Caching
+ *
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'bp_forums_new_forum', 'bp_core_clear_cache' );
+add_action( 'bp_forums_new_topic', 'bp_core_clear_cache' );
+add_action( 'bp_forums_new_post',  'bp_core_clear_cache' );
+
+
+/** Embeds *******************************************************************/
+
+/**
+ * Grabs the topic post ID and attempts to retrieve the oEmbed cache (if it exists)
+ * during the forum topic loop.  If no cache and link is embeddable, cache it.
+ *
+ * @see BP_Embed
+ * @see bp_embed_forum_cache()
+ * @see bp_embed_forum_save_cache()
+ * @package BuddyPress_Forums
+ * @since 1.5
+ */
+function bp_forums_embed() {
+	add_filter( 'embed_post_id',         'bp_get_the_topic_post_id'         );
+	add_filter( 'bp_embed_get_cache',    'bp_embed_forum_cache',      10, 3 );
+	add_action( 'bp_embed_update_cache', 'bp_embed_forum_save_cache', 10, 3 );
+}
+add_action( 'topic_loop_start', 'bp_forums_embed' );
+
+/**
+ * Wrapper function for {@link bb_get_postmeta()}.
+ * Used during {@link BP_Embed::parse_oembed()} via {@link bp_forums_embed()}.
+ *
+ * @package BuddyPress_Forums
+ * @since 1.5
+ */
+function bp_embed_forum_cache( $cache, $id, $cachekey ) {
+	return bb_get_postmeta( $id, $cachekey );
+}
+
+/**
+ * Wrapper function for {@link bb_update_postmeta()}.
+ * Used during {@link BP_Embed::parse_oembed()} via {@link bp_forums_embed()}.
+ *
+ * @package BuddyPress_Forums
+ * @since 1.5
+ */
+function bp_embed_forum_save_cache( $cache, $cachekey, $id ) {
+	bb_update_postmeta( $id, $cachekey, $cache );
+}
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-loader.php	(revision 5057)
@@ -0,0 +1,250 @@
+<?php
+/**
+ * BuddyPress Forums Loader
+ *
+ * A discussion forums component. Comes bundled with bbPress stand-alone.
+ *
+ * @package BuddyPress
+ * @subpackage Forums Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Forums_Component extends BP_Component {
+
+	/**
+	 * Start the forums component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'forums',
+			__( 'Discussion Forums', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_FORUMS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define the parent forum ID
+		if ( !defined( 'BP_FORUMS_PARENT_FORUM_ID' ) )
+			define( 'BP_FORUMS_PARENT_FORUM_ID', 1 );
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_FORUMS_SLUG' ) )
+			define( 'BP_FORUMS_SLUG', $this->id );
+
+		// The location of the bbPress stand-alone config file
+		if ( isset( $bp->site_options['bb-config-location'] ) )
+			$this->bbconfig = $bp->site_options['bb-config-location'];
+
+		// All globals for messaging component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_FORUMS_SLUG,
+			'root_slug'             => isset( $bp->pages->forums->slug ) ? $bp->pages->forums->slug : BP_FORUMS_SLUG,
+			'has_directory'         => true,
+			'notification_callback' => 'messages_format_notifications',
+			'search_string'         => __( 'Search Forums...', 'buddypress' ),
+		);
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+
+		// Files to include
+		$includes = array(
+			'actions',
+			'screens',
+			'classes',
+			'filters',
+			'template',
+			'functions',
+		);
+
+		// Admin area
+		if ( is_admin() )
+			$includes[] = 'admin';
+
+		// bbPress stand-alone
+		if ( !defined( 'BB_PATH' ) )
+			$includes[] = 'bbpress-sa';
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Stop if forums haven't been set up yet
+		if ( !bp_forums_is_installed_correctly() )
+			return;
+
+		// Stop if there is no user displayed or logged in
+		if ( !is_user_logged_in() && !isset( $bp->displayed_user->id ) )
+			return;
+
+		// Add 'Forums' to the main navigation
+		$main_nav = array(
+			'name'                => __( 'Forums', 'buddypress' ),
+			'slug'                => $this->slug,
+			'position'            => 80,
+			'screen_function'     => 'bp_member_forums_screen_topics',
+			'default_subnav_slug' => 'topics',
+			'item_css_id'         => $this->id
+		);
+
+		// Determine user to use
+		if ( isset( $bp->displayed_user->domain ) ) {
+			$user_domain = $bp->displayed_user->domain;
+			$user_login  = $bp->displayed_user->userdata->user_login;
+		} elseif ( isset( $bp->loggedin_user->domain ) ) {
+			$user_domain = $bp->loggedin_user->domain;
+			$user_login  = $bp->loggedin_user->userdata->user_login;
+		} else {
+			return;
+		}
+
+		// User link
+		$forums_link = trailingslashit( $user_domain . $this->slug );
+
+		// Additional menu if friends is active
+		$sub_nav[] = array(
+			'name'            => __( 'Topics Started', 'buddypress' ),
+			'slug'            => 'topics',
+			'parent_url'      => $forums_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_member_forums_screen_topics',
+			'position'        => 20,
+			'item_css_id'     => 'topics'
+		);
+
+		// Additional menu if friends is active
+		$sub_nav[] = array(
+			'name'            => __( 'Replied To', 'buddypress' ),
+			'slug'            => 'replies',
+			'parent_url'      => $forums_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_member_forums_screen_replies',
+			'position'        => 40,
+			'item_css_id'     => 'replies'
+		);
+
+		// Favorite forums items. Disabled until future release.
+		/*
+		$sub_nav[] = array(
+			'name'            => __( 'Favorites', 'buddypress' ),
+			'slug'            => 'favorites',
+			'parent_url'      => $forums_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_member_forums_screen_favorites',
+			'position'        => 60,
+			'item_css_id'     => 'favorites'
+		);
+		*/
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain = $bp->loggedin_user->domain;
+			$user_login  = $bp->loggedin_user->userdata->user_login;
+			$forums_link = trailingslashit( $user_domain . $this->slug );
+
+			// Add the "My Account" sub menus
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => __( 'Forums', 'buddypress' ),
+				'href'   => trailingslashit( $forums_link )
+			);
+
+			// Topics
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Topics Started', 'buddypress' ),
+				'href'   => trailingslashit( $forums_link . 'topics' )
+			);
+
+			// Replies
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Replies', 'buddypress' ),
+				'href'   => trailingslashit( $forums_link . 'replies' )
+			);
+
+			// Favorites
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Favorite Topics', 'buddypress' ),
+				'href'   => trailingslashit( $forums_link . 'favorites' )
+			);
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		// Adjust title based on view
+		if ( bp_is_forums_component() ) {
+			if ( bp_is_my_profile() ) {
+				$bp->bp_options_title = __( 'Forums', 'buddypress' );
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title  = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the forums component
+$bp->forums = new BP_Forums_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-screens.php	(revision 5057)
@@ -0,0 +1,127 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_forums_directory_forums_setup() {
+	global $bp;
+
+	if ( bp_is_forums_component() && ( !bp_current_action() || ( 'tag' == bp_current_action() && bp_action_variables() ) ) && !bp_current_item() ) {
+		if ( !bp_forums_has_directory() )
+			return false;
+
+		if ( !bp_forums_is_installed_correctly() ) {
+			bp_core_add_message( __( 'The forums component has not been set up yet.', 'buddypress' ), 'error' );
+			bp_core_redirect( bp_get_root_domain() );
+		}
+
+		bp_update_is_directory( true, 'forums' );
+
+		do_action( 'bbpress_init' );
+
+		// Check to see if the user has posted a new topic from the forums page.
+		if ( isset( $_POST['submit_topic'] ) && bp_is_active( 'forums' ) ) {
+			check_admin_referer( 'bp_forums_new_topic' );
+
+			$bp->groups->current_group = groups_get_group( array( 'group_id' => $_POST['topic_group_id'] ) );
+			if ( !empty( $bp->groups->current_group->id ) ) {
+				// Auto join this user if they are not yet a member of this group
+				if ( !is_super_admin() && 'public' == $bp->groups->current_group->status && !groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) )
+					groups_join_group( $bp->groups->current_group->id );
+
+				$error_message = '';
+
+				$forum_id = groups_get_groupmeta( $bp->groups->current_group->id, 'forum_id' );
+				if ( !empty( $forum_id ) ) {
+					if ( empty( $_POST['topic_title'] ) )
+						$error_message = __( 'Please provide a title for your forum topic.', 'buddypress' );
+					else if ( empty( $_POST['topic_text'] ) )
+						$error_message = __( 'Forum posts cannot be empty. Please enter some text.', 'buddypress' );
+
+					if ( $error_message ) {
+						bp_core_add_message( $error_message, 'error' );
+						$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum';
+					} else {
+						if ( !$topic = groups_new_group_forum_topic( $_POST['topic_title'], $_POST['topic_text'], $_POST['topic_tags'], $forum_id ) ) {
+							bp_core_add_message( __( 'There was an error when creating the topic', 'buddypress'), 'error' );
+							$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum';
+						} else {
+							bp_core_add_message( __( 'The topic was created successfully', 'buddypress') );
+							$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/';
+						}
+					}
+
+					bp_core_redirect( $redirect );
+
+				} else {
+					bp_core_add_message( __( 'Please pick the group forum where you would like to post this topic.', 'buddypress' ), 'error' );
+					bp_core_redirect( add_query_arg( 'new', '', bp_get_forums_directory_permalink() ) );
+				}
+
+			}	 else {
+				bp_core_add_message( __( 'Please pick the group forum where you would like to post this topic.', 'buddypress' ), 'error' );
+				bp_core_redirect( add_query_arg( 'new', '', bp_get_forum_directory_permalink() ) );
+			}
+		}
+
+		do_action( 'bp_forums_directory_forums_setup' );
+
+		bp_core_load_template( apply_filters( 'bp_forums_template_directory_forums_setup', 'forums/index' ) );
+	}
+}
+add_action( 'bp_screens', 'bp_forums_directory_forums_setup', 2 );
+
+function bp_member_forums_screen_topics() {
+	global $bp;
+
+	do_action( 'bp_member_forums_screen_topics' );
+
+	bp_core_load_template( apply_filters( 'bp_member_forums_screen_topics', 'members/single/home' ) );
+}
+
+function bp_member_forums_screen_replies() {
+	global $bp;
+
+	do_action( 'bp_member_forums_screen_replies' );
+
+	bp_core_load_template( apply_filters( 'bp_member_forums_screen_replies', 'members/single/home' ) );
+}
+
+/**
+ * Loads the template content for a user's Favorites forum tab.
+ *
+ * Note that this feature is not fully implemented at the moment.
+ *
+ * @package BuddyPress Forums
+ */
+function bp_member_forums_screen_favorites() {
+	global $bp;
+
+	do_action( 'bp_member_forums_screen_favorites' );
+
+	bp_core_load_template( apply_filters( 'bp_member_forums_screen_favorites', 'members/single/home' ) );
+}
+
+function bp_forums_screen_single_forum() {
+	global $bp;
+
+	if ( !bp_is_forums_component() || !bp_is_current_action( 'forum' ) || !bp_action_variable( 0 ) )
+		return false;
+
+	do_action( 'bp_forums_screen_single_forum' );
+
+	bp_core_load_template( apply_filters( 'bp_forums_screen_single_forum', 'forums/single/forum' ) );
+}
+add_action( 'bp_screens', 'bp_forums_screen_single_forum' );
+
+function bp_forums_screen_single_topic() {
+	global $bp;
+
+	if ( !bp_is_forums_component() || !bp_is_current_action( 'topic' ) || !bp_action_variable( 0 ) )
+		return false;
+
+	do_action( 'bp_forums_screen_single_topic' );
+
+	bp_core_load_template( apply_filters( 'bp_forums_screen_single_topic', 'forums/single/topic' ) );
+}
+add_action( 'bp_screens', 'bp_forums_screen_single_topic' );
+?>
Index: /tags/1.5-beta-3/bp-forums/bp-forums-template.php
===================================================================
--- /tags/1.5-beta-3/bp-forums/bp-forums-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-forums/bp-forums-template.php	(revision 5057)
@@ -0,0 +1,1405 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the forums component slug
+ *
+ * @package BuddyPress
+ * @subpackage Forums Template
+ * @since 1.5
+ *
+ * @uses bp_get_forums_slug()
+ */
+function bp_forums_slug() {
+	echo bp_get_forums_slug();
+}
+	/**
+	 * Return the forums component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Forums Template
+	 * @since 1.5
+	 */
+	function bp_get_forums_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_forums_slug', $bp->forums->slug );
+	}
+
+/**
+ * Output the forums component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Forums Template
+ * @since 1.5
+ *
+ * @uses bp_get_forums_root_slug()
+ */
+function bp_forums_root_slug() {
+	echo bp_get_forums_root_slug();
+}
+	/**
+	 * Return the forums component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Forums Template
+	 * @since 1.5
+	 */
+	function bp_get_forums_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_forums_root_slug', $bp->forums->root_slug );
+	}
+
+/**
+ * Output forum directory permalink
+ *
+ * @package BuddyPress
+ * @subpackage Forums Template
+ * @since 1.5
+ * @uses bp_get_forums_directory_permalink()
+ */
+function bp_forums_directory_permalink() {
+	echo bp_get_forums_directory_permalink();
+}
+	/**
+	 * Return forum directory permalink
+	 *
+	 * @package BuddyPress
+	 * @subpackage Forums Template
+	 * @since 1.5
+	 * @uses apply_filters()
+	 * @uses traisingslashit()
+	 * @uses bp_get_root_domain()
+	 * @uses bp_get_forums_root_slug()
+	 * @return string
+	 */
+	function bp_get_forums_directory_permalink() {
+		return apply_filters( 'bp_get_forums_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_forums_root_slug() ) );
+	}
+
+class BP_Forums_Template_Forum {
+	var $current_topic = -1;
+	var $topic_count;
+	var $topics;
+	var $topic;
+
+	var $forum_id;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_topic_count;
+
+	var $single_topic = false;
+
+	var $sort_by;
+	var $order;
+
+	function BP_Forums_Template_Forum( $type, $forum_id, $user_id, $page, $per_page, $max, $no_stickies, $search_terms, $offset = false, $number = false ) {
+		$this->__construct( $type, $forum_id, $user_id, $page, $per_page, $max, $no_stickies, $search_terms, $offset, $number );
+	}
+
+	function __construct( $type, $forum_id, $user_id, $page, $per_page, $max, $no_stickies, $search_terms, $offset = false, $number = false ) {
+		global $bp;
+
+		$this->pag_page     = $page;
+		$this->pag_num      = $per_page;
+		$this->type         = $type;
+		$this->search_terms = $search_terms;
+		$this->forum_id     = $forum_id;
+		$this->offset	    = $offset;
+		$this->number	    = $number;
+
+		switch ( $type ) {
+			case 'newest': default:
+				$this->topics = bp_forums_get_forum_topics( array( 'user_id' => $user_id, 'forum_id' => $forum_id, 'filter' => $search_terms, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'show_stickies' => $no_stickies, 'offset' => $offset, 'number' => $number ) );
+				break;
+
+			case 'popular':
+				$this->topics = bp_forums_get_forum_topics( array( 'user_id' => $user_id, 'type' => 'popular', 'filter' => $search_terms, 'forum_id' => $forum_id, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'show_stickies' => $no_stickies ) );
+				break;
+
+			case 'unreplied':
+				$this->topics = bp_forums_get_forum_topics( array( 'user_id' => $user_id, 'type' => 'unreplied', 'filter' => $search_terms, 'forum_id' => $forum_id, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'show_stickies' => $no_stickies ) );
+				break;
+
+			case 'tags':
+				$this->topics = bp_forums_get_forum_topics( array( 'user_id' => $user_id, 'type' => 'tags', 'filter' => $search_terms, 'forum_id' => $forum_id, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'show_stickies' => $no_stickies ) );
+				break;
+		}
+
+		$this->topics = apply_filters( 'bp_forums_template_topics', $this->topics, $type, $forum_id, $per_page, $max, $no_stickies );
+
+		if ( !(int)$this->topics ) {
+			$this->topic_count       = 0;
+			$this->total_topic_count = 0;
+		} else {
+			if ( !empty( $forum_id ) ) {
+				$topic_count = bp_forums_get_forum( $forum_id );
+				$topic_count = (int)$topic_count->topics;
+			} else if ( !empty( $bp->groups->current_group ) ) {
+				$topic_count = (int)groups_total_public_forum_topic_count( $type );
+			} else if ( bp_is_user_forums_started() ) {
+				$topic_count = bp_forums_total_topic_count_for_user( bp_displayed_user_id() );
+			} else if ( bp_is_user_forums_replied_to() ) {
+				$topic_count = bp_forums_total_replied_count_for_user( bp_displayed_user_id() );
+			} else {
+				// For forum directories, get a true count
+				$status = is_super_admin() ? 'all' : 'public'; // todo: member-of
+				$topic_count = (int)groups_total_forum_topic_count( $status, $search_terms );
+			}
+
+			if ( !$max || $max >= $topic_count ) {
+				$this->total_topic_count = $topic_count;
+			} else {
+				$this->total_topic_count = (int)$max;
+			}
+
+			if ( $max ) {
+				if ( $max >= count($this->topics) ) {
+					$this->topic_count = count( $this->topics );
+				} else {
+					$this->topic_count = (int)$max;
+				}
+			} else {
+				$this->topic_count = count( $this->topics );
+			}
+		}
+
+		$this->topic_count       = apply_filters_ref_array( 'bp_forums_template_topic_count',                                 array( $this->topic_count, &$this->topics, $type, $forum_id, $per_page, $max, $no_stickies ) );
+		$this->total_topic_count = apply_filters_ref_array( 'bp_forums_template_total_topic_count', array( $this->total_topic_count, $this->topic_count, &$this->topics, $type, $forum_id, $per_page, $max, $no_stickies ) );
+
+		// Fetch extra information for topics, so we don't have to query inside the loop
+		$this->topics = bp_forums_get_topic_extras( $this->topics );
+
+		if ( (int)$this->total_topic_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( array( 'p' => '%#%', 'n' => $this->pag_num ) ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_topic_count / (int)$this->pag_num),
+				'current'   => $this->pag_page,
+				'prev_text' => _x( '&larr;', 'Forum topic pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Forum topic pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+		}
+	}
+
+	function has_topics() {
+		if ( $this->topic_count )
+			return true;
+
+		return false;
+	}
+
+	function next_topic() {
+		$this->current_topic++;
+		$this->topic = $this->topics[$this->current_topic];
+
+		return $this->topic;
+	}
+
+	function rewind_topics() {
+		$this->current_topic = -1;
+		if ( $this->topic_count > 0 ) {
+			$this->topic = $this->topics[0];
+		}
+	}
+
+	function user_topics() {
+		if ( $this->current_topic + 1 < $this->topic_count ) {
+			return true;
+		} elseif ( $this->current_topic + 1 == $this->topic_count ) {
+			do_action('forum_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_topics();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_topic() {
+		global $topic;
+
+		$this->in_the_loop = true;
+		$this->topic = $this->next_topic();
+		$this->topic = (object)$this->topic;
+
+		if ( $this->current_topic == 0 ) // loop has just started
+			do_action('forum_loop_start');
+	}
+}
+
+/**
+ * Initiate the forum topics loop.
+ *
+ * Like other BuddyPress custom loops, the default arguments for this function are determined
+ * dynamically, depending on your current page. All of these $defaults can be overridden in the
+ * $args parameter.
+ *
+ * @package BuddyPress
+ * @uses apply_filters() Filter bp_has_topics to manipulate the $forums_template global before
+ *   it's rendered, or to modify the value of has_topics().
+ *
+ * @param array $args See inline definition of $defaults for explanation of arguments
+ * @return bool Returns true when forum topics are found corresponding to the args, false otherwise.
+ */
+function bp_has_forum_topics( $args = '' ) {
+	global $forum_template, $bp;
+
+	/***
+	 * Set the defaults based on the current page. Any of these will be overridden
+	 * if arguments are directly passed into the loop. Custom plugins should always
+	 * pass their parameters directly to the loop.
+	 */
+	$type         = 'newest';
+	$user_id      = 0;
+	$forum_id     = false;
+	$search_terms = false;
+	$do_stickies  = true;
+
+	// User filtering
+	if ( !empty( $bp->displayed_user->id ) )
+		$user_id = $bp->displayed_user->id;
+
+	// "Replied" query must be manually modified
+	if ( 'replies' == bp_current_action() ) {
+		$user_id = 0; // User id must be handled manually by the filter, not by BB_Query
+
+		add_filter( 'get_topics_distinct',   'bp_forums_add_replied_distinct_sql', 20 );
+		add_filter( 'get_topics_join', 	     'bp_forums_add_replied_join_sql', 20 );
+		add_filter( 'get_topics_where',      'bp_forums_add_replied_where_sql', 20  );
+	}
+
+	// If we're in a single group, set this group's forum_id
+	if ( !$forum_id && !empty( $bp->groups->current_group ) ) {
+		$bp->groups->current_group->forum_id = groups_get_groupmeta( $bp->groups->current_group->id, 'forum_id' );
+
+		// If it turns out there is no forum for this group, return false so
+		// we don't fetch all global topics
+		if ( empty( $bp->groups->current_group->forum_id ) )
+			return false;
+
+		$forum_id = $bp->groups->current_group->forum_id;
+	}
+
+	// If $_GET['fs'] is set, let's auto populate the search_terms var
+	if ( bp_is_directory() && !empty( $_GET['fs'] ) )
+		$search_terms = $_GET['fs'];
+
+	// Get the pagination arguments from $_REQUEST
+	$page     = isset( $_REQUEST['p'] ) ? intval( $_REQUEST['p'] ) : 1;
+	$per_page = isset( $_REQUEST['n'] ) ? intval( $_REQUEST['n'] ) : 20;
+
+	// Unless set otherwise, stickies appear in normal order on the global forum directory
+	if ( bp_is_directory() && bp_is_forums_component() && !bp_forums_enable_global_directory_stickies() )
+		$do_stickies = false;
+
+	$defaults = array(
+		'type'         => $type,
+		'forum_id'     => $forum_id,
+		'user_id'      => $user_id,
+		'page'         => $page,
+		'per_page'     => $per_page,
+		'max'          => false,
+		'number'       => false,
+		'offset'       => false,
+		'search_terms' => $search_terms,
+		'do_stickies'  => $do_stickies
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	// If we're viewing a tag URL in the directory, let's override the type and
+	// set it to tags and the filter to the tag name
+	if ( bp_is_current_action( 'tag' ) && $search_terms = bp_action_variable( 0 ) ) {
+		$type = 'tags';
+	}
+
+	/** Sticky logic ******************************************************************/
+
+	if ( $do_stickies ) {
+		// Fetch the stickies
+		$stickies_template = new BP_Forums_Template_Forum( $type, $forum_id, $user_id, 0, 0, $max, 'sticky', $search_terms );
+
+		// If stickies are found, try merging them
+		if ( $stickies_template->has_topics() ) {
+
+			// If stickies are for current $page
+			$page_start_num = ( ( $page - 1 ) * $per_page ) + 1;
+			$page_end_num 	= $page * $per_page <= $stickies_template->total_topic_count ? $page * $per_page : $stickies_template->total_topic_count;
+
+			// Calculate the number of sticky topics that will be shown on this page
+			if ( $stickies_template->topic_count < $page_start_num ) {
+				$this_page_stickies = 0;
+			} else {
+				$this_page_stickies = $stickies_template->topic_count - $per_page * floor( $stickies_template->topic_count / $per_page ) * ( $page - 1 ); // Total stickies minus sticky count through this page
+
+				// $this_page_stickies cannot be more than $per_page or less than 0
+				if ( $this_page_stickies > $per_page )
+					$this_page_stickies = $per_page;
+				else if ( $this_page_stickies < 0 )
+					$this_page_stickies = 0;
+			}
+
+			// Calculate the total number of topics that will be shown on this page
+			$this_page_topics = $stickies_template->total_topic_count >= ( $page * $per_page ) ? $per_page : $page_end_num - ( $page_start_num - 1 );
+
+			// If the number of stickies to be shown is less than $per_page, fetch some
+			// non-stickies to fill in the rest
+			if ( $this_page_stickies < $this_page_topics ) {
+				// How many non-stickies do we need?
+				$non_sticky_number = $this_page_topics - $this_page_stickies;
+
+				// Calculate the non-sticky offset
+				// How many non-stickies on all pages up to this point?
+				$non_sticky_total = $page_end_num - $stickies_template->topic_count;
+
+				// The offset is the number of total non-stickies, less the number
+				// to be shown on this page
+				$non_sticky_offset = $non_sticky_total - $non_sticky_number;
+
+				// Fetch the non-stickies
+				$forum_template = new BP_Forums_Template_Forum( $type, $forum_id, $user_id, 1, $per_page, $max, 'no', $search_terms, $non_sticky_offset, $non_sticky_number );
+
+				// If there are stickies to merge on this page, do it now
+				if ( $this_page_stickies ) {
+					// Correct the topic_count
+					$forum_template->topic_count += (int)$this_page_stickies;
+
+					// Figure out which stickies need to be included
+					$this_page_sticky_topics = array_slice( $stickies_template->topics, 0 - $this_page_stickies );
+
+					// Merge these topics into the forum template
+					$forum_template->topics = array_merge( $this_page_sticky_topics, (array)$forum_template->topics );
+				}
+			} else {
+				// This page has no non-stickies
+				$forum_template = $stickies_template;
+
+				// Adjust the topic count and trim the topics
+				$forum_template->topic_count = $this_page_stickies;
+				$forum_template->topics      = array_slice( $forum_template->topics, $page - 1 );
+			}
+
+			// Because we're using a manual offset and number for the topic query, we
+			// must set the page number manually, and recalculate the pagination links
+			$forum_template->pag_num     = $per_page;
+			$forum_template->pag_page    = $page;
+
+			$forum_template->pag_links = paginate_links( array(
+				'base'      => add_query_arg( array( 'p' => '%#%', 'n' => $forum_template->pag_num ) ),
+				'format'    => '',
+				'total'     => ceil( (int)$forum_template->total_topic_count / (int)$forum_template->pag_num ),
+				'current'   => $forum_template->pag_page,
+				'prev_text' => _x( '&larr;', 'Forum topic pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Forum topic pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+
+		} else {
+			// Fetch the non-sticky topics if no stickies were found
+			$forum_template = new BP_Forums_Template_Forum( $type, $forum_id, $user_id, $page, $per_page, $max, 'all', $search_terms );
+		}
+	} else {
+		// When skipping the sticky logic, just pull up the forum topics like usual
+		$forum_template = new BP_Forums_Template_Forum( $type, $forum_id, $user_id, $page, $per_page, $max, 'all', $search_terms );
+	}
+
+	return apply_filters( 'bp_has_topics', $forum_template->has_topics(), $forum_template );
+}
+
+function bp_forum_topics() {
+	global $forum_template;
+	return $forum_template->user_topics();
+}
+
+function bp_the_forum_topic() {
+	global $forum_template;
+	return $forum_template->the_topic();
+}
+
+function bp_the_topic_id() {
+	echo bp_get_the_topic_id();
+}
+	function bp_get_the_topic_id() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_id', $forum_template->topic->topic_id );
+	}
+
+function bp_the_topic_title() {
+	echo bp_get_the_topic_title();
+}
+	function bp_get_the_topic_title() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_title', stripslashes( $forum_template->topic->topic_title ) );
+	}
+
+function bp_the_topic_slug() {
+	echo bp_get_the_topic_slug();
+}
+	function bp_get_the_topic_slug() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_slug', $forum_template->topic->topic_slug );
+	}
+
+function bp_the_topic_text() {
+	echo bp_get_the_topic_text();
+}
+	function bp_get_the_topic_text() {
+		global $forum_template;
+
+		$post = bb_get_first_post( (int)$forum_template->topic->topic_id, false );
+		return apply_filters( 'bp_get_the_topic_text', esc_attr( $post->post_text ) );
+	}
+
+function bp_the_topic_poster_id() {
+	echo bp_get_the_topic_poster_id();
+}
+	function bp_get_the_topic_poster_id() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_poster_id', $forum_template->topic->topic_poster );
+	}
+
+function bp_the_topic_poster_avatar( $args = '' ) {
+	echo bp_get_the_topic_poster_avatar( $args );
+}
+	function bp_get_the_topic_poster_avatar( $args = '' ) {
+		global $forum_template;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => false,
+			'height' => false,
+			'alt'    => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_the_topic_poster_avatar', bp_core_fetch_avatar( array( 'item_id' => $forum_template->topic->topic_poster, 'type' => $type, 'width' => $width, 'height' => $height, 'alt' => $alt ) ) );
+	}
+
+function bp_the_topic_poster_name() {
+	echo bp_get_the_topic_poster_name();
+}
+	function bp_get_the_topic_poster_name() {
+		global $forum_template;
+
+		$poster_id = ( empty( $forum_template->topic->poster_id ) ) ? $forum_template->topic->topic_poster : $forum_template->topic->poster_id;
+
+		if ( !$name = bp_core_get_userlink( $poster_id ) )
+			return __( 'Deleted User', 'buddypress' );
+
+		return apply_filters( 'bp_get_the_topic_poster_name', $name );
+	}
+
+function bp_the_topic_object_id() {
+	echo bp_get_the_topic_object_id();
+}
+	function bp_get_the_topic_object_id() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_object_id', $forum_template->topic->object_id );
+	}
+
+function bp_the_topic_object_name() {
+	echo bp_get_the_topic_object_name();
+}
+	function bp_get_the_topic_object_name() {
+		global $forum_template;
+
+		if ( isset( $forum_template->topic->object_name ) )
+			$retval = $forum_template->topic->object_name;
+		else
+			$retval = '';
+
+		return apply_filters( 'bp_get_the_topic_object_name', $retval );
+	}
+
+function bp_the_topic_object_slug() {
+	echo bp_get_the_topic_object_slug();
+}
+	function bp_get_the_topic_object_slug() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_object_slug', $forum_template->topic->object_slug );
+	}
+
+function bp_the_topic_object_permalink() {
+	echo bp_get_the_topic_object_permalink();
+}
+	function bp_get_the_topic_object_permalink() {
+		global $bp, $forum_template;
+
+		// Currently this will only work with group forums, extended support in the future
+		if ( bp_is_active( 'groups' ) )
+			$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $forum_template->topic->object_slug . '/forum/' );
+		else
+			$permalink = '';
+
+		return apply_filters( 'bp_get_the_topic_object_permalink', $permalink );
+	}
+
+function bp_the_topic_last_poster_name() {
+	echo bp_get_the_topic_last_poster_name();
+}
+	function bp_get_the_topic_last_poster_name() {
+		global $forum_template;
+
+		if ( !$domain = bp_core_get_user_domain( $forum_template->topic->topic_last_poster, $forum_template->topic->topic_last_poster_nicename, $forum_template->topic->topic_last_poster_login ) )
+			return __( 'Deleted User', 'buddypress' );
+
+		return apply_filters( 'bp_get_the_topic_last_poster_name', '<a href="' . $domain . '">' . $forum_template->topic->topic_last_poster_displayname . '</a>' );
+	}
+
+function bp_the_topic_object_avatar( $args = '' ) {
+	echo bp_get_the_topic_object_avatar( $args );
+}
+	function bp_get_the_topic_object_avatar( $args = '' ) {
+		global $forum_template;
+
+		if ( !isset( $forum_template->topic->object_id ) )
+			return false;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => false,
+			'height' => false,
+			'alt'    => __( 'Group logo for %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_the_topic_object_avatar', bp_core_fetch_avatar( array( 'item_id' => $forum_template->topic->object_id, 'type' => $type, 'object' => 'group', 'width' => $width, 'height' => $height, 'alt' => $alt ) ) );
+	}
+
+function bp_the_topic_last_poster_avatar( $args = '' ) {
+	echo bp_get_the_topic_last_poster_avatar( $args );
+}
+	function bp_get_the_topic_last_poster_avatar( $args = '' ) {
+		global $forum_template;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => false,
+			'height' => false,
+			'alt'    => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_the_topic_last_poster_avatar', bp_core_fetch_avatar( array( 'email' => $forum_template->topic->topic_last_poster_email, 'item_id' => $forum_template->topic->topic_last_poster, 'type' => $type, 'width' => $width, 'height' => $height, 'alt' => $alt ) ) );
+	}
+
+function bp_the_topic_start_time() {
+	echo bp_get_the_topic_start_time();
+}
+	function bp_get_the_topic_start_time() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_start_time', $forum_template->topic->topic_start_time );
+	}
+
+function bp_the_topic_time() {
+	echo bp_get_the_topic_time();
+}
+	function bp_get_the_topic_time() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_time', $forum_template->topic->topic_time );
+	}
+
+function bp_the_topic_forum_id() {
+	echo bp_get_the_topic_forum_id();
+}
+	function bp_get_the_topic_forum_id() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_forum_id', $forum_template->topic->topic_forum_id );
+	}
+
+function bp_the_topic_status() {
+	echo bp_get_the_topic_status();
+}
+	function bp_get_the_topic_status() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_status', $forum_template->topic->topic_status );
+	}
+
+function bp_the_topic_is_topic_open() {
+	echo bp_get_the_topic_is_topic_open();
+}
+	function bp_get_the_topic_is_topic_open() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_is_topic_open', $forum_template->topic->topic_open );
+	}
+
+function bp_the_topic_last_post_id() {
+	echo bp_get_the_topic_last_post_id();
+}
+	function bp_get_the_topic_last_post_id() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_last_post_id', $forum_template->topic->topic_last_post_id );
+	}
+
+function bp_the_topic_is_sticky() {
+	echo bp_get_the_topic_is_sticky();
+}
+	function bp_get_the_topic_is_sticky() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_is_sticky', $forum_template->topic->topic_sticky );
+	}
+
+function bp_the_topic_total_post_count() {
+	echo bp_get_the_topic_total_post_count();
+}
+	function bp_get_the_topic_total_post_count() {
+		global $forum_template;
+
+		if ( $forum_template->topic->topic_posts == 1 )
+			return apply_filters( 'bp_get_the_topic_total_post_count', sprintf( __( '%d post', 'buddypress' ), $forum_template->topic->topic_posts ) );
+		else
+			return apply_filters( 'bp_get_the_topic_total_post_count', sprintf( __( '%d posts', 'buddypress' ), $forum_template->topic->topic_posts ) );
+	}
+
+function bp_the_topic_total_posts() {
+	echo bp_get_the_topic_total_posts();
+}
+	function bp_get_the_topic_total_posts() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_total_posts', $forum_template->topic->topic_posts );
+	}
+
+function bp_the_topic_tag_count() {
+	echo bp_get_the_topic_tag_count();
+}
+	function bp_get_the_topic_tag_count() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_tag_count', $forum_template->topic->tag_count );
+	}
+
+function bp_the_topic_permalink() {
+	echo bp_get_the_topic_permalink();
+}
+	function bp_get_the_topic_permalink() {
+		global $forum_template, $bp;
+
+		if ( bp_is_active( 'groups' ) && !empty( $forum_template->topic->object_slug ) )
+			$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $forum_template->topic->object_slug . '/forum' );
+		else if ( bp_is_single_item() )
+			$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/' . bp_current_item() );
+		else
+			$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_forums_root_slug() );
+
+		return apply_filters( 'bp_get_the_topic_permalink', trailingslashit( $permalink . 'topic/' . $forum_template->topic->topic_slug ) );
+	}
+
+function bp_the_topic_time_since_created() {
+	echo bp_get_the_topic_time_since_created();
+}
+	function bp_get_the_topic_time_since_created() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_time_since_created', bp_core_time_since( strtotime( $forum_template->topic->topic_start_time ) ) );
+	}
+
+function bp_the_topic_latest_post_excerpt( $args = '' ) {
+	echo bp_get_the_topic_latest_post_excerpt( $args );
+}
+	function bp_get_the_topic_latest_post_excerpt( $args = '' ) {
+		global $forum_template;
+
+		$defaults = array(
+			'length' => 225
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		$post = bp_forums_get_post( $forum_template->topic->topic_last_post_id );
+		$post = bp_create_excerpt( $post->post_text, $length );
+
+		return apply_filters( 'bp_get_the_topic_latest_post_excerpt', $post, $length );
+	}
+
+function bp_the_topic_time_since_last_post() {
+	echo bp_get_the_topic_time_since_last_post();
+}
+	function bp_get_the_topic_time_since_last_post() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_the_topic_time_since_last_post', bp_core_time_since( strtotime( $forum_template->topic->topic_time ) ) );
+	}
+
+function bp_the_topic_is_mine() {
+	echo bp_get_the_topic_is_mine();
+}
+	function bp_get_the_topic_is_mine() {
+		global $bp, $forum_template;
+
+		return $bp->loggedin_user->id == $forum_template->topic->topic_poster;
+	}
+
+function bp_the_topic_admin_links( $args = '' ) {
+	echo bp_get_the_topic_admin_links( $args );
+}
+	function bp_get_the_topic_admin_links( $args = '' ) {
+		global $bp, $forum_template;
+
+		$defaults = array(
+			'seperator' => '|'
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		$links[] = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'edit', 'bp_forums_edit_topic' ) . '">' . __( 'Edit Topic', 'buddypress' ) . '</a>';
+
+		if ( $bp->is_item_admin || $bp->is_item_mod || is_super_admin() ) {
+			if ( 0 == (int)$forum_template->topic->topic_sticky )
+				$links[] = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'stick', 'bp_forums_stick_topic' ) . '">' . __( 'Sticky Topic', 'buddypress' ) . '</a>';
+			else
+				$links[] = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'unstick', 'bp_forums_unstick_topic' ) . '">' . __( 'Un-stick Topic', 'buddypress' ) . '</a>';
+
+			if ( 0 == (int)$forum_template->topic->topic_open )
+				$links[] = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'open', 'bp_forums_open_topic' ) . '">' . __( 'Open Topic', 'buddypress' ) . '</a>';
+			else
+				$links[] = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'close', 'bp_forums_close_topic' ) . '">' . __( 'Close Topic', 'buddypress' ) . '</a>';
+
+			$links[] = '<a class="confirm" id="topic-delete-link" href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'delete', 'bp_forums_delete_topic' ) . '">' . __( 'Delete Topic', 'buddypress' ) . '</a>';
+		}
+
+		return implode( ' ' . $seperator . ' ', (array) $links );
+	}
+
+function bp_the_topic_css_class() {
+	echo bp_get_the_topic_css_class();
+}
+
+	function bp_get_the_topic_css_class() {
+		global $forum_template;
+
+		$class = false;
+
+		if ( $forum_template->current_topic % 2 == 1 )
+			$class .= 'alt';
+
+		if ( isset( $forum_template->topic->topic_sticky ) && 1 == (int)$forum_template->topic->topic_sticky )
+			$class .= ' sticky';
+
+		if ( !isset( $forum_template->topic->topic_open ) || 0 == (int)$forum_template->topic->topic_open )
+			$class .= ' closed';
+
+		return apply_filters( 'bp_get_the_topic_css_class', trim( $class ) );
+	}
+
+function bp_my_forum_topics_link() {
+	echo bp_get_my_forum_topics_link();
+}
+	function bp_get_my_forum_topics_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_my_forum_topics_link', bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/personal/' );
+	}
+
+function bp_unreplied_forum_topics_link() {
+	echo bp_get_unreplied_forum_topics_link();
+}
+	function bp_get_unreplied_forum_topics_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_unreplied_forum_topics_link', bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/unreplied/' );
+	}
+
+
+function bp_popular_forum_topics_link() {
+	echo bp_get_popular_forum_topics_link();
+}
+	function bp_get_popular_forum_topics_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_popular_forum_topics_link', bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/popular/' );
+	}
+
+function bp_newest_forum_topics_link() {
+	echo bp_get_newest_forum_topics_link();
+}
+	function bp_get_newest_forum_topics_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_newest_forum_topics_link', bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/' );
+	}
+
+function bp_forum_topic_type() {
+	echo bp_get_forum_topic_type();
+}
+	function bp_get_forum_topic_type() {
+		global $bp;
+
+		if ( !bp_is_directory() || !bp_current_action() )
+			return 'newest';
+
+		return apply_filters( 'bp_get_forum_topic_type', bp_current_action() );
+	}
+
+/**
+ * Echoes the output of bp_get_forums_tag_name()
+ *
+ * @package BuddyPress
+ * @todo Deprecate?
+ */
+function bp_forums_tag_name() {
+	echo bp_get_forums_tag_name();
+}
+	/**
+	 * Outputs the currently viewed tag name
+	 *
+	 * @package BuddyPress
+	 * @todo Deprecate? Seems unused
+	 */
+	function bp_get_forums_tag_name() {
+		$tag_name = bp_is_directory() && bp_is_forums_component() ? bp_action_variable( 0 ) : false;
+
+		return apply_filters( 'bp_get_forums_tag_name', $tag_name );
+	}
+
+function bp_forum_pagination() {
+	echo bp_get_forum_pagination();
+}
+	function bp_get_forum_pagination() {
+		global $forum_template;
+
+		return apply_filters( 'bp_get_forum_pagination', $forum_template->pag_links );
+	}
+
+function bp_forum_pagination_count() {
+	echo bp_get_forum_pagination_count();
+}
+	function bp_get_forum_pagination_count() {
+		global $bp, $forum_template;
+
+		$start_num  = intval( ( $forum_template->pag_page - 1 ) * $forum_template->pag_num ) + 1;
+		$from_num   = bp_core_number_format( $start_num );
+		$to_num     = bp_core_number_format( ( $start_num + ( $forum_template->pag_num - 1  ) > $forum_template->total_topic_count ) ? $forum_template->total_topic_count : $start_num + ( $forum_template->pag_num - 1 ) );
+		$total      = bp_core_number_format( $forum_template->total_topic_count );
+		$pag_filter = false;
+
+		if ( 'tags' == $forum_template->type && !empty( $forum_template->search_terms ) )
+			$pag_filter = sprintf( __( ' matching tag "%s"', 'buddypress' ), $forum_template->search_terms );
+
+		return apply_filters( 'bp_get_forum_pagination_count', sprintf( __( 'Viewing topic %s to %s (of %s total topics%s)', 'buddypress' ), $from_num, $to_num, $total, $pag_filter ) );
+	}
+
+function bp_is_edit_topic() {
+	global $bp;
+
+	if ( bp_is_action_variable( 'post' ) && bp_is_action_variable( 'edit' ) )
+		return false;
+
+	return true;
+}
+
+
+class BP_Forums_Template_Topic {
+	var $current_post = -1;
+	var $post_count;
+	var $posts;
+	var $post;
+
+	var $forum_id;
+	var $topic_id;
+	var $topic;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_post_count;
+
+	var $single_post = false;
+
+	var $sort_by;
+	var $order;
+
+	function BP_Forums_Template_Topic( $topic_id, $per_page, $max, $order ) {
+		$this->__construct( $topic_id, $per_page, $max, $order );
+	}
+
+	function __construct( $topic_id, $per_page, $max, $order ) {
+		global $bp, $current_user, $forum_template;
+
+		$this->pag_page        = isset( $_REQUEST['topic_page'] ) ? intval( $_REQUEST['topic_page'] ) : 1;
+		$this->pag_num         = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		$this->order           = $order;
+		$this->topic_id        = $topic_id;
+		$forum_template->topic = (object) bp_forums_get_topic_details( $this->topic_id );
+		$this->forum_id        = $forum_template->topic->forum_id;
+
+		$this->posts           = bp_forums_get_topic_posts( array( 'topic_id' => $this->topic_id, 'page' => $this->pag_page, 'per_page' => $this->pag_num, 'order' => $this->order ) );
+
+		if ( !$this->posts ) {
+			$this->post_count       = 0;
+			$this->total_post_count = 0;
+		} else {
+			if ( !$max || $max >= (int)$forum_template->topic->topic_posts ) {
+				$this->total_post_count = (int)$forum_template->topic->topic_posts;
+			} else {
+				$this->total_post_count = (int)$max;
+			}
+
+			if ( $max ) {
+				if ( $max >= count( $this->posts ) ) {
+					$this->post_count = count( $this->posts );
+				} else {
+					$this->post_count = (int)$max;
+				}
+			} else {
+				$this->post_count = count( $this->posts );
+			}
+		}
+
+		// Load topic tags
+		$this->topic_tags = bb_get_topic_tags( $this->topic_id );
+
+		if ( (int)$this->total_post_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( array( 'topic_page' => '%#%', 'num' => (int)$this->pag_num ) ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_post_count / (int)$this->pag_num ),
+				'current'   => $this->pag_page,
+				'prev_text' => _x( '&larr;', 'Forum thread pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Forum thread pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+
+			$this->pag->total_pages = ceil( (int)$this->total_post_count / (int)$this->pag_num );
+		} else {
+			$this->pag->total_pages = 1;
+		}
+	}
+
+	function has_posts() {
+		if ( $this->post_count )
+			return true;
+
+		return false;
+	}
+
+	function next_post() {
+		$this->current_post++;
+		$this->post = $this->posts[$this->current_post];
+
+		return $this->post;
+	}
+
+	function rewind_posts() {
+		$this->current_post = -1;
+		if ( $this->post_count > 0 ) {
+			$this->post = $this->posts[0];
+		}
+	}
+
+	function user_posts() {
+		if ( $this->current_post + 1 < $this->post_count ) {
+			return true;
+		} elseif ( $this->current_post + 1 == $this->post_count ) {
+			do_action('topic_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_posts();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_post() {
+		global $post;
+
+		$this->in_the_loop = true;
+		$this->post = $this->next_post();
+		$this->post = (object)$this->post;
+
+		if ( $this->current_post == 0 ) // loop has just started
+			do_action('topic_loop_start');
+	}
+}
+
+function bp_has_forum_topic_posts( $args = '' ) {
+	global $topic_template;
+
+	$defaults = array(
+		'topic_id' => false,
+		'per_page' => 15,
+		'max'      => false,
+		'order'    => 'ASC'
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( empty( $topic_id ) && bp_is_current_component( 'groups') && bp_is_current_action( 'forum' ) && bp_is_action_variable( 'topic', 0 ) && bp_action_variable( 1 ) )
+		$topic_id = bp_forums_get_topic_id_from_slug( bp_action_variable( 1 ) );
+	elseif ( empty( $topic_id ) && bp_is_current_component( 'forums') && bp_is_current_action( 'topic' ) && bp_action_variable( 0 ) )
+		$topic_id = bp_forums_get_topic_id_from_slug( bp_action_variable( 0 ) );
+
+	if ( empty( $topic_id ) ) {
+		return false;
+
+	} else {
+		$topic_template = new BP_Forums_Template_Topic( (int) $topic_id, $per_page, $max, $order );
+
+		// Current topic forum_id needs to match current_group forum_id
+		if ( bp_is_groups_component() && $topic_template->forum_id != groups_get_groupmeta( bp_get_current_group_id(), 'forum_id' ) )
+			return false;
+	}
+
+	return apply_filters( 'bp_has_topic_posts', $topic_template->has_posts(), $topic_template );
+}
+
+function bp_forum_topic_posts() {
+	global $topic_template;
+	return $topic_template->user_posts();
+}
+
+function bp_the_forum_topic_post() {
+	global $topic_template;
+	return $topic_template->the_post();
+}
+
+function bp_the_topic_post_id() {
+	echo bp_get_the_topic_post_id();
+}
+	function bp_get_the_topic_post_id() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_the_topic_post_id', $topic_template->post->post_id );
+	}
+
+function bp_the_topic_post_content() {
+	echo bp_get_the_topic_post_content();
+}
+	function bp_get_the_topic_post_content() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_the_topic_post_content', stripslashes( $topic_template->post->post_text ) );
+	}
+
+function bp_the_topic_post_css_class() {
+	echo bp_get_the_topic_post_css_class();
+}
+
+	function bp_get_the_topic_post_css_class() {
+		global $topic_template;
+
+		$class = false;
+
+		if ( $topic_template->current_post % 2 == 1 )
+			$class .= 'alt';
+
+		if ( 1 == (int)$topic_template->post->post_status )
+			$class .= ' deleted';
+
+		if ( 0 == (int)$topic_template->post->post_status )
+			$class .= ' open';
+
+		return apply_filters( 'bp_get_the_topic_post_css_class', trim( $class ) );
+	}
+
+function bp_the_topic_post_poster_avatar( $args = '' ) {
+	echo bp_get_the_topic_post_poster_avatar( $args );
+}
+	function bp_get_the_topic_post_poster_avatar( $args = '' ) {
+		global $topic_template;
+
+		$defaults = array(
+			'type' => 'thumb',
+			'width' => 20,
+			'height' => 20,
+			'alt' => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_the_topic_post_poster_avatar', bp_core_fetch_avatar( array( 'item_id' => $topic_template->post->poster_id, 'type' => $type, 'width' => $width, 'height' => $height, 'alt' => $alt ) ) );
+	}
+
+function bp_the_topic_post_poster_name() {
+	echo bp_get_the_topic_post_poster_name();
+}
+	function bp_get_the_topic_post_poster_name() {
+		global $topic_template;
+
+		if ( !$link = bp_core_get_user_domain( $topic_template->post->poster_id, $topic_template->post->poster_nicename, $topic_template->post->poster_login ) )
+			return __( 'Deleted User', 'buddypress' );
+
+		return apply_filters( 'bp_get_the_topic_post_poster_name', '<a href="' . $link . '" title="' . $topic_template->post->poster_name . '">' . $topic_template->post->poster_name . '</a>' );
+	}
+
+function bp_the_topic_post_poster_link() {
+	echo bp_get_the_topic_post_poster_link();
+}
+	function bp_get_the_topic_post_poster_link() {
+		global $topic_template;
+
+		return apply_filters( 'bp_the_topic_post_poster_link', bp_core_get_user_domain( $topic_template->post->poster_id, $topic_template->post->poster_nicename, $topic_template->post->poster_login ) );
+	}
+
+function bp_the_topic_post_time_since() {
+	echo bp_get_the_topic_post_time_since();
+}
+	function bp_get_the_topic_post_time_since() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_the_topic_post_time_since', bp_core_time_since( strtotime( $topic_template->post->post_time ) ) );
+	}
+
+function bp_the_topic_post_is_mine() {
+	echo bp_the_topic_post_is_mine();
+}
+	function bp_get_the_topic_post_is_mine() {
+		global $bp, $topic_template;
+
+		return $bp->loggedin_user->id == $topic_template->post->poster_id;
+	}
+
+function bp_the_topic_post_admin_links( $args = '' ) {
+	echo bp_get_the_topic_post_admin_links( $args );
+}
+	function bp_get_the_topic_post_admin_links( $args = '' ) {
+		global $topic_template;
+
+		// Never show for the first post in a topic.
+		if ( 0 == $topic_template->current_post && 1 == $topic_template->pag_page )
+			return;
+
+		$defaults = array(
+			'separator' => ' | '
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		$query_vars = '';
+		if ( $_SERVER['QUERY_STRING'] )
+			$query_vars = '?' . $_SERVER['QUERY_STRING'];
+
+		$links    = array();
+		$links[]  = '<a href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'edit/post/' . $topic_template->post->post_id . '/' . $query_vars, 'bp_forums_edit_post' ) . '">' . __( 'Edit', 'buddypress' ) . '</a>';
+		$links[] .= '<a class="confirm" id="post-delete-link" href="' . wp_nonce_url( bp_get_the_topic_permalink() . 'delete/post/' . $topic_template->post->post_id, 'bp_forums_delete_post' ) . '">' . __( 'Delete', 'buddypress' ) . '</a>';
+
+		return apply_filters( 'bp_get_the_topic_post_admin_links', implode( $separator, $links ), $links, $r );
+	}
+
+function bp_the_topic_post_edit_text() {
+	echo bp_get_the_topic_post_edit_text();
+}
+	function bp_get_the_topic_post_edit_text() {
+		$post = bp_forums_get_post( bp_action_variable( 4 ) );
+		return apply_filters( 'bp_get_the_topic_post_edit_text', esc_attr( $post->post_text ) );
+	}
+
+function bp_the_topic_pagination() {
+	echo bp_get_the_topic_pagination();
+}
+	function bp_get_the_topic_pagination() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_the_topic_pagination', $topic_template->pag_links );
+	}
+
+function bp_the_topic_pagination_count() {
+	global $bp, $topic_template;
+
+	$start_num = intval( ( $topic_template->pag_page - 1 ) * $topic_template->pag_num ) + 1;
+	$from_num = bp_core_number_format( $start_num );
+	$to_num = bp_core_number_format( ( $start_num + ( $topic_template->pag_num - 1  ) > $topic_template->total_post_count ) ? $topic_template->total_post_count : $start_num + ( $topic_template->pag_num - 1 ) );
+	$total = bp_core_number_format( $topic_template->total_post_count );
+
+	echo apply_filters( 'bp_the_topic_pagination_count', sprintf( __( 'Viewing post %1$s to %2$s (%3$s total posts)', 'buddypress' ), $from_num, $to_num, $total ) );
+}
+
+function bp_the_topic_is_last_page() {
+	echo bp_get_the_topic_is_last_page();
+}
+	function bp_get_the_topic_is_last_page() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_the_topic_is_last_page', $topic_template->pag_page == $topic_template->pag->total_pages );
+	}
+
+function bp_directory_forums_search_form() {
+	global $bp;
+
+	$default_search_value = bp_get_search_default_text( 'forums' );
+	$search_value = !empty( $_REQUEST['fs'] ) ? stripslashes( $_REQUEST['fs'] ) : $default_search_value;  ?>
+
+	<form action="" method="get" id="search-forums-form">
+		<label><input type="text" name="s" id="forums_search" value="<?php echo esc_attr( $search_value ); ?>"  onfocus="if (this.value == '<?php echo $default_search_value ?>') {this.value = '';}" onblur="if (this.value == '') {this.value = '<?php echo $default_search_value ?>';}" /></label>
+		<input type="submit" id="forums_search_submit" name="forums_search_submit" value="<?php _e( 'Search', 'buddypress' ); ?>" />
+	</form>
+
+<?php
+}
+
+function bp_forum_permalink( $forum_id = 0 ) {
+	echo bp_get_forum_permalink( $forum_id );
+}
+	function bp_get_forum_permalink( $forum_id = 0 ) {
+		global $bp;
+
+		if ( bp_is_groups_component() ) {
+			$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . bp_current_item() . 'forum' );
+		} else {
+			if ( empty( $forum_id ) ) {
+				global $topic_template;
+				if ( isset( $topic_template->forum_id ) )
+					$forum_id = $topic_template->forum_id;
+			}
+
+			if ( $forum = bp_forums_get_forum( $forum_id ) )
+				$permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_forums_root_slug() . '/forum/' . $forum->forum_slug );
+			else
+				return false;
+		}
+
+		return apply_filters( 'bp_get_forum_permalink', trailingslashit( $permalink ) );
+	}
+
+function bp_forum_name( $forum_id = 0 ) {
+	echo bp_get_forum_name( $forum_id );
+}
+	function bp_get_forum_name( $forum_id = 0 ) {
+		global $bp;
+
+		if ( empty( $forum_id ) ) {
+			global $topic_template;
+			if ( isset( $topic_template->forum_id ) )
+				$forum_id = $topic_template->forum_id;
+		}
+
+		if ( $forum = bp_forums_get_forum( $forum_id ) )
+			return apply_filters( 'bp_get_forum_name', $forum->forum_name, $forum->forum_id );
+		else
+			return false;
+	}
+
+function bp_forums_tag_heat_map( $args = '' ) {
+	$defaults = array(
+		'smallest' => '10',
+		'largest'  => '42',
+		'sizing'   => 'px',
+		'limit'    => '50'
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	bb_tag_heat_map( $smallest, $largest, $sizing, $limit );
+}
+
+/**
+ * Echo the current topic's tag list, comma-separated
+ *
+ * @package BuddyPress
+ * @since 1.5
+ */
+function bp_forum_topic_tag_list() {
+	echo bp_get_forum_topic_tag_list();
+}
+	/**
+	 * Get the current topic's tag list
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 *
+	 * @param str $format 'string' returns comma-separated string; otherwise returns array
+	 * @return mixed $tags
+	 */
+	function bp_get_forum_topic_tag_list( $format = 'string' ) {
+		global $topic_template;
+
+		$tags_data = !empty( $topic_template->topic_tags ) ? $topic_template->topic_tags : false;
+
+		$tags = array();
+
+		if ( $tags_data ) {
+			foreach( $tags_data as $tag_data ) {
+				$tags[] = $tag_data->name;
+			}
+		}
+
+		if ( 'string' == $format )
+			$tags = implode( ', ', $tags );
+
+		return apply_filters( 'bp_forum_topic_tag_list', $tags, $format );
+	}
+
+/**
+ * Returns true if the current topic has tags
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return bool
+ */
+function bp_forum_topic_has_tags() {
+	global $topic_template;
+
+	$has_tags = false;
+
+	if ( !empty( $topic_template->topic_tags ) )
+		$has_tags = true;
+
+	return apply_filters( 'bp_forum_topic_has_tags', $has_tags );
+}
+
+function bp_forum_action() {
+	echo bp_get_forum_action();
+}
+	function bp_get_forum_action() {
+		global $topic_template;
+
+		return apply_filters( 'bp_get_forum_action', bp_get_root_domain() . esc_attr( $_SERVER['REQUEST_URI'] ) );
+	}
+
+function bp_forum_topic_action() {
+	echo bp_get_forum_topic_action();
+}
+	function bp_get_forum_topic_action() {
+		return apply_filters( 'bp_get_forum_topic_action', $_SERVER['REQUEST_URI'] );
+	}
+
+function bp_forum_topic_count_for_user( $user_id = 0 ) {
+	echo bp_get_forum_topic_count_for_user( $user_id );
+}
+	function bp_get_forum_topic_count_for_user( $user_id = 0 ) {
+		return apply_filters( 'bp_get_forum_topic_count_for_user', bp_forums_total_topic_count_for_user( $user_id ) );
+	}
+
+function bp_forum_topic_count( $user_id = 0 ) {
+	echo bp_get_forum_topic_count( $user_id );
+}
+	function bp_get_forum_topic_count( $user_id = 0 ) {
+		return apply_filters( 'bp_get_forum_topic_count', bp_forums_total_topic_count( $user_id ) );
+	}
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-actions.php	(revision 5057)
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Action functions are exactly the same as screen functions, however they do not
+ * have a template screen associated with them. Usually they will send the user
+ * back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_action_add_friend() {
+	if ( !bp_is_friends_component() || !bp_is_current_action( 'add-friend' ) )
+		return false;
+
+	if ( !$potential_friend_id = (int)bp_action_variable( 0 ) )
+		return false;
+
+	if ( $potential_friend_id == bp_loggedin_user_id() )
+		return false;
+
+	$friendship_status = BP_Friends_Friendship::check_is_friend( bp_loggedin_user_id(), $potential_friend_id );
+
+	if ( 'not_friends' == $friendship_status ) {
+
+		if ( !check_admin_referer( 'friends_add_friend' ) )
+			return false;
+
+		if ( !friends_add_friend( bp_loggedin_user_id(), $potential_friend_id ) ) {
+			bp_core_add_message( __( 'Friendship could not be requested.', 'buddypress' ), 'error' );
+		} else {
+			bp_core_add_message( __( 'Friendship requested', 'buddypress' ) );
+		}
+
+	} else if ( 'is_friend' == $friendship_status ) {
+		bp_core_add_message( __( 'You are already friends with this user', 'buddypress' ), 'error' );
+	} else {
+		bp_core_add_message( __( 'You already have a pending friendship request with this user', 'buddypress' ), 'error' );
+	}
+
+	bp_core_redirect( wp_get_referer() );
+
+	return false;
+}
+add_action( 'bp_init', 'friends_action_add_friend' );
+
+function friends_action_remove_friend() {
+	if ( !bp_is_friends_component() || !bp_is_current_action( 'remove-friend' ) )
+		return false;
+
+	if ( !$potential_friend_id = (int)bp_action_variable( 0 ) )
+		return false;
+
+	if ( $potential_friend_id == bp_loggedin_user_id() )
+		return false;
+
+	$friendship_status = BP_Friends_Friendship::check_is_friend( bp_loggedin_user_id(), $potential_friend_id );
+
+	if ( 'is_friend' == $friendship_status ) {
+
+		if ( !check_admin_referer( 'friends_remove_friend' ) )
+			return false;
+
+		if ( !friends_remove_friend( bp_loggedin_user_id(), $potential_friend_id ) ) {
+			bp_core_add_message( __( 'Friendship could not be canceled.', 'buddypress' ), 'error' );
+		} else {
+			bp_core_add_message( __( 'Friendship canceled', 'buddypress' ) );
+		}
+
+	} else if ( 'is_friends' == $friendship_status ) {
+		bp_core_add_message( __( 'You are not yet friends with this user', 'buddypress' ), 'error' );
+	} else {
+		bp_core_add_message( __( 'You have a pending friendship request with this user', 'buddypress' ), 'error' );
+	}
+
+	bp_core_redirect( wp_get_referer() );
+
+	return false;
+}
+add_action( 'bp_init', 'friends_action_remove_friend' );
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-activity.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-activity.php	(revision 5057)
@@ -0,0 +1,115 @@
+<?php
+/**
+ * These functions handle the recording, deleting and formatting of activity and
+ * notifications for the user and for this specific component.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_record_activity( $args = '' ) {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	$defaults = array (
+		'user_id'           => $bp->loggedin_user->id,
+		'action'            => '',
+		'content'           => '',
+		'primary_link'      => '',
+		'component'         => $bp->friends->id,
+		'type'              => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'recorded_time'     => bp_core_current_time(),
+		'hide_sitewide'     => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bp_activity_add( array( 'user_id' => $user_id, 'action' => $action, 'content' => $content, 'primary_link' => $primary_link, 'component' => $component, 'type' => $type, 'item_id' => $item_id, 'secondary_item_id' => $secondary_item_id, 'recorded_time' => $recorded_time, 'hide_sitewide' => $hide_sitewide ) );
+}
+
+function friends_delete_activity( $args ) {
+	global $bp;
+
+	if ( bp_is_active( 'activity' ) ) {
+		extract( (array)$args );
+		bp_activity_delete_by_item_id( array( 'item_id' => $item_id, 'component' => $bp->friends->id, 'type' => $type, 'user_id' => $user_id ) );
+	}
+}
+
+function friends_register_activity_actions() {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	bp_activity_set_action( $bp->friends->id, 'friends_register_activity_action', __( 'New friendship created', 'buddypress' ) );
+
+	do_action( 'friends_register_activity_actions' );
+}
+add_action( 'bp_register_activity_actions', 'friends_register_activity_actions' );
+
+/**
+ * Format the BuddyBar/admin bar notifications for the Friends component
+ *
+ * @package BuddyPress
+ *
+ * @param str $action The kind of notification being rendered
+ * @param int $item_id The primary item id
+ * @param int $secondary_item_id The secondary item id
+ * @param int $total_items The total number of messaging-related notifications waiting for the user
+ * @param str $format 'string' for BuddyBar-compatible notifications; 'array' for WP Admin Bar
+ */
+function friends_format_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
+	global $bp;
+
+	switch ( $action ) {
+		case 'friendship_accepted':
+			$link = bp_loggedin_user_domain() . bp_get_friends_slug() . '/my-friends/newest';
+
+			// Set up the string and the filter
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( '%d friends accepted your friendship requests', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_friends_multiple_friendship_accepted_notification';
+			} else {
+				$text = sprintf( __( '%s accepted your friendship request', 'buddypress' ),  bp_core_get_user_displayname( $item_id ) );
+				$filter = 'bp_friends_single_friendship_accepted_notification';
+			}
+
+			break;
+
+		case 'friendship_request':
+			$link = bp_loggedin_user_domain() . bp_get_friends_slug() . '/requests?new';
+
+			// Set up the string and the filter
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( 'You have %d pending friendship requests', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_friends_multiple_friendship_request_notification';
+			} else {
+				$text = sprintf( __( 'You have a friendship request from %s', 'buddypress' ),  bp_core_get_user_displayname( $item_id ) );
+				$filter = 'bp_friends_single_friendship_request_notification';
+			}
+
+			break;
+	}
+
+	// Return either an HTML link or an array, depending on the requested format
+	if ( 'string' == $format ) {
+		$return = apply_filters( $filter, '<a href="' . $link . '">' . $text . '</a>', (int)$total_items );
+	} else {
+		$return = apply_filters( $filter, array(
+			'link' => $link,
+			'text' => $text
+		), (int)$total_items );
+	}
+
+	do_action( 'friends_format_notifications', $action, $item_id, $secondary_item_id, $total_items, $return );
+
+	return $return;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-cache.php	(revision 5057)
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_clear_friend_object_cache( $friendship_id ) {
+	if ( !$friendship = new BP_Friends_Friendship( $friendship_id ) )
+		return false;
+
+	wp_cache_delete( 'friends_friend_ids_' .    $friendship->initiator_user_id, 'bp' );
+	wp_cache_delete( 'friends_friend_ids_' .    $friendship->friend_user_id,    'bp' );
+	wp_cache_delete( 'bp_total_friend_count_' . $friendship->initiator_user_id, 'bp' );
+	wp_cache_delete( 'bp_total_friend_count_' . $friendship->friend_user_id,    'bp' );
+}
+
+function friends_clear_friend_notifications() {
+	global $bp;
+
+	if ( isset( $_GET['new'] ) )
+		bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->friends->id, 'friendship_accepted' );
+}
+add_action( 'bp_activity_screen_my_activity', 'friends_clear_friend_notifications' );
+
+// List actions to clear object caches on
+add_action( 'friends_friendship_accepted', 'friends_clear_friend_object_cache' );
+add_action( 'friends_friendship_deleted',  'friends_clear_friend_object_cache' );
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'friends_friendship_rejected',  'bp_core_clear_cache' );
+add_action( 'friends_friendship_accepted',  'bp_core_clear_cache' );
+add_action( 'friends_friendship_deleted',   'bp_core_clear_cache' );
+add_action( 'friends_friendship_requested', 'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-classes.php	(revision 5057)
@@ -0,0 +1,340 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Friends_Friendship {
+	var $id;
+	var $initiator_user_id;
+	var $friend_user_id;
+	var $is_confirmed;
+	var $is_limited;
+	var $date_created;
+
+	var $is_request;
+	var $populate_friend_details;
+
+	var $friend;
+
+	function bp_friends_friendship( $id = null, $is_request = false, $populate_friend_details = true ) {
+		$this->__construct( $id, $is_request, $populate_friend_details );
+	}
+
+	function __construct( $id = null, $is_request = false, $populate_friend_details = true ) {
+		$this->is_request = $is_request;
+
+		if ( $id ) {
+			$this->id = $id;
+			$this->populate_friend_details = $populate_friend_details;
+			$this->populate( $this->id );
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp, $creds;
+
+		if ( $friendship = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->friends->table_name} WHERE id = %d", $this->id ) ) ) {
+			$this->initiator_user_id = $friendship->initiator_user_id;
+			$this->friend_user_id    = $friendship->friend_user_id;
+			$this->is_confirmed      = $friendship->is_confirmed;
+			$this->is_limited        = $friendship->is_limited;
+			$this->date_created      = $friendship->date_created;
+		}
+
+		// if running from ajax.
+		if ( !$bp->displayed_user->id )
+			$bp->displayed_user->id = $creds['current_userid'];
+
+		if ( $this->populate_friend_details ) {
+			if ( $this->friend_user_id == $bp->displayed_user->id ) {
+				$this->friend = new BP_Core_User( $this->initiator_user_id );
+			} else {
+				$this->friend = new BP_Core_User( $this->friend_user_id );
+			}
+		}
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->initiator_user_id = apply_filters( 'friends_friendship_initiator_user_id_before_save', $this->initiator_user_id, $this->id );
+		$this->friend_user_id    = apply_filters( 'friends_friendship_friend_user_id_before_save',    $this->friend_user_id,    $this->id );
+		$this->is_confirmed      = apply_filters( 'friends_friendship_is_confirmed_before_save',      $this->is_confirmed,      $this->id );
+		$this->is_limited        = apply_filters( 'friends_friendship_is_limited_before_save',        $this->is_limited,        $this->id );
+		$this->date_created      = apply_filters( 'friends_friendship_date_created_before_save',      $this->date_created,      $this->id );
+
+		do_action_ref_array( 'friends_friendship_before_save', array( &$this ) );
+
+		if ( $this->id ) {
+			// Update
+			$result = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->friends->table_name} SET initiator_user_id = %d, friend_user_id = %d, is_confirmed = %d, is_limited = %d, date_created = %s ) WHERE id = %d", $this->initiator_user_id, $this->friend_user_id, $this->is_confirmed, $this->is_limited, $this->date_created, $this->id ) );
+		} else {
+			// Save
+			$result = $wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->friends->table_name} ( initiator_user_id, friend_user_id, is_confirmed, is_limited, date_created ) VALUES ( %d, %d, %d, %d, %s )", $this->initiator_user_id, $this->friend_user_id, $this->is_confirmed, $this->is_limited, $this->date_created ) );
+			$this->id = $wpdb->insert_id;
+		}
+
+		do_action( 'friends_friendship_after_save', array( &$this ) );
+
+		return $result;
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->friends->table_name} WHERE id = %d", $this->id ) );
+	}
+
+	/* Static Functions */
+
+	function get_friend_user_ids( $user_id, $friend_requests_only = false, $assoc_arr = false ) {
+		global $wpdb, $bp;
+
+		if ( $friend_requests_only ) {
+			$oc_sql = $wpdb->prepare( "AND is_confirmed = 0" );
+			$friend_sql = $wpdb->prepare ( " WHERE friend_user_id = %d", $user_id );
+		} else {
+			$oc_sql = $wpdb->prepare( "AND is_confirmed = 1" );
+			$friend_sql = $wpdb->prepare ( " WHERE (initiator_user_id = %d OR friend_user_id = %d)", $user_id, $user_id );
+		}
+
+		$friends = $wpdb->get_results( $wpdb->prepare( "SELECT friend_user_id, initiator_user_id FROM {$bp->friends->table_name} $friend_sql $oc_sql ORDER BY date_created DESC" ) );
+		$fids = array();
+
+		for ( $i = 0, $count = count( $friends ); $i < $count; ++$i ) {
+			if ( $assoc_arr )
+				$fids[] = array( 'user_id' => ( $friends[$i]->friend_user_id == $user_id ) ? $friends[$i]->initiator_user_id : $friends[$i]->friend_user_id );
+			else
+				$fids[] = ( $friends[$i]->friend_user_id == $user_id ) ? $friends[$i]->initiator_user_id : $friends[$i]->friend_user_id;
+		}
+
+		return $fids;
+	}
+
+	function get_friendship_id( $user_id, $friend_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->friends->table_name} WHERE ( initiator_user_id = %d AND friend_user_id = %d ) OR ( initiator_user_id = %d AND friend_user_id = %d ) AND is_confirmed = 1", $user_id, $friend_id, $friend_id, $user_id ) );
+	}
+
+	function get_friendship_request_user_ids( $user_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT initiator_user_id FROM {$bp->friends->table_name} WHERE friend_user_id = %d AND is_confirmed = 0", $user_id ) );
+	}
+
+	function total_friend_count( $user_id = 0 ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+		/* This is stored in 'total_friend_count' usermeta.
+		   This function will recalculate, update and return. */
+
+		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d OR friend_user_id = %d) AND is_confirmed = 1", $user_id, $user_id ) );
+
+		// Do not update meta if user has never had friends
+		if ( !$count && !bp_get_user_meta( $user_id, 'total_friend_count', true ) )
+			return 0;
+
+		bp_update_user_meta( $user_id, 'total_friend_count', (int)$count );
+		return (int)$count;
+	}
+
+	function search_friends( $filter, $user_id, $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		// TODO: Optimize this function.
+
+		if ( !$user_id )
+			$user_id = $bp->loggedin_user->id;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( !$friend_ids = BP_Friends_Friendship::get_friend_user_ids( $user_id ) )
+			return false;
+
+		// Get all the user ids for the current user's friends.
+		$fids = implode( ',', $friend_ids );
+
+		if ( empty($fids) )
+			return false;
+
+		// filter the user_ids based on the search criteria.
+		if ( bp_is_active( 'xprofile' ) ) {
+			$sql = "SELECT DISTINCT user_id FROM {$bp->profile->table_name_data} WHERE user_id IN ($fids) AND value LIKE '$filter%%' {$pag_sql}";
+			$total_sql = "SELECT COUNT(DISTINCT user_id) FROM {$bp->profile->table_name_data} WHERE user_id IN ($fids) AND value LIKE '$filter%%'";
+		} else {
+			$sql = "SELECT DISTINCT user_id FROM {$wpdb->usermeta} WHERE user_id IN ($fids) AND meta_key = 'nickname' AND meta_value LIKE '$filter%%' {$pag_sql}";
+			$total_sql = "SELECT COUNT(DISTINCT user_id) FROM {$wpdb->usermeta} WHERE user_id IN ($fids) AND meta_key = 'nickname' AND meta_value LIKE '$filter%%'";
+		}
+
+		$filtered_friend_ids = $wpdb->get_col($sql);
+		$total_friend_ids = $wpdb->get_var($total_sql);
+
+		if ( !$filtered_friend_ids )
+			return false;
+
+		return array( 'friends' => $filtered_friend_ids, 'total' => (int)$total_friend_ids );
+	}
+
+	function check_is_friend( $loggedin_userid, $possible_friend_userid ) {
+		global $wpdb, $bp;
+
+		if ( !$loggedin_userid || !$possible_friend_userid )
+			return false;
+
+		$result = $wpdb->get_results( $wpdb->prepare( "SELECT id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id = %d) OR (initiator_user_id = %d AND friend_user_id = %d)", $loggedin_userid, $possible_friend_userid, $possible_friend_userid, $loggedin_userid ) );
+
+		if ( $result ) {
+			if ( 0 == (int)$result[0]->is_confirmed ) {
+				return 'pending';
+			} else {
+				return 'is_friend';
+			}
+		} else {
+			return 'not_friends';
+		}
+	}
+
+	function get_bulk_last_active( $user_ids ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT meta_value as last_activity, user_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} ) ORDER BY meta_value DESC", bp_get_user_meta_key( 'last_activity' ) ) );
+	}
+
+	function accept($friendship_id) {
+		global $wpdb, $bp;
+
+	 	return $wpdb->query( $wpdb->prepare( "UPDATE {$bp->friends->table_name} SET is_confirmed = 1, date_created = %s WHERE id = %d AND friend_user_id = %d", bp_core_current_time(), $friendship_id, $bp->loggedin_user->id ) );
+	}
+
+	function reject($friendship_id) {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->friends->table_name} WHERE id = %d AND friend_user_id = %d", $friendship_id, $bp->loggedin_user->id ) );
+	}
+
+	function search_users( $filter, $user_id, $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		$usermeta_table = $wpdb->base_prefix . 'usermeta';
+		$users_table = $wpdb->base_prefix . 'users';
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * intval( $limit ) ), intval( $limit ) );
+
+		// filter the user_ids based on the search criteria.
+		if ( bp_is_active( 'xprofile' ) ) {
+			$sql = $wpdb->prepare( "SELECT DISTINCT d.user_id as id FROM {$bp->profile->table_name_data} d, $users_table u WHERE d.user_id = u.id AND d.value LIKE '$filter%%' ORDER BY d.value DESC $pag_sql" );
+		} else {
+			$sql = $wpdb->prepare( "SELECT DISTINCT user_id as id FROM $usermeta_table WHERE meta_value LIKE '$filter%%' ORDER BY d.value DESC $pag_sql" );
+		}
+
+		$filtered_fids = $wpdb->get_col($sql);
+
+		if ( !$filtered_fids )
+			return false;
+
+		return $filtered_fids;
+	}
+
+	function search_users_count( $filter ) {
+		global $wpdb, $bp;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		$usermeta_table = $wpdb->prefix . 'usermeta';
+		$users_table = $wpdb->base_prefix . 'users';
+
+		// filter the user_ids based on the search criteria.
+		if ( bp_is_active( 'xprofile' ) ) {
+			$sql = $wpdb->prepare( "SELECT COUNT(DISTINCT d.user_id) FROM {$bp->profile->table_name_data} d, $users_table u WHERE d.user_id = u.id AND d.value LIKE '$filter%%'" );
+		} else {
+			$sql = $wpdb->prepare( "SELECT COUNT(DISTINCT user_id) FROM $usermeta_table WHERE meta_value LIKE '$filter%%'" );
+		}
+
+		$user_count = $wpdb->get_col($sql);
+
+		if ( !$user_count )
+			return false;
+
+		return $user_count[0];
+	}
+
+	function sort_by_name( $user_ids ) {
+		global $wpdb, $bp;
+
+		if ( !bp_is_active( 'xprofile' ) )
+			return false;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT user_id FROM {$bp->profile->table_name_data} pd, {$bp->profile->table_name_fields} pf WHERE pf.id = pd.field_id AND pf.name = %s AND pd.user_id IN ( {$user_ids} ) ORDER BY pd.value ASC", bp_xprofile_fullname_field_name() ) );
+	}
+
+	function get_random_friends( $user_id, $total_friends = 5 ) {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "SELECT friend_user_id, initiator_user_id FROM {$bp->friends->table_name} WHERE (friend_user_id = %d || initiator_user_id = %d) && is_confirmed = 1 ORDER BY rand() LIMIT %d", $user_id, $user_id, $total_friends );
+		$results = $wpdb->get_results($sql);
+
+		for ( $i = 0, $count = count( $results ); $i < $count; ++$i ) {
+			$fids[] = ( $results[$i]->friend_user_id == $user_id ) ? $results[$i]->initiator_user_id : $results[$i]->friend_user_id;
+		}
+
+		// remove duplicates
+		if ( count( $fids ) > 0 )
+			return array_flip(array_flip($fids));
+		else
+			return false;
+	}
+
+	function get_invitable_friend_count( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		$friend_ids = BP_Friends_Friendship::get_friend_user_ids( $user_id );
+
+		$invitable_count = 0;
+		for ( $i = 0, $count = count( $friend_ids ); $i < $count; ++$i ) {
+
+			if ( BP_Groups_Member::check_is_member( (int)$friend_ids[$i], $group_id ) )
+				continue;
+
+			if ( BP_Groups_Member::check_has_invite( (int)$friend_ids[$i], $group_id )  )
+				continue;
+
+			$invitable_count++;
+		}
+
+		return $invitable_count;
+	}
+
+	function get_user_ids_for_friendship( $friendship_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_row( $wpdb->prepare( "SELECT friend_user_id, initiator_user_id FROM {$bp->friends->table_name} WHERE id = %d", $friendship_id ) );
+	}
+
+	function delete_all_for_user( $user_id ) {
+		global $wpdb, $bp;
+
+		// Get friends of $user_id
+		$friend_ids = BP_Friends_Friendship::get_friend_user_ids( $user_id );
+
+		// Delete all friendships related to $user_id
+		$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->friends->table_name} WHERE friend_user_id = %d OR initiator_user_id = %d", $user_id, $user_id ) );
+
+		// Delete friend request notifications for members who have a notification from this user.
+		$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE component_name = 'friends' AND ( component_action = 'friendship_request' OR component_action = 'friendship_accepted' ) AND item_id = %d", $user_id ) );
+
+		// Loop through friend_ids and update their counts
+		foreach ( (array)$friend_ids as $friend_id ) {
+			BP_Friends_Friendship::total_friend_count( $friend_id );
+		}
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-functions.php	(revision 5057)
@@ -0,0 +1,288 @@
+<?php
+/********************************************************************************
+ * Business Functions
+ *
+ * Business functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_add_friend( $initiator_userid, $friend_userid, $force_accept = false ) {
+	global $bp;
+
+	$friendship = new BP_Friends_Friendship;
+
+	if ( (int)$friendship->is_confirmed )
+		return true;
+
+	$friendship->initiator_user_id = $initiator_userid;
+	$friendship->friend_user_id    = $friend_userid;
+	$friendship->is_confirmed      = 0;
+	$friendship->is_limited        = 0;
+	$friendship->date_created      = bp_core_current_time();
+
+	if ( $force_accept )
+		$friendship->is_confirmed = 1;
+
+	if ( $friendship->save() ) {
+
+		if ( !$force_accept ) {
+			// Add the on screen notification
+			bp_core_add_notification( $friendship->initiator_user_id, $friendship->friend_user_id, $bp->friends->id, 'friendship_request' );
+
+			// Send the email notification
+			friends_notification_new_request( $friendship->id, $friendship->initiator_user_id, $friendship->friend_user_id );
+
+			do_action( 'friends_friendship_requested', $friendship->id, $friendship->initiator_user_id, $friendship->friend_user_id );
+		} else {
+			do_action( 'friends_friendship_accepted', $friendship->id, $friendship->initiator_user_id, $friendship->friend_user_id );
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+function friends_remove_friend( $initiator_userid, $friend_userid ) {
+	global $bp;
+
+	$friendship_id = BP_Friends_Friendship::get_friendship_id( $initiator_userid, $friend_userid );
+	$friendship    = new BP_Friends_Friendship( $friendship_id );
+
+	do_action( 'friends_before_friendship_delete', $friendship_id, $initiator_userid, $friend_userid );
+
+	// Remove the activity stream item for the user who canceled the friendship
+	friends_delete_activity( array( 'item_id' => $friendship_id, 'type' => 'friendship_accepted', 'user_id' => $bp->displayed_user->id ) );
+
+	do_action( 'friends_friendship_deleted', $friendship_id, $initiator_userid, $friend_userid );
+
+	if ( $friendship->delete() ) {
+		friends_update_friend_totals( $initiator_userid, $friend_userid, 'remove' );
+
+		return true;
+	}
+
+	return false;
+}
+
+function friends_accept_friendship( $friendship_id ) {
+	global $bp;
+
+	$friendship = new BP_Friends_Friendship( $friendship_id, true, false );
+
+	if ( !$friendship->is_confirmed && BP_Friends_Friendship::accept( $friendship_id ) ) {
+		friends_update_friend_totals( $friendship->initiator_user_id, $friendship->friend_user_id );
+
+		// Remove the friend request notice
+		bp_core_delete_notifications_by_item_id( $friendship->friend_user_id, $friendship->initiator_user_id, $bp->friends->id, 'friendship_request' );
+
+		// Add a friend accepted notice for the initiating user
+		bp_core_add_notification( $friendship->friend_user_id, $friendship->initiator_user_id, $bp->friends->id, 'friendship_accepted' );
+
+		$initiator_link = bp_core_get_userlink( $friendship->initiator_user_id );
+		$friend_link = bp_core_get_userlink( $friendship->friend_user_id );
+
+		// Record in activity streams for the initiator
+		friends_record_activity( array(
+			'user_id'           => $friendship->initiator_user_id,
+			'type'              => 'friendship_created',
+			'action'            => apply_filters( 'friends_activity_friendship_accepted_action', sprintf( __( '%1$s and %2$s are now friends', 'buddypress' ), $initiator_link, $friend_link ), $friendship ),
+			'item_id'           => $friendship_id,
+			'secondary_item_id' => $friendship->friend_user_id
+		) );
+
+		// Record in activity streams for the friend
+		friends_record_activity( array(
+			'user_id'           => $friendship->friend_user_id,
+			'type'              => 'friendship_created',
+			'action'            => apply_filters( 'friends_activity_friendship_accepted_action', sprintf( __( '%1$s and %2$s are now friends', 'buddypress' ), $friend_link, $initiator_link ), $friendship ),
+			'item_id'           => $friendship_id,
+			'secondary_item_id' => $friendship->initiator_user_id,
+			'hide_sitewide'     => true // We've already got the first entry site wide
+		) );
+
+		// Send the email notification
+		friends_notification_accepted_request( $friendship->id, $friendship->initiator_user_id, $friendship->friend_user_id );
+
+		do_action( 'friends_friendship_accepted', $friendship->id, $friendship->initiator_user_id, $friendship->friend_user_id );
+
+		return true;
+	}
+
+	return false;
+}
+
+function friends_reject_friendship( $friendship_id ) {
+	global $bp;
+
+	$friendship = new BP_Friends_Friendship( $friendship_id, true, false );
+
+	if ( !$friendship->is_confirmed && BP_Friends_Friendship::reject( $friendship_id ) ) {
+		// Remove the friend request notice
+		bp_core_delete_notifications_by_item_id( $friendship->friend_user_id, $friendship->initiator_user_id, $bp->friends->id, 'friendship_request' );
+
+		do_action_ref_array( 'friends_friendship_rejected', array( $friendship_id, &$friendship ) );
+		return true;
+	}
+
+	return false;
+}
+
+function friends_check_friendship( $user_id, $possible_friend_id ) {
+	global $bp;
+
+	if ( 'is_friend' == BP_Friends_Friendship::check_is_friend( $user_id, $possible_friend_id ) )
+		return true;
+
+	return false;
+}
+
+// Returns - 'is_friend', 'not_friends', 'pending'
+function friends_check_friendship_status( $user_id, $possible_friend_id ) {
+	return BP_Friends_Friendship::check_is_friend( $user_id, $possible_friend_id );
+}
+
+function friends_get_total_friend_count( $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	if ( !$count = wp_cache_get( 'bp_total_friend_count_' . $user_id, 'bp' ) ) {
+		$count = bp_get_user_meta( $user_id, 'total_friend_count', true );
+		if ( empty( $count ) ) $count = 0;
+		wp_cache_set( 'bp_total_friend_count_' . $user_id, $count, 'bp' );
+	}
+
+	return apply_filters( 'friends_get_total_friend_count', $count );
+}
+
+function friends_check_user_has_friends( $user_id ) {
+	$friend_count = friends_get_total_friend_count( $user_id );
+
+	if ( empty( $friend_count ) )
+		return false;
+
+	if ( !(int)$friend_count )
+		return false;
+
+	return true;
+}
+
+function friends_get_friendship_id( $initiator_user_id, $friend_user_id ) {
+	return BP_Friends_Friendship::get_friendship_id( $initiator_user_id, $friend_user_id );
+}
+
+function friends_get_friend_user_ids( $user_id, $friend_requests_only = false, $assoc_arr = false, $filter = false ) {
+	return BP_Friends_Friendship::get_friend_user_ids( $user_id, $friend_requests_only, $assoc_arr );
+}
+
+function friends_search_friends( $search_terms, $user_id, $pag_num = 10, $pag_page = 1 ) {
+	return BP_Friends_Friendship::search_friends( $search_terms, $user_id, $pag_num, $pag_page );
+}
+
+function friends_get_friendship_request_user_ids( $user_id ) {
+	return BP_Friends_Friendship::get_friendship_request_user_ids( $user_id );
+}
+
+function friends_get_recently_active( $user_id, $per_page = 0, $page = 0, $filter = '' ) {
+	return apply_filters( 'friends_get_recently_active', BP_Core_User::get_users( 'active', $per_page, $page, $user_id, $filter ) );
+}
+
+function friends_get_alphabetically( $user_id, $per_page = 0, $page = 0, $filter = '' ) {
+	return apply_filters( 'friends_get_alphabetically', BP_Core_User::get_users( 'alphabetical', $per_page, $page, $user_id, $filter ) );
+}
+
+function friends_get_newest( $user_id, $per_page = 0, $page = 0, $filter = '' ) {
+	return apply_filters( 'friends_get_newest', BP_Core_User::get_users( 'newest', $per_page, $page, $user_id, $filter ) );
+}
+
+function friends_get_bulk_last_active( $friend_ids ) {
+	return BP_Friends_Friendship::get_bulk_last_active( $friend_ids );
+}
+
+function friends_get_friends_invite_list( $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	if ( bp_has_members( 'user_id=' . $user_id . '&type=alphabetical&per_page=0' ) ) {
+		while ( bp_members() ) : bp_the_member();
+			$friends[] = array(
+				'id' => bp_get_member_user_id(),
+				'full_name' => bp_get_member_name()
+			);
+		endwhile;
+	}
+
+	if ( empty($friends) )
+		return false;
+
+	return $friends;
+}
+
+function friends_count_invitable_friends( $user_id, $group_id ) {
+	return BP_Friends_Friendship::get_invitable_friend_count( $user_id, $group_id );
+}
+
+function friends_get_friend_count_for_user( $user_id ) {
+	return BP_Friends_Friendship::total_friend_count( $user_id );
+}
+
+function friends_search_users( $search_terms, $user_id, $pag_num = 0, $pag_page = 0 ) {
+	global $bp;
+
+	$user_ids = BP_Friends_Friendship::search_users( $search_terms, $user_id, $pag_num, $pag_page );
+
+	if ( !$user_ids )
+		return false;
+
+	for ( $i = 0, $count = count( $user_ids ); $i < $count; ++$i )
+		$users[] = new BP_Core_User( $user_ids[$i] );
+
+	return array( 'users' => $users, 'count' => BP_Friends_Friendship::search_users_count( $search_terms ) );
+}
+
+function friends_is_friendship_confirmed( $friendship_id ) {
+	$friendship = new BP_Friends_Friendship( $friendship_id );
+	return $friendship->is_confirmed;
+}
+
+function friends_update_friend_totals( $initiator_user_id, $friend_user_id, $status = 'add' ) {
+	global $bp;
+
+	if ( 'add' == $status ) {
+		bp_update_user_meta( $initiator_user_id, 'total_friend_count', (int)bp_get_user_meta( $initiator_user_id, 'total_friend_count', true ) + 1 );
+		bp_update_user_meta( $friend_user_id, 'total_friend_count', (int)bp_get_user_meta( $friend_user_id, 'total_friend_count', true ) + 1 );
+	} else {
+		bp_update_user_meta( $initiator_user_id, 'total_friend_count', (int)bp_get_user_meta( $initiator_user_id, 'total_friend_count', true ) - 1 );
+		bp_update_user_meta( $friend_user_id, 'total_friend_count', (int)bp_get_user_meta( $friend_user_id, 'total_friend_count', true ) - 1 );
+	}
+}
+
+function friends_remove_data( $user_id ) {
+	global $bp;
+
+	do_action( 'friends_before_remove_data', $user_id );
+
+	BP_Friends_Friendship::delete_all_for_user($user_id);
+
+	// Remove usermeta
+	bp_delete_user_meta( $user_id, 'total_friend_count' );
+
+	// Remove friendship requests FROM user
+	bp_core_delete_notifications_from_user( $user_id, $bp->friends->id, 'friendship_request' );
+
+	do_action( 'friends_remove_data', $user_id );
+}
+add_action( 'wpmu_delete_user',  'friends_remove_data' );
+add_action( 'delete_user',       'friends_remove_data' );
+add_action( 'bp_make_spam_user', 'friends_remove_data' );
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-loader.php	(revision 5057)
@@ -0,0 +1,210 @@
+<?php
+/**
+ * BuddyPress Friends Streams Loader
+ *
+ * The friends component is for users to create relationships with each other
+ *
+ * @package BuddyPress
+ * @subpackage Friends Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Friends_Component extends BP_Component {
+
+	/**
+	 * Start the friends component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'friends',
+			__( 'Friend Connections', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		// Files to include
+		$includes = array(
+			'actions',
+			'screens',
+			'filters',
+			'classes',
+			'activity',
+			'template',
+			'functions',
+			'notifications',
+		);
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_FRIENDS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		define ( 'BP_FRIENDS_DB_VERSION', '1800' );
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_FRIENDS_SLUG' ) )
+			define( 'BP_FRIENDS_SLUG', $this->id );
+
+		// Global tables for the friends component
+		$global_tables = array(
+			'table_name'      => $bp->table_prefix . 'bp_friends',
+			'table_name_meta' => $bp->table_prefix . 'bp_friends_meta',
+		);
+
+		// All globals for the friends component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_FRIENDS_SLUG,
+			'has_directory'         => false,
+			'search_string'         => __( 'Search Friends...', 'buddypress' ),
+			'notification_callback' => 'friends_format_notifications',
+			'global_tables'         => $global_tables
+		);
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add 'Friends' to the main navigation
+		$main_nav = array(
+			'name'                => sprintf( __( 'Friends <span>%d</span>', 'buddypress' ), friends_get_total_friend_count() ),
+			'slug'                => $this->slug,
+			'position'            => 60,
+			'screen_function'     => 'friends_screen_my_friends',
+			'default_subnav_slug' => 'my-friends',
+			'item_css_id'         => $bp->friends->id
+		);
+
+		$friends_link = trailingslashit( $bp->loggedin_user->domain . bp_get_friends_slug() );
+
+		// Add the subnav items to the friends nav item
+		$sub_nav[] = array(
+			'name'            => __( 'Friendships', 'buddypress' ),
+			'slug'            => 'my-friends',
+			'parent_url'      => $friends_link,
+			'parent_slug'     => bp_get_friends_slug(),
+			'screen_function' => 'friends_screen_my_friends',
+			'position'        => 10,
+			'item_css_id'     => 'friends-my-friends'
+		);
+
+		$sub_nav[] = array(
+			'name'            => __( 'Requests',   'buddypress' ),
+			'slug'            => 'requests',
+			'parent_url'      => $friends_link,
+			'parent_slug'     => bp_get_friends_slug(),
+			'screen_function' => 'friends_screen_requests',
+			'position'        => 20,
+			'user_has_access' => bp_is_my_profile()
+		);
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain  = $bp->loggedin_user->domain;
+			$friends_link = trailingslashit( $user_domain . $this->slug );
+
+			// Pending friend requests
+			if ( $count = count( friends_get_friendship_request_user_ids( $bp->loggedin_user->id ) ) ) {
+				$title   = sprintf( __( 'Friends <span class="count">%s</span>',          'buddypress' ), $count );
+				$pending = sprintf( __( 'Pending Requests <span class="count">%s</span>', 'buddypress' ), $count );
+			} else {
+				$title   = __( 'Friends',             'buddypress' );
+				$pending = __( 'No Pending Requests', 'buddypress' );
+			}
+
+			// Add the "My Account" sub menus
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => $title,
+				'href'   => trailingslashit( $friends_link )
+			);
+
+			// My Groups
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Friendships', 'buddypress' ),
+				'href'   => trailingslashit( $friends_link )
+			);
+
+			// Requests
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => $pending,
+				'href'   => trailingslashit( $friends_link . 'requests' )
+			);
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		// Adjust title
+		if ( bp_is_friends_component() ) {
+			if ( bp_is_my_profile() ) {
+				$bp->bp_options_title = __( 'Friendships', 'buddypress' );
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title  = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the friends component
+$bp->friends = new BP_Friends_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-notifications.php	(revision 5057)
@@ -0,0 +1,88 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_notification_new_request( $friendship_id, $initiator_id, $friend_id ) {
+	global $bp;
+
+	$initiator_name = bp_core_get_user_displayname( $initiator_id );
+
+	if ( 'no' == bp_get_user_meta( (int)$friend_id, 'notification_friends_friendship_request', true ) )
+		return false;
+
+	$ud = get_userdata( $friend_id );
+	$initiator_ud = get_userdata( $initiator_id );
+
+	$all_requests_link = bp_core_get_user_domain( $friend_id ) . bp_get_friends_slug() . '/requests/';
+	$settings_link = bp_core_get_user_domain( $friend_id ) .  bp_get_settings_slug() . '/notifications';
+
+	$initiator_link = bp_core_get_user_domain( $initiator_id );
+
+	// Set up and send the message
+	$to       = $ud->user_email;
+	$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+	$subject  = '[' . $sitename . '] ' . sprintf( __( 'New friendship request from %s', 'buddypress' ), $initiator_name );
+
+	$message = sprintf( __(
+'%1$s wants to add you as a friend.
+
+To view all of your pending friendship requests: %2$s
+
+To view %3$s\'s profile: %4$s
+
+---------------------
+', 'buddypress' ), $initiator_name, $all_requests_link, $initiator_name, $initiator_link );
+
+	$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+	/* Send the message */
+	$to = apply_filters( 'friends_notification_new_request_to', $to );
+	$subject = apply_filters( 'friends_notification_new_request_subject', $subject, $initiator_name );
+	$message = apply_filters( 'friends_notification_new_request_message', $message, $initiator_name, $initiator_link, $all_requests_link, $settings_link );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_friends_sent_request_email', $friend_id, $subject, $message, $friendship_id, $initiator_id );
+}
+
+function friends_notification_accepted_request( $friendship_id, $initiator_id, $friend_id ) {
+	global $bp;
+
+	$friendship = new BP_Friends_Friendship( $friendship_id, false, false );
+
+	$friend_name = bp_core_get_user_displayname( $friend_id );
+
+	if ( 'no' == bp_get_user_meta( (int)$initiator_id, 'notification_friends_friendship_accepted', true ) )
+		return false;
+
+	$ud = get_userdata( $initiator_id );
+
+	$friend_link = bp_core_get_user_domain( $friend_id );
+	$settings_link = bp_core_get_user_domain( $initiator_id ) . bp_get_settings_slug() . '/notifications';
+
+	// Set up and send the message
+	$to       = $ud->user_email;
+	$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+	$subject  = '[' . $sitename . '] ' . sprintf( __( '%s accepted your friendship request', 'buddypress' ), $friend_name );
+
+	$message = sprintf( __(
+'%1$s accepted your friend request.
+
+To view %2$s\'s profile: %3$s
+
+---------------------
+', 'buddypress' ), $friend_name, $friend_name, $friend_link );
+
+	$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+	/* Send the message */
+	$to = apply_filters( 'friends_notification_accepted_request_to', $to );
+	$subject = apply_filters( 'friends_notification_accepted_request_subject', $subject, $friend_name );
+	$message = apply_filters( 'friends_notification_accepted_request_message', $message, $friend_name, $friend_link, $settings_link );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_friends_sent_accepted_email', $initiator_id, $subject, $message, $friendship_id, $friend_id );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-screens.php	(revision 5057)
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Screen functions are the controllers of BuddyPress. They will execute when their
+ * specific URL is caught. They will first save or manipulate data using business
+ * functions, then pass on the user to a template file.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function friends_screen_my_friends() {
+	global $bp;
+
+	// Delete any friendship acceptance notifications for the user when viewing a profile
+	bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->friends->id, 'friendship_accepted' );
+
+	do_action( 'friends_screen_my_friends' );
+
+	bp_core_load_template( apply_filters( 'friends_template_my_friends', 'members/single/home' ) );
+}
+
+function friends_screen_requests() {
+	if ( bp_is_action_variable( 'accept', 0 ) && is_numeric( bp_action_variable( 1 ) ) ) {
+		// Check the nonce
+		check_admin_referer( 'friends_accept_friendship' );
+
+		if ( friends_accept_friendship( bp_action_variable( 1 ) ) )
+			bp_core_add_message( __( 'Friendship accepted', 'buddypress' ) );
+		else
+			bp_core_add_message( __( 'Friendship could not be accepted', 'buddypress' ), 'error' );
+
+		bp_core_redirect( bp_loggedin_user_domain() . bp_current_component() . '/' . bp_current_action() );
+
+	} elseif ( bp_is_action_variable( 'reject', 0 ) && is_numeric( bp_action_variable( 1 ) ) ) {
+		// Check the nonce
+		check_admin_referer( 'friends_reject_friendship' );
+
+		if ( friends_reject_friendship( bp_action_variable( 1 ) ) )
+			bp_core_add_message( __( 'Friendship rejected', 'buddypress' ) );
+		else
+			bp_core_add_message( __( 'Friendship could not be rejected', 'buddypress' ), 'error' );
+
+		bp_core_redirect( bp_loggedin_user_domain() . bp_current_component() . '/' . bp_current_action() );
+	}
+
+	do_action( 'friends_screen_requests' );
+
+	if ( isset( $_GET['new'] ) )
+		bp_core_delete_notifications_by_type( bp_loggedin_user_id(), 'friends', 'friendship_request' );
+
+	bp_core_load_template( apply_filters( 'friends_template_requests', 'members/single/home' ) );
+}
+
+function friends_screen_notification_settings() {
+	global $bp;
+
+	if ( !$send_requests = bp_get_user_meta( $bp->displayed_user->id, 'notification_friends_friendship_request', true ) )
+		$send_requests   = 'yes';
+
+	if ( !$accept_requests = bp_get_user_meta( $bp->displayed_user->id, 'notification_friends_friendship_accepted', true ) )
+		$accept_requests = 'yes';
+?>
+
+	<table class="notification-settings" id="friends-notification-settings">
+		<thead>
+			<tr>
+				<th class="icon"></th>
+				<th class="title"><?php _e( 'Friends', 'buddypress' ) ?></th>
+				<th class="yes"><?php _e( 'Yes', 'buddypress' ) ?></th>
+				<th class="no"><?php _e( 'No', 'buddypress' )?></th>
+			</tr>
+		</thead>
+
+		<tbody>
+			<tr id="friends-notification-settings-request">
+				<td></td>
+				<td><?php _e( 'A member sends you a friendship request', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_friends_friendship_request]" value="yes" <?php checked( $send_requests, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_friends_friendship_request]" value="no" <?php checked( $send_requests, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="friends-notification-settings-accepted">
+				<td></td>
+				<td><?php _e( 'A member accepts your friendship request', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_friends_friendship_accepted]" value="yes" <?php checked( $accept_requests, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_friends_friendship_accepted]" value="no" <?php checked( $accept_requests, 'no', true ) ?>/></td>
+			</tr>
+
+			<?php do_action( 'friends_screen_notification_settings' ); ?>
+
+		</tbody>
+	</table>
+
+<?php
+}
+add_action( 'bp_notification_settings', 'friends_screen_notification_settings' );
+
+?>
Index: /tags/1.5-beta-3/bp-friends/bp-friends-template.php
===================================================================
--- /tags/1.5-beta-3/bp-friends/bp-friends-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-friends/bp-friends-template.php	(revision 5057)
@@ -0,0 +1,449 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the friends component slug
+ *
+ * @package BuddyPress
+ * @subpackage Friends Template
+ * @since 1.5
+ *
+ * @uses bp_get_friends_slug()
+ */
+function bp_friends_slug() {
+	echo bp_get_friends_slug();
+}
+	/**
+	 * Return the friends component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Friends Template
+	 * @since 1.5
+	 */
+	function bp_get_friends_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_friends_slug', $bp->friends->slug );
+	}
+
+/**
+ * Output the friends component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Friends Template
+ * @since 1.5
+ *
+ * @uses bp_get_friends_root_slug()
+ */
+function bp_friends_root_slug() {
+	echo bp_get_friends_root_slug();
+}
+	/**
+	 * Return the friends component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Friends Template
+	 * @since 1.5
+	 */
+	function bp_get_friends_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_friends_root_slug', $bp->friends->root_slug );
+	}
+
+/**
+ * Displays Friends header tabs
+ *
+ * @package BuddyPress
+ * @todo Deprecate?
+ */
+function bp_friends_header_tabs() {
+	global $bp; ?>
+
+	<li<?php if ( !bp_action_variable( 0 ) || bp_is_action_variable( 'recently-active', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_friends_slug() ?>/my-friends/recently-active"><?php _e( 'Recently Active', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'newest', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_friends_slug() ?>/my-friends/newest"><?php _e( 'Newest', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'alphabetically', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_friends_slug() ?>/my-friends/alphabetically"><?php _e( 'Alphabetically', 'buddypress' ) ?></a></li>
+
+<?php
+	do_action( 'friends_header_tabs' );
+}
+
+/**
+ * Filters the title for the Friends component
+ *
+ * @package BuddyPress
+ * @todo Deprecate?
+ */
+function bp_friends_filter_title() {
+	$current_filter = bp_action_variable( 0 );
+
+	switch ( $current_filter ) {
+		case 'recently-active': default:
+			_e( 'Recently Active', 'buddypress' );
+			break;
+		case 'newest':
+			_e( 'Newest', 'buddypress' );
+			break;
+		case 'alphabetically':
+			_e( 'Alphabetically', 'buddypress' );
+			break;
+	}
+}
+
+function bp_friends_random_friends() {
+	global $bp;
+
+	if ( !$friend_ids = wp_cache_get( 'friends_friend_ids_' . $bp->displayed_user->id, 'bp' ) ) {
+		$friend_ids = BP_Friends_Friendship::get_random_friends( $bp->displayed_user->id );
+		wp_cache_set( 'friends_friend_ids_' . $bp->displayed_user->id, $friend_ids, 'bp' );
+	} ?>
+
+	<div class="info-group">
+		<h4><?php bp_word_or_name( __( "My Friends", 'buddypress' ), __( "%s's Friends", 'buddypress' ) ) ?>  (<?php echo BP_Friends_Friendship::total_friend_count( $bp->displayed_user->id ) ?>) <span><a href="<?php echo $bp->displayed_user->domain . bp_get_friends_slug() ?>"><?php _e('See All', 'buddypress') ?></a></span></h4>
+
+		<?php if ( $friend_ids ) { ?>
+
+			<ul class="horiz-gallery">
+
+			<?php for ( $i = 0, $count = count( $friend_ids ); $i < $count; ++$i ) { ?>
+
+				<li>
+					<a href="<?php echo bp_core_get_user_domain( $friend_ids[$i] ) ?>"><?php echo bp_core_fetch_avatar( array( 'item_id' => $friend_ids[$i], 'type' => 'thumb' ) ) ?></a>
+					<h5><?php echo bp_core_get_userlink($friend_ids[$i]) ?></h5>
+				</li>
+
+			<?php } ?>
+
+			</ul>
+
+		<?php } else { ?>
+
+			<div id="message" class="info">
+				<p><?php bp_word_or_name( __( "You haven't added any friend connections yet.", 'buddypress' ), __( "%s hasn't created any friend connections yet.", 'buddypress' ) ) ?></p>
+			</div>
+
+		<?php } ?>
+		<div class="clear"></div>
+	</div>
+<?php
+}
+
+/**
+ * Pull up a group of random members, and display some profile data about them
+ *
+ * This function is no longer used by BuddyPress core.
+ *
+ * @package BuddyPress
+ *
+ * @param int $total_members The number of members to retrieve
+ */
+function bp_friends_random_members( $total_members = 5 ) {
+	global $bp;
+
+	if ( !$user_ids = wp_cache_get( 'friends_random_users', 'bp' ) ) {
+		$user_ids = BP_Core_User::get_users( 'random', $total_members );
+		wp_cache_set( 'friends_random_users', $user_ids, 'bp' );
+	}
+
+	?>
+
+	<?php if ( $user_ids['users'] ) { ?>
+
+		<ul class="item-list" id="random-members-list">
+
+		<?php for ( $i = 0, $count = count( $user_ids['users'] ); $i < $count; ++$i ) { ?>
+
+			<li>
+				<a href="<?php echo bp_core_get_user_domain( $user_ids['users'][$i]->id ) ?>"><?php echo bp_core_fetch_avatar( array( 'item_id' => $user_ids['users'][$i]->id, 'type' => 'thumb' ) ) ?></a>
+				<h5><?php echo bp_core_get_userlink( $user_ids['users'][$i]->id ) ?></h5>
+
+				<?php if ( bp_is_active( 'xprofile' ) ) { ?>
+
+					<?php $random_data = xprofile_get_random_profile_data( $user_ids['users'][$i]->id, true ); ?>
+
+					<div class="profile-data">
+						<p class="field-name"><?php echo $random_data[0]->name ?></p>
+
+						<?php echo $random_data[0]->value ?>
+
+					</div>
+
+				<?php } ?>
+
+				<div class="action">
+
+					<?php if ( bp_is_active( 'friends' ) ) { ?>
+
+						<?php bp_add_friend_button( $user_ids['users'][$i]->id ) ?>
+
+					<?php } ?>
+
+				</div>
+			</li>
+
+		<?php } ?>
+
+		</ul>
+
+	<?php } else { ?>
+
+		<div id="message" class="info">
+			<p><?php _e( "There aren't enough site members to show a random sample just yet.", 'buddypress' ) ?></p>
+		</div>
+
+	<?php } ?>
+<?php
+}
+
+function bp_friend_search_form() {
+	global $friends_template, $bp;
+
+	$action = $bp->displayed_user->domain . bp_get_friends_slug() . '/my-friends/search/';
+	$label = __( 'Filter Friends', 'buddypress' ); ?>
+
+		<form action="<?php echo $action ?>" id="friend-search-form" method="post">
+
+			<label for="friend-search-box" id="friend-search-label"><?php echo $label ?></label>
+			<input type="search" name="friend-search-box" id="friend-search-box" value="<?php echo $value ?>"<?php echo $disabled ?> />
+
+			<?php wp_nonce_field( 'friends_search', '_wpnonce_friend_search' ) ?>
+
+			<input type="hidden" name="initiator" id="initiator" value="<?php echo esc_attr( $bp->displayed_user->id ) ?>" />
+
+		</form>
+
+	<?php
+}
+
+function bp_member_add_friend_button() {
+	global $members_template;
+
+	if ( !isset( $members_template->member->is_friend ) || null === $members_template->member->is_friend )
+		$friend_status = 'not_friends';
+	else
+		$friend_status = ( 0 == $members_template->member->is_friend ) ? 'pending' : 'is_friend';
+
+	echo bp_get_add_friend_button( $members_template->member->id, $friend_status );
+}
+add_action( 'bp_directory_members_actions', 'bp_member_add_friend_button' );
+
+function bp_member_total_friend_count() {
+	global $members_template;
+
+	echo bp_get_member_total_friend_count();
+}
+	function bp_get_member_total_friend_count() {
+		global $members_template;
+
+		if ( 1 == (int) $members_template->member->total_friend_count )
+			return apply_filters( 'bp_get_member_total_friend_count', sprintf( __( '%d friend', 'buddypress' ), (int) $members_template->member->total_friend_count ) );
+		else
+			return apply_filters( 'bp_get_member_total_friend_count', sprintf( __( '%d friends', 'buddypress' ), (int) $members_template->member->total_friend_count ) );
+	}
+
+/**
+ * bp_potential_friend_id( $user_id )
+ *
+ * Outputs the ID of the potential friend
+ *
+ * @uses bp_get_potential_friend_id()
+ * @param <type> $user_id
+ */
+function bp_potential_friend_id( $user_id = 0 ) {
+	echo bp_get_potential_friend_id( $user_id );
+}
+	/**
+	 * bp_get_potential_friend_id( $user_id )
+	 *
+	 * Returns the ID of the potential friend
+	 *
+	 * @global object $bp
+	 * @global object $friends_template
+	 * @param int $user_id
+	 * @return int ID of potential friend
+	 */
+	function bp_get_potential_friend_id( $user_id = 0 ) {
+		global $bp, $friends_template;
+
+		if ( empty( $user_id ) && isset( $friends_template->friendship->friend ) )
+			$user_id = $friends_template->friendship->friend->id;
+		else if ( empty( $user_id ) && !isset( $friends_template->friendship->friend ) )
+			$user_id = $bp->displayed_user->id;
+
+		return apply_filters( 'bp_get_potential_friend_id', (int)$user_id );
+	}
+
+/**
+ * bp_is_friend( $user_id )
+ *
+ * Returns - 'is_friend', 'not_friends', 'pending'
+ *
+ * @global object $bp
+ * @param int $potential_friend_id
+ * @return string
+ */
+function bp_is_friend( $user_id = 0 ) {
+	global $bp;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+	if ( empty( $user_id ) )
+		$user_id = bp_get_potential_friend_id( $user_id );
+
+	if ( $bp->loggedin_user->id == $user_id )
+		return false;
+
+	return apply_filters( 'bp_is_friend', friends_check_friendship_status( $bp->loggedin_user->id, $user_id ), $user_id );
+}
+
+function bp_add_friend_button( $potential_friend_id = 0, $friend_status = false ) {
+	echo bp_get_add_friend_button( $potential_friend_id, $friend_status );
+}
+	function bp_get_add_friend_button( $potential_friend_id = 0, $friend_status = false ) {
+		global $bp, $friends_template;
+
+		if ( empty( $potential_friend_id ) )
+			$potential_friend_id = bp_get_potential_friend_id( $potential_friend_id );
+
+		$is_friend = bp_is_friend( $potential_friend_id );
+
+		if ( empty( $is_friend ) )
+			return false;
+
+		switch ( $is_friend ) {
+			case 'pending' :
+				$button = array(
+					'id'                => 'pending',
+					'component'         => 'friends',
+					'must_be_logged_in' => true,
+					'block_self'        => true,
+					'wrapper_class'     => 'friendship-button pending',
+					'wrapper_id'        => 'friendship-button-' . $potential_friend_id,
+					'link_href'         => trailingslashit( $bp->loggedin_user->domain . bp_get_friends_slug() . '/requests' ),
+					'link_text'         => __( 'Friendship Requested', 'buddypress' ),
+					'link_title'        => __( 'Friendship Requested', 'buddypress' ),
+					'link_class'        => 'friendship-button pending requested'
+				);
+				break;
+
+			case 'is_friend' :
+				$button = array(
+					'id'                => 'is_friend',
+					'component'         => 'friends',
+					'must_be_logged_in' => true,
+					'block_self'        => false,
+					'wrapper_class'     => 'friendship-button is_friend',
+					'wrapper_id'        => 'friendship-button-' . $potential_friend_id,
+					'link_href'         => wp_nonce_url( $bp->loggedin_user->domain . bp_get_friends_slug() . '/remove-friend/' . $potential_friend_id . '/', 'friends_remove_friend' ),
+					'link_text'         => __( 'Cancel Friendship', 'buddypress' ),
+					'link_title'        => __( 'Cancel Friendship', 'buddypress' ),
+					'link_id'           => 'friend-' . $potential_friend_id,
+					'link_rel'          => 'remove',
+					'link_class'        => 'friendship-button is_friend remove'
+				);
+				break;
+
+			default:
+				$button = array(
+					'id'                => 'not_friends',
+					'component'         => 'friends',
+					'must_be_logged_in' => true,
+					'block_self'        => true,
+					'wrapper_class'     => 'friendship-button not_friends',
+					'wrapper_id'        => 'friendship-button-' . $potential_friend_id,
+					'link_href'         => wp_nonce_url( $bp->loggedin_user->domain . bp_get_friends_slug() . '/add-friend/' . $potential_friend_id . '/', 'friends_add_friend' ),
+					'link_text'         => __( 'Add Friend', 'buddypress' ),
+					'link_title'        => __( 'Add Friend', 'buddypress' ),
+					'link_id'           => 'friend-' . $potential_friend_id,
+					'link_rel'          => 'add',
+					'link_class'        => 'friendship-button not_friends add'
+				);
+				break;
+		}
+
+		// Filter and return the HTML button
+		return bp_get_button( apply_filters( 'bp_get_add_friend_button', $button ) );
+	}
+
+function bp_get_friend_ids( $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	$friend_ids = friends_get_friend_user_ids( $user_id );
+
+	if ( empty( $friend_ids ) )
+		return false;
+
+	return implode( ',', friends_get_friend_user_ids( $user_id ) );
+}
+function bp_get_friendship_requests() {
+	global $bp;
+
+	return apply_filters( 'bp_get_friendship_requests', implode( ',', (array) friends_get_friendship_request_user_ids( $bp->loggedin_user->id ) ) );
+}
+
+function bp_friend_friendship_id() {
+	echo bp_get_friend_friendship_id();
+}
+	function bp_get_friend_friendship_id() {
+		global $members_template, $bp;
+
+		if ( !$friendship_id = wp_cache_get( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id ) ) {
+			$friendship_id = friends_get_friendship_id( $members_template->member->id, $bp->loggedin_user->id );
+			wp_cache_set( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id, $friendship_id, 'bp' );
+		}
+
+		return apply_filters( 'bp_get_friend_friendship_id', $friendship_id );
+	}
+
+function bp_friend_accept_request_link() {
+	echo bp_get_friend_accept_request_link();
+}
+	function bp_get_friend_accept_request_link() {
+		global $members_template, $bp;
+
+		if ( !$friendship_id = wp_cache_get( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id ) ) {
+			$friendship_id = friends_get_friendship_id( $members_template->member->id, $bp->loggedin_user->id );
+			wp_cache_set( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id, $friendship_id, 'bp' );
+		}
+
+		return apply_filters( 'bp_get_friend_accept_request_link', wp_nonce_url( $bp->loggedin_user->domain . bp_get_friends_slug() . '/requests/accept/' . $friendship_id, 'friends_accept_friendship' ) );
+	}
+
+function bp_friend_reject_request_link() {
+	echo bp_get_friend_reject_request_link();
+}
+	function bp_get_friend_reject_request_link() {
+		global $members_template, $bp;
+
+		if ( !$friendship_id = wp_cache_get( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id ) ) {
+			$friendship_id = friends_get_friendship_id( $members_template->member->id, $bp->loggedin_user->id );
+			wp_cache_set( 'friendship_id_' . $members_template->member->id . '_' . $bp->loggedin_user->id, $friendship_id, 'bp' );
+		}
+
+		return apply_filters( 'bp_get_friend_reject_request_link', wp_nonce_url( $bp->loggedin_user->domain . bp_get_friends_slug() . '/requests/reject/' . $friendship_id, 'friends_reject_friendship' ) );
+	}
+
+function bp_total_friend_count( $user_id = 0 ) {
+	echo bp_get_total_friend_count( $user_id );
+}
+	function bp_get_total_friend_count( $user_id = 0 ) {
+		return apply_filters( 'bp_get_total_friend_count', friends_get_total_friend_count( $user_id ) );
+	}
+	add_filter( 'bp_get_total_friend_count', 'bp_core_number_format' );
+
+function bp_friend_total_requests_count( $user_id = 0 ) {
+	echo bp_friend_get_total_requests_count( $user_id );
+}
+	function bp_friend_get_total_requests_count( $user_id = 0 ) {
+		global $bp;
+
+		if ( empty( $user_id ) )
+			$user_id = $bp->loggedin_user->id;
+
+		return apply_filters( 'bp_friend_get_total_requests_count', count( BP_Friends_Friendship::get_friend_user_ids( $user_id, true ) ) );
+	}
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-actions.php	(revision 5057)
@@ -0,0 +1,312 @@
+<?php
+/********************************************************************************
+ * Action Functions
+ *
+ * Action functions are exactly the same as screen functions, however they do not
+ * have a template screen associated with them. Usually they will send the user
+ * back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_action_create_group() {
+	global $bp;
+
+	// If we're not at domain.org/groups/create/ then return false
+	if ( !bp_is_groups_component() || !bp_is_current_action( 'create' ) )
+		return false;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+ 	if ( !bp_user_can_create_groups() ) {
+		bp_core_add_message( __( 'Sorry, you are not allowed to create groups.', 'buddypress' ), 'error' );
+		bp_core_redirect( trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ) );
+	}
+
+	// Make sure creation steps are in the right order
+	groups_action_sort_creation_steps();
+
+	// If no current step is set, reset everything so we can start a fresh group creation
+	$bp->groups->current_create_step = bp_action_variable( 1 );
+	if ( !$bp->groups->current_create_step ) {
+		unset( $bp->groups->current_create_step );
+		unset( $bp->groups->completed_create_steps );
+
+		setcookie( 'bp_new_group_id', false, time() - 1000, COOKIEPATH );
+		setcookie( 'bp_completed_create_steps', false, time() - 1000, COOKIEPATH );
+
+		$reset_steps = true;
+		bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . array_shift( array_keys( $bp->groups->group_creation_steps ) ) . '/' );
+	}
+
+	// If this is a creation step that is not recognized, just redirect them back to the first screen
+	if ( !empty( $bp->groups->current_create_step ) && empty( $bp->groups->group_creation_steps[$bp->groups->current_create_step] ) ) {
+		bp_core_add_message( __('There was an error saving group details. Please try again.', 'buddypress'), 'error' );
+		bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/' );
+	}
+
+	// Fetch the currently completed steps variable
+	if ( isset( $_COOKIE['bp_completed_create_steps'] ) && !isset( $reset_steps ) )
+		$bp->groups->completed_create_steps = unserialize( stripslashes( $_COOKIE['bp_completed_create_steps'] ) );
+
+	// Set the ID of the new group, if it has already been created in a previous step
+	if ( isset( $_COOKIE['bp_new_group_id'] ) ) {
+		$bp->groups->new_group_id = $_COOKIE['bp_new_group_id'];
+		$bp->groups->current_group = new BP_Groups_Group( $bp->groups->new_group_id );
+	}
+
+	// If the save, upload or skip button is hit, lets calculate what we need to save
+	if ( isset( $_POST['save'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'groups_create_save_' . $bp->groups->current_create_step );
+
+		if ( 'group-details' == $bp->groups->current_create_step ) {
+			if ( empty( $_POST['group-name'] ) || empty( $_POST['group-desc'] ) || !strlen( trim( $_POST['group-name'] ) ) || !strlen( trim( $_POST['group-desc'] ) ) ) {
+				bp_core_add_message( __( 'Please fill in all of the required fields', 'buddypress' ), 'error' );
+				bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . $bp->groups->current_create_step . '/' );
+			}
+
+			$new_group_id = isset( $bp->groups->new_group_id ) ? $bp->groups->new_group_id : 0;
+
+			if ( !$bp->groups->new_group_id = groups_create_group( array( 'group_id' => $new_group_id, 'name' => $_POST['group-name'], 'description' => $_POST['group-desc'], 'slug' => groups_check_slug( sanitize_title( esc_attr( $_POST['group-name'] ) ) ), 'date_created' => bp_core_current_time(), 'status' => 'public' ) ) ) {
+				bp_core_add_message( __( 'There was an error saving group details, please try again.', 'buddypress' ), 'error' );
+				bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . $bp->groups->current_create_step . '/' );
+			}
+
+			groups_update_groupmeta( $bp->groups->new_group_id, 'total_member_count', 1 );
+			groups_update_groupmeta( $bp->groups->new_group_id, 'last_activity', bp_core_current_time() );
+		}
+
+		if ( 'group-settings' == $bp->groups->current_create_step ) {
+			$group_status = 'public';
+			$group_enable_forum = 1;
+
+			if ( !isset($_POST['group-show-forum']) ) {
+				$group_enable_forum = 0;
+			} else {
+				// Create the forum if enable_forum = 1
+				if ( bp_is_active( 'forums' ) && '' == groups_get_groupmeta( $bp->groups->new_group_id, 'forum_id' ) ) {
+					groups_new_group_forum();
+				}
+			}
+
+			if ( 'private' == $_POST['group-status'] )
+				$group_status = 'private';
+			else if ( 'hidden' == $_POST['group-status'] )
+				$group_status = 'hidden';
+
+			if ( !$bp->groups->new_group_id = groups_create_group( array( 'group_id' => $bp->groups->new_group_id, 'status' => $group_status, 'enable_forum' => $group_enable_forum ) ) ) {
+				bp_core_add_message( __( 'There was an error saving group details, please try again.', 'buddypress' ), 'error' );
+				bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . $bp->groups->current_create_step . '/' );
+			}
+
+			// Set the invite status
+			// Checked against a whitelist for security
+			$allowed_invite_status = apply_filters( 'groups_allowed_invite_status', array( 'members', 'mods', 'admins' ) );
+			$invite_status	       = in_array( $_POST['group-invite-status'], (array)$allowed_invite_status ) ? $_POST['group-invite-status'] : 'members';
+
+			groups_update_groupmeta( $bp->groups->new_group_id, 'invite_status', $invite_status );
+		}
+
+		if ( 'group-invites' == $bp->groups->current_create_step )
+			groups_send_invites( $bp->loggedin_user->id, $bp->groups->new_group_id );
+
+		do_action( 'groups_create_group_step_save_' . $bp->groups->current_create_step );
+		do_action( 'groups_create_group_step_complete' ); // Mostly for clearing cache on a generic action name
+
+		/**
+		 * Once we have successfully saved the details for this step of the creation process
+		 * we need to add the current step to the array of completed steps, then update the cookies
+		 * holding the information
+		 */
+		$completed_create_steps = isset( $bp->groups->completed_create_steps ) ? $bp->groups->completed_create_steps : array();
+		if ( !in_array( $bp->groups->current_create_step, $completed_create_steps ) )
+			$bp->groups->completed_create_steps[] = $bp->groups->current_create_step;
+
+		// Reset cookie info
+		setcookie( 'bp_new_group_id', $bp->groups->new_group_id, time()+60*60*24, COOKIEPATH );
+		setcookie( 'bp_completed_create_steps', serialize( $bp->groups->completed_create_steps ), time()+60*60*24, COOKIEPATH );
+
+		// If we have completed all steps and hit done on the final step we
+		// can redirect to the completed group
+		if ( count( $bp->groups->completed_create_steps ) == count( $bp->groups->group_creation_steps ) && $bp->groups->current_create_step == array_pop( array_keys( $bp->groups->group_creation_steps ) ) ) {
+			unset( $bp->groups->current_create_step );
+			unset( $bp->groups->completed_create_steps );
+
+			// Once we compelete all steps, record the group creation in the activity stream.
+			groups_record_activity( array(
+				'action' => apply_filters( 'groups_activity_created_group_action', sprintf( __( '%1$s created the group %2$s', 'buddypress'), bp_core_get_userlink( $bp->loggedin_user->id ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' ) ),
+				'type' => 'created_group',
+				'item_id' => $bp->groups->new_group_id
+			) );
+
+			do_action( 'groups_group_create_complete', $bp->groups->new_group_id );
+
+			bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+		} else {
+			/**
+			 * Since we don't know what the next step is going to be (any plugin can insert steps)
+			 * we need to loop the step array and fetch the next step that way.
+			 */
+			foreach ( (array)$bp->groups->group_creation_steps as $key => $value ) {
+				if ( $key == $bp->groups->current_create_step ) {
+					$next = 1;
+					continue;
+				}
+
+				if ( isset( $next ) ) {
+					$next_step = $key;
+					break;
+				}
+			}
+
+			bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . $next_step . '/' );
+		}
+	}
+
+	// Group avatar is handled separately
+	if ( 'group-avatar' == $bp->groups->current_create_step && isset( $_POST['upload'] ) ) {
+		if ( !empty( $_FILES ) && isset( $_POST['upload'] ) ) {
+			// Normally we would check a nonce here, but the group save nonce is used instead
+
+			// Pass the file to the avatar upload handler
+			if ( bp_core_avatar_handle_upload( $_FILES, 'groups_avatar_upload_dir' ) ) {
+				$bp->avatar_admin->step = 'crop-image';
+
+				// Make sure we include the jQuery jCrop file for image cropping
+				add_action( 'wp_print_scripts', 'bp_core_add_jquery_cropper' );
+			}
+		}
+
+		// If the image cropping is done, crop the image and save a full/thumb version
+		if ( isset( $_POST['avatar-crop-submit'] ) && isset( $_POST['upload'] ) ) {
+			// Normally we would check a nonce here, but the group save nonce is used instead
+
+			if ( !bp_core_avatar_handle_crop( array( 'object' => 'group', 'avatar_dir' => 'group-avatars', 'item_id' => $bp->groups->current_group->id, 'original_file' => $_POST['image_src'], 'crop_x' => $_POST['x'], 'crop_y' => $_POST['y'], 'crop_w' => $_POST['w'], 'crop_h' => $_POST['h'] ) ) )
+				bp_core_add_message( __( 'There was an error saving the group avatar, please try uploading again.', 'buddypress' ), 'error' );
+			else
+				bp_core_add_message( __( 'The group avatar was uploaded successfully!', 'buddypress' ) );
+		}
+	}
+
+ 	bp_core_load_template( apply_filters( 'groups_template_create_group', 'groups/create' ) );
+}
+add_action( 'bp_actions', 'groups_action_create_group' );
+
+function groups_action_join_group() {
+	global $bp;
+
+	if ( !bp_is_single_item() || !bp_is_groups_component() || !bp_is_current_action( 'join' ) )
+		return false;
+
+	// Nonce check
+	if ( !check_admin_referer( 'groups_join_group' ) )
+		return false;
+
+	// Skip if banned or already a member
+	if ( !groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) && !groups_is_user_banned( $bp->loggedin_user->id, $bp->groups->current_group->id ) ) {
+
+		// User wants to join a group that is not public
+		if ( $bp->groups->current_group->status != 'public' ) {
+			if ( !groups_check_user_has_invite( $bp->loggedin_user->id, $bp->groups->current_group->id ) ) {
+				bp_core_add_message( __( 'There was an error joining the group.', 'buddypress' ), 'error' );
+				bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+			}
+		}
+
+		// User wants to join any group
+		if ( !groups_join_group( $bp->groups->current_group->id ) )
+			bp_core_add_message( __( 'There was an error joining the group.', 'buddypress' ), 'error' );
+		else
+			bp_core_add_message( __( 'You joined the group!', 'buddypress' ) );
+
+		bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+	}
+
+	bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
+}
+add_action( 'bp_actions', 'groups_action_join_group' );
+
+
+function groups_action_leave_group() {
+	global $bp;
+
+	if ( !bp_is_single_item() || !bp_is_groups_component() || !bp_is_current_action( 'leave-group' ) )
+		return false;
+
+	// Nonce check
+	if ( !check_admin_referer( 'groups_leave_group' ) )
+		return false;
+
+	// User wants to leave any group
+	if ( groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) ) {
+		if ( !groups_leave_group( $bp->groups->current_group->id ) ) {
+			bp_core_add_message( __( 'There was an error leaving the group.', 'buddypress' ), 'error' );
+		} else {
+			bp_core_add_message( __( 'You successfully left the group.', 'buddypress' ) );
+		}
+		bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+	}
+
+	bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
+}
+add_action( 'bp_actions', 'groups_action_leave_group' );
+
+
+function groups_action_sort_creation_steps() {
+	global $bp;
+
+	if ( !bp_is_groups_component() || !bp_is_current_action( 'create' ) )
+		return false;
+
+	if ( !is_array( $bp->groups->group_creation_steps ) )
+		return false;
+
+	foreach ( (array)$bp->groups->group_creation_steps as $slug => $step ) {
+		while ( !empty( $temp[$step['position']] ) )
+			$step['position']++;
+
+		$temp[$step['position']] = array( 'name' => $step['name'], 'slug' => $slug );
+	}
+
+	// Sort the steps by their position key
+	ksort($temp);
+	unset($bp->groups->group_creation_steps);
+
+	foreach( (array)$temp as $position => $step )
+		$bp->groups->group_creation_steps[$step['slug']] = array( 'name' => $step['name'], 'position' => $position );
+}
+
+function groups_action_redirect_to_random_group() {
+	global $bp, $wpdb;
+
+	if ( bp_is_groups_component() && isset( $_GET['random-group'] ) ) {
+		$group = groups_get_groups( array( 'type' => 'random', 'per_page' => 1 ) );
+
+		bp_core_redirect( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group['groups'][0]->slug . '/' );
+	}
+}
+add_action( 'bp_actions', 'groups_action_redirect_to_random_group' );
+
+function groups_action_group_feed() {
+	global $bp, $wp_query;
+
+	if ( !bp_is_active( 'activity' ) || !bp_is_groups_component() || !isset( $bp->groups->current_group ) || !bp_is_current_action( 'feed' ) )
+		return false;
+
+	$wp_query->is_404 = false;
+	status_header( 200 );
+
+	if ( 'public' != $bp->groups->current_group->status ) {
+		if ( !groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) )
+			return false;
+	}
+
+	include_once( BP_PLUGIN_DIR . '/bp-activity/feeds/bp-activity-group-feed.php' );
+	die;
+}
+add_action( 'bp_actions', 'groups_action_group_feed' );
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-activity.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-activity.php	(revision 5057)
@@ -0,0 +1,307 @@
+<?php
+/**
+ * BuddyPress Groups Activity & Notification Functions
+ *
+ * These functions handle the recording, deleting and formatting of activity and
+ * notifications for the user and for this specific component.
+ *
+ * @package BuddyPress
+ * @subpackage Groups
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_register_activity_actions() {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	bp_activity_set_action( $bp->groups->id, 'created_group',   __( 'Created a group',       'buddypress' ) );
+	bp_activity_set_action( $bp->groups->id, 'joined_group',    __( 'Joined a group',        'buddypress' ) );
+	bp_activity_set_action( $bp->groups->id, 'new_forum_topic', __( 'New group forum topic', 'buddypress' ) );
+	bp_activity_set_action( $bp->groups->id, 'new_forum_post',  __( 'New group forum post',  'buddypress' ) );
+
+	do_action( 'groups_register_activity_actions' );
+}
+add_action( 'bp_register_activity_actions', 'groups_register_activity_actions' );
+
+function groups_record_activity( $args = '' ) {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	// If the group is not public, hide the activity sitewide.
+	if ( isset( $bp->groups->current_group->status ) && 'public' == $bp->groups->current_group->status )
+		$hide_sitewide = false;
+	else
+		$hide_sitewide = true;
+
+	$defaults = array (
+		'id'                => false,
+		'user_id'           => $bp->loggedin_user->id,
+		'action'            => '',
+		'content'           => '',
+		'primary_link'      => '',
+		'component'         => $bp->groups->id,
+		'type'              => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'recorded_time'     => bp_core_current_time(),
+		'hide_sitewide'     => $hide_sitewide
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	return bp_activity_add( array( 'id' => $id, 'user_id' => $user_id, 'action' => $action, 'content' => $content, 'primary_link' => $primary_link, 'component' => $component, 'type' => $type, 'item_id' => $item_id, 'secondary_item_id' => $secondary_item_id, 'recorded_time' => $recorded_time, 'hide_sitewide' => $hide_sitewide ) );
+}
+
+function groups_update_last_activity( $group_id = 0 ) {
+	global $bp;
+
+	if ( !$group_id )
+		$group_id = $bp->groups->current_group->id;
+
+	if ( !$group_id )
+		return false;
+
+	groups_update_groupmeta( $group_id, 'last_activity', bp_core_current_time() );
+}
+add_action( 'groups_leave_group', 'groups_update_last_activity' );
+add_action( 'groups_created_group', 'groups_update_last_activity' );
+add_action( 'groups_new_forum_topic', 'groups_update_last_activity' );
+add_action( 'groups_new_forum_topic_post', 'groups_update_last_activity' );
+
+function groups_format_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
+	global $bp;
+
+	switch ( $action ) {
+		case 'new_membership_request':
+			$group_id = $secondary_item_id;
+			$requesting_user_id = $item_id;
+
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			// Set up the string and the filter
+			// Because different values are passed to the filters, we'll return the
+			// values inline
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( '%1$d new membership requests for the group "%2$s"', 'buddypress' ), (int)$total_items, $group->name );
+				$filter = 'bp_groups_multiple_new_membership_requests_notification';
+				$notification_link = $group_link . 'admin/membership-requests/?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Group Membership Requests', 'buddypress' ) . '">' . $text . '</a>', $group_link, $total_items, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $total_items, $group->name, $text, $notification_link );
+				}
+			} else {
+				$user_fullname = bp_core_get_user_displayname( $requesting_user_id );
+				$text = sprintf( __( '%s requests group membership', 'buddypress' ), $user_fullname );
+				$filter = 'bp_groups_single_new_membership_request_notification';
+				$notification_link = $group_link . 'admin/membership-requests/?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . sprintf( __( '%s requests group membership', 'buddypress' ), $user_fullname ) . '">' . $text . '</a>', $group_link, $user_fullname, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $user_fullname, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+
+		case 'membership_request_accepted':
+			$group_id = $item_id;
+
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( '%d accepted group membership requests', 'buddypress' ), (int)$total_items, $group->name );
+				$filter = 'bp_groups_multiple_membership_request_accepted_notification';
+				$notification_link = bp_loggedin_user_domain() . bp_get_groups_slug() . '/?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Groups', 'buddypress' ) . '">' . $text . '</a>', $total_items, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $total_items, $group->name, $text, $notification_link );
+				}
+			} else {
+				$text = sprintf( __( 'Membership for group "%s" accepted', 'buddypress' ), $group->name );
+				$filter = 'bp_groups_single_membership_request_accepted_notification';
+				$notification_link = $group_link . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '">' . $text . '</a>', $group_link, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+
+		case 'membership_request_rejected':
+			$group_id = $item_id;
+
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( '%d rejected group membership requests', 'buddypress' ), (int)$total_items, $group->name );
+				$filter = 'bp_groups_multiple_membership_request_rejected_notification';
+				$notification_link = bp_loggedin_user_domain() . bp_get_groups_slug() . '/?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Groups', 'buddypress' ) . '">' . $text . '</a>', $total_items, $group->name );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $total_items, $group->name, $text, $notification_link );
+				}
+			} else {
+				$text = sprintf( __( 'Membership for group "%s" rejected', 'buddypress' ), $group->name );
+				$filter = 'bp_groups_single_membership_request_rejected_notification';
+				$notification_link = $group_link . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '">' . $text . '</a>', $group_link, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+
+		case 'member_promoted_to_admin':
+			$group_id = $item_id;
+
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( 'You were promoted to an admin in %d groups', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_groups_multiple_member_promoted_to_admin_notification';
+				$notification_link = bp_loggedin_user_domain() . bp_get_groups_slug() . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Groups', 'buddypress' ) . '">' . $text . '</a>', $total_items, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $total_items, $text, $notification_link );
+				}
+			} else {
+				$text = sprintf( __( 'You were promoted to an admin in the group "%s"', 'buddypress' ), $group->name );
+				$filter = 'bp_groups_single_member_promoted_to_admin_notification';
+				$notification_link = $group_link . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '">' . $text . '</a>', $group_link, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+
+		case 'member_promoted_to_mod':
+			$group_id = $item_id;
+
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( 'You were promoted to a mod in %d groups', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_groups_multiple_member_promoted_to_mod_notification';
+				$notification_link = bp_loggedin_user_domain() . bp_get_groups_slug() . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Groups', 'buddypress' ) . '">' . $text . '</a>', $total_items, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $total_items, $text, $notification_link );
+				}
+			} else {
+				$text = sprintf( __( 'You were promoted to a mod in the group "%s"', 'buddypress' ), $group->name );
+				$filter = 'bp_groups_single_member_promoted_to_mod_notification';
+				$notification_link = $group_link . '?n=1';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '">' . $text . '</a>', $group_link, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+
+		case 'group_invite':
+			$group_id = $item_id;
+			$group = new BP_Groups_Group( $group_id );
+			$group_link = bp_get_group_permalink( $group );
+
+			$notification_link = bp_loggedin_user_domain() . bp_get_groups_slug() . '/invites/?n=1';
+
+			if ( (int)$total_items > 1 ) {
+				$text = sprintf( __( 'You have %d new group invitations', 'buddypress' ), (int)$total_items );
+				$filter = 'bp_groups_multiple_group_invite_notification';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '" title="' . __( 'Group Invites', 'buddypress' ) . '">' . $text . '</a>', $total_items, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $total_items, $text, $notification_link );
+				}
+			} else {
+				$text = sprintf( __( 'You have an invitation to the group: %s', 'buddypress' ), $group->name );
+				$filter = 'bp_groups_single_group_invite_notification';
+
+				if ( 'string' == $format ) {
+					return apply_filters( $filter, '<a href="' . $notification_link . '">' . $text . '</a>', $group_link, $group->name, $text, $notification_link );
+				} else {
+					return apply_filters( $filter, array(
+						'link' => $notification_link,
+						'text' => $text
+					), $group_link, $group->name, $text, $notification_link );
+				}
+			}
+
+			break;
+	}
+
+	do_action( 'groups_format_notifications', $action, $item_id, $secondary_item_id, $total_items );
+
+	return false;
+}
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-adminbar.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-adminbar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-adminbar.php	(revision 5057)
@@ -0,0 +1,117 @@
+<?php
+/**
+ * BuddyPress Groups Admin Bar
+ *
+ * Handles the groups functions related to the WordPress Admin Bar
+ *
+ * @package BuddyPress
+ * @subpackage Groups
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Adds the Group Admin top-level menu to group pages
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @todo Add dynamic menu items for group extensions
+ */
+function bp_groups_group_admin_menu() {
+	global $wp_admin_bar, $bp;
+
+	// Only show if viewing a group
+	if ( !bp_is_group() )
+		return false;
+
+	// Only show this menu to group admins and super admins
+	if ( !is_super_admin() && !bp_group_is_admin() )
+		return false;
+
+	// Group avatar
+	$avatar = bp_core_fetch_avatar( array(
+		'object'     => 'group',
+		'type'       => 'thumb',
+		'avatar_dir' => 'group-avatars',
+		'item_id'    => $bp->groups->current_group->id,
+		'width'      => 16,
+		'height'     => 16
+	) );
+
+	// Unique ID for the 'My Account' menu
+	$bp->group_admin_menu_id = ( ! empty( $avatar ) ) ? 'group-admin-with-avatar' : 'group-admin';
+
+	// Add the top-level Group Admin button
+	$wp_admin_bar->add_menu( array(
+		'id'    => $bp->group_admin_menu_id,
+		'title' => $avatar . bp_get_current_group_name(),
+		'href'  => bp_get_group_permalink( $bp->groups->current_group )
+	) );
+
+	// Group Admin > Edit details
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->group_admin_menu_id,
+		'id'     => 'edit-details',
+		'title'  => __( 'Edit Details', 'buddypress' ),
+		'href'   =>  bp_get_groups_action_link( 'admin/edit-details' )
+	) );
+
+	// Group Admin > Group settings
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->group_admin_menu_id,
+		'id'     => 'group-settings',
+		'title'  => __( 'Edit Settings', 'buddypress' ),
+		'href'   =>  bp_get_groups_action_link( 'admin/group-settings' )
+	) );
+
+	// Group Admin > Group avatar
+	if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) {
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->group_admin_menu_id,
+			'id'     => 'group-avatar',
+			'title'  => __( 'Edit Avatar', 'buddypress' ),
+			'href'   =>  bp_get_groups_action_link( 'admin/group-avatar' )
+		) );
+	}
+
+	// Group Admin > Manage invitations
+	if ( bp_is_active( 'friends' ) ) {
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->group_admin_menu_id,
+			'id'     => 'manage-invitations',
+			'title'  => __( 'Manage Invitations', 'buddypress' ),
+			'href'   =>  bp_get_groups_action_link( 'send-invites' )
+		) );
+	}
+
+	// Group Admin > Manage members
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->group_admin_menu_id,
+		'id'     => 'manage-members',
+		'title'  => __( 'Manage Members', 'buddypress' ),
+		'href'   =>  bp_get_groups_action_link( 'admin/manage-members' )
+	) );
+
+	// Group Admin > Membership Requests
+	if ( bp_get_group_status( $bp->groups->current_group ) == 'private' ) {
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->group_admin_menu_id,
+			'id'     => 'membership-requests',
+			'title'  => __( 'Membership Requests', 'buddypress' ),
+			'href'   =>  bp_get_groups_action_link( 'admin/membership-requests' )
+		) );
+	}
+
+	// Delete Group
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->group_admin_menu_id,
+		'id'     => 'delete-group',
+		'title'  => __( 'Delete Group', 'buddypress' ),
+		'href'   =>  bp_get_groups_action_link( 'admin/delete-group' )
+	) );
+}
+add_action( 'bp_setup_admin_bar', 'bp_groups_group_admin_menu', 99 );
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-buddybar.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-buddybar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-buddybar.php	(revision 5057)
@@ -0,0 +1,54 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_groups_adminbar_admin_menu() {
+	global $bp, $groups_template;
+
+	if ( empty( $bp->groups->current_group ) )
+		return false;
+
+	// Don't show this menu to non site admins or if you're viewing your own profile
+	if ( !current_user_can( 'edit_users' ) || !is_super_admin() || ( !$bp->is_item_admin && !$bp->is_item_mod ) )
+		return false; ?>
+
+	<li id="bp-adminbar-adminoptions-menu">
+		<a href="<?php bp_groups_action_link( 'admin' ); ?>"><?php _e( 'Admin Options', 'buddypress' ); ?></a>
+
+		<ul>
+			<li><a href="<?php bp_groups_action_link( 'admin/edit-details' ); ?>"><?php _e( 'Edit Details', 'buddypress' ); ?></a></li>
+
+			<li><a href="<?php bp_groups_action_link( 'admin/group-settings' );  ?>"><?php _e( 'Group Settings', 'buddypress' ); ?></a></li>
+
+			<?php if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) : ?>
+
+				<li><a href="<?php bp_groups_action_link( 'admin/group-avatar' ); ?>"><?php _e( 'Group Avatar', 'buddypress' ); ?></a></li>
+
+			<?php endif; ?>
+
+			<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+				<li><a href="<?php bp_groups_action_link( 'send-invites' ); ?>"><?php _e( 'Manage Invitations', 'buddypress' ); ?></a></li>
+
+			<?php endif; ?>
+
+			<li><a href="<?php bp_groups_action_link( 'admin/manage-members' ); ?>"><?php _e( 'Manage Members', 'buddypress' ); ?></a></li>
+
+			<?php if ( $bp->groups->current_group->status == 'private' ) : ?>
+
+				<li><a href="<?php bp_groups_action_link( 'admin/membership-requests' ); ?>"><?php _e( 'Membership Requests', 'buddypress' ); ?></a></li>
+
+			<?php endif; ?>
+
+			<li><a class="confirm" href="<?php echo wp_nonce_url( bp_get_group_permalink( $bp->groups->current_group ) . 'admin/delete-group/', 'groups_delete_group' ); ?>&amp;delete-group-button=1&amp;delete-group-understand=1"><?php _e( "Delete Group", 'buddypress' ) ?></a></li>
+
+			<?php do_action( 'bp_groups_adminbar_admin_menu' ) ?>
+
+		</ul>
+	</li>
+
+	<?php
+}
+add_action( 'bp_adminbar_menus', 'bp_groups_adminbar_admin_menu', 20 );
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-cache.php	(revision 5057)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * BuddyPress Groups Caching
+ *
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ *
+ * @package BuddyPress
+ * @subpackage Groups
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_clear_group_object_cache( $group_id ) {
+	wp_cache_delete( 'bp_total_group_count', 'bp' );
+}
+add_action( 'groups_group_deleted',              'groups_clear_group_object_cache' );
+add_action( 'groups_settings_updated',           'groups_clear_group_object_cache' );
+add_action( 'groups_details_updated',            'groups_clear_group_object_cache' );
+add_action( 'groups_group_avatar_updated',       'groups_clear_group_object_cache' );
+add_action( 'groups_create_group_step_complete', 'groups_clear_group_object_cache' );
+
+function groups_clear_group_user_object_cache( $group_id, $user_id ) {
+	wp_cache_delete( 'bp_total_groups_for_user_' . $user_id );
+}
+add_action( 'groups_join_group',   'groups_clear_group_user_object_cache', 10, 2 );
+add_action( 'groups_leave_group',  'groups_clear_group_user_object_cache', 10, 2 );
+add_action( 'groups_ban_member',   'groups_clear_group_user_object_cache', 10, 2 );
+add_action( 'groups_unban_member', 'groups_clear_group_user_object_cache', 10, 2 );
+
+/* List actions to clear super cached pages on, if super cache is installed */
+add_action( 'groups_join_group',                 'bp_core_clear_cache' );
+add_action( 'groups_leave_group',                'bp_core_clear_cache' );
+add_action( 'groups_accept_invite',              'bp_core_clear_cache' );
+add_action( 'groups_reject_invite',              'bp_core_clear_cache' );
+add_action( 'groups_invite_user',                'bp_core_clear_cache' );
+add_action( 'groups_uninvite_user',              'bp_core_clear_cache' );
+add_action( 'groups_details_updated',            'bp_core_clear_cache' );
+add_action( 'groups_settings_updated',           'bp_core_clear_cache' );
+add_action( 'groups_unban_member',               'bp_core_clear_cache' );
+add_action( 'groups_ban_member',                 'bp_core_clear_cache' );
+add_action( 'groups_demote_member',              'bp_core_clear_cache' );
+add_action( 'groups_premote_member',             'bp_core_clear_cache' );
+add_action( 'groups_membership_rejected',        'bp_core_clear_cache' );
+add_action( 'groups_membership_accepted',        'bp_core_clear_cache' );
+add_action( 'groups_membership_requested',       'bp_core_clear_cache' );
+add_action( 'groups_create_group_step_complete', 'bp_core_clear_cache' );
+add_action( 'groups_created_group',              'bp_core_clear_cache' );
+add_action( 'groups_group_avatar_updated',       'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-classes.php	(revision 5057)
@@ -0,0 +1,1286 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_Groups_Group {
+	var $id;
+	var $creator_id;
+	var $name;
+	var $slug;
+	var $description;
+	var $status;
+	var $enable_forum;
+	var $date_created;
+
+	var $admins;
+	var $mods;
+	var $total_member_count;
+
+	function bp_groups_group( $id = null ) {
+		$this->__construct($id);
+	}
+
+	function __construct( $id = null ) {
+		if ( $id ) {
+			$this->id = $id;
+			$this->populate();
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp;
+
+		if ( $group = $wpdb->get_row( $wpdb->prepare( "SELECT g.*, gm.meta_value as last_activity, gm2.meta_value as total_member_count FROM {$bp->groups->table_name} g, {$bp->groups->table_name_groupmeta} gm, {$bp->groups->table_name_groupmeta} gm2 WHERE g.id = gm.group_id AND g.id = gm2.group_id AND gm.meta_key = 'last_activity' AND gm2.meta_key = 'total_member_count' AND g.id = %d", $this->id ) ) ) {
+			$this->id                 = $group->id;
+			$this->creator_id         = $group->creator_id;
+			$this->name               = stripslashes($group->name);
+			$this->slug               = $group->slug;
+			$this->description        = stripslashes($group->description);
+			$this->status             = $group->status;
+			$this->enable_forum       = $group->enable_forum;
+			$this->date_created       = $group->date_created;
+			$this->last_activity      = $group->last_activity;
+			$this->total_member_count = $group->total_member_count;
+			$this->is_member          = BP_Groups_Member::check_is_member( $bp->loggedin_user->id, $this->id );
+
+			// Get group admins and mods
+			$admin_mods = $wpdb->get_results( apply_filters( 'bp_group_admin_mods_user_join_filter', $wpdb->prepare( "SELECT u.ID as user_id, u.user_login, u.user_email, u.user_nicename, m.is_admin, m.is_mod FROM {$wpdb->users} u, {$bp->groups->table_name_members} m WHERE u.ID = m.user_id AND m.group_id = %d AND ( m.is_admin = 1 OR m.is_mod = 1 )", $this->id ) ) );
+			foreach( (array)$admin_mods as $user ) {
+				if ( (int)$user->is_admin )
+					$this->admins[] = $user;
+				else
+					$this->mods[] = $user;
+			}
+		}
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->creator_id   = apply_filters( 'groups_group_creator_id_before_save',   $this->creator_id,   $this->id );
+		$this->name         = apply_filters( 'groups_group_name_before_save',         $this->name,         $this->id );
+ 		$this->slug         = apply_filters( 'groups_group_slug_before_save',         $this->slug,         $this->id );
+		$this->description  = apply_filters( 'groups_group_description_before_save',  $this->description,  $this->id );
+ 		$this->status       = apply_filters( 'groups_group_status_before_save',       $this->status,       $this->id );
+		$this->enable_forum = apply_filters( 'groups_group_enable_forum_before_save', $this->enable_forum, $this->id );
+		$this->date_created = apply_filters( 'groups_group_date_created_before_save', $this->date_created, $this->id );
+
+		do_action_ref_array( 'groups_group_before_save', array( &$this ) );
+
+		if ( !empty( $this->id ) ) {
+			$sql = $wpdb->prepare(
+				"UPDATE {$bp->groups->table_name} SET
+					creator_id = %d,
+					name = %s,
+					slug = %s,
+					description = %s,
+					status = %s,
+					enable_forum = %d,
+					date_created = %s
+				WHERE
+					id = %d
+				",
+					$this->creator_id,
+					$this->name,
+					$this->slug,
+					$this->description,
+					$this->status,
+					$this->enable_forum,
+					$this->date_created,
+					$this->id
+			);
+		} else {
+			$sql = $wpdb->prepare(
+				"INSERT INTO {$bp->groups->table_name} (
+					creator_id,
+					name,
+					slug,
+					description,
+					status,
+					enable_forum,
+					date_created
+				) VALUES (
+					%d, %s, %s, %s, %s, %d, %s
+				)",
+					$this->creator_id,
+					$this->name,
+					$this->slug,
+					$this->description,
+					$this->status,
+					$this->enable_forum,
+					$this->date_created
+			);
+		}
+
+		if ( false === $wpdb->query($sql) )
+			return false;
+
+		if ( empty( $this->id ) )
+			$this->id = $wpdb->insert_id;
+
+		do_action_ref_array( 'groups_group_after_save', array( &$this ) );
+
+		return true;
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		// Delete groupmeta for the group
+		groups_delete_groupmeta( $this->id );
+
+		// Fetch the user IDs of all the members of the group
+		$user_ids = BP_Groups_Member::get_group_member_ids( $this->id );
+		$user_ids = implode( ',', (array)$user_ids );
+
+		// Modify group count usermeta for members
+		$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->usermeta} SET meta_value = meta_value - 1 WHERE meta_key = 'total_group_count' AND user_id IN ( {$user_ids} )" ) );
+
+		// Now delete all group member entries
+		BP_Groups_Member::delete_all( $this->id );
+
+		do_action_ref_array( 'bp_groups_delete_group', array( &$this ) );
+
+		// Finally remove the group entry from the DB
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name} WHERE id = %d", $this->id ) ) )
+			return false;
+
+		return true;
+	}
+
+	/* Static Functions */
+
+	function group_exists( $slug, $table_name = false ) {
+		global $wpdb, $bp;
+
+		if ( !$table_name )
+			$table_name = $bp->groups->table_name;
+
+		if ( !$slug )
+			return false;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table_name} WHERE slug = %s", $slug ) );
+	}
+
+	function get_id_from_slug( $slug ) {
+		return BP_Groups_Group::group_exists( $slug );
+	}
+
+	function get_invites( $user_id, $group_id ) {
+		global $wpdb, $bp;
+		return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d and is_confirmed = 0 AND inviter_id = %d", $group_id, $user_id ) );
+	}
+
+	function filter_user_groups( $filter, $user_id = 0, $order = false, $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		// Get all the group ids for the current user's groups.
+		$gids = BP_Groups_Member::get_group_ids( $user_id );
+
+		if ( !$gids['groups'] )
+			return false;
+
+		$gids = implode( ',', $gids['groups'] );
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE '{$filter}%%' OR description LIKE '{$filter}%%' ) AND id IN ({$gids}) {$pag_sql}" ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE '{$filter}%%' OR description LIKE '{$filter}%%' ) AND id IN ({$gids})" ) );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function search_groups( $filter, $limit = null, $page = null, $sort_by = false, $order = false ) {
+		global $wpdb, $bp;
+
+		$filter = like_escape( $wpdb->escape( $filter ) );
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( $sort_by && $order ) {
+			$sort_by = $wpdb->escape( $sort_by );
+			$order = $wpdb->escape( $order );
+			$order_sql = "ORDER BY $sort_by $order";
+		}
+
+		if ( !is_super_admin() )
+			$hidden_sql = "AND status != 'hidden'";
+
+		$paged_groups = $wpdb->get_results( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE '%%$filter%%' OR description LIKE '%%$filter%%' ) {$hidden_sql} {$order_sql} {$pag_sql}" );
+		$total_groups = $wpdb->get_var( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE '%%$filter%%' OR description LIKE '%%$filter%%' ) {$hidden_sq}" );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function check_slug( $slug ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE slug = %s", $slug ) );
+	}
+
+	function get_slug( $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE id = %d", $group_id ) );
+	}
+
+	function has_members( $group_id ) {
+		global $wpdb, $bp;
+
+		$members = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) );
+
+		if ( !$members )
+			return false;
+
+		return true;
+	}
+
+	function has_membership_requests( $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0", $group_id ) );
+	}
+
+	function get_membership_requests( $group_id, $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		if ( $limit && $page ) {
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+		}
+
+		$paged_requests = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0{$pag_sql}", $group_id ) );
+		$total_requests = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0", $group_id ) );
+
+		return array( 'requests' => $paged_requests, 'total' => $total_requests );
+	}
+
+	function get( $type = 'newest', $per_page = null, $page = null, $user_id = 0, $search_terms = false, $include = false, $populate_extras = true, $exclude = false, $show_hidden = false ) {
+		global $wpdb, $bp;
+
+		$sql = array();
+
+		$sql['select'] = "SELECT g.*, gm1.meta_value AS total_member_count, gm2.meta_value AS last_activity";
+		$sql['from']   = " FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2,";
+
+		if ( !empty( $user_id ) )
+			$sql['members_from'] = " {$bp->groups->table_name_members} m,";
+
+		$sql['group_from'] = " {$bp->groups->table_name} g WHERE";
+
+		if ( !empty( $user_id ) )
+			$sql['user_where'] = " g.id = m.group_id AND";
+
+		$sql['where'] = " g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'";
+
+		if ( empty( $show_hidden ) )
+			$sql['hidden'] = " AND g.status != 'hidden'";
+
+		if ( !empty( $search_terms ) ) {
+			$search_terms = like_escape( $wpdb->escape( $search_terms ) );
+			$sql['search'] = " AND ( g.name LIKE '%%{$search_terms}%%' OR g.description LIKE '%%{$search_terms}%%' )";
+		}
+
+		if ( !empty( $user_id ) )
+			$sql['user'] = $wpdb->prepare( " AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id );
+
+		if ( !empty( $include ) ) {
+			if ( is_array( $include ) )
+				$include = implode( ',', $include );
+
+			$include = $wpdb->escape( $include );
+			$sql['include'] = " AND g.id IN ({$include})";
+		}
+
+		if ( !empty( $exclude ) ) {
+			if ( is_array( $exclude ) )
+				$exclude = implode( ',', $exclude );
+
+			$exclude = $wpdb->escape( $exclude );
+			$sql['exclude'] = " AND g.id NOT IN ({$exclude})";
+		}
+
+		switch ( $type ) {
+			case 'newest': default:
+				$sql['order'] = " ORDER BY g.date_created DESC";
+				break;
+			case 'active':
+				$sql[] = "ORDER BY last_activity DESC";
+				break;
+			case 'popular':
+				$sql[] = "ORDER BY CONVERT(gm1.meta_value, SIGNED) DESC";
+				break;
+			case 'alphabetical':
+				$sql[] = "ORDER BY g.name ASC";
+				break;
+			case 'random':
+				$sql[] = "ORDER BY rand()";
+				break;
+		}
+
+		if ( $per_page && $page )
+			$sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page), intval( $per_page ) );
+
+		/* Get paginated results */
+		$paged_groups_sql = apply_filters( 'bp_groups_get_paged_groups_sql', join( ' ', (array)$sql ), $sql );
+		$paged_groups     = $wpdb->get_results( $paged_groups_sql );
+
+		$total_sql['select'] = "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name} g, {$bp->groups->table_name_members} gm1, {$bp->groups->table_name_groupmeta} gm2";
+
+		if ( !empty( $user_id ) )
+			$total_sql['select'] .= ", {$bp->groups->table_name_members} m";
+
+		if ( !empty( $sql['hidden'] ) )
+			$total_sql['where'][] = "g.status != 'hidden'";
+
+		if ( !empty( $sql['search'] ) )
+			$total_sql['where'][] = "( g.name LIKE '%%{$search_terms}%%' OR g.description LIKE '%%{$search_terms}%%' )";
+
+		if ( !empty( $user_id ) )
+			$total_sql['where'][] = "m.group_id = g.id AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0";
+
+		if ( !empty( $exclude ) ) {
+			$exclude = $wpdb->escape( $exclude );
+			$total_sql['where'][] = " g.id NOT IN ({$exclude})";
+		}
+
+		$total_sql['where'][] = "g.id = gm1.group_id";
+		$total_sql['where'][] = "g.id = gm2.group_id";
+		$total_sql['where'][] = "gm2.meta_key = 'last_activity'";
+
+		$t_sql = $total_sql['select'];
+
+		if ( !empty( $total_sql['where'] ) )
+			$t_sql .= " WHERE " . join( ' AND ', (array)$total_sql['where'] );
+
+		/* Get total group results */
+		$total_groups_sql = apply_filters( 'bp_groups_get_total_groups_sql', join( ' ', (array)$t_sql ), $t_sql );
+		$total_groups     = $wpdb->get_var( $total_groups_sql );
+
+		/* Populate some extra information instead of querying each time in the loop */
+		if ( !empty( $populate_extras ) ) {
+			$group_ids = array();
+			foreach ( (array)$paged_groups as $group ) $group_ids[] = $group->id;
+			$group_ids = $wpdb->escape( join( ',', (array)$group_ids ) );
+			$paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, $type );
+		}
+
+		unset( $sql, $total_sql );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_by_most_forum_topics( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
+		global $wpdb, $bp, $bbdb;
+
+		if ( !$bbdb )
+			do_action( 'bbpress_init' );
+
+		if ( $limit && $page ) {
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+		}
+
+		if ( !is_user_logged_in() || ( !is_super_admin() && ( $user_id != $bp->loggedin_user->id ) ) )
+			$hidden_sql = " AND g.status != 'hidden'";
+
+		if ( !empty( $search_terms ) ) {
+			$search_terms = like_escape( $wpdb->escape( $search_terms ) );
+			$search_sql = " AND ( g.name LIKE '%%{$search_terms}%%' OR g.description LIKE '%%{$search_terms}%%' )";
+		}
+
+		if ( !empty( $exclude ) ) {
+			$exclude = $wpdb->escape( $exclude );
+			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		}
+
+		if ( $user_id ) {
+			$user_id = $wpdb->escape( $user_id );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
+		} else {
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql}" );
+		}
+
+		if ( !empty( $populate_extras ) ) {
+			foreach ( (array)$paged_groups as $group ) $group_ids[] = $group->id;
+			$group_ids = $wpdb->escape( join( ',', (array)$group_ids ) );
+			$paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
+		}
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_by_most_forum_posts( $limit = null, $page = null, $search_terms = false, $populate_extras = true, $exclude = false ) {
+		global $wpdb, $bp, $bbdb;
+
+		if ( !$bbdb )
+			do_action( 'bbpress_init' );
+
+		if ( $limit && $page ) {
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+		}
+
+		if ( !is_user_logged_in() || ( !is_super_admin() && ( $user_id != $bp->loggedin_user->id ) ) )
+			$hidden_sql = " AND g.status != 'hidden'";
+
+		if ( $search_terms ) {
+			$search_terms = like_escape( $wpdb->escape( $search_terms ) );
+			$search_sql = " AND ( g.name LIKE '%%{$search_terms}%%' OR g.description LIKE '%%{$search_terms}%%' )";
+		}
+
+		if ( !empty( $exclude ) ) {
+			$exclude = $wpdb->escape( $exclude );
+			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		}
+
+		if ( $user_id ) {
+			$user_id = $wpdb->escape( $user_id );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
+			$total_groups = $wpdb->get_results( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} " );
+		} else {
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} {$exclude_sql}" );
+		}
+
+		if ( !empty( $populate_extras ) ) {
+			foreach ( (array)$paged_groups as $group ) $group_ids[] = $group->id;
+			$group_ids = $wpdb->escape( join( ',', (array)$group_ids ) );
+			$paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
+		}
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_by_letter( $letter, $limit = null, $page = null, $populate_extras = true, $exclude = false ) {
+		global $wpdb, $bp;
+
+		// Multibyte compliance
+		if ( function_exists( 'mb_strlen' ) ) {
+			if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) {
+				return false;
+			}
+		} else {
+			if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) {
+				return false;
+			}
+		}
+
+		if ( !empty( $exclude ) ) {
+			$exclude = $wpdb->escape( $exclude );
+			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		}
+
+		if ( !is_super_admin() )
+			$hidden_sql = $wpdb->prepare( " AND status != 'hidden'");
+
+		$letter = like_escape( $wpdb->escape( $letter ) );
+
+		if ( $limit && $page ) {
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+			$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE '$letter%%' {$hidden_sql} {$search_sql} {$exclude_sql}" ) );
+		}
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE '$letter%%' {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY g.name ASC {$pag_sql}"  ) );
+
+		if ( !empty( $populate_extras ) ) {
+			foreach ( (array)$paged_groups as $group ) $group_ids[] = $group->id;
+			$group_ids = $wpdb->escape( join( ',', (array)$group_ids ) );
+			$paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
+		}
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_random( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
+		global $wpdb, $bp;
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( !is_user_logged_in() || ( !is_super_admin() && ( $user_id != $bp->loggedin_user->id ) ) )
+			$hidden_sql = "AND g.status != 'hidden'";
+
+		if ( $search_terms ) {
+			$search_terms = like_escape( $wpdb->escape( $search_terms ) );
+			$search_sql = " AND ( g.name LIKE '%%{$search_terms}%%' OR g.description LIKE '%%{$search_terms}%%' )";
+		}
+
+		if ( !empty( $exclude ) ) {
+			$exclude = $wpdb->escape( $exclude );
+			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		}
+
+		if ( $user_id ) {
+			$user_id = $wpdb->escape( $user_id );
+			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY rand() {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m LEFT JOIN {$bp->groups->table_name_groupmeta} gm ON m.group_id = gm.group_id INNER JOIN {$bp->groups->table_name} g ON m.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
+		} else {
+			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY rand() {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm INNER JOIN {$bp->groups->table_name} g ON gm.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} {$exclude_sql}" );
+		}
+
+		if ( !empty( $populate_extras ) ) {
+			foreach ( (array)$paged_groups as $group ) $group_ids[] = $group->id;
+			$group_ids = $wpdb->escape( join( ',', (array)$group_ids ) );
+			$paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
+		}
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_group_extras( &$paged_groups, &$group_ids, $type = false ) {
+		global $bp, $wpdb;
+
+		if ( empty( $group_ids ) )
+			return $paged_groups;
+
+		/* Fetch the logged in users status within each group */
+		$user_status = $wpdb->get_col( $wpdb->prepare( "SELECT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id IN ( {$group_ids} ) AND is_confirmed = 1 AND is_banned = 0", $bp->loggedin_user->id ) );
+		for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) {
+			$paged_groups[$i]->is_member = false;
+
+			foreach ( (array)$user_status as $group_id ) {
+				if ( $group_id == $paged_groups[$i]->id )
+					$paged_groups[$i]->is_member = true;
+			}
+		}
+
+		$user_banned = $wpdb->get_col( $wpdb->prepare( "SELECT group_id FROM {$bp->groups->table_name_members} WHERE is_banned = 1 AND user_id = %d AND group_id IN ( {$group_ids} )", $bp->loggedin_user->id ) );
+		for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) {
+			$paged_groups[$i]->is_banned = false;
+
+			foreach ( (array)$user_banned as $group_id ) {
+				if ( $group_id == $paged_groups[$i]->id )
+					$paged_groups[$i]->is_banned = true;
+			}
+		}
+
+		return $paged_groups;
+	}
+
+	function delete_all_invites( $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d AND invite_sent = 1", $group_id ) );
+	}
+
+	function get_total_group_count() {
+		global $wpdb, $bp;
+
+		$hidden_sql = '';
+		if ( !is_super_admin() )
+			$hidden_sql = "WHERE status != 'hidden'";
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} {$hidden_sql}" ) );
+	}
+
+	function get_global_forum_topic_count( $type ) {
+		global $bbdb, $wpdb, $bp;
+
+		if ( 'unreplied' == $type )
+			$bp->groups->filter_sql = ' AND t.topic_posts = 1';
+
+		$extra_sql = apply_filters( 'groups_total_public_forum_topic_count', $bp->groups->filter_sql, $type );
+
+		return $wpdb->get_var( "SELECT COUNT(t.topic_id) FROM {$bbdb->topics} AS t, {$bp->groups->table_name} AS g LEFT JOIN {$bp->groups->table_name_groupmeta} AS gm ON g.id = gm.group_id WHERE (gm.meta_key = 'forum_id' AND gm.meta_value = t.forum_id) AND g.status = 'public' AND t.topic_status = '0' AND t.topic_sticky != '2' {$extra_sql} " );
+	}
+
+	function get_total_member_count( $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) );
+	}
+
+
+	/**
+	 * Get a total count of all topics of a given status, across groups/forums
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 *
+	 * @param str $status 'public', 'private', 'hidden', 'all' Which group types to count
+	 * @return int The topic count
+	 */
+	function get_global_topic_count( $status = 'public', $search_terms = false ) {
+		global $bbdb, $wpdb, $bp;
+
+		switch ( $status ) {
+			case 'all' :
+				$status_sql = '';
+				break;
+
+			case 'hidden' :
+				$status_sql = $wpdb->prepare( "AND g.status = 'hidden'" );
+				break;
+
+			case 'private' :
+				$status_sql = $wpdb->prepare( "AND g.status = 'private'" );
+				break;
+
+			case 'public' :
+			default :
+				$status_sql = $wpdb->prepare( "AND g.status = 'public'" );
+				break;
+		}
+
+		$sql = array();
+
+		$sql['select'] = "SELECT COUNT(t.topic_id)";
+
+		$sql['from'] = "FROM {$bbdb->topics} AS t INNER JOIN {$bp->groups->table_name_groupmeta} AS gm ON t.forum_id = gm.meta_value INNER JOIN {$bp->groups->table_name} AS g ON gm.group_id = g.id";
+
+		$sql['where'] = "WHERE gm.meta_key = 'forum_id' {$status_sql} AND t.topic_status = '0' AND t.topic_sticky != '2'";
+
+		if ( $search_terms ) {
+			$st = like_escape( $search_terms );
+			$sql['where'] .= " AND (  t.topic_title LIKE '%{$st}%' )";
+		}
+
+		return $wpdb->get_var( implode( ' ', $sql ) );
+	}
+}
+
+Class BP_Groups_Member {
+	var $id;
+	var $group_id;
+	var $user_id;
+	var $inviter_id;
+	var $is_admin;
+	var $is_mod;
+	var $is_banned;
+	var $user_title;
+	var $date_modified;
+	var $is_confirmed;
+	var $comments;
+	var $invite_sent;
+
+	var $user;
+
+	function bp_groups_member( $user_id = 0, $group_id = 0, $id = false, $populate = true ) {
+		$this->__construct($user_id,$group_id,$id,$populate);
+	}
+
+	function __construct( $user_id = 0, $group_id = 0, $id = false, $populate = true ) {
+		if ( $user_id && $group_id && !$id ) {
+			$this->user_id = $user_id;
+			$this->group_id = $group_id;
+
+			if ( $populate )
+				$this->populate();
+		}
+
+		if ( $id ) {
+			$this->id = $id;
+
+			if ( $populate )
+				$this->populate();
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp;
+
+		if ( $this->user_id && $this->group_id && !$this->id )
+			$sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id );
+
+		if ( $this->id )
+			$sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE id = %d", $this->id );
+
+		$member = $wpdb->get_row($sql);
+
+		if ( $member ) {
+			$this->id            = $member->id;
+			$this->group_id      = $member->group_id;
+			$this->user_id       = $member->user_id;
+			$this->inviter_id    = $member->inviter_id;
+			$this->is_admin      = $member->is_admin;
+			$this->is_mod        = $member->is_mod;
+			$this->is_banned     = $member->is_banned;
+			$this->user_title    = $member->user_title;
+			$this->date_modified = $member->date_modified;
+			$this->is_confirmed  = $member->is_confirmed;
+			$this->comments      = $member->comments;
+			$this->invite_sent   = $member->invite_sent;
+
+			$this->user = new BP_Core_User( $this->user_id );
+		}
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->user_id       = apply_filters( 'groups_member_user_id_before_save',       $this->user_id,       $this->id );
+		$this->group_id      = apply_filters( 'groups_member_group_id_before_save',      $this->group_id,      $this->id );
+		$this->inviter_id    = apply_filters( 'groups_member_inviter_id_before_save',    $this->inviter_id,    $this->id );
+		$this->is_admin      = apply_filters( 'groups_member_is_admin_before_save',      $this->is_admin,      $this->id );
+		$this->is_mod        = apply_filters( 'groups_member_is_mod_before_save',        $this->is_mod,        $this->id );
+		$this->is_banned     = apply_filters( 'groups_member_is_banned_before_save',     $this->is_banned,     $this->id );
+		$this->user_title    = apply_filters( 'groups_member_user_title_before_save',    $this->user_title,    $this->id );
+		$this->date_modified = apply_filters( 'groups_member_date_modified_before_save', $this->date_modified, $this->id );
+		$this->is_confirmed  = apply_filters( 'groups_member_is_confirmed_before_save',  $this->is_confirmed,  $this->id );
+		$this->comments      = apply_filters( 'groups_member_comments_before_save',      $this->comments,      $this->id );
+		$this->invite_sent   = apply_filters( 'groups_member_invite_sent_before_save',   $this->invite_sent,   $this->id );
+
+		do_action_ref_array( 'groups_member_before_save', array( &$this ) );
+
+		if ( $this->id )
+			$sql = $wpdb->prepare( "UPDATE {$bp->groups->table_name_members} SET inviter_id = %d, is_admin = %d, is_mod = %d, is_banned = %d, user_title = %s, date_modified = %s, is_confirmed = %d, comments = %s, invite_sent = %d WHERE id = %d", $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent, $this->id );
+		else
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->groups->table_name_members} ( user_id, group_id, inviter_id, is_admin, is_mod, is_banned, user_title, date_modified, is_confirmed, comments, invite_sent ) VALUES ( %d, %d, %d, %d, %d, %d, %s, %s, %d, %s, %d )", $this->user_id, $this->group_id, $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent );
+
+		if ( !$wpdb->query($sql) )
+			return false;
+
+		$this->id = $wpdb->insert_id;
+
+		do_action_ref_array( 'groups_member_after_save', array( &$this ) );
+
+		return true;
+	}
+
+	function promote( $status = 'mod' ) {
+		if ( 'mod' == $status ) {
+			$this->is_admin = 0;
+			$this->is_mod = 1;
+			$this->user_title = __( 'Group Mod', 'buddypress' );
+		}
+
+		if ( 'admin' == $status ) {
+			$this->is_admin = 1;
+			$this->is_mod = 0;
+			$this->user_title = __( 'Group Admin', 'buddypress' );
+		}
+
+		return $this->save();
+	}
+
+	function demote() {
+		$this->is_mod = 0;
+		$this->is_admin = 0;
+		$this->user_title = false;
+
+		return $this->save();
+	}
+
+	function ban() {
+		global $bp;
+
+		if ( $this->is_admin )
+			return false;
+
+		$this->is_mod = 0;
+		$this->is_banned = 1;
+
+		groups_update_groupmeta( $this->group_id, 'total_member_count', ( (int) groups_get_groupmeta( $this->group_id, 'total_member_count' ) - 1 ) );
+
+		$group_count = bp_get_user_meta( $this->user_id, 'total_group_count', true );
+		if ( !empty( $group_count ) )
+			bp_update_user_meta( $this->user_id, 'total_group_count', (int)$group_count - 1 );
+
+		return $this->save();
+	}
+
+	function unban() {
+		global $bp;
+
+		if ( $this->is_admin )
+			return false;
+
+		$this->is_banned = 0;
+
+		groups_update_groupmeta( $this->group_id, 'total_member_count', ( (int) groups_get_groupmeta( $this->group_id, 'total_member_count' ) + 1 ) );
+		bp_update_user_meta( $this->user_id, 'total_group_count', (int)bp_get_user_meta( $this->user_id, 'total_group_count', true ) + 1 );
+
+		return $this->save();
+	}
+
+	function accept_invite() {
+		global $bp;
+
+		$this->inviter_id    = 0;
+		$this->is_confirmed  = 1;
+		$this->date_modified = bp_core_current_time();
+
+		bp_update_user_meta( $this->user_id, 'total_group_count', (int)bp_get_user_meta( $this->user_id, 'total_group_count', true ) + 1 );
+	}
+
+	function accept_request() {
+		global $bp;
+
+		$this->is_confirmed = 1;
+		$this->date_modified = bp_core_current_time();
+
+		bp_update_user_meta( $this->user_id, 'total_group_count', (int)bp_get_user_meta( $this->user_id, 'total_group_count', true ) + 1 );
+	}
+
+	function remove() {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id );
+
+		if ( !$result = $wpdb->query( $sql ) )
+			return false;
+
+		groups_update_groupmeta( $this->group_id, 'total_member_count', ( (int) groups_get_groupmeta( $this->group_id, 'total_member_count' ) - 1 ) );
+
+		$group_count = bp_get_user_meta( $this->user_id, 'total_group_count', true );
+		if ( !empty( $group_count ) )
+			bp_update_user_meta( $this->user_id, 'total_group_count', (int)$group_count - 1 );
+
+		return $result;
+	}
+
+	/* Static Functions */
+
+	function delete( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) );
+	}
+
+	function get_group_ids( $user_id, $limit = false, $page = false ) {
+		global $wpdb, $bp;
+
+		$pag_sql = '';
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		// If the user is logged in and viewing their own groups, we can show hidden and private groups
+		if ( $user_id != $bp->loggedin_user->id ) {
+			$group_sql = $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0{$pag_sql}", $user_id );
+			$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
+		} else {
+			$group_sql = $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$pag_sql}", $user_id );
+			$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT group_id) FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id ) );
+		}
+
+		$groups = $wpdb->get_col( $group_sql );
+
+		return array( 'groups' => $groups, 'total' => (int) $total_groups );
+	}
+
+	function get_recently_joined( $user_id, $limit = false, $page = false, $filter = false ) {
+		global $wpdb, $bp;
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( $filter ) {
+			$filter = like_escape( $wpdb->escape( $filter ) );
+			$filter_sql = " AND ( g.name LIKE '%%{$filter}%%' OR g.description LIKE '%%{$filter}%%' )";
+		}
+
+		if ( $user_id != $bp->loggedin_user->id )
+			$hidden_sql = " AND g.status != 'hidden'";
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY m.date_modified DESC {$pag_sql}", $user_id ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_banned = 0 AND m.is_confirmed = 1 ORDER BY m.date_modified DESC", $user_id ) );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_is_admin_of( $user_id, $limit = false, $page = false, $filter = false ) {
+		global $wpdb, $bp;
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( $filter ) {
+			$filter = like_escape( $wpdb->escape( $filter ) );
+			$filter_sql = " AND ( g.name LIKE '%%{$filter}%%' OR g.description LIKE '%%{$filter}%%' )";
+		}
+
+		if ( $user_id != $bp->loggedin_user->id )
+			$hidden_sql = " AND g.status != 'hidden'";
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY m.date_modified ASC {$pag_sql}", $user_id ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY date_modified ASC", $user_id ) );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function get_is_mod_of( $user_id, $limit = false, $page = false, $filter = false ) {
+		global $wpdb, $bp;
+
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( $filter ) {
+			$filter = like_escape( $wpdb->escape( $filter ) );
+			$filter_sql = " AND ( g.name LIKE '%%{$filter}%%' OR g.description LIKE '%%{$filter}%%' )";
+		}
+
+		if ( $user_id != $bp->loggedin_user->id )
+			$hidden_sql = " AND g.status != 'hidden'";
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY m.date_modified ASC {$pag_sql}", $user_id ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY date_modified ASC", $user_id ) );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function total_group_count( $user_id = 0 ) {
+		global $bp, $wpdb;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		if ( $user_id != $bp->loggedin_user->id && !is_super_admin() ) {
+			return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
+		} else {
+			return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
+		}
+	}
+
+	function get_invites( $user_id, $limit = false, $page = false, $exclude = false ) {
+		global $wpdb, $bp;
+
+		$pag_sql = ( !empty( $limit ) && !empty( $page ) ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : '';
+
+		$exclude_sql = !empty( $exclude ) ? $wpdb->prepare( " AND g.id NOT IN (%s)", $exclude ) : '';
+
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d {$exclude_sql} ORDER BY m.date_modified ASC {$pag_sql}", $user_id ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d {$exclude_sql} ORDER BY date_modified ASC", $user_id ) );
+
+		return array( 'groups' => $paged_groups, 'total' => $total_groups );
+	}
+
+	function check_has_invite( $user_id, $group_id, $type = 'sent' ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		$sql = "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0";
+
+		if ( 'sent' == $type )
+			$sql .= " AND invite_sent = 1";
+
+		return $wpdb->get_var( $wpdb->prepare( $sql, $user_id, $group_id ) );
+	}
+
+	function delete_invite( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0 AND invite_sent = 1", $user_id, $group_id ) );
+	}
+
+	function delete_request( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+ 		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id = 0 AND invite_sent = 0", $user_id, $group_id ) );
+	}
+
+	function check_is_admin( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_admin = 1 AND is_banned = 0", $user_id, $group_id ) );
+	}
+
+	function check_is_mod( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_mod = 1 AND is_banned = 0", $user_id, $group_id ) );
+	}
+
+	function check_is_member( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id, $group_id ) );
+	}
+
+	function check_is_banned( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT is_banned FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) );
+	}
+
+	/**
+	 * Is the specified user the creator of the group?
+	 *
+	 * @global object $bp BuddyPress global settings
+	 * @global wpdb $wpdb WordPress database object
+	 * @param int $user_id
+	 * @param int $group_id
+	 * @since 1.2.6
+	 */
+	function check_is_creator( $user_id, $group_id ) {
+		global $bp, $wpdb;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name} WHERE creator_id = %d AND id = %d", $user_id, $group_id ) );
+	}
+
+	function check_for_membership_request( $user_id, $group_id ) {
+		global $wpdb, $bp;
+
+		if ( !$user_id )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND is_banned = 0 AND inviter_id = 0", $user_id, $group_id ) );
+	}
+
+	function get_random_groups( $user_id, $total_groups = 5 ) {
+		global $wpdb, $bp;
+
+		// If the user is logged in and viewing their random groups, we can show hidden and private groups
+		if ( bp_is_my_profile() ) {
+			return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0 ORDER BY rand() LIMIT $total_groups", $user_id ) );
+		} else {
+			return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY rand() LIMIT $total_groups", $user_id ) );
+		}
+	}
+
+	function get_group_member_ids( $group_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) );
+	}
+
+	function get_group_administrator_ids( $group_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_admin = 1 AND is_banned = 0", $group_id ) );
+	}
+
+	function get_group_moderator_ids( $group_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_mod = 1 AND is_banned = 0", $group_id ) );
+	}
+
+	function get_all_membership_request_user_ids( $group_id ) {
+		global $bp, $wpdb;
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0", $group_id ) );
+	}
+
+	function get_all_for_group( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false ) {
+		global $bp, $wpdb;
+
+		$pag_sql = '';
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		$exclude_admins_sql = '';
+		if ( $exclude_admins_mods )
+			$exclude_admins_sql = $wpdb->prepare( "AND is_admin = 0 AND is_mod = 0" );
+
+		$banned_sql = '';
+		if ( $exclude_banned )
+			$banned_sql = $wpdb->prepare( " AND is_banned = 0" );
+
+		$exclude_sql = '';
+		if ( $exclude )
+			$exclude_sql = $wpdb->prepare( " AND m.user_id NOT IN ({$exclude})" );
+
+		if ( bp_is_active( 'xprofile' ) )
+			$members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, pd.value as display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u, {$bp->profile->table_name_data} pd WHERE u.ID = m.user_id AND u.ID = pd.user_id AND pd.field_id = 1 AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) );
+		else
+			$members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, u.display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u WHERE u.ID = m.user_id AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) );
+
+		if ( !$members )
+			return false;
+
+		if ( empty( $pag_sql ) )
+			$total_member_count = count( $members );
+		else
+			$total_member_count = $wpdb->get_var( apply_filters( 'bp_group_members_count_user_join_filter', $wpdb->prepare( "SELECT COUNT(user_id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql}", $group_id ) ) );
+
+		/* Fetch whether or not the user is a friend */
+		foreach ( (array)$members as $user ) $user_ids[] = $user->user_id;
+		$user_ids = $wpdb->escape( join( ',', (array)$user_ids ) );
+
+		if ( bp_is_active( 'friends' ) ) {
+			$friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids} ) ) OR (initiator_user_id IN ( {$user_ids} ) AND friend_user_id = %d )", $bp->loggedin_user->id, $bp->loggedin_user->id ) );
+			for ( $i = 0, $count = count( $members ); $i < $count; ++$i ) {
+				foreach ( (array)$friend_status as $status ) {
+					if ( $status->initiator_user_id == $members[$i]->user_id || $status->friend_user_id == $members[$i]->user_id )
+						$members[$i]->is_friend = $status->is_confirmed;
+				}
+			}
+		}
+
+		return array( 'members' => $members, 'count' => $total_member_count );
+	}
+
+	function delete_all( $group_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) );
+	}
+
+	/**
+	 * Delete all group membership information for the specified user
+	 *
+	 * @global object $bp BuddyPress global settings
+	 * @global wpdb $wpdb WordPress database object
+	 * @param int $user_id
+	 * @since 1.0
+	 * @uses BP_Groups_Member
+	 */
+	function delete_all_for_user( $user_id ) {
+		global $bp, $wpdb;
+
+		// Get all the group ids for the current user's groups and update counts
+		$group_ids = BP_Groups_Member::get_group_ids( $user_id );
+		foreach ( $group_ids['groups'] as $group_id ) {
+			groups_update_groupmeta( $group_id, 'total_member_count', groups_get_total_member_count( $group_id ) - 1 );
+
+			// If current user is the creator of a group and is the sole admin, delete that group to avoid counts going out-of-sync
+			if ( groups_is_user_admin( $user_id, $group_id ) && count( groups_get_group_admins( $group_id ) ) < 2 && groups_is_user_creator( $user_id, $group_id ) )
+				groups_delete_group( $group_id );
+		}
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d", $user_id ) );
+	}
+}
+
+/**
+ * API for creating group extensions without having to hardcode the content into
+ * the theme.
+ *
+ * This class must be extended for each group extension and the following methods overridden:
+ *
+ * BP_Group_Extension::widget_display(), BP_Group_Extension::display(),
+ * BP_Group_Extension::edit_screen_save(), BP_Group_Extension::edit_screen(),
+ * BP_Group_Extension::create_screen_save(), BP_Group_Extension::create_screen()
+ *
+ * @package BuddyPress
+ * @subpackage Groups
+ * @since 1.1
+ */
+class BP_Group_Extension {
+	var $name = false;
+	var $slug = false;
+
+	/* Will this extension be visible to non-members of a group? Options: public/private */
+	var $visibility = 'public';
+
+	var $create_step_position = 81;
+	var $nav_item_position = 81;
+
+	var $enable_create_step = true;
+	var $enable_nav_item = true;
+	var $enable_edit_item = true;
+
+	var $nav_item_name = false;
+
+	var $display_hook = 'groups_custom_group_boxes';
+	var $template_file = 'groups/single/plugins';
+
+	// Methods you should override
+
+	function display() {}
+
+	function widget_display() {}
+
+	function edit_screen() {}
+
+	function edit_screen_save() {}
+
+	function create_screen() {}
+
+	function create_screen_save() {}
+
+	// Private Methods
+
+	function _register() {
+		global $bp;
+
+		if ( !empty( $this->enable_create_step ) ) {
+			// Insert the group creation step for the new group extension
+			$bp->groups->group_creation_steps[$this->slug] = array( 'name' => $this->name, 'slug' => $this->slug, 'position' => $this->create_step_position );
+
+			// Attach the group creation step display content action
+			add_action( 'groups_custom_create_steps', array( &$this, 'create_screen' ) );
+
+			// Attach the group creation step save content action
+			add_action( 'groups_create_group_step_save_' . $this->slug, array( &$this, 'create_screen_save' ) );
+		}
+
+		// When we are viewing a single group, add the group extension nav item
+		if ( bp_is_group() ) {
+			if ( $this->visibility == 'public' || ( $this->visibility != 'public' && $bp->groups->current_group->user_has_access ) ) {
+				if ( $this->enable_nav_item ) {
+					bp_core_new_subnav_item( array( 'name' => ( !$this->nav_item_name ) ? $this->name : $this->nav_item_name, 'slug' => $this->slug, 'parent_slug' => $bp->groups->current_group->slug, 'parent_url' => bp_get_group_permalink( $bp->groups->current_group ), 'position' => $this->nav_item_position, 'item_css_id' => 'nav-' . $this->slug, 'screen_function' => array( &$this, '_display_hook' ), 'user_has_access' => $this->enable_nav_item ) );
+
+					// When we are viewing the extension display page, set the title and options title
+					if ( bp_is_current_action( $this->slug ) ) {
+						add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
+						add_action( 'bp_template_title', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
+					}
+				}
+
+				// Hook the group home widget
+				if ( !bp_current_action() && bp_is_current_action( 'home' ) )
+					add_action( $this->display_hook, array( &$this, 'widget_display' ) );
+			}
+		}
+
+		// Construct the admin edit tab for the new group extension
+		if ( !empty( $this->enable_edit_item ) && !empty( $bp->is_item_admin ) ) {
+			add_action( 'groups_admin_tabs', create_function( '$current, $group_slug', '$selected = ""; if ( "' . esc_attr( $this->slug ) . '" == $current ) $selected = " class=\"current\""; echo "<li{$selected}><a href=\"' . bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/{$group_slug}/admin/' . esc_attr( $this->slug ) . '\">' . esc_attr( $this->name ) . '</a></li>";' ), 10, 2 );
+
+			// Catch the edit screen and forward it to the plugin template
+			if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $this->slug, 0 ) ) {
+				// Check whether the user is saving changes
+				$this->edit_screen_save();
+
+				add_action( 'groups_custom_edit_steps', array( &$this, 'edit_screen' ) );
+
+				if ( '' != locate_template( array( 'groups/single/home.php' ), false ) ) {
+					bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
+				} else {
+					add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) );
+					add_action( 'bp_template_content', array( &$this, 'edit_screen' ) );
+					bp_core_load_template( apply_filters( 'bp_core_template_plugin', '/groups/single/plugins' ) );
+				}
+			}
+		}
+	}
+
+	function _display_hook() {
+		add_action( 'bp_template_content', array( &$this, 'display' ) );
+		bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) );
+	}
+}
+
+function bp_register_group_extension( $group_extension_class ) {
+	global $bp;
+
+	if ( !class_exists( $group_extension_class ) )
+		return false;
+
+	/* Register the group extension on the bp_init action so we have access to all plugins */
+	add_action( 'bp_init', create_function( '', '$extension = new ' . $group_extension_class . '; add_action( "bp_actions", array( &$extension, "_register" ), 8 );' ), 11 );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-filters.php	(revision 5057)
@@ -0,0 +1,155 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Apply WordPress defined filters */
+add_filter( 'bp_get_group_description',         'wptexturize' );
+add_filter( 'bp_get_group_description_excerpt', 'wptexturize' );
+add_filter( 'bp_get_group_name',                'wptexturize' );
+
+add_filter( 'bp_get_group_description',         'convert_smilies' );
+add_filter( 'bp_get_group_description_excerpt', 'convert_smilies' );
+
+add_filter( 'bp_get_group_description',         'convert_chars' );
+add_filter( 'bp_get_group_description_excerpt', 'convert_chars' );
+add_filter( 'bp_get_group_name',                'convert_chars' );
+
+add_filter( 'bp_get_group_description',         'wpautop' );
+add_filter( 'bp_get_group_description_excerpt', 'wpautop' );
+
+add_filter( 'bp_get_group_description',         'make_clickable', 9 );
+add_filter( 'bp_get_group_description_excerpt', 'make_clickable', 9 );
+
+add_filter( 'bp_get_group_name',                    'wp_filter_kses', 1 );
+add_filter( 'bp_get_group_permalink',               'wp_filter_kses', 1 );
+add_filter( 'bp_get_group_description',             'bp_groups_filter_kses', 1 );
+add_filter( 'bp_get_group_description_excerpt',     'wp_filter_kses', 1 );
+add_filter( 'groups_group_name_before_save',        'wp_filter_kses', 1 );
+add_filter( 'groups_group_description_before_save', 'wp_filter_kses', 1 );
+
+add_filter( 'bp_get_group_description',         'stripslashes' );
+add_filter( 'bp_get_group_description_excerpt', 'stripslashes' );
+add_filter( 'bp_get_group_name',                'stripslashes' );
+add_filter( 'bp_get_group_member_name',         'stripslashes' );
+add_filter( 'bp_get_group_member_link',         'stripslashes' );
+
+add_filter( 'groups_new_group_forum_desc', 'bp_create_excerpt' );
+
+add_filter( 'groups_group_name_before_save',        'force_balance_tags' );
+add_filter( 'groups_group_description_before_save', 'force_balance_tags' );
+
+add_filter( 'bp_get_total_group_count',      'bp_core_number_format' );
+add_filter( 'bp_get_group_total_for_member', 'bp_core_number_format' );
+add_filter( 'bp_get_group_total_members',    'bp_core_number_format' );
+
+function bp_groups_filter_kses( $content ) {
+	global $allowedtags;
+
+	$groups_allowedtags                  = $allowedtags;
+	$groups_allowedtags['a']['class']    = array();
+	$groups_allowedtags['img']           = array();
+	$groups_allowedtags['img']['src']    = array();
+	$groups_allowedtags['img']['alt']    = array();
+	$groups_allowedtags['img']['class']  = array();
+	$groups_allowedtags['img']['width']  = array();
+	$groups_allowedtags['img']['height'] = array();
+	$groups_allowedtags['img']['class']  = array();
+	$groups_allowedtags['img']['id']     = array();
+	$groups_allowedtags['code']          = array();
+
+	$groups_allowedtags = apply_filters( 'bp_groups_filter_kses', $groups_allowedtags );
+	return wp_kses( $content, $groups_allowedtags );
+}
+
+/**** Filters for group forums ****/
+
+function groups_add_forum_privacy_sql() {
+	global $bp;
+
+	// Only filter the forum SQL on group pages or on the forums directory
+	add_filter( 'get_topics_fields',     'groups_add_forum_fields_sql' );
+	add_filter( 'get_topics_join', 	     'groups_add_forum_tables_sql' );
+	add_filter( 'get_topics_where',      'groups_add_forum_where_sql'  );
+}
+add_filter( 'bbpress_init', 'groups_add_forum_privacy_sql' );
+
+function groups_add_forum_fields_sql( $sql = '' ) {
+	return 't.*, g.id as object_id, g.name as object_name, g.slug as object_slug';
+}
+
+function groups_add_forum_tables_sql( $sql = '' ) {
+	global $bp;
+
+	$sql .= 'JOIN ' . $bp->groups->table_name . ' AS g LEFT JOIN ' . $bp->groups->table_name_groupmeta . ' AS gm ON g.id = gm.group_id ';
+
+	return $sql;
+}
+
+function groups_add_forum_where_sql( $sql = '' ) {
+	global $bp;
+
+	// Set this for groups
+	$parts['groups'] = "(gm.meta_key = 'forum_id' AND gm.meta_value = t.forum_id)";
+
+	// Restrict to public...
+	$parts['private'] = "g.status = 'public'";
+
+	/**
+	 * ...but do some checks to possibly remove public restriction.
+	 *
+	 * Decide if private are visible
+	 */
+	// Are we in our own profile?
+	if ( bp_is_my_profile() )
+		unset( $parts['private'] );
+
+	// Are we a super admin?
+	elseif ( is_super_admin() )
+		unset( $parts['private'] );
+
+	// No need to filter on a single item
+	elseif ( bp_is_single_item() )
+		unset( $parts['private'] );
+
+	// Check the SQL filter that was passed
+	if ( !empty( $sql ) )
+		$parts['passed'] = $sql;
+
+	// Assemble Voltron
+	$parts_string = implode( ' AND ', $parts );
+
+	// Set it to the global filter
+	$bp->groups->filter_sql = $parts_string;
+
+	// Return the global filter
+	return $bp->groups->filter_sql;
+}
+
+function groups_filter_bbpress_caps( $value, $cap, $args ) {
+	global $bp;
+
+	if ( is_super_admin() )
+		return true;
+
+	if ( 'add_tag_to' == $cap )
+		if ( $bp->groups->current_group->user_has_access ) return true;
+
+	if ( 'manage_forums' == $cap && is_user_logged_in() )
+		return true;
+
+	return $value;
+}
+add_filter( 'bb_current_user_can', 'groups_filter_bbpress_caps', 10, 3 );
+
+/**
+ * Amends the forum directory's "last active" bbPress SQL query to stop it fetching
+ * information we aren't going to use. This speeds up the query.
+ *
+ * @see BB_Query::_filter_sql()
+ * @since 1.5
+ */
+function groups_filter_forums_root_page_sql( $sql ) {
+	return apply_filters( 'groups_filter_bbpress_root_page_sql', 't.topic_id' );
+}
+add_filter( 'get_latest_topics_fields', 'groups_filter_forums_root_page_sql' );
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-forums.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-forums.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-forums.php	(revision 5057)
@@ -0,0 +1,276 @@
+<?php
+/*** Group Forums **************************************************************/
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_new_group_forum( $group_id = 0, $group_name = '', $group_desc = '' ) {
+	global $bp;
+
+	if ( empty( $group_id ) )
+		$group_id = $bp->groups->current_group->id;
+
+	if ( empty( $group_name ) )
+		$group_name = $bp->groups->current_group->name;
+
+	if ( empty( $group_desc ) )
+		$group_desc = $bp->groups->current_group->description;
+
+	$forum_id = bp_forums_new_forum( array( 'forum_name' => $group_name, 'forum_desc' => $group_desc ) );
+
+	groups_update_groupmeta( $group_id, 'forum_id', $forum_id );
+
+	do_action( 'groups_new_group_forum', $forum_id, $group_id );
+}
+
+function groups_update_group_forum( $group_id ) {
+
+	$group = new BP_Groups_Group( $group_id );
+
+	if ( empty( $group->enable_forum ) || !bp_is_active( 'forums' ) )
+		return false;
+
+	$args = array(
+		'forum_id'      => groups_get_groupmeta( $group_id, 'forum_id' ),
+		'forum_name'    => $group->name,
+		'forum_desc'    => $group->description,
+		'forum_slug'    => $group->slug
+	);
+
+	bp_forums_update_forum( apply_filters( 'groups_update_group_forum', $args ) );
+}
+add_action( 'groups_details_updated', 'groups_update_group_forum' );
+
+function groups_new_group_forum_post( $post_text, $topic_id, $page = false ) {
+	global $bp;
+
+	if ( empty( $post_text ) )
+		return false;
+
+	$post_text = apply_filters( 'group_forum_post_text_before_save', $post_text );
+	$topic_id  = apply_filters( 'group_forum_post_topic_id_before_save', $topic_id );
+
+	if ( $post_id = bp_forums_insert_post( array( 'post_text' => $post_text, 'topic_id' => $topic_id ) ) ) {
+		$topic = bp_forums_get_topic_details( $topic_id );
+
+		$activity_action = sprintf( __( '%1$s replied to the forum topic %2$s in the group %3$s', 'buddypress'), bp_core_get_userlink( $bp->loggedin_user->id ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug .'/">' . esc_attr( $topic->topic_title ) . '</a>', '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' );
+		$activity_content = bp_create_excerpt( $post_text );
+		$primary_link = bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/';
+
+		if ( $page )
+			$primary_link .= "?topic_page=" . $page;
+
+		// Record this in activity streams
+		groups_record_activity( array(
+			'action'            => apply_filters_ref_array( 'groups_activity_new_forum_post_action',       array( $activity_action,  $post_id, $post_text, &$topic ) ),
+			'content'           => apply_filters_ref_array( 'groups_activity_new_forum_post_content',      array( $activity_content, $post_id, $post_text, &$topic ) ),
+			'primary_link'      => apply_filters( 'groups_activity_new_forum_post_primary_link', "{$primary_link}#post-{$post_id}" ),
+			'type'              => 'new_forum_post',
+			'item_id'           => $bp->groups->current_group->id,
+			'secondary_item_id' => $post_id
+		) );
+
+		do_action( 'groups_new_forum_topic_post', $bp->groups->current_group->id, $post_id );
+
+		return $post_id;
+	}
+
+	return false;
+}
+
+function groups_new_group_forum_topic( $topic_title, $topic_text, $topic_tags, $forum_id ) {
+	global $bp;
+
+	if ( empty( $topic_title ) || empty( $topic_text ) )
+		return false;
+
+	$topic_title = apply_filters( 'group_forum_topic_title_before_save', $topic_title );
+	$topic_text  = apply_filters( 'group_forum_topic_text_before_save', $topic_text );
+	$topic_tags  = apply_filters( 'group_forum_topic_tags_before_save', $topic_tags );
+	$forum_id    = apply_filters( 'group_forum_topic_forum_id_before_save', $forum_id );
+
+	if ( $topic_id = bp_forums_new_topic( array( 'topic_title' => $topic_title, 'topic_text' => $topic_text, 'topic_tags' => $topic_tags, 'forum_id' => $forum_id ) ) ) {
+		$topic = bp_forums_get_topic_details( $topic_id );
+
+		$activity_action = sprintf( __( '%1$s started the forum topic %2$s in the group %3$s', 'buddypress'), bp_core_get_userlink( $bp->loggedin_user->id ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug .'/">' . esc_attr( $topic->topic_title ) . '</a>', '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' );
+		$activity_content = bp_create_excerpt( $topic_text );
+
+		// Record this in activity streams
+		groups_record_activity( array(
+			'action'            => apply_filters_ref_array( 'groups_activity_new_forum_topic_action',  array( $activity_action,  $topic_text, &$topic ) ),
+			'content'           => apply_filters_ref_array( 'groups_activity_new_forum_topic_content', array( $activity_content, $topic_text, &$topic ) ),
+			'primary_link'      => apply_filters( 'groups_activity_new_forum_topic_primary_link', bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/' ),
+			'type'              => 'new_forum_topic',
+			'item_id'           => $bp->groups->current_group->id,
+			'secondary_item_id' => $topic->topic_id
+		) );
+
+	  do_action_ref_array( 'groups_new_forum_topic', array( $bp->groups->current_group->id, &$topic ) );
+
+		return $topic;
+	}
+
+	return false;
+}
+
+function groups_update_group_forum_topic( $topic_id, $topic_title, $topic_text, $topic_tags = false ) {
+	global $bp;
+
+	$topic_title = apply_filters( 'group_forum_topic_title_before_save', $topic_title );
+	$topic_text  = apply_filters( 'group_forum_topic_text_before_save',  $topic_text  );
+
+	if ( $topic = bp_forums_update_topic( array( 'topic_title' => $topic_title, 'topic_text' => $topic_text, 'topic_id' => $topic_id, 'topic_tags' => $topic_tags ) ) ) {
+		// Update the activity stream item
+		if ( bp_is_active( 'activity' ) )
+			bp_activity_delete_by_item_id( array( 'item_id' => $bp->groups->current_group->id, 'secondary_item_id' => $topic_id, 'component' => $bp->groups->id, 'type' => 'new_forum_topic' ) );
+
+		$activity_action = sprintf( __( '%1$s started the forum topic %2$s in the group %3$s', 'buddypress'), bp_core_get_userlink( $topic->topic_poster ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug .'/">' . esc_attr( $topic->topic_title ) . '</a>', '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' );
+		$activity_content = bp_create_excerpt( $topic_text );
+
+		// Record this in activity streams
+		groups_record_activity( array(
+			'action'            => apply_filters_ref_array( 'groups_activity_new_forum_topic_action',  array( $activity_action,  $topic_text, &$topic ) ),
+			'content'           => apply_filters_ref_array( 'groups_activity_new_forum_topic_content', array( $activity_content, $topic_text, &$topic ) ),
+			'primary_link'      => apply_filters( 'groups_activity_new_forum_topic_primary_link', bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/' ),
+			'type'              => 'new_forum_topic',
+			'item_id'           => (int)$bp->groups->current_group->id,
+			'user_id'           => (int)$topic->topic_poster,
+			'secondary_item_id' => $topic->topic_id,
+			'recorded_time '    => $topic->topic_time
+		) );
+
+		do_action_ref_array( 'groups_update_group_forum_topic', array( &$topic ) );
+
+		return $topic;
+	}
+
+	return false;
+}
+
+function groups_update_group_forum_post( $post_id, $post_text, $topic_id, $page = false ) {
+	global $bp;
+
+	$post_text = apply_filters( 'group_forum_post_text_before_save', $post_text );
+	$topic_id  = apply_filters( 'group_forum_post_topic_id_before_save', $topic_id );
+	$post      = bp_forums_get_post( $post_id );
+
+	if ( $post_id = bp_forums_insert_post( array( 'post_id' => $post_id, 'post_text' => $post_text, 'post_time' => $post->post_time, 'topic_id' => $topic_id, 'poster_id' => $post->poster_id ) ) ) {
+		$topic = bp_forums_get_topic_details( $topic_id );
+
+		$activity_action = sprintf( __( '%1$s replied to the forum topic %2$s in the group %3$s', 'buddypress'), bp_core_get_userlink( $post->poster_id ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug .'">' . esc_attr( $topic->topic_title ) . '</a>', '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' );
+		$activity_content = bp_create_excerpt( $post_text );
+		$primary_link = bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/';
+
+		if ( $page )
+			$primary_link .= "?topic_page=" . $page;
+
+		// Fetch an existing entry and update if one exists.
+		if ( bp_is_active( 'activity' ) )
+			$id = bp_activity_get_activity_id( array( 'user_id' => $post->poster_id, 'component' => $bp->groups->id, 'type' => 'new_forum_post', 'item_id' => $bp->groups->current_group->id, 'secondary_item_id' => $post_id ) );
+
+		// Update the entry in activity streams
+		groups_record_activity( array(
+			'id'                => $id,
+			'action'            => apply_filters_ref_array( 'groups_activity_new_forum_post_action',  array( $activity_action,  $post_text, &$topic, &$forum_post ) ),
+			'content'           => apply_filters_ref_array( 'groups_activity_new_forum_post_content', array( $activity_content, $post_text, &$topic, &$forum_post ) ),
+			'primary_link'      => apply_filters( 'groups_activity_new_forum_post_primary_link', $primary_link . "#post-" . $post_id ),
+			'type'              => 'new_forum_post',
+			'item_id'           => (int)$bp->groups->current_group->id,
+			'user_id'           => (int)$post->poster_id,
+			'secondary_item_id' => $post_id,
+			'recorded_time'     => $post->post_time
+		) );
+
+		do_action_ref_array( 'groups_update_group_forum_post', array( $post, &$topic ) );
+
+		return $post_id;
+	}
+
+	return false;
+}
+
+/**
+ * Handles the forum topic deletion routine
+ *
+ * @package BuddyPress
+ *
+ * @uses bp_activity_delete() to delete corresponding activity items
+ * @uses bp_forums_get_topic_posts() to get the child posts
+ * @uses bp_forums_delete_topic() to do the deletion itself
+ * @param int $topic_id The id of the topic to be deleted
+ * @return bool True if the delete routine went through properly
+ */
+function groups_delete_group_forum_topic( $topic_id ) {
+	global $bp;
+
+	// Before deleting the thread, get the post ids so that their activity items can be deleted
+	$posts = bp_forums_get_topic_posts( array( 'topic_id' => $topic_id, 'per_page' => -1 ) );
+
+	if ( bp_forums_delete_topic( array( 'topic_id' => $topic_id ) ) ) {
+		do_action( 'groups_before_delete_group_forum_topic', $topic_id );
+
+		// Delete the activity stream items
+		if ( bp_is_active( 'activity' ) ) {
+			// The activity item for the initial topic
+			bp_activity_delete( array( 'item_id' => $bp->groups->current_group->id, 'secondary_item_id' => $topic_id, 'component' => $bp->groups->id, 'type' => 'new_forum_topic' ) );
+
+			// The activity item for each post
+			foreach ( (array)$posts as $post ) {
+				bp_activity_delete( array( 'item_id' => $bp->groups->current_group->id, 'secondary_item_id' => $post->post_id, 'component' => $bp->groups->id, 'type' => 'new_forum_post' ) );
+			}
+		}
+
+		do_action( 'groups_delete_group_forum_topic', $topic_id );
+
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * Delete a forum post
+ *
+ * @package BuddyPress
+ *
+ * @param int $post_id The id of the post you want to delete
+ * @param int $topic_id Optional. The topic to which the post belongs. This value isn't used in the
+ *   function but is passed along to do_action() hooks.
+ * @return bool True on success.
+ */
+function groups_delete_group_forum_post( $post_id, $topic_id = false ) {
+	global $bp;
+
+	if ( bp_forums_delete_post( array( 'post_id' => $post_id ) ) ) {
+		do_action( 'groups_before_delete_group_forum_post', $post_id, $topic_id );
+
+		// Delete the activity stream item
+		if ( bp_is_active( 'activity' ) )
+			bp_activity_delete( array( 'item_id' => $bp->groups->current_group->id, 'secondary_item_id' => $post_id, 'component' => $bp->groups->id, 'type' => 'new_forum_post' ) );
+
+		do_action( 'groups_delete_group_forum_post', $post_id, $topic_id );
+
+		return true;
+	}
+
+	return false;
+}
+
+function groups_total_public_forum_topic_count( $type = 'newest' ) {
+	return apply_filters( 'groups_total_public_forum_topic_count', BP_Groups_Group::get_global_forum_topic_count( $type ) );
+}
+
+/**
+ * Get a total count of all topics of a given status, across groups/forums
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param str $status 'public', 'private', 'hidden', 'all' Which group types to count
+ * @return int The topic count
+ */
+function groups_total_forum_topic_count( $status = 'public', $search_terms = false ) {
+	return apply_filters( 'groups_total_forum_topic_count', BP_Groups_Group::get_global_topic_count( $status, $search_terms ) );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-functions.php	(revision 5057)
@@ -0,0 +1,969 @@
+<?php
+/**
+ * Functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Checks $bp pages global and looks for directory page
+ *
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @return bool True if set, False if empty
+ */
+function bp_groups_has_directory() {
+	global $bp;
+
+	return (bool) !empty( $bp->pages->groups->id );
+}
+
+function groups_get_group( $args = '' ) {
+	$defaults = array(
+		'group_id'   => false,
+		'load_users' => false
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	return apply_filters( 'groups_get_group', new BP_Groups_Group( $group_id, true, $load_users ) );
+}
+
+/*** Group Creation, Editing & Deletion *****************************************/
+
+function groups_create_group( $args = '' ) {
+	global $bp;
+
+	extract( $args );
+
+	/**
+	 * Possible parameters (pass as assoc array):
+	 *	'group_id'
+	 *	'creator_id'
+	 *	'name'
+	 *	'description'
+	 *	'slug'
+	 *	'status'
+	 *	'enable_forum'
+	 *	'date_created'
+	 */
+
+	if ( isset( $group_id ) && $group_id )
+		$group = new BP_Groups_Group( $group_id );
+	else
+		$group = new BP_Groups_Group;
+
+	if ( isset( $creator_id ) && $creator_id )
+		$group->creator_id = $creator_id;
+	else
+		$group->creator_id = $bp->loggedin_user->id;
+
+	if ( isset( $name ) )
+		$group->name = $name;
+
+	if ( isset( $description ) )
+		$group->description = $description;
+
+	if ( isset( $slug ) && groups_check_slug( $slug ) )
+		$group->slug = $slug;
+
+	if ( isset( $status ) ) {
+		if ( groups_is_valid_status( $status ) )
+			$group->status = $status;
+	}
+
+	if ( isset( $enable_forum ) )
+		$group->enable_forum = $enable_forum;
+	else if ( !$group_id && !isset( $enable_forum ) )
+		$group->enable_forum = 1;
+
+	if ( isset( $date_created ) )
+		$group->date_created = $date_created;
+
+	if ( !$group->save() )
+		return false;
+
+	// If this is a new group, set up the creator as the first member and admin
+	if ( empty( $group_id ) ) {
+		$member                = new BP_Groups_Member;
+		$member->group_id      = $group->id;
+		$member->user_id       = $group->creator_id;
+		$member->is_admin      = 1;
+		$member->user_title    = __( 'Group Admin', 'buddypress' );
+		$member->is_confirmed  = 1;
+		$member->date_modified = bp_core_current_time();
+
+		$member->save();
+		do_action( 'groups_create_group', $group->id, $member, $group );
+
+	} else {
+		do_action( 'groups_update_group', $group->id, $group );
+	}
+
+	do_action( 'groups_created_group', $group->id );
+
+	return $group->id;
+}
+
+function groups_edit_base_group_details( $group_id, $group_name, $group_desc, $notify_members ) {
+	global $bp;
+
+	if ( empty( $group_name ) || empty( $group_desc ) )
+		return false;
+
+	$group              = new BP_Groups_Group( $group_id );
+	$group->name        = $group_name;
+	$group->description = $group_desc;
+
+	if ( !$group->save() )
+		return false;
+
+	if ( $notify_members ) {
+		groups_notification_group_updated( $group->id );
+	}
+
+	do_action( 'groups_details_updated', $group->id );
+
+	return true;
+}
+
+function groups_edit_group_settings( $group_id, $enable_forum, $status, $invite_status = false ) {
+	global $bp;
+
+	$group = new BP_Groups_Group( $group_id );
+	$group->enable_forum = $enable_forum;
+
+	/***
+	 * Before we potentially switch the group status, if it has been changed to public
+	 * from private and there are outstanding membership requests, auto-accept those requests.
+	 */
+	if ( 'private' == $group->status && 'public' == $status )
+		groups_accept_all_pending_membership_requests( $group->id );
+
+	// Now update the status
+	$group->status = $status;
+
+	if ( !$group->save() )
+		return false;
+
+	// If forums have been enabled, and a forum does not yet exist, we need to create one.
+	if ( $group->enable_forum ) {
+		if ( bp_is_active( 'forums' ) && !groups_get_groupmeta( $group->id, 'forum_id' ) ) {
+			groups_new_group_forum( $group->id, $group->name, $group->description );
+		}
+	}
+
+	// Set the invite status
+	if ( $invite_status )
+		groups_update_groupmeta( $group->id, 'invite_status', $invite_status );
+
+	groups_update_groupmeta( $group->id, 'last_activity', bp_core_current_time() );
+	do_action( 'groups_settings_updated', $group->id );
+
+	return true;
+}
+
+/**
+ * Delete a group and all of its associated meta
+ *
+ * @global object $bp BuddyPress global settings
+ * @param int $group_id
+ * @since 1.0
+ */
+function groups_delete_group( $group_id ) {
+	global $bp;
+
+	// Check the user is the group admin.
+	if ( !$bp->is_item_admin )
+		return false;
+
+	// Get the group object
+	$group = new BP_Groups_Group( $group_id );
+	if ( !$group->delete() )
+		return false;
+
+	do_action( 'groups_before_delete_group', $group_id );
+
+	// Delete all group activity from activity streams
+	if ( bp_is_active( 'activity' ) )
+		bp_activity_delete_by_item_id( array( 'item_id' => $group_id, 'component' => $bp->groups->id ) );
+
+	// Remove all outstanding invites for this group
+	groups_delete_all_group_invites( $group_id );
+
+	// Remove all notifications for any user belonging to this group
+	bp_core_delete_all_notifications_by_type( $group_id, $bp->groups->id );
+
+	// Remove forum if component is active and current group has one
+	if ( bp_is_active( 'forums' ) && $forum_id = groups_get_groupmeta( $group_id, 'forum_id' ) ) {
+		do_action( 'bbpress_init' );
+		bb_delete_forum( $forum_id );
+	}
+
+	do_action( 'groups_delete_group', $group_id);
+
+	return true;
+}
+
+function groups_is_valid_status( $status ) {
+	global $bp;
+
+	return in_array( $status, (array)$bp->groups->valid_status );
+}
+
+function groups_check_slug( $slug ) {
+	global $bp;
+
+	if ( 'wp' == substr( $slug, 0, 2 ) )
+		$slug = substr( $slug, 2, strlen( $slug ) - 2 );
+
+	if ( in_array( $slug, (array)$bp->groups->forbidden_names ) )
+		$slug = $slug . '-' . rand();
+
+	if ( BP_Groups_Group::check_slug( $slug ) ) {
+		do {
+			$slug = $slug . '-' . rand();
+		}
+		while ( BP_Groups_Group::check_slug( $slug ) );
+	}
+
+	return $slug;
+}
+
+function groups_get_slug( $group_id ) {
+	$group = new BP_Groups_Group( $group_id );
+	return $group->slug;
+}
+
+/*** User Actions ***************************************************************/
+
+function groups_leave_group( $group_id, $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	// Don't let single admins leave the group.
+	if ( count( groups_get_group_admins( $group_id ) ) < 2 ) {
+		if ( groups_is_user_admin( $user_id, $group_id ) ) {
+			bp_core_add_message( __( 'As the only Admin, you cannot leave the group.', 'buddypress' ), 'error' );
+			return false;
+		}
+	}
+
+	$membership = new BP_Groups_Member( $user_id, $group_id );
+
+	// This is exactly the same as deleting an invite, just is_confirmed = 1 NOT 0.
+	if ( !groups_uninvite_user( $user_id, $group_id ) )
+		return false;
+
+	// Modify group member count
+	groups_update_groupmeta( $group_id, 'total_member_count', (int) groups_get_groupmeta( $group_id, 'total_member_count') - 1 );
+
+	// Modify user's group memberhip count
+	bp_update_user_meta( $user_id, 'total_group_count', (int) bp_get_user_meta( $user_id, 'total_group_count', true ) - 1 );
+
+	/**
+	 * If the user joined this group less than five minutes ago, remove the
+	 * joined_group activity so users cannot flood the activity stream by
+	 * joining/leaving the group in quick succession.
+	 */
+	if ( bp_is_active( 'activity' ) && gmmktime() <= strtotime( '+5 minutes', (int)strtotime( $membership->date_modified ) ) )
+		bp_activity_delete( array( 'component' => $bp->groups->id, 'type' => 'joined_group', 'user_id' => $user_id, 'item_id' => $group_id ) );
+
+	bp_core_add_message( __( 'You successfully left the group.', 'buddypress' ) );
+
+	do_action( 'groups_leave_group', $group_id, $user_id );
+
+	return true;
+}
+
+function groups_join_group( $group_id, $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	// Check if the user has an outstanding invite, is so delete it.
+	if ( groups_check_user_has_invite( $user_id, $group_id ) )
+		groups_delete_invite( $user_id, $group_id );
+
+	// Check if the user has an outstanding request, is so delete it.
+	if ( groups_check_for_membership_request( $user_id, $group_id ) )
+		groups_delete_membership_request( $user_id, $group_id );
+
+	// User is already a member, just return true
+	if ( groups_is_user_member( $user_id, $group_id ) )
+		return true;
+
+	$new_member                = new BP_Groups_Member;
+	$new_member->group_id      = $group_id;
+	$new_member->user_id       = $user_id;
+	$new_member->inviter_id    = 0;
+	$new_member->is_admin      = 0;
+	$new_member->user_title    = '';
+	$new_member->date_modified = bp_core_current_time();
+	$new_member->is_confirmed  = 1;
+
+	if ( !$new_member->save() )
+		return false;
+
+	if ( !isset( $bp->groups->current_group ) || !$bp->groups->current_group || $group_id != $bp->groups->current_group->id )
+		$group = new BP_Groups_Group( $group_id );
+	else
+		$group = $bp->groups->current_group;
+
+	// Record this in activity streams
+	groups_record_activity( array(
+		'action'  => apply_filters( 'groups_activity_joined_group', sprintf( __( '%1$s joined the group %2$s', 'buddypress'), bp_core_get_userlink( $user_id ), '<a href="' . bp_get_group_permalink( $group ) . '">' . esc_attr( bp_get_group_name( $group ) ) . '</a>' ) ),
+		'type'    => 'joined_group',
+		'item_id' => $group_id,
+		'user_id' => $user_id
+	) );
+
+	// Modify group meta
+	groups_update_groupmeta( $group_id, 'total_member_count', (int) groups_get_groupmeta( $group_id, 'total_member_count') + 1 );
+	groups_update_groupmeta( $group_id, 'last_activity', bp_core_current_time() );
+
+	do_action( 'groups_join_group', $group_id, $user_id );
+
+	return true;
+}
+
+/*** General Group Functions ****************************************************/
+
+function groups_check_group_exists( $group_id ) {
+	return BP_Groups_Group::group_exists( $group_id );
+}
+
+function groups_get_group_admins( $group_id ) {
+	return BP_Groups_Member::get_group_administrator_ids( $group_id );
+}
+
+function groups_get_group_mods( $group_id ) {
+	return BP_Groups_Member::get_group_moderator_ids( $group_id );
+}
+
+function groups_get_group_members( $group_id, $limit = false, $page = false ) {
+	return BP_Groups_Member::get_all_for_group( $group_id, $limit, $page );
+}
+
+function groups_get_total_member_count( $group_id ) {
+	return BP_Groups_Group::get_total_member_count( $group_id );
+}
+
+/*** Group Fetching, Filtering & Searching  *************************************/
+
+function groups_get_groups( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'type'            => 'active', // active, newest, alphabetical, random, popular, most-forum-topics or most-forum-posts
+		'user_id'         => false,    // Pass a user_id to limit to only groups that this user is a member of
+		'include'         => false,    // Only include these specific groups (group_ids)
+		'exclude'         => false,    // Do not include these specific groups (group_ids)
+		'search_terms'    => false,    // Limit to groups that match these search terms
+		'show_hidden'     => false,    // Show hidden groups to non-admins
+
+		'per_page'        => 20,       // The number of results to return per page
+		'page'            => 1,        // The page to return if limiting per page
+		'populate_extras' => true,     // Fetch meta such as is_banned and is_member
+	);
+
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	$groups = BP_Groups_Group::get( $type, $per_page, $page, $user_id, $search_terms, $include, $populate_extras, $exclude, $show_hidden );
+
+	return apply_filters_ref_array( 'groups_get_groups', array( &$groups, &$params ) );
+}
+
+function groups_get_total_group_count() {
+	if ( !$count = wp_cache_get( 'bp_total_group_count', 'bp' ) ) {
+		$count = BP_Groups_Group::get_total_group_count();
+		wp_cache_set( 'bp_total_group_count', $count, 'bp' );
+	}
+
+	return $count;
+}
+
+function groups_get_user_groups( $user_id = 0, $pag_num = 0, $pag_page = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->displayed_user->id;
+
+	return BP_Groups_Member::get_group_ids( $user_id, $pag_num, $pag_page );
+}
+
+function groups_total_groups_for_user( $user_id = 0 ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+
+	if ( !$count = wp_cache_get( 'bp_total_groups_for_user_' . $user_id, 'bp' ) ) {
+		$count = BP_Groups_Member::total_group_count( $user_id );
+		wp_cache_set( 'bp_total_groups_for_user_' . $user_id, $count, 'bp' );
+	}
+
+	return $count;
+}
+
+/**
+ * Returns the group object for the group currently being viewed
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return obj The current group object
+ */
+function groups_get_current_group() {
+	global $bp;
+
+	$current_group = isset( $bp->groups->current_group ) ? $bp->groups->current_group : false;
+
+	return apply_filters( 'groups_get_current_group', $current_group );
+}
+
+/*** Group Avatars *************************************************************/
+
+function groups_avatar_upload_dir( $group_id = 0 ) {
+	global $bp;
+
+	if ( !$group_id )
+		$group_id = $bp->groups->current_group->id;
+
+	$path    = bp_core_avatar_upload_path() . '/group-avatars/' . $group_id;
+	$newbdir = $path;
+
+	if ( !file_exists( $path ) )
+		@wp_mkdir_p( $path );
+
+	$newurl    = bp_core_avatar_url() . '/group-avatars/' . $group_id;
+	$newburl   = $newurl;
+	$newsubdir = '/group-avatars/' . $group_id;
+
+	return apply_filters( 'groups_avatar_upload_dir', array( 'path' => $path, 'url' => $newurl, 'subdir' => $newsubdir, 'basedir' => $newbdir, 'baseurl' => $newburl, 'error' => false ) );
+}
+
+/*** Group Member Status Checks ************************************************/
+
+function groups_is_user_admin( $user_id, $group_id ) {
+	return BP_Groups_Member::check_is_admin( $user_id, $group_id );
+}
+
+function groups_is_user_mod( $user_id, $group_id ) {
+	return BP_Groups_Member::check_is_mod( $user_id, $group_id );
+}
+
+function groups_is_user_member( $user_id, $group_id ) {
+	return BP_Groups_Member::check_is_member( $user_id, $group_id );
+}
+
+function groups_is_user_banned( $user_id, $group_id ) {
+	return BP_Groups_Member::check_is_banned( $user_id, $group_id );
+}
+
+/**
+ * Is the specified user the creator of the group?
+ *
+ * @param int $user_id
+ * @param int $group_id
+ * @since 1.2.6
+ * @uses BP_Groups_Member
+ */
+function groups_is_user_creator( $user_id, $group_id ) {
+	return BP_Groups_Member::check_is_creator( $user_id, $group_id );
+}
+
+/*** Group Activity Posting **************************************************/
+
+function groups_post_update( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'content'  => false,
+		'user_id'  => $bp->loggedin_user->id,
+		'group_id' => 0
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( empty( $group_id ) && !empty( $bp->groups->current_group->id ) )
+		$group_id = $bp->groups->current_group->id;
+
+	if ( empty( $content ) || !strlen( trim( $content ) ) || empty( $user_id ) || empty( $group_id ) )
+		return false;
+
+	$bp->groups->current_group = new BP_Groups_Group( $group_id );
+
+	// Be sure the user is a member of the group before posting.
+	if ( !is_super_admin() && !groups_is_user_member( $user_id, $group_id ) )
+		return false;
+
+	// Record this in activity streams
+	$activity_action  = sprintf( __( '%1$s posted an update in the group %2$s', 'buddypress'), bp_core_get_userlink( $user_id ), '<a href="' . bp_get_group_permalink( $bp->groups->current_group ) . '">' . esc_attr( $bp->groups->current_group->name ) . '</a>' );
+	$activity_content = $content;
+
+	$activity_id = groups_record_activity( array(
+		'user_id' => $user_id,
+		'action'  => apply_filters( 'groups_activity_new_update_action',  $activity_action  ),
+		'content' => apply_filters( 'groups_activity_new_update_content', $activity_content ),
+		'type'    => 'activity_update',
+		'item_id' => $group_id
+	) );
+
+	groups_update_groupmeta( $group_id, 'last_activity', bp_core_current_time() );
+	do_action( 'bp_groups_posted_update', $content, $user_id, $group_id, $activity_id );
+
+	return $activity_id;
+}
+
+/*** Group Invitations *********************************************************/
+
+function groups_get_invites_for_user( $user_id = 0, $limit = false, $page = false, $exclude = false ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	return BP_Groups_Member::get_invites( $user_id, $limit, $page, $exclude );
+}
+
+function groups_invite_user( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'user_id'       => false,
+		'group_id'      => false,
+		'inviter_id'    => $bp->loggedin_user->id,
+		'date_modified' => bp_core_current_time(),
+		'is_confirmed'  => 0
+	);
+
+	$args = wp_parse_args( $args, $defaults );
+	extract( $args, EXTR_SKIP );
+
+	if ( !$user_id || !$group_id )
+		return false;
+
+	if ( !groups_is_user_member( $user_id, $group_id ) && !groups_check_user_has_invite( $user_id, $group_id, 'all' ) ) {
+		$invite                = new BP_Groups_Member;
+		$invite->group_id      = $group_id;
+		$invite->user_id       = $user_id;
+		$invite->date_modified = $date_modified;
+		$invite->inviter_id    = $inviter_id;
+		$invite->is_confirmed  = $is_confirmed;
+
+		if ( !$invite->save() )
+			return false;
+
+		do_action( 'groups_invite_user', $args );
+	}
+
+	return true;
+}
+
+function groups_uninvite_user( $user_id, $group_id ) {
+	global $bp;
+
+	if ( !BP_Groups_Member::delete( $user_id, $group_id ) )
+		return false;
+
+	do_action( 'groups_uninvite_user', $group_id, $user_id );
+
+	return true;
+}
+
+function groups_accept_invite( $user_id, $group_id ) {
+	global $bp;
+
+	if ( groups_is_user_member( $user_id, $group_id ) )
+		return false;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+	$member->accept_invite();
+
+	if ( !$member->save() )
+		return false;
+
+	// Remove request to join
+	if ( $member->check_for_membership_request( $user_id, $group_id ) )
+		$member->delete_request( $user_id, $group_id );
+
+	// Modify group meta
+	groups_update_groupmeta( $group_id, 'total_member_count', (int) groups_get_groupmeta( $group_id, 'total_member_count') + 1 );
+	groups_update_groupmeta( $group_id, 'last_activity', bp_core_current_time() );
+
+	bp_core_delete_notifications_by_item_id( $user_id, $group_id, $bp->groups->id, 'group_invite' );
+
+	do_action( 'groups_accept_invite', $user_id, $group_id );
+	return true;
+}
+
+function groups_reject_invite( $user_id, $group_id ) {
+	if ( !BP_Groups_Member::delete( $user_id, $group_id ) )
+		return false;
+
+	do_action( 'groups_reject_invite', $user_id, $group_id );
+
+	return true;
+}
+
+function groups_delete_invite( $user_id, $group_id ) {
+	global $bp;
+
+	$delete = BP_Groups_Member::delete_invite( $user_id, $group_id );
+
+	if ( $delete )
+		bp_core_delete_notifications_by_item_id( $user_id, $group_id, $bp->groups->id, 'group_invite' );
+
+	return $delete;
+}
+
+function groups_send_invites( $user_id, $group_id ) {
+	global $bp;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	// Send friend invites.
+	$invited_users = groups_get_invites_for_group( $user_id, $group_id );
+	$group = new BP_Groups_Group( $group_id );
+
+	for ( $i = 0, $count = count( $invited_users ); $i < $count; ++$i ) {
+		$member = new BP_Groups_Member( $invited_users[$i], $group_id );
+
+		// Send the actual invite
+		groups_notification_group_invites( $group, $member, $user_id );
+
+		$member->invite_sent = 1;
+		$member->save();
+	}
+
+	do_action( 'groups_send_invites', $bp->groups->current_group->id, $invited_users );
+}
+
+function groups_get_invites_for_group( $user_id, $group_id ) {
+	return BP_Groups_Group::get_invites( $user_id, $group_id );
+}
+
+/**
+ * Check to see whether a user has already been invited to a group
+ *
+ * By default, the function checks for invitations that have been sent. Entering 'all' as the $type
+ * parameter will return unsent invitations as well (useful to make sure AJAX requests are not
+ * duplicated)
+ *
+ * @package BuddyPress Groups
+ *
+ * @param int $user_id Potential group member
+ * @param int $group_id Potential group
+ * @param str $type Optional. Use 'sent' to check for sent invites, 'all' to check for all
+ * @return bool Returns true if an invitation is found
+ */
+function groups_check_user_has_invite( $user_id, $group_id, $type = 'sent' ) {
+	return BP_Groups_Member::check_has_invite( $user_id, $group_id, $type );
+}
+
+function groups_delete_all_group_invites( $group_id ) {
+	return BP_Groups_Group::delete_all_invites( $group_id );
+}
+
+/*** Group Promotion & Banning *************************************************/
+
+function groups_promote_member( $user_id, $group_id, $status ) {
+	global $bp;
+
+	if ( !$bp->is_item_admin )
+		return false;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+
+	do_action( 'groups_premote_member', $group_id, $user_id, $status );
+
+	return $member->promote( $status );
+}
+
+function groups_demote_member( $user_id, $group_id ) {
+	global $bp;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+
+	do_action( 'groups_demote_member', $group_id, $user_id );
+
+	return $member->demote();
+}
+
+function groups_ban_member( $user_id, $group_id ) {
+	global $bp;
+
+	if ( !$bp->is_item_admin )
+		return false;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+
+	do_action( 'groups_ban_member', $group_id, $user_id );
+
+	return $member->ban();
+}
+
+function groups_unban_member( $user_id, $group_id ) {
+	global $bp;
+
+	if ( !$bp->is_item_admin )
+		return false;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+
+	do_action( 'groups_unban_member', $group_id, $user_id );
+
+	return $member->unban();
+}
+
+/*** Group Removal *******************************************************/
+
+function groups_remove_member( $user_id, $group_id ) {
+	global $bp;
+
+	if ( !$bp->is_item_admin )
+		return false;
+
+	$member = new BP_Groups_Member( $user_id, $group_id );
+
+	do_action( 'groups_remove_member', $group_id, $user_id );
+
+	return $member->remove();
+}
+
+/*** Group Membership ****************************************************/
+
+function groups_send_membership_request( $requesting_user_id, $group_id ) {
+	global $bp;
+
+	// Prevent duplicate requests
+	if ( groups_check_for_membership_request( $requesting_user_id, $group_id ) )
+		return false;
+
+	// Check if the user is already a member or is banned
+	if ( groups_is_user_member( $requesting_user_id, $group_id ) || groups_is_user_banned( $requesting_user_id, $group_id ) )
+		return false;
+
+	$requesting_user                = new BP_Groups_Member;
+	$requesting_user->group_id      = $group_id;
+	$requesting_user->user_id       = $requesting_user_id;
+	$requesting_user->inviter_id    = 0;
+	$requesting_user->is_admin      = 0;
+	$requesting_user->user_title    = '';
+	$requesting_user->date_modified = bp_core_current_time();
+	$requesting_user->is_confirmed  = 0;
+	$requesting_user->comments      = isset( $_POST['group-request-membership-comments'] ) ? $_POST['group-request-membership-comments'] : '';
+
+	if ( $requesting_user->save() ) {
+		$admins = groups_get_group_admins( $group_id );
+
+		// Saved okay, now send the email notification
+		for ( $i = 0, $count = count( $admins ); $i < $count; ++$i )
+			groups_notification_new_membership_request( $requesting_user_id, $admins[$i]->user_id, $group_id, $requesting_user->id );
+
+		do_action( 'groups_membership_requested', $requesting_user_id, $admins, $group_id, $requesting_user->id );
+
+		return true;
+	}
+
+	return false;
+}
+
+function groups_accept_membership_request( $membership_id, $user_id = 0, $group_id = 0 ) {
+	global $bp;
+
+	if ( $user_id && $group_id )
+		$membership = new BP_Groups_Member( $user_id, $group_id );
+	else
+		$membership = new BP_Groups_Member( false, false, $membership_id );
+
+	$membership->accept_request();
+
+	if ( !$membership->save() )
+		return false;
+
+	// Check if the user has an outstanding invite, if so delete it.
+	if ( groups_check_user_has_invite( $membership->user_id, $membership->group_id ) )
+		groups_delete_invite( $membership->user_id, $membership->group_id );
+
+	// Modify group member count
+	groups_update_groupmeta( $membership->group_id, 'total_member_count', (int) groups_get_groupmeta( $membership->group_id, 'total_member_count') + 1 );
+
+	// Record this in activity streams
+	$group = new BP_Groups_Group( $membership->group_id );
+
+	groups_record_activity( array(
+		'action'  => apply_filters_ref_array( 'groups_activity_membership_accepted_action', array( sprintf( __( '%1$s joined the group %2$s', 'buddypress'), bp_core_get_userlink( $membership->user_id ), '<a href="' . bp_get_group_permalink( $group ) . '">' . esc_attr( $group->name ) . '</a>' ), $membership->user_id, &$group ) ),
+		'type'    => 'joined_group',
+		'item_id' => $membership->group_id,
+		'user_id' => $membership->user_id
+	) );
+
+	// Send a notification to the user.
+	groups_notification_membership_request_completed( $membership->user_id, $membership->group_id, true );
+
+	do_action( 'groups_membership_accepted', $membership->user_id, $membership->group_id );
+
+	return true;
+}
+
+function groups_reject_membership_request( $membership_id, $user_id = 0, $group_id = 0 ) {
+	if ( !$membership = groups_delete_membership_request( $membership_id, $user_id, $group_id ) )
+		return false;
+
+	// Send a notification to the user.
+	groups_notification_membership_request_completed( $membership->user_id, $membership->group_id, false );
+
+	do_action( 'groups_membership_rejected', $membership->user_id, $membership->group_id );
+
+	return true;
+}
+
+function groups_delete_membership_request( $membership_id, $user_id = 0, $group_id = 0 ) {
+	if ( $user_id && $group_id )
+		$membership = new BP_Groups_Member( $user_id, $group_id );
+	else
+		$membership = new BP_Groups_Member( false, false, $membership_id );
+
+	if ( !BP_Groups_Member::delete( $membership->user_id, $membership->group_id ) )
+		return false;
+
+	return $membership;
+}
+
+function groups_check_for_membership_request( $user_id, $group_id ) {
+	return BP_Groups_Member::check_for_membership_request( $user_id, $group_id );
+}
+
+function groups_accept_all_pending_membership_requests( $group_id ) {
+	$user_ids = BP_Groups_Member::get_all_membership_request_user_ids( $group_id );
+
+	if ( !$user_ids )
+		return false;
+
+	foreach ( (array) $user_ids as $user_id )
+		groups_accept_membership_request( false, $user_id, $group_id );
+
+	do_action( 'groups_accept_all_pending_membership_requests', $group_id );
+
+	return true;
+}
+
+/*** Group Meta ****************************************************/
+
+function groups_delete_groupmeta( $group_id, $meta_key = false, $meta_value = false ) {
+	global $wpdb, $bp;
+
+	if ( !is_numeric( $group_id ) )
+		return false;
+
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_array( $meta_value ) || is_object( $meta_value ) )
+		$meta_value = serialize($meta_value);
+
+	$meta_value = trim( $meta_value );
+
+	if ( !$meta_key )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d", $group_id ) );
+	else if ( $meta_value )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s AND meta_value = %s", $group_id, $meta_key, $meta_value ) );
+	else
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key ) );
+
+	// Delete the cached object
+	wp_cache_delete( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, 'bp' );
+
+	return true;
+}
+
+function groups_get_groupmeta( $group_id, $meta_key = '') {
+	global $wpdb, $bp;
+
+	$group_id = (int) $group_id;
+
+	if ( !$group_id )
+		return false;
+
+	if ( !empty($meta_key) ) {
+		$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+		if ( !$metas = wp_cache_get( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, 'bp' ) ) {
+			$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key) );
+			wp_cache_set( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, $metas, 'bp' );
+		}
+	} else {
+		$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d", $group_id) );
+	}
+
+	if ( empty( $metas ) ) {
+		if ( empty( $meta_key ) )
+			return array();
+		else
+			return '';
+	}
+
+	$metas = array_map( 'maybe_unserialize', (array)$metas );
+
+	if ( 1 == count( $metas ) )
+		return $metas[0];
+	else
+		return $metas;
+}
+
+function groups_update_groupmeta( $group_id, $meta_key, $meta_value ) {
+	global $wpdb, $bp;
+
+	if ( !is_numeric( $group_id ) )
+		return false;
+
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_string( $meta_value ) )
+		$meta_value = stripslashes( $wpdb->escape( $meta_value ) );
+
+	$meta_value = maybe_serialize( $meta_value );
+
+	if ( empty( $meta_value ) )
+		return groups_delete_groupmeta( $group_id, $meta_key );
+
+	$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key ) );
+
+	if ( !$cur )
+		$wpdb->query( $wpdb->prepare( "INSERT INTO " . $bp->groups->table_name_groupmeta . " ( group_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", $group_id, $meta_key, $meta_value ) );
+	else if ( $cur->meta_value != $meta_value )
+		$wpdb->query( $wpdb->prepare( "UPDATE " . $bp->groups->table_name_groupmeta . " SET meta_value = %s WHERE group_id = %d AND meta_key = %s", $meta_value, $group_id, $meta_key ) );
+	else
+		return false;
+
+	// Update the cached object and recache
+	wp_cache_set( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, $meta_value, 'bp' );
+
+	return true;
+}
+
+/*** Group Cleanup Functions ****************************************************/
+
+function groups_remove_data_for_user( $user_id ) {
+	BP_Groups_Member::delete_all_for_user( $user_id );
+
+	do_action( 'groups_remove_data_for_user', $user_id );
+}
+add_action( 'wpmu_delete_user',  'groups_remove_data_for_user' );
+add_action( 'delete_user',       'groups_remove_data_for_user' );
+add_action( 'bp_make_spam_user', 'groups_remove_data_for_user' );
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-loader.php	(revision 5057)
@@ -0,0 +1,469 @@
+<?php
+/**
+ * BuddyPress Groups Loader
+ *
+ * A groups component, for users to group themselves together. Includes a
+ * robust sub-component API that allows Groups to be extended.
+ * Comes preconfigured with an activity stream, discussion forums, and settings.
+ *
+ * @package BuddyPress
+ * @subpackage Groups Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Groups_Component extends BP_Component {
+
+	/**
+	 * Start the groups component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'groups',
+			__( 'User Groups', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		$includes = array(
+			'cache',
+			'forums',
+			'actions',
+			'filters',
+			'screens',
+			'classes',
+			'widgets',
+			'activity',
+			'template',
+			'buddybar',
+			'adminbar',
+			'functions',
+			'notifications'
+		);
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_GROUPS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_GROUPS_SLUG' ) )
+			define( 'BP_GROUPS_SLUG', $this->id );
+
+		// Global tables for messaging component
+		$global_tables = array(
+			'table_name'           => $bp->table_prefix . 'bp_groups',
+			'table_name_members'   => $bp->table_prefix . 'bp_groups_members',
+			'table_name_groupmeta' => $bp->table_prefix . 'bp_groups_groupmeta'
+		);
+
+		// All globals for messaging component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_GROUPS_SLUG,
+			'root_slug'             => isset( $bp->pages->groups->slug ) ? $bp->pages->groups->slug : BP_GROUPS_SLUG,
+			'has_directory'         => true,
+			'notification_callback' => 'groups_format_notifications',
+			'search_string'         => __( 'Search Groups...', 'buddypress' ),
+			'global_tables'         => $global_tables
+		);
+
+		parent::setup_globals( $globals );
+
+		/** Single Group Globals **********************************************/
+
+		// Are we viewing a single group?
+		if ( bp_is_groups_component() && $group_id = BP_Groups_Group::group_exists( bp_current_action() ) ) {
+
+			$bp->is_single_item  = true;
+			$current_group_class = apply_filters( 'bp_groups_current_group_class', 'BP_Groups_Group' );
+			$this->current_group = apply_filters( 'bp_groups_current_group_object', new $current_group_class( $group_id ) );
+
+			// When in a single group, the first action is bumped down one because of the
+			// group name, so we need to adjust this and set the group name to current_item.
+			$bp->current_item   = bp_current_action();
+			$bp->current_action = bp_action_variable( 0 );
+			array_shift( $bp->action_variables );
+
+			// Using "item" not "group" for generic support in other components.
+			if ( is_super_admin() )
+				bp_update_is_item_admin( true, 'groups' );
+			else
+				bp_update_is_item_admin( groups_is_user_admin( $bp->loggedin_user->id, $this->current_group->id ), 'groups' );
+
+			// If the user is not an admin, check if they are a moderator
+			if ( !bp_is_item_admin() )
+				bp_update_is_item_mod  ( groups_is_user_mod  ( $bp->loggedin_user->id, $this->current_group->id ), 'groups' );
+
+			// Is the logged in user a member of the group?
+			if ( ( is_user_logged_in() && groups_is_user_member( $bp->loggedin_user->id, $this->current_group->id ) ) )
+				$this->current_group->is_user_member = true;
+			else
+				$this->current_group->is_user_member = false;
+
+			// Should this group be visible to the logged in user?
+			if ( 'public' == $this->current_group->status || $this->current_group->is_user_member )
+				$this->current_group->is_visible = true;
+			else
+				$this->current_group->is_visible = false;
+
+			// If this is a private or hidden group, does the user have access?
+			if ( 'private' == $this->current_group->status || 'hidden' == $this->current_group->status ) {
+				if ( $this->current_group->is_user_member && is_user_logged_in() || is_super_admin() )
+					$this->current_group->user_has_access = true;
+				else
+					$this->current_group->user_has_access = false;
+			} else {
+				$this->current_group->user_has_access = true;
+			}
+
+		// Set current_group to 0 to prevent debug errors
+		} else {
+			$this->current_group = 0;
+		}
+
+		// Illegal group names/slugs
+		$this->forbidden_names = apply_filters( 'groups_forbidden_names', array(
+			'my-groups',
+			'create',
+			'invites',
+			'send-invites',
+			'forum',
+			'delete',
+			'add',
+			'admin',
+			'request-membership',
+			'members',
+			'settings',
+			'avatar',
+			$this->slug,
+			$this->root_slug,
+		) );
+
+		// If the user was attempting to access a group, but no group by that name was found, 404
+		if ( bp_is_groups_component() && empty( $this->current_group ) && !empty( $bp->current_action ) && !in_array( $bp->current_action, $this->forbidden_names ) ) {
+			bp_do_404();
+			return;
+		}
+
+		// Group access control
+		if ( bp_is_groups_component() && !empty( $this->current_group ) && !empty( $bp->current_action ) && !$this->current_group->user_has_access ) {
+			if ( is_user_logged_in() ) {
+				// Off-limits to this user. Throw an error and redirect to the
+				// group's home page
+				bp_core_no_access( array(
+					'message'  => __( 'You do not have access to this group.', 'buddypress' ),
+					'root'     => bp_get_group_permalink( $bp->groups->current_group ),
+					'redirect' => false
+				) );
+			} else {
+				// Allow the user to log in
+				bp_core_no_access();
+			}
+		}
+
+		// Preconfigured group creation steps
+		$this->group_creation_steps = apply_filters( 'groups_create_group_steps', array(
+			'group-details'  => array(
+				'name'       => __( 'Details',  'buddypress' ),
+				'position'   => 0
+			),
+			'group-settings' => array(
+				'name'       => __( 'Settings', 'buddypress' ),
+				'position'   => 10
+			)
+		) );
+
+		// If avatar uploads are not disabled, add avatar option
+		if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) {
+			$this->group_creation_steps['group-avatar'] = array(
+				'name'     => __( 'Avatar',   'buddypress' ),
+				'position' => 20
+			);
+		}
+
+		// If friends component is active, add invitations
+		if ( bp_is_active( 'friends' ) ) {
+			$this->group_creation_steps['group-invites'] = array(
+				'name'     => __( 'Invites', 'buddypress' ),
+				'position' => 30
+			);
+		}
+
+		// Groups statuses
+		$this->valid_status = apply_filters( 'groups_valid_status', array(
+			'public',
+			'private',
+			'hidden'
+		) );
+
+		// Auto join group when non group member performs group activity
+		$this->auto_join = defined( 'BP_DISABLE_AUTO_GROUP_JOIN' );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add 'Groups' to the main navigation
+		$main_nav = array(
+			'name'                => sprintf( __( 'Groups <span>%d</span>', 'buddypress' ), groups_total_groups_for_user() ),
+			'slug'                => $this->slug,
+			'position'            => 70,
+			'screen_function'     => 'groups_screen_my_groups',
+			'default_subnav_slug' => 'my-groups',
+			'item_css_id'         => $this->id
+		);
+
+		$groups_link = trailingslashit( $bp->loggedin_user->domain . $this->slug );
+
+		// Add the My Groups nav item
+		$sub_nav[] = array(
+			'name'            => __( 'Memberships', 'buddypress' ),
+			'slug'            => 'my-groups',
+			'parent_url'      => $groups_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'groups_screen_my_groups',
+			'position'        => 10,
+			'item_css_id'     => 'groups-my-groups'
+		);
+
+		// Add the Group Invites nav item
+		$sub_nav[] = array(
+			'name'            => __( 'Invitations', 'buddypress' ),
+			'slug'            => 'invites',
+			'parent_url'      => $groups_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'groups_screen_group_invites',
+			'user_has_access' =>  bp_is_my_profile(),
+			'position'        => 30
+		);
+
+		parent::setup_nav( $main_nav, $sub_nav );
+
+		if ( bp_is_groups_component() && bp_is_single_item() ) {
+
+			unset( $main_nav ); unset( $sub_nav );
+
+			// Add 'Groups' to the main navigation
+			$main_nav = array(
+				'name'                => __( 'Memberships', 'buddypress' ),
+				'slug'                => $this->current_group->slug,
+				'position'            => -1, // Do not show in BuddyBar
+				'screen_function'     => 'groups_screen_group_home',
+				'default_subnav_slug' => 'home',
+				'item_css_id'         => $this->id
+			);
+
+			$group_link = trailingslashit( bp_get_root_domain() . '/' . $this->root_slug . '/' . $this->current_group->slug );
+
+			// Add the "Home" subnav item, as this will always be present
+			$sub_nav[] = array(
+				'name'            =>  _x( 'Home', 'Group home navigation title', 'buddypress' ),
+				'slug'            => 'home',
+				'parent_url'      => $group_link,
+				'parent_slug'     => $this->current_group->slug,
+				'screen_function' => 'groups_screen_group_home',
+				'position'        => 10,
+				'item_css_id'     => 'home'
+			);
+
+			// If the user is a group mod or more, then show the group admin nav item
+			if ( bp_is_item_admin() || bp_is_item_mod() ) {
+				$sub_nav[] = array(
+					'name'            => __( 'Admin', 'buddypress' ),
+					'slug'            => 'admin',
+					'parent_url'      => $group_link,
+					'parent_slug'     => $this->current_group->slug,
+					'screen_function' => 'groups_screen_group_admin',
+					'position'        => 20,
+					'user_has_access' => ( $bp->is_item_admin + (int)$bp->is_item_mod ),
+					'item_css_id'     => 'admin'
+				);
+			}
+
+			// If this is a private group, and the user is not a member, show a "Request Membership" nav item.
+			if ( is_user_logged_in() &&
+				 !is_super_admin() &&
+				 !$this->current_group->is_user_member &&
+				 !groups_check_for_membership_request( $bp->loggedin_user->id, $this->current_group->id ) &&
+				 $this->current_group->status == 'private'
+				) {
+				$sub_nav[] = array(
+					'name'               => __( 'Request Membership', 'buddypress' ),
+					'slug'               => 'request-membership',
+					'parent_url'         => $group_link,
+					'parent_slug'        => $this->current_group->slug,
+					'screen_function'    => 'groups_screen_group_request_membership',
+					'position'           => 30
+				);
+			}
+
+			// Forums are enabled and turned on
+			if ( $this->current_group->enable_forum && bp_is_active( 'forums' ) ) {
+				$sub_nav[] = array(
+					'name'            => __( 'Forum', 'buddypress' ),
+					'slug'            => 'forum',
+					'parent_url'      => $group_link,
+					'parent_slug'     => $this->current_group->slug,
+					'screen_function' => 'groups_screen_group_forum',
+					'position'        => 40,
+					'user_has_access' => $this->current_group->user_has_access,
+					'item_css_id'     => 'forums'
+				);
+			}
+
+			$sub_nav[] = array(
+				'name'            => sprintf( __( 'Members <span>%s</span>', 'buddypress' ), number_format( $this->current_group->total_member_count ) ),
+				'slug'            => 'members',
+				'parent_url'      => $group_link,
+				'parent_slug'     => $this->current_group->slug,
+				'screen_function' => 'groups_screen_group_members',
+				'position'        => 60,
+				'user_has_access' => $this->current_group->user_has_access,
+				'item_css_id'     => 'members'
+			);
+
+			if ( bp_is_active( 'friends' ) && bp_groups_user_can_send_invites() ) {
+				$sub_nav[] = array(
+					'name'            => __( 'Send Invites', 'buddypress' ),
+					'slug'            => 'send-invites',
+					'parent_url'      => $group_link,
+					'parent_slug'     => $this->current_group->slug,
+					'screen_function' => 'groups_screen_group_invite',
+					'item_css_id'     => 'invite',
+					'position'        => 70,
+					'user_has_access' => $this->current_group->user_has_access
+				);
+			}
+
+			parent::setup_nav( $main_nav, $sub_nav );
+		}
+
+		if ( isset( $this->current_group->user_has_access ) )
+			do_action( 'groups_setup_nav', $this->current_group->user_has_access );
+		else
+			do_action( 'groups_setup_nav');
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain = $bp->loggedin_user->domain;
+			$groups_link = trailingslashit( $user_domain . $this->slug );
+
+			// Pending group invites
+			$count = groups_get_invites_for_user( $bp->loggedin_user->id );
+
+			if ( !empty( $count->total ) ) {
+				$title   = sprintf( __( 'Groups <span class="count">%s</span>',          'buddypress' ), $count->total );
+				$pending = sprintf( __( 'Pending Invites <span class="count">%s</span>', 'buddypress' ), $count->total );
+			} else {
+				$title   = __( 'Groups',             'buddypress' );
+				$pending = __( 'No Pending Invites', 'buddypress' );
+			}
+
+			// Add the "My Account" sub menus
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => $title,
+				'href'   => trailingslashit( $groups_link )
+			);
+
+			// My Groups
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Memberships', 'buddypress' ),
+				'href'   => trailingslashit( $groups_link )
+			);
+
+			// Invitations
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => $pending,
+				'href'   => trailingslashit( $groups_link . 'invites' )
+			);
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		if ( bp_is_groups_component() ) {
+
+			if ( bp_is_my_profile() && !bp_is_single_item() ) {
+
+				$bp->bp_options_title = __( 'Memberships', 'buddypress' );
+
+			} else if ( !bp_is_my_profile() && !bp_is_single_item() ) {
+
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title  = $bp->displayed_user->fullname;
+
+			// We are viewing a single group, so set up the
+			// group navigation menu using the $this->current_group global.
+			} else if ( bp_is_single_item() ) {
+				$bp->bp_options_title  = $this->current_group->name;
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id'    => $this->current_group->id,
+					'object'     => 'group',
+					'type'       => 'thumb',
+					'avatar_dir' => 'group-avatars',
+					'alt'        => __( 'Group Avatar', 'buddypress' )
+				) );
+				if ( empty( $bp->bp_options_avatar ) )
+					$bp->bp_options_avatar = '<img src="' . esc_attr( $group->avatar_full ) . '" class="avatar" alt="' . esc_attr( $group->name ) . '" />';
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the groups component
+$bp->groups = new BP_Groups_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-notifications.php	(revision 5057)
@@ -0,0 +1,254 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_notification_group_updated( $group_id ) {
+	global $bp;
+
+	$group    = new BP_Groups_Group( $group_id );
+	$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+	$subject  = '[' . $sitename . '] ' . __( 'Group Details Updated', 'buddypress' );
+
+	$user_ids = BP_Groups_Member::get_group_member_ids( $group->id );
+	foreach ( (array)$user_ids as $user_id ) {
+		if ( 'no' == bp_get_user_meta( $user_id, 'notification_groups_group_updated', true ) ) continue;
+
+		$ud = bp_core_get_core_userdata( $user_id );
+
+		// Set up and send the message
+		$to = $ud->user_email;
+
+		$group_link = site_url( bp_get_groups_root_slug(). '/' . $group->slug );
+		$settings_link = bp_core_get_user_domain( $user_id ) . bp_get_settings_slug() . '/notifications/';
+
+		$message = sprintf( __(
+'Group details for the group "%1$s" were updated:
+
+To view the group: %2$s
+
+---------------------
+', 'buddypress' ), $group->name, $group_link );
+
+		$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		/* Send the message */
+		$to      = apply_filters( 'groups_notification_group_updated_to', $to );
+		$subject = apply_filters_ref_array( 'groups_notification_group_updated_subject', array( $subject, &$group ) );
+		$message = apply_filters_ref_array( 'groups_notification_group_updated_message', array( $message, &$group, $group_link, $settings_link ) );
+
+		wp_mail( $to, $subject, $message );
+
+		unset( $message, $to );
+	}
+
+	do_action( 'bp_groups_sent_updated_email', $user_ids, $subject, $message, $group_id );
+}
+
+function groups_notification_new_membership_request( $requesting_user_id, $admin_id, $group_id, $membership_id ) {
+	global $bp;
+
+	bp_core_add_notification( $requesting_user_id, $admin_id, 'groups', 'new_membership_request', $group_id );
+
+	if ( 'no' == bp_get_user_meta( $admin_id, 'notification_groups_membership_request', true ) )
+		return false;
+
+	$requesting_user_name = bp_core_get_user_displayname( $requesting_user_id );
+	$group = new BP_Groups_Group( $group_id );
+
+	$ud = bp_core_get_core_userdata($admin_id);
+	$requesting_ud = bp_core_get_core_userdata($requesting_user_id);
+
+	$group_requests = bp_get_group_permalink( $group ) . 'admin/membership-requests';
+	$profile_link = bp_core_get_user_domain( $requesting_user_id );
+	$settings_link = bp_core_get_user_domain( $requesting_user_id ) . bp_get_settings_slug() . '/notifications/';
+
+	// Set up and send the message
+	$to       = $ud->user_email;
+	$sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
+	$subject  = '[' . $sitename . '] ' . sprintf( __( 'Membership request for group: %s', 'buddypress' ), $group->name );
+
+$message = sprintf( __(
+'%1$s wants to join the group "%2$s".
+
+Because you are the administrator of this group, you must either accept or reject the membership request.
+
+To view all pending membership requests for this group, please visit:
+%3$s
+
+To view %4$s\'s profile: %5$s
+
+---------------------
+', 'buddypress' ), $requesting_user_name, $group->name, $group_requests, $requesting_user_name, $profile_link );
+
+	$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+	/* Send the message */
+	$to      = apply_filters( 'groups_notification_new_membership_request_to', $to );
+	$subject = apply_filters_ref_array( 'groups_notification_new_membership_request_subject', array( $subject, &$group ) );
+	$message = apply_filters_ref_array( 'groups_notification_new_membership_request_message', array( $message, &$group, $requesting_user_name, $profile_link, $group_requests, $settings_link ) );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_groups_sent_membership_request_email', $admin_id, $subject, $message, $requesting_user_id, $group_id, $membership_id );
+}
+
+function groups_notification_membership_request_completed( $requesting_user_id, $group_id, $accepted = true ) {
+	global $bp;
+
+	// Post a screen notification first.
+	if ( $accepted )
+		bp_core_add_notification( $group_id, $requesting_user_id, 'groups', 'membership_request_accepted' );
+	else
+		bp_core_add_notification( $group_id, $requesting_user_id, 'groups', 'membership_request_rejected' );
+
+	if ( 'no' == bp_get_user_meta( $requesting_user_id, 'notification_membership_request_completed', true ) )
+		return false;
+
+	$group = new BP_Groups_Group( $group_id );
+
+	$ud = bp_core_get_core_userdata($requesting_user_id);
+
+	$group_link = bp_get_group_permalink( $group );
+	$settings_link = bp_core_get_user_domain( $requesting_user_id ) . bp_get_settings_slug() . '/notifications/';
+
+	// Set up and send the message
+	$to       = $ud->user_email;
+	$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+
+	if ( $accepted ) {
+		$subject = '[' . $sitename . '] ' . sprintf( __( 'Membership request for group "%s" accepted', 'buddypress' ), $group->name );
+		$message = sprintf( __(
+'Your membership request for the group "%1$s" has been accepted.
+
+To view the group please login and visit: %2$s
+
+---------------------
+', 'buddypress' ), $group->name, $group_link );
+
+	} else {
+		$subject = '[' . $sitename . '] ' . sprintf( __( 'Membership request for group "%s" rejected', 'buddypress' ), $group->name );
+		$message = sprintf( __(
+'Your membership request for the group "%1$s" has been rejected.
+
+To submit another request please log in and visit: %2$s
+
+---------------------
+', 'buddypress' ), $group->name, $group_link );
+	}
+
+	$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+	/* Send the message */
+	$to      = apply_filters( 'groups_notification_membership_request_completed_to', $to );
+	$subject = apply_filters_ref_array( 'groups_notification_membership_request_completed_subject', array( $subject, &$group ) );
+	$message = apply_filters_ref_array( 'groups_notification_membership_request_completed_message', array( $message, &$group, $group_link, $settings_link ) );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_groups_sent_membership_approved_email', $requesting_user_id, $subject, $message, $group_id );
+}
+
+function groups_notification_promoted_member( $user_id, $group_id ) {
+	global $bp;
+
+	if ( groups_is_user_admin( $user_id, $group_id ) ) {
+		$promoted_to = __( 'an administrator', 'buddypress' );
+		$type = 'member_promoted_to_admin';
+	} else {
+		$promoted_to = __( 'a moderator', 'buddypress' );
+		$type = 'member_promoted_to_mod';
+	}
+
+	// Post a screen notification first.
+	bp_core_add_notification( $group_id, $user_id, 'groups', $type );
+
+	if ( 'no' == bp_get_user_meta( $user_id, 'notification_groups_admin_promotion', true ) )
+		return false;
+
+	$group = new BP_Groups_Group( $group_id );
+	$ud = bp_core_get_core_userdata($user_id);
+
+	$group_link = bp_get_group_permalink( $group );
+	$settings_link = bp_core_get_user_domain( $user_id ) . bp_get_settings_slug() . '/notifications/';
+
+	// Set up and send the message
+	$to       = $ud->user_email;
+	$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+	$subject  = '[' . $sitename . '] ' . sprintf( __( 'You have been promoted in the group: "%s"', 'buddypress' ), $group->name );
+
+	$message = sprintf( __(
+'You have been promoted to %1$s for the group: "%2$s".
+
+To view the group please visit: %3$s
+
+---------------------
+', 'buddypress' ), $promoted_to, $group->name, $group_link );
+
+	$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+	/* Send the message */
+	$to      = apply_filters( 'groups_notification_promoted_member_to', $to );
+	$subject = apply_filters_ref_array( 'groups_notification_promoted_member_subject', array( $subject, &$group ) );
+	$message = apply_filters_ref_array( 'groups_notification_promoted_member_message', array( $message, &$group, $promoted_to, $group_link, $settings_link ) );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_groups_sent_promoted_email', $user_id, $subject, $message, $group_id );
+}
+add_action( 'groups_promoted_member', 'groups_notification_promoted_member', 10, 2 );
+
+function groups_notification_group_invites( &$group, &$member, $inviter_user_id ) {
+	global $bp;
+
+	$inviter_ud = bp_core_get_core_userdata( $inviter_user_id );
+	$inviter_name = bp_core_get_userlink( $inviter_user_id, true, false, true );
+	$inviter_link = bp_core_get_user_domain( $inviter_user_id );
+
+	$group_link = bp_get_group_permalink( $group );
+
+	if ( !$member->invite_sent ) {
+		$invited_user_id = $member->user_id;
+
+		// Post a screen notification first.
+		bp_core_add_notification( $group->id, $invited_user_id, 'groups', 'group_invite' );
+
+		if ( 'no' == bp_get_user_meta( $invited_user_id, 'notification_groups_invite', true ) )
+			return false;
+
+		$invited_ud = bp_core_get_core_userdata($invited_user_id);
+
+		$settings_link = bp_core_get_user_domain( $invited_user_id ) . bp_get_settings_slug() . '/notifications/';
+		$invited_link = bp_core_get_user_domain( $invited_user_id );
+		$invites_link = $invited_link . bp_get_groups_slug() . '/invites';
+
+		// Set up and send the message
+		$to       = $invited_ud->user_email;
+		$sitename = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+		$subject  = '[' . $sitename . '] ' . sprintf( __( 'You have an invitation to the group: "%s"', 'buddypress' ), $group->name );
+
+		$message = sprintf( __(
+'One of your friends %1$s has invited you to the group: "%2$s".
+
+To view your group invites visit: %3$s
+
+To view the group visit: %4$s
+
+To view %5$s\'s profile visit: %6$s
+
+---------------------
+', 'buddypress' ), $inviter_name, $group->name, $invites_link, $group_link, $inviter_name, $inviter_link );
+
+		$message .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		/* Send the message */
+		$to      = apply_filters( 'groups_notification_group_invites_to', $to );
+		$subject = apply_filters_ref_array( 'groups_notification_group_invites_subject', array( $subject, &$group ) );
+		$message = apply_filters_ref_array( 'groups_notification_group_invites_message', array( $message, &$group, $inviter_name, $inviter_link, $invites_link, $group_link, $settings_link ) );
+
+		wp_mail( $to, $subject, $message );
+
+		do_action( 'bp_groups_sent_invited_email', $invited_user_id, $subject, $message, $group );
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-screens.php	(revision 5057)
@@ -0,0 +1,866 @@
+<?php
+/********************************************************************************
+ * Screen Functions
+ *
+ * Screen functions are the controllers of BuddyPress. They will execute when their
+ * specific URL is caught. They will first save or manipulate data using business
+ * functions, then pass on the user to a template file.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function groups_directory_groups_setup() {
+	if ( bp_is_groups_component() && !bp_current_action() && !bp_current_item() ) {
+		bp_update_is_directory( true, 'groups' );
+
+		do_action( 'groups_directory_groups_setup' );
+
+		bp_core_load_template( apply_filters( 'groups_template_directory_groups', 'groups/index' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_directory_groups_setup', 2 );
+
+function groups_screen_my_groups() {
+	global $bp;
+
+	// Delete group request notifications for the user
+	if ( isset( $_GET['n'] ) ) {
+		bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'membership_request_accepted' );
+		bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'membership_request_rejected' );
+		bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'member_promoted_to_mod'      );
+		bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'member_promoted_to_admin'    );
+	}
+
+	do_action( 'groups_screen_my_groups' );
+
+	bp_core_load_template( apply_filters( 'groups_template_my_groups', 'members/single/home' ) );
+}
+
+function groups_screen_group_invites() {
+	$group_id = (int)bp_action_variable( 1 );
+
+	if ( bp_is_action_variable( 'accept' ) && is_numeric( $group_id ) ) {
+		// Check the nonce
+		if ( !check_admin_referer( 'groups_accept_invite' ) )
+			return false;
+
+		if ( !groups_accept_invite( bp_loggedin_user_id(), $group_id ) ) {
+			bp_core_add_message( __('Group invite could not be accepted', 'buddypress'), 'error' );
+		} else {
+			bp_core_add_message( __('Group invite accepted', 'buddypress') );
+
+			// Record this in activity streams
+			$group = new BP_Groups_Group( $group_id );
+
+			groups_record_activity( array(
+				'action'  => apply_filters_ref_array( 'groups_activity_accepted_invite_action', array( sprintf( __( '%1$s joined the group %2$s', 'buddypress'), bp_core_get_userlink( bp_loggedin_user_id() ), '<a href="' . bp_get_group_permalink( $group ) . '">' . esc_attr( $group->name ) . '</a>' ), bp_loggedin_user_id(), &$group ) ),
+				'type'    => 'joined_group',
+				'item_id' => $group->id
+			) );
+		}
+
+		bp_core_redirect( bp_loggedin_user_domain() . bp_get_groups_slug() . '/' . bp_current_action() );
+
+	} else if ( bp_is_action_variable( 'reject' ) && is_numeric( $group_id ) ) {
+		// Check the nonce
+		if ( !check_admin_referer( 'groups_reject_invite' ) )
+			return false;
+
+		if ( !groups_reject_invite( bp_loggedin_user_id(), $group_id ) )
+			bp_core_add_message( __('Group invite could not be rejected', 'buddypress'), 'error' );
+		else
+			bp_core_add_message( __('Group invite rejected', 'buddypress') );
+
+		bp_core_redirect( bp_loggedin_user_domain() . bp_get_groups_slug() . '/' . bp_current_action() );
+	}
+
+	// Remove notifications
+	bp_core_delete_notifications_by_type( bp_loggedin_user_id(), 'groups', 'group_invite' );
+
+	do_action( 'groups_screen_group_invites', $group_id );
+
+	bp_core_load_template( apply_filters( 'groups_template_group_invites', 'members/single/home' ) );
+}
+
+function groups_screen_group_home() {
+	global $bp;
+
+	if ( bp_is_single_item() ) {
+		if ( isset( $_GET['n'] ) ) {
+			bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'membership_request_accepted' );
+			bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'membership_request_rejected' );
+			bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'member_promoted_to_mod'      );
+			bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->groups->id, 'member_promoted_to_admin'    );
+		}
+
+		do_action( 'groups_screen_group_home' );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
+	}
+}
+
+/**
+ * This screen function handles actions related to group forums
+ *
+ * @package BuddyPress
+ */
+function groups_screen_group_forum() {
+	global $bp;
+
+	if ( !bp_is_active( 'forums' ) || !bp_forums_is_installed_correctly() )
+		return false;
+
+	if ( bp_action_variable( 0 ) && !bp_is_action_variable( 'topic', 0 ) ) {
+		bp_do_404();
+		return;
+	}
+
+	if ( !$bp->groups->current_group->user_has_access ) {
+		bp_core_no_access();
+		return;
+	}
+
+	if ( bp_is_single_item() ) {
+
+		// Fetch the details we need
+		$topic_slug	= (string)bp_action_variable( 1 );
+		$topic_id       = bp_forums_get_topic_id_from_slug( $topic_slug );
+		$forum_id       = groups_get_groupmeta( $bp->groups->current_group->id, 'forum_id' );
+		$user_is_banned = false;
+
+		if ( !is_super_admin() && groups_is_user_banned( $bp->loggedin_user->id, $bp->groups->current_group->id ) )
+			$user_is_banned = true;
+
+		if ( !empty( $topic_slug ) && !empty( $topic_id ) ) {
+
+			// Posting a reply
+			if ( !$user_is_banned && !bp_action_variable( 2 ) && isset( $_POST['submit_reply'] ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_new_reply' );
+
+				// Auto join this user if they are not yet a member of this group
+				if ( $bp->groups->auto_join && !is_super_admin() && 'public' == $bp->groups->current_group->status && !groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) )
+					groups_join_group( $bp->groups->current_group->id, $bp->loggedin_user->id );
+
+				$topic_page = isset( $_GET['topic_page'] ) ? $_GET['topic_page'] : false;
+
+				if ( !$post_id = groups_new_group_forum_post( $_POST['reply_text'], $topic_id, $topic_page ) )
+					bp_core_add_message( __( 'There was an error when replying to that topic', 'buddypress'), 'error' );
+				else
+					bp_core_add_message( __( 'Your reply was posted successfully', 'buddypress') );
+
+				if ( isset( $_SERVER['QUERY_STRING'] ) )
+					$query_vars = '?' . $_SERVER['QUERY_STRING'];
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'forum/topic/' . $topic_slug . '/' . $query_vars . '#post-' . $post_id );
+			}
+
+			// Sticky a topic
+			else if ( bp_is_action_variable( 'stick', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_stick_topic' );
+
+				if ( !bp_forums_sticky_topic( array( 'topic_id' => $topic_id ) ) )
+					bp_core_add_message( __( 'There was an error when making that topic a sticky', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'The topic was made sticky successfully', 'buddypress' ) );
+
+				do_action( 'groups_stick_forum_topic', $topic_id );
+				bp_core_redirect( wp_get_referer() );
+			}
+
+			// Un-Sticky a topic
+			else if ( bp_is_action_variable( 'unstick', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_unstick_topic' );
+
+				if ( !bp_forums_sticky_topic( array( 'topic_id' => $topic_id, 'mode' => 'unstick' ) ) )
+					bp_core_add_message( __( 'There was an error when unsticking that topic', 'buddypress'), 'error' );
+				else
+					bp_core_add_message( __( 'The topic was unstuck successfully', 'buddypress') );
+
+				do_action( 'groups_unstick_forum_topic', $topic_id );
+				bp_core_redirect( wp_get_referer() );
+			}
+
+			// Close a topic
+			else if ( bp_is_action_variable( 'close', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_close_topic' );
+
+				if ( !bp_forums_openclose_topic( array( 'topic_id' => $topic_id ) ) )
+					bp_core_add_message( __( 'There was an error when closing that topic', 'buddypress'), 'error' );
+				else
+					bp_core_add_message( __( 'The topic was closed successfully', 'buddypress') );
+
+				do_action( 'groups_close_forum_topic', $topic_id );
+				bp_core_redirect( wp_get_referer() );
+			}
+
+			// Open a topic
+			else if ( bp_is_action_variable( 'open', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_open_topic' );
+
+				if ( !bp_forums_openclose_topic( array( 'topic_id' => $topic_id, 'mode' => 'open' ) ) )
+					bp_core_add_message( __( 'There was an error when opening that topic', 'buddypress'), 'error' );
+				else
+					bp_core_add_message( __( 'The topic was opened successfully', 'buddypress') );
+
+				do_action( 'groups_open_forum_topic', $topic_id );
+				bp_core_redirect( wp_get_referer() );
+			}
+
+			// Delete a topic
+			else if ( empty( $user_is_banned ) && bp_is_action_variable( 'delete', 2 ) && !bp_action_variable( 3 ) ) {
+				// Fetch the topic
+				$topic = bp_forums_get_topic_details( $topic_id );
+
+				/* Check the logged in user can delete this topic */
+				if ( !$bp->is_item_admin && !$bp->is_item_mod && (int)$bp->loggedin_user->id != (int)$topic->topic_poster )
+					bp_core_redirect( wp_get_referer() );
+
+				// Check the nonce
+				check_admin_referer( 'bp_forums_delete_topic' );
+
+				do_action( 'groups_before_delete_forum_topic', $topic_id );
+
+				if ( !groups_delete_group_forum_topic( $topic_id ) )
+					bp_core_add_message( __( 'There was an error deleting the topic', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'The topic was deleted successfully', 'buddypress' ) );
+
+				do_action( 'groups_delete_forum_topic', $topic_id );
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'forum/' );
+			}
+
+			// Editing a topic
+			else if ( empty( $user_is_banned ) && bp_is_action_variable( 'edit', 2 ) && !bp_action_variable( 3 ) ) {
+				// Fetch the topic
+				$topic = bp_forums_get_topic_details( $topic_id );
+
+				// Check the logged in user can edit this topic
+				if ( !$bp->is_item_admin && !$bp->is_item_mod && (int)$bp->loggedin_user->id != (int)$topic->topic_poster )
+					bp_core_redirect( wp_get_referer() );
+
+				if ( isset( $_POST['save_changes'] ) ) {
+					// Check the nonce
+					check_admin_referer( 'bp_forums_edit_topic' );
+
+					$topic_tags = !empty( $_POST['topic_tags'] ) ? $_POST['topic_tags'] : false;
+
+					if ( !groups_update_group_forum_topic( $topic_id, $_POST['topic_title'], $_POST['topic_text'], $topic_tags ) )
+						bp_core_add_message( __( 'There was an error when editing that topic', 'buddypress'), 'error' );
+					else
+						bp_core_add_message( __( 'The topic was edited successfully', 'buddypress') );
+
+					do_action( 'groups_edit_forum_topic', $topic_id );
+					bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'forum/topic/' . $topic_slug . '/' );
+				}
+
+				bp_core_load_template( apply_filters( 'groups_template_group_forum_topic_edit', 'groups/single/home' ) );
+			}
+
+			// Delete a post
+			else if ( empty( $user_is_banned ) && bp_is_action_variable( 'delete', 2 ) && $post_id = bp_action_variable( 4 ) ) {
+				// Fetch the post
+				$post = bp_forums_get_post( $post_id );
+
+				// Check the logged in user can edit this topic
+				if ( !$bp->is_item_admin && !$bp->is_item_mod && (int)$bp->loggedin_user->id != (int)$post->poster_id )
+					bp_core_redirect( wp_get_referer() );
+
+				// Check the nonce
+				check_admin_referer( 'bp_forums_delete_post' );
+
+				do_action( 'groups_before_delete_forum_post', $post_id );
+
+				if ( !groups_delete_group_forum_post( $post_id ) )
+					bp_core_add_message( __( 'There was an error deleting that post', 'buddypress'), 'error' );
+				else
+					bp_core_add_message( __( 'The post was deleted successfully', 'buddypress') );
+
+				do_action( 'groups_delete_forum_post', $post_id );
+				bp_core_redirect( wp_get_referer() );
+			}
+
+			// Editing a post
+			else if ( empty( $user_is_banned ) && bp_is_action_variable( 'edit', 2 ) && $post_id = bp_action_variable( 4 ) ) {
+				// Fetch the post
+				$post = bp_forums_get_post( $post_id );
+
+				// Check the logged in user can edit this topic
+				if ( !$bp->is_item_admin && !$bp->is_item_mod && (int)$bp->loggedin_user->id != (int)$post->poster_id )
+					bp_core_redirect( wp_get_referer() );
+
+				if ( isset( $_POST['save_changes'] ) ) {
+					// Check the nonce
+					check_admin_referer( 'bp_forums_edit_post' );
+
+					$topic_page = isset( $_GET['topic_page'] ) ? $_GET['topic_page'] : false;
+
+					if ( !$post_id = groups_update_group_forum_post( $post_id, $_POST['post_text'], $topic_id, $topic_page ) )
+						bp_core_add_message( __( 'There was an error when editing that post', 'buddypress'), 'error' );
+					else
+						bp_core_add_message( __( 'The post was edited successfully', 'buddypress') );
+
+					if ( $_SERVER['QUERY_STRING'] )
+						$query_vars = '?' . $_SERVER['QUERY_STRING'];
+
+					do_action( 'groups_edit_forum_post', $post_id );
+					bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic_slug . '/' . $query_vars . '#post-' . $post_id );
+				}
+
+				bp_core_load_template( apply_filters( 'groups_template_group_forum_topic_edit', 'groups/single/home' ) );
+			}
+
+			// Standard topic display
+			else {
+				if ( !empty( $user_is_banned ) )
+					bp_core_add_message( __( "You have been banned from this group.", 'buddypress' ) );
+
+				bp_core_load_template( apply_filters( 'groups_template_group_forum_topic', 'groups/single/home' ) );
+			}
+
+		// Forum topic does not exist
+		} elseif ( !empty( $topic_slug ) && empty( $topic_id ) ) {
+			bp_do_404();
+			return;
+
+		} else {
+			// Posting a topic
+			if ( isset( $_POST['submit_topic'] ) && bp_is_active( 'forums' ) ) {
+				// Check the nonce
+				check_admin_referer( 'bp_forums_new_topic' );
+
+				if ( $user_is_banned ) {
+				 	$error_message = __( "You have been banned from this group.", 'buddypress' );
+
+				} elseif ( $bp->groups->auto_join && !is_super_admin() && 'public' == $bp->groups->current_group->status && !groups_is_user_member( $bp->loggedin_user->id, $bp->groups->current_group->id ) ) {
+					// Auto join this user if they are not yet a member of this group
+					groups_join_group( $bp->groups->current_group->id, $bp->loggedin_user->id );
+				}
+
+				if ( empty( $_POST['topic_title'] ) )
+					$error_message = __( 'Please provide a title for your forum topic.', 'buddypress' );
+				else if ( empty( $_POST['topic_text'] ) )
+					$error_message = __( 'Forum posts cannot be empty. Please enter some text.', 'buddypress' );
+
+				if ( empty( $forum_id ) )
+					$error_message = __( 'This group does not have a forum setup yet.', 'buddypress' );
+
+				if ( isset( $error_message ) ) {
+					bp_core_add_message( $error_message, 'error' );
+					$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum';
+				} else {
+					if ( !$topic = groups_new_group_forum_topic( $_POST['topic_title'], $_POST['topic_text'], $_POST['topic_tags'], $forum_id ) ) {
+						bp_core_add_message( __( 'There was an error when creating the topic', 'buddypress'), 'error' );
+						$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum';
+					} else {
+						bp_core_add_message( __( 'The topic was created successfully', 'buddypress') );
+						$redirect = bp_get_group_permalink( $bp->groups->current_group ) . 'forum/topic/' . $topic->topic_slug . '/';
+					}
+				}
+
+				bp_core_redirect( $redirect );
+			}
+
+			do_action( 'groups_screen_group_forum', $topic_id, $forum_id );
+
+			bp_core_load_template( apply_filters( 'groups_template_group_forum', 'groups/single/home' ) );
+		}
+	}
+}
+
+function groups_screen_group_members() {
+	global $bp;
+
+	if ( $bp->is_single_item ) {
+		// Refresh the group member count meta
+		groups_update_groupmeta( $bp->groups->current_group->id, 'total_member_count', groups_get_total_member_count( $bp->groups->current_group->id ) );
+
+		do_action( 'groups_screen_group_members', $bp->groups->current_group->id );
+		bp_core_load_template( apply_filters( 'groups_template_group_members', 'groups/single/home' ) );
+	}
+}
+
+function groups_screen_group_invite() {
+	global $bp;
+
+	if ( $bp->is_single_item ) {
+		if ( bp_is_action_variable( 'send', 0 ) ) {
+
+			if ( !check_admin_referer( 'groups_send_invites', '_wpnonce_send_invites' ) )
+				return false;
+
+			if ( !empty( $_POST['friends'] ) ) {
+				foreach( (array)$_POST['friends'] as $friend ) {
+					groups_invite_user( array( 'user_id' => $friend, 'group_id' => $bp->groups->current_group->id ) );
+				}
+			}
+
+			// Send the invites.
+			groups_send_invites( $bp->loggedin_user->id, $bp->groups->current_group->id );
+			bp_core_add_message( __('Group invites sent.', 'buddypress') );
+			do_action( 'groups_screen_group_invite', $bp->groups->current_group->id );
+			bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+
+		} elseif ( !bp_action_variable( 0 ) ) {
+			// Show send invite page
+			bp_core_load_template( apply_filters( 'groups_template_group_invite', 'groups/single/home' ) );
+
+		} else {
+			bp_do_404();
+		}
+	}
+}
+
+function groups_screen_group_request_membership() {
+	global $bp;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+	if ( 'private' == $bp->groups->current_group->status ) {
+		// If the user has submitted a request, send it.
+		if ( isset( $_POST['group-request-send']) ) {
+			// Check the nonce
+			if ( !check_admin_referer( 'groups_request_membership' ) )
+				return false;
+
+			if ( !groups_send_membership_request( $bp->loggedin_user->id, $bp->groups->current_group->id ) ) {
+				bp_core_add_message( __( 'There was an error sending your group membership request, please try again.', 'buddypress' ), 'error' );
+			} else {
+				bp_core_add_message( __( 'Your membership request was sent to the group administrator successfully. You will be notified when the group administrator responds to your request.', 'buddypress' ) );
+			}
+			bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
+		}
+
+		do_action( 'groups_screen_group_request_membership', $bp->groups->current_group->id );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_request_membership', 'groups/single/home' ) );
+	}
+}
+
+function groups_screen_group_activity_permalink() {
+	global $bp;
+
+	if ( !bp_is_groups_component() || !bp_is_active( 'activity' ) || ( bp_is_active( 'activity' ) && !bp_is_current_action( bp_get_activity_slug() ) ) || !bp_action_variable( 0 ) )
+		return false;
+
+	$bp->is_single_item = true;
+
+	bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
+}
+add_action( 'bp_screens', 'groups_screen_group_activity_permalink' );
+
+function groups_screen_group_admin() {
+	if ( !bp_is_groups_component() || !bp_is_current_action( 'admin' ) )
+		return false;
+
+	if ( bp_action_variables() )
+		return false;
+
+	bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/edit-details/' );
+}
+
+function groups_screen_group_admin_edit_details() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'edit-details', 0 ) ) {
+
+		if ( $bp->is_item_admin || $bp->is_item_mod  ) {
+
+			// If the edit form has been submitted, save the edited details
+			if ( isset( $_POST['save'] ) ) {
+				// Check the nonce
+				if ( !check_admin_referer( 'groups_edit_group_details' ) )
+					return false;
+
+				if ( !groups_edit_base_group_details( $_POST['group-id'], $_POST['group-name'], $_POST['group-desc'], (int)$_POST['group-notify-members'] ) ) {
+					bp_core_add_message( __( 'There was an error updating group details, please try again.', 'buddypress' ), 'error' );
+				} else {
+					bp_core_add_message( __( 'Group details were successfully updated.', 'buddypress' ) );
+				}
+
+				do_action( 'groups_group_details_edited', $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/edit-details/' );
+			}
+
+			do_action( 'groups_screen_group_admin_edit_details', $bp->groups->current_group->id );
+
+			bp_core_load_template( apply_filters( 'groups_template_group_admin', 'groups/single/home' ) );
+		}
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_edit_details' );
+
+function groups_screen_group_admin_settings() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'group-settings', 0 ) ) {
+
+		if ( !$bp->is_item_admin )
+			return false;
+
+		// If the edit form has been submitted, save the edited details
+		if ( isset( $_POST['save'] ) ) {
+			$enable_forum   = ( isset($_POST['group-show-forum'] ) ) ? 1 : 0;
+
+			// Checked against a whitelist for security
+			$allowed_status = apply_filters( 'groups_allowed_status', array( 'public', 'private', 'hidden' ) );
+			$status         = ( in_array( $_POST['group-status'], (array)$allowed_status ) ) ? $_POST['group-status'] : 'public';
+
+			// Checked against a whitelist for security
+			$allowed_invite_status = apply_filters( 'groups_allowed_invite_status', array( 'members', 'mods', 'admins' ) );
+			$invite_status	       = in_array( $_POST['group-invite-status'], (array)$allowed_invite_status ) ? $_POST['group-invite-status'] : 'members';
+
+			// Check the nonce
+			if ( !check_admin_referer( 'groups_edit_group_settings' ) )
+				return false;
+
+			if ( !groups_edit_group_settings( $_POST['group-id'], $enable_forum, $status, $invite_status ) ) {
+				bp_core_add_message( __( 'There was an error updating group settings, please try again.', 'buddypress' ), 'error' );
+			} else {
+				bp_core_add_message( __( 'Group settings were successfully updated.', 'buddypress' ) );
+			}
+
+			do_action( 'groups_group_settings_edited', $bp->groups->current_group->id );
+
+			bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/group-settings/' );
+		}
+
+		do_action( 'groups_screen_group_admin_settings', $bp->groups->current_group->id );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_admin_settings', 'groups/single/home' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_settings' );
+
+function groups_screen_group_admin_avatar() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'group-avatar', 0 ) ) {
+
+		// If the logged-in user doesn't have permission or if avatar uploads are disabled, then stop here
+		if ( !$bp->is_item_admin || (int)bp_get_option( 'bp-disable-avatar-uploads' ) )
+			return false;
+
+		// If the group admin has deleted the admin avatar
+		if ( bp_is_action_variable( 'delete', 1 ) ) {
+
+			// Check the nonce
+			check_admin_referer( 'bp_group_avatar_delete' );
+
+			if ( bp_core_delete_existing_avatar( array( 'item_id' => $bp->groups->current_group->id, 'object' => 'group' ) ) )
+				bp_core_add_message( __( 'Your avatar was deleted successfully!', 'buddypress' ) );
+			else
+				bp_core_add_message( __( 'There was a problem deleting that avatar, please try again.', 'buddypress' ), 'error' );
+
+		}
+
+		$bp->avatar_admin->step = 'upload-image';
+
+		if ( !empty( $_FILES ) ) {
+
+			// Check the nonce
+			check_admin_referer( 'bp_avatar_upload' );
+
+			// Pass the file to the avatar upload handler
+			if ( bp_core_avatar_handle_upload( $_FILES, 'groups_avatar_upload_dir' ) ) {
+				$bp->avatar_admin->step = 'crop-image';
+
+				// Make sure we include the jQuery jCrop file for image cropping
+				add_action( 'wp_print_scripts', 'bp_core_add_jquery_cropper' );
+			}
+
+		}
+
+		// If the image cropping is done, crop the image and save a full/thumb version
+		if ( isset( $_POST['avatar-crop-submit'] ) ) {
+
+			// Check the nonce
+			check_admin_referer( 'bp_avatar_cropstore' );
+
+			if ( !bp_core_avatar_handle_crop( array( 'object' => 'group', 'avatar_dir' => 'group-avatars', 'item_id' => $bp->groups->current_group->id, 'original_file' => $_POST['image_src'], 'crop_x' => $_POST['x'], 'crop_y' => $_POST['y'], 'crop_w' => $_POST['w'], 'crop_h' => $_POST['h'] ) ) )
+				bp_core_add_message( __( 'There was a problem cropping the avatar, please try uploading it again', 'buddypress' ) );
+			else
+				bp_core_add_message( __( 'The new group avatar was uploaded successfully!', 'buddypress' ) );
+
+		}
+
+		do_action( 'groups_screen_group_admin_avatar', $bp->groups->current_group->id );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_admin_avatar', 'groups/single/home' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_avatar' );
+
+/**
+ * This function handles actions related to member management on the group admin.
+ *
+ * @package BuddyPress
+ */
+function groups_screen_group_admin_manage_members() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'manage-members', 0 ) ) {
+
+		if ( !$bp->is_item_admin )
+			return false;
+
+		if ( bp_action_variable( 1 ) && bp_action_variable( 2 ) && bp_action_variable( 3 ) ) {
+			if ( bp_is_action_variable( 'promote', 1 ) && ( bp_is_action_variable( 'mod', 2 ) || bp_is_action_variable( 'admin', 2 ) ) && is_numeric( bp_action_variable( 3 ) ) ) {
+				$user_id = bp_action_variable( 3 );
+				$status  = bp_action_variable( 2 );
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_promote_member' ) )
+					return false;
+
+				// Promote a user.
+				if ( !groups_promote_member( $user_id, $bp->groups->current_group->id, $status ) )
+					bp_core_add_message( __( 'There was an error when promoting that user, please try again', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'User promoted successfully', 'buddypress' ) );
+
+				do_action( 'groups_promoted_member', $user_id, $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/manage-members/' );
+			}
+		}
+
+		if ( bp_action_variable( 1 ) && bp_action_variable( 2 ) ) {
+			if ( bp_is_action_variable( 'demote', 1 ) && is_numeric( bp_action_variable( 2 ) ) ) {
+				$user_id = bp_action_variable( 2 );
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_demote_member' ) )
+					return false;
+
+				// Demote a user.
+				if ( !groups_demote_member( $user_id, $bp->groups->current_group->id ) )
+					bp_core_add_message( __( 'There was an error when demoting that user, please try again', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'User demoted successfully', 'buddypress' ) );
+
+				do_action( 'groups_demoted_member', $user_id, $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/manage-members/' );
+			}
+
+			if ( bp_is_action_variable( 'ban', 1 ) && is_numeric( bp_action_variable( 2 ) ) ) {
+				$user_id = bp_action_variable( 2 );
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_ban_member' ) )
+					return false;
+
+				// Ban a user.
+				if ( !groups_ban_member( $user_id, $bp->groups->current_group->id ) )
+					bp_core_add_message( __( 'There was an error when banning that user, please try again', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'User banned successfully', 'buddypress' ) );
+
+				do_action( 'groups_banned_member', $user_id, $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/manage-members/' );
+			}
+
+			if ( bp_is_action_variable( 'unban', 1 ) && is_numeric( bp_action_variable( 2 ) ) ) {
+				$user_id = bp_action_variable( 2 );
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_unban_member' ) )
+					return false;
+
+				// Remove a ban for user.
+				if ( !groups_unban_member( $user_id, $bp->groups->current_group->id ) )
+					bp_core_add_message( __( 'There was an error when unbanning that user, please try again', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'User ban removed successfully', 'buddypress' ) );
+
+				do_action( 'groups_unbanned_member', $user_id, $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/manage-members/' );
+			}
+
+			if ( bp_is_action_variable( 'remove', 1 ) && is_numeric( bp_action_variable( 2 ) ) ) {
+				$user_id = bp_action_variable( 2 );
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_remove_member' ) )
+					return false;
+
+				// Remove a user.
+				if ( !groups_remove_member( $user_id, $bp->groups->current_group->id ) )
+					bp_core_add_message( __( 'There was an error removing that user from the group, please try again', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'User removed successfully', 'buddypress' ) );
+
+				do_action( 'groups_removed_member', $user_id, $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/manage-members/' );
+			}
+		}
+
+		do_action( 'groups_screen_group_admin_manage_members', $bp->groups->current_group->id );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_admin_manage_members', 'groups/single/home' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_manage_members' );
+
+function groups_screen_group_admin_requests() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'membership-requests', 0 ) ) {
+
+		if ( !$bp->is_item_admin || 'public' == $bp->groups->current_group->status )
+			return false;
+
+		// Remove any screen notifications
+		bp_core_delete_notifications_by_type( bp_loggedin_user_id(), $bp->groups->id, 'new_membership_request' );
+
+		$request_action = (string)bp_action_variable( 1 );
+		$membership_id  = (int)bp_action_variable( 2 );
+
+		if ( !empty( $request_action ) && !empty( $membership_id ) ) {
+			if ( 'accept' == $request_action && is_numeric( $membership_id ) ) {
+
+				// Check the nonce first.
+				if ( !check_admin_referer( 'groups_accept_membership_request' ) )
+					return false;
+
+				// Accept the membership request
+				if ( !groups_accept_membership_request( $membership_id ) )
+					bp_core_add_message( __( 'There was an error accepting the membership request, please try again.', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'Group membership request accepted', 'buddypress' ) );
+
+			} elseif ( 'reject' == $request_action && is_numeric( $membership_id ) ) {
+				/* Check the nonce first. */
+				if ( !check_admin_referer( 'groups_reject_membership_request' ) )
+					return false;
+
+				// Reject the membership request
+				if ( !groups_reject_membership_request( $membership_id ) )
+					bp_core_add_message( __( 'There was an error rejecting the membership request, please try again.', 'buddypress' ), 'error' );
+				else
+					bp_core_add_message( __( 'Group membership request rejected', 'buddypress' ) );
+			}
+
+			do_action( 'groups_group_request_managed', $bp->groups->current_group->id, $request_action, $membership_id );
+			bp_core_redirect( bp_get_group_permalink( groups_get_current_group() ) . 'admin/membership-requests/' );
+		}
+
+		do_action( 'groups_screen_group_admin_requests', $bp->groups->current_group->id );
+		bp_core_load_template( apply_filters( 'groups_template_group_admin_requests', 'groups/single/home' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_requests' );
+
+function groups_screen_group_admin_delete_group() {
+	global $bp;
+
+	if ( bp_is_groups_component() && bp_is_action_variable( 'delete-group', 0 ) ) {
+
+		if ( !$bp->is_item_admin && !is_super_admin() )
+			return false;
+
+		if ( isset( $_REQUEST['delete-group-button'] ) && isset( $_REQUEST['delete-group-understand'] ) ) {
+			// Check the nonce first.
+			if ( !check_admin_referer( 'groups_delete_group' ) )
+				return false;
+
+			do_action( 'groups_before_group_deleted', $bp->groups->current_group->id );
+
+			// Group admin has deleted the group, now do it.
+			if ( !groups_delete_group( $bp->groups->current_group->id ) ) {
+				bp_core_add_message( __( 'There was an error deleting the group, please try again.', 'buddypress' ), 'error' );
+			} else {
+				bp_core_add_message( __( 'The group was deleted successfully', 'buddypress' ) );
+
+				do_action( 'groups_group_deleted', $bp->groups->current_group->id );
+
+				bp_core_redirect( bp_loggedin_user_domain() . bp_get_groups_slug() . '/' );
+			}
+
+			bp_core_redirect( bp_loggedin_user_domain() . bp_get_groups_slug() );
+		}
+
+		do_action( 'groups_screen_group_admin_delete_group', $bp->groups->current_group->id );
+
+		bp_core_load_template( apply_filters( 'groups_template_group_admin_delete_group', 'groups/single/home' ) );
+	}
+}
+add_action( 'bp_screens', 'groups_screen_group_admin_delete_group' );
+
+/**
+ * Renders the group settings fields on the Notification Settings page
+ *
+ * @package BuddyPress
+ */
+function groups_screen_notification_settings() {
+	global $bp;
+
+	if ( !$group_invite = bp_get_user_meta( $bp->displayed_user->id, 'notification_groups_invite', true ) )
+		$group_invite  = 'yes';
+
+	if ( !$group_update = bp_get_user_meta( $bp->displayed_user->id, 'notification_groups_group_updated', true ) )
+		$group_update  = 'yes';
+
+	if ( !$group_promo = bp_get_user_meta( $bp->displayed_user->id, 'notification_groups_admin_promotion', true ) )
+		$group_promo   = 'yes';
+
+	if ( !$group_request = bp_get_user_meta( $bp->displayed_user->id, 'notification_groups_membership_request', true ) )
+		$group_request = 'yes';
+?>
+
+	<table class="notification-settings" id="groups-notification-settings">
+		<thead>
+			<tr>
+				<th class="icon"></th>
+				<th class="title"><?php _e( 'Groups', 'buddypress' ) ?></th>
+				<th class="yes"><?php _e( 'Yes', 'buddypress' ) ?></th>
+				<th class="no"><?php _e( 'No', 'buddypress' )?></th>
+			</tr>
+		</thead>
+
+		<tbody>
+			<tr id="groups-notification-settings-invitation">
+				<td></td>
+				<td><?php _e( 'A member invites you to join a group', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_groups_invite]" value="yes" <?php checked( $group_invite, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_groups_invite]" value="no" <?php checked( $group_invite, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="groups-notification-settings-info-updated">
+				<td></td>
+				<td><?php _e( 'Group information is updated', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_groups_group_updated]" value="yes" <?php checked( $group_update, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_groups_group_updated]" value="no" <?php checked( $group_update, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="groups-notification-settings-promoted">
+				<td></td>
+				<td><?php _e( 'You are promoted to a group administrator or moderator', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_groups_admin_promotion]" value="yes" <?php checked( $group_promo, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_groups_admin_promotion]" value="no" <?php checked( $group_promo, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="groups-notification-settings-request">
+				<td></td>
+				<td><?php _e( 'A member requests to join a private group for which you are an admin', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_groups_membership_request]" value="yes" <?php checked( $group_request, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_groups_membership_request]" value="no" <?php checked( $group_request, 'no', true ) ?>/></td>
+			</tr>
+
+			<?php do_action( 'groups_screen_notification_settings' ); ?>
+
+		</tbody>
+	</table>
+
+<?php
+}
+add_action( 'bp_notification_settings', 'groups_screen_notification_settings' );
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-template.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-template.php	(revision 5057)
@@ -0,0 +1,2860 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the groups component slug
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ *
+ * @uses bp_get_groups_slug()
+ */
+function bp_groups_slug() {
+	echo bp_get_groups_slug();
+}
+	/**
+	 * Return the groups component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Groups Template
+	 * @since 1.5
+	 */
+	function bp_get_groups_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_groups_slug', $bp->groups->slug );
+	}
+
+/**
+ * Output the groups component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ *
+ * @uses bp_get_groups_root_slug()
+ */
+function bp_groups_root_slug() {
+	echo bp_get_groups_root_slug();
+}
+	/**
+	 * Return the groups component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Groups Template
+	 * @since 1.5
+	 */
+	function bp_get_groups_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_groups_root_slug', $bp->groups->root_slug );
+	}
+
+/**
+ * Output group directory permalink
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ * @uses bp_get_groups_directory_permalink()
+ */
+function bp_groups_directory_permalink() {
+	echo bp_get_groups_directory_permalink();
+}
+	/**
+	 * Return group directory permalink
+	 *
+	 * @package BuddyPress
+	 * @subpackage Groups Template
+	 * @since 1.5
+	 * @uses apply_filters()
+	 * @uses traisingslashit()
+	 * @uses bp_get_root_domain()
+	 * @uses bp_get_groups_root_slug()
+	 * @return string
+	 */
+	function bp_get_groups_directory_permalink() {
+		return apply_filters( 'bp_get_groups_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ) );
+	}
+
+/*****************************************************************************
+ * Groups Template Class/Tags
+ **/
+
+class BP_Groups_Template {
+	var $current_group = -1;
+	var $group_count;
+	var $groups;
+	var $group;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_group_count;
+
+	var $single_group = false;
+
+	var $sort_by;
+	var $order;
+
+	function bp_groups_template( $user_id, $type, $page, $per_page, $max, $slug, $search_terms, $populate_extras, $include = false, $exclude = false, $show_hidden = false ) {
+		$this->__construct( $user_id, $type, $page, $per_page, $max, $slug, $search_terms, $include, $populate_extras, $exclude, $show_hidden );
+	}
+
+	function __construct( $user_id, $type, $page, $per_page, $max, $slug, $search_terms, $populate_extras, $include = false, $exclude = false, $show_hidden = false ){
+
+		global $bp;
+
+		$this->pag_page = isset( $_REQUEST['grpage'] ) ? intval( $_REQUEST['grpage'] ) : $page;
+		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		if ( $bp->loggedin_user->is_super_admin || ( is_user_logged_in() && $user_id == $bp->loggedin_user->id ) )
+			$show_hidden = true;
+
+		if ( 'invites' == $type ) {
+			$this->groups = groups_get_invites_for_user( $user_id, $this->pag_num, $this->pag_page, $exclude );
+		} else if ( 'single-group' == $type ) {
+			$group           = new stdClass;
+			$group->group_id = BP_Groups_Group::get_id_from_slug( $slug );
+			$this->groups    = array( $group );
+		} else {
+			$this->groups = groups_get_groups( array(
+				'type'            => $type,
+				'per_page'        => $this->pag_num,
+				'page'            => $this->pag_page,
+				'user_id'         => $user_id,
+				'search_terms'    => $search_terms,
+				'include'         => $include,
+				'exclude'         => $exclude,
+				'populate_extras' => $populate_extras,
+				'show_hidden'     => $show_hidden
+			) );
+		}
+
+		if ( 'invites' == $type ) {
+			$this->total_group_count = (int)$this->groups['total'];
+			$this->group_count       = (int)$this->groups['total'];
+			$this->groups            = $this->groups['groups'];
+		} else if ( 'single-group' == $type ) {
+			$this->single_group      = true;
+			$this->total_group_count = 1;
+			$this->group_count       = 1;
+		} else {
+			if ( empty( $max ) || $max >= (int)$this->groups['total'] ) {
+				$this->total_group_count = (int)$this->groups['total'];
+			} else {
+				$this->total_group_count = (int)$max;
+			}
+
+			$this->groups = $this->groups['groups'];
+
+			if ( !empty( $max ) ) {
+				if ( $max >= count( $this->groups ) ) {
+					$this->group_count = count( $this->groups );
+				} else {
+					$this->group_count = (int)$max;
+				}
+			} else {
+				$this->group_count = count( $this->groups );
+			}
+		}
+
+		// Build pagination links
+		if ( (int)$this->total_group_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( array( 'grpage' => '%#%', 'num' => $this->pag_num, 's' => $search_terms, 'sortby' => $this->sort_by, 'order' => $this->order ) ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_group_count / (int)$this->pag_num ),
+				'current'   => $this->pag_page,
+				'prev_text' => _x( '&larr;', 'Group pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Group pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+		}
+	}
+
+	function has_groups() {
+		if ( $this->group_count )
+			return true;
+
+		return false;
+	}
+
+	function next_group() {
+		$this->current_group++;
+		$this->group = $this->groups[$this->current_group];
+
+		return $this->group;
+	}
+
+	function rewind_groups() {
+		$this->current_group = -1;
+		if ( $this->group_count > 0 ) {
+			$this->group = $this->groups[0];
+		}
+	}
+
+	function groups() {
+		if ( $this->current_group + 1 < $this->group_count ) {
+			return true;
+		} elseif ( $this->current_group + 1 == $this->group_count ) {
+			do_action('group_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_groups();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_group() {
+		global $group;
+
+		$this->in_the_loop = true;
+		$this->group = $this->next_group();
+
+		if ( $this->single_group )
+			$this->group = new BP_Groups_Group( $this->group->group_id );
+
+		if ( 0 == $this->current_group ) // loop has just started
+			do_action('group_loop_start');
+	}
+}
+
+function bp_has_groups( $args = '' ) {
+	global $groups_template, $bp;
+
+	/***
+	 * Set the defaults based on the current page. Any of these will be overridden
+	 * if arguments are directly passed into the loop. Custom plugins should always
+	 * pass their parameters directly to the loop.
+	 */
+	$slug    = false;
+	$type    = 'active';
+	$user_id = 0;
+	$order   = '';
+
+	// User filtering
+	if ( !empty( $bp->displayed_user->id ) )
+		$user_id = $bp->displayed_user->id;
+
+	// Type
+	if ( 'my-groups' == $bp->current_action ) {
+		if ( 'most-popular' == $order ) {
+			$type = 'popular';
+		} elseif ( 'alphabetically' == $order ) {
+			$type = 'alphabetical';
+		}
+	} elseif ( 'invites' == $bp->current_action ) {
+		$type = 'invites';
+	} elseif ( isset( $bp->groups->current_group->slug ) && $bp->groups->current_group->slug ) {
+		$type = 'single-group';
+		$slug = $bp->groups->current_group->slug;
+	}
+
+	$defaults = array(
+		'type'            => $type,
+		'page'            => 1,
+		'per_page'        => 20,
+		'max'             => false,
+		'show_hidden'     => false,
+
+		'user_id'         => $user_id, // Pass a user ID to limit to groups this user has joined
+		'slug'            => $slug,    // Pass a group slug to only return that group
+		'search_terms'    => '',       // Pass search terms to return only matching groups
+		'include'         => false,    // Pass comma separated list or array of group ID's to return only these groups
+		'exclude'         => false,    // Pass comma separated list or array of group ID's to exclude these groups
+
+		'populate_extras' => true      // Get extra meta - is_member, is_banned
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	if ( is_null( $search_terms ) ) {
+		if ( isset( $_REQUEST['group-filter-box'] ) && !empty( $_REQUEST['group-filter-box'] ) )
+			$search_terms = $_REQUEST['group-filter-box'];
+		elseif ( isset( $_REQUEST['s'] ) && !empty( $_REQUEST['s'] ) )
+			$search_terms = $_REQUEST['s'];
+		else
+			$search_terms = false;
+	}
+
+	$groups_template = new BP_Groups_Template( (int)$user_id, $type, (int)$page, (int)$per_page, (int)$max, $slug, $search_terms, (bool)$populate_extras, $include, $exclude, $show_hidden );
+	return apply_filters( 'bp_has_groups', $groups_template->has_groups(), $groups_template );
+}
+
+function bp_groups() {
+	global $groups_template;
+	return $groups_template->groups();
+}
+
+function bp_the_group() {
+	global $groups_template;
+	return $groups_template->the_group();
+}
+
+function bp_group_is_visible( $group = false ) {
+	global $bp, $groups_template;
+
+	if ( $bp->loggedin_user->is_super_admin )
+		return true;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( 'public' == $group->status ) {
+		return true;
+	} else {
+		if ( groups_is_user_member( $bp->loggedin_user->id, $group->id ) ) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+function bp_group_id() {
+	echo bp_get_group_id();
+}
+	function bp_get_group_id( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_id', $group->id );
+	}
+
+function bp_group_name() {
+	echo bp_get_group_name();
+}
+	function bp_get_group_name( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_name', $group->name );
+	}
+
+function bp_group_type() {
+	echo bp_get_group_type();
+}
+	function bp_get_group_type( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( 'public' == $group->status ) {
+			$type = __( "Public Group", "buddypress" );
+		} else if ( 'hidden' == $group->status ) {
+			$type = __( "Hidden Group", "buddypress" );
+		} else if ( 'private' == $group->status ) {
+			$type = __( "Private Group", "buddypress" );
+		} else {
+			$type = ucwords( $group->status ) . ' ' . __( 'Group', 'buddypress' );
+		}
+
+		return apply_filters( 'bp_get_group_type', $type );
+	}
+
+function bp_group_status() {
+	echo bp_get_group_status();
+}
+	function bp_get_group_status( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_status', $group->status );
+	}
+
+function bp_group_avatar( $args = '' ) {
+	echo bp_get_group_avatar( $args );
+}
+	function bp_get_group_avatar( $args = '' ) {
+		global $bp, $groups_template;
+
+		$defaults = array(
+			'type' => 'full',
+			'width' => false,
+			'height' => false,
+			'class' => 'avatar',
+			'id' => false,
+			'alt' => __( 'Group logo of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		/* Fetch the avatar from the folder, if not provide backwards compat. */
+		if ( !$avatar = bp_core_fetch_avatar( array( 'item_id' => $groups_template->group->id, 'object' => 'group', 'type' => $type, 'avatar_dir' => 'group-avatars', 'alt' => $alt, 'css_id' => $id, 'class' => $class, 'width' => $width, 'height' => $height ) ) )
+			$avatar = '<img src="' . esc_attr( $groups_template->group->avatar_thumb ) . '" class="avatar" alt="' . esc_attr( $groups_template->group->name ) . '" />';
+
+		return apply_filters( 'bp_get_group_avatar', $avatar );
+	}
+
+function bp_group_avatar_thumb() {
+	echo bp_get_group_avatar_thumb();
+}
+	function bp_get_group_avatar_thumb( $group = false ) {
+		return bp_get_group_avatar( 'type=thumb' );
+	}
+
+function bp_group_avatar_mini() {
+	echo bp_get_group_avatar_mini();
+}
+	function bp_get_group_avatar_mini( $group = false ) {
+		return bp_get_group_avatar( 'type=thumb&width=30&height=30' );
+	}
+
+function bp_group_last_active() {
+	echo bp_get_group_last_active();
+}
+	function bp_get_group_last_active( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		$last_active = $group->last_activity;
+
+		if ( !$last_active )
+			$last_active = groups_get_groupmeta( $group->id, 'last_activity' );
+
+		if ( empty( $last_active ) ) {
+			return __( 'not yet active', 'buddypress' );
+		} else {
+			return apply_filters( 'bp_get_group_last_active', bp_core_time_since( $last_active ) );
+		}
+	}
+
+function bp_group_permalink() {
+	echo bp_get_group_permalink();
+}
+	function bp_get_group_permalink( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_permalink', bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug . '/' );
+	}
+
+function bp_group_admin_permalink() {
+	echo bp_get_group_admin_permalink();
+}
+	function bp_get_group_admin_permalink( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_admin_permalink', bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug . '/admin' );
+	}
+
+function bp_group_slug() {
+	echo bp_get_group_slug();
+}
+	function bp_get_group_slug( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_slug', $group->slug );
+	}
+
+function bp_group_description() {
+	echo bp_get_group_description();
+}
+	function bp_get_group_description( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_description', stripslashes($group->description) );
+	}
+
+function bp_group_description_editable() {
+	echo bp_get_group_description_editable();
+}
+	function bp_get_group_description_editable( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_description_editable', $group->description );
+	}
+
+function bp_group_description_excerpt() {
+	echo bp_get_group_description_excerpt();
+}
+	function bp_get_group_description_excerpt( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_description_excerpt', bp_create_excerpt( $group->description ) );
+	}
+
+
+function bp_group_public_status() {
+	echo bp_get_group_public_status();
+}
+	function bp_get_group_public_status( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( $group->is_public ) {
+			return __( 'Public', 'buddypress' );
+		} else {
+			return __( 'Private', 'buddypress' );
+		}
+	}
+
+function bp_group_is_public() {
+	echo bp_get_group_is_public();
+}
+	function bp_get_group_is_public( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_is_public', $group->is_public );
+	}
+
+function bp_group_date_created() {
+	echo bp_get_group_date_created();
+}
+	function bp_get_group_date_created( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_date_created', bp_core_time_since( strtotime( $group->date_created ) ) );
+	}
+
+function bp_group_is_admin() {
+	global $bp;
+
+	return $bp->is_item_admin;
+}
+
+function bp_group_is_mod() {
+	global $bp;
+
+	return $bp->is_item_mod;
+}
+
+function bp_group_list_admins( $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( $group->admins ) { ?>
+		<ul id="group-admins">
+			<?php foreach( (array)$group->admins as $admin ) { ?>
+				<li>
+					<a href="<?php echo bp_core_get_user_domain( $admin->user_id, $admin->user_nicename, $admin->user_login ) ?>"><?php echo bp_core_fetch_avatar( array( 'item_id' => $admin->user_id, 'email' => $admin->user_email, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?></a>
+				</li>
+			<?php } ?>
+		</ul>
+	<?php } else { ?>
+		<span class="activity"><?php _e( 'No Admins', 'buddypress' ) ?></span>
+	<?php } ?>
+<?php
+}
+
+function bp_group_list_mods( $group = false ) {
+	global $groups_template;
+
+	if ( empty( $group ) )
+		$group =& $groups_template->group;
+
+	if ( !empty( $group->mods ) ) : ?>
+
+		<ul id="group-mods">
+
+			<?php foreach( (array)$group->mods as $mod ) { ?>
+
+				<li>
+					<a href="<?php echo bp_core_get_user_domain( $mod->user_id, $mod->user_nicename, $mod->user_login ) ?>"><?php echo bp_core_fetch_avatar( array( 'item_id' => $mod->user_id, 'email' => $mod->user_email, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?></a>
+				</li>
+
+			<?php } ?>
+
+		</ul>
+
+<?php else : ?>
+
+		<span class="activity"><?php _e( 'No Mods', 'buddypress' ) ?></span>
+
+<?php endif;
+
+}
+
+/**
+ * Return a list of user_ids for a group's admins
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param obj $group (optional) The group being queried. Defaults to the current group in the loop
+ * @param str $format 'string' to get a comma-separated string, 'array' to get an array
+ * @return mixed $admin_ids A string or array of user_ids
+ */
+function bp_group_admin_ids( $group = false, $format = 'string' ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	$admin_ids = array();
+
+	if ( $group->admins ) {
+		foreach( $group->admins as $admin ) {
+			$admin_ids[] = $admin->user_id;
+		}
+	}
+
+	if ( 'string' == $format )
+		$admin_ids = implode( ',', $admin_ids );
+
+	return apply_filters( 'bp_group_admin_ids', $admin_ids );
+}
+
+/**
+ * Return a list of user_ids for a group's moderators
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @param obj $group (optional) The group being queried. Defaults to the current group in the loop
+ * @param str $format 'string' to get a comma-separated string, 'array' to get an array
+ * @return mixed $mod_ids A string or array of user_ids
+ */
+function bp_group_mod_ids( $group = false, $format = 'string' ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	$mod_ids = array();
+
+	if ( $group->mods ) {
+		foreach( $group->mods as $mod ) {
+			$mod_ids[] = $mod->user_id;
+		}
+	}
+
+	if ( 'string' == $format )
+		$mod_ids = implode( ',', $mod_ids );
+
+	return apply_filters( 'bp_group_mod_ids', $mod_ids );
+}
+
+function bp_group_all_members_permalink() {
+	echo bp_get_group_all_members_permalink();
+}
+	function bp_get_group_all_members_permalink( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_all_members_permalink', bp_get_group_permalink( $group ) . 'members' );
+	}
+
+function bp_group_search_form() {
+	global $groups_template, $bp;
+
+	$action = $bp->displayed_user->domain . bp_get_groups_slug() . '/my-groups/search/';
+	$label = __('Filter Groups', 'buddypress');
+	$name = 'group-filter-box';
+
+?>
+	<form action="<?php echo $action ?>" id="group-search-form" method="post">
+		<label for="<?php echo $name ?>" id="<?php echo $name ?>-label"><?php echo $label ?></label>
+		<input type="search" name="<?php echo $name ?>" id="<?php echo $name ?>" value="<?php echo $value ?>"<?php echo $disabled ?> />
+
+		<?php wp_nonce_field( 'group-filter-box', '_wpnonce_group_filter' ) ?>
+	</form>
+<?php
+}
+
+function bp_group_show_no_groups_message() {
+	global $bp;
+
+	if ( !groups_total_groups_for_user( $bp->displayed_user->id ) )
+		return true;
+
+	return false;
+}
+
+function bp_group_is_activity_permalink() {
+
+	if ( !bp_is_single_item() || !bp_is_groups_component() || !bp_is_current_action( bp_get_activity_slug() ) )
+		return false;
+
+	return true;
+}
+
+function bp_groups_pagination_links() {
+	echo bp_get_groups_pagination_links();
+}
+	function bp_get_groups_pagination_links() {
+		global $groups_template;
+
+		return apply_filters( 'bp_get_groups_pagination_links', $groups_template->pag_links );
+	}
+
+function bp_groups_pagination_count() {
+	echo bp_get_groups_pagination_count();
+}
+	function bp_get_groups_pagination_count() {
+		global $bp, $groups_template;
+
+		$start_num = intval( ( $groups_template->pag_page - 1 ) * $groups_template->pag_num ) + 1;
+		$from_num = bp_core_number_format( $start_num );
+		$to_num = bp_core_number_format( ( $start_num + ( $groups_template->pag_num - 1 ) > $groups_template->total_group_count ) ? $groups_template->total_group_count : $start_num + ( $groups_template->pag_num - 1 ) );
+		$total = bp_core_number_format( $groups_template->total_group_count );
+
+		return apply_filters( 'bp_get_groups_pagination_count', sprintf( __( 'Viewing group %1$s to %2$s (of %3$s groups)', 'buddypress' ), $from_num, $to_num, $total ) );
+	}
+
+function bp_groups_auto_join() {
+	global $bp;
+
+	return apply_filters( 'bp_groups_auto_join', (bool)$bp->groups->auto_join );
+}
+
+function bp_group_total_members( $group = false ) {
+	echo bp_get_group_total_members( $group );
+}
+	function bp_get_group_total_members( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_total_members', $group->total_member_count );
+	}
+
+function bp_group_member_count() {
+	echo bp_get_group_member_count();
+}
+	function bp_get_group_member_count() {
+		global $groups_template;
+
+		if ( 1 == (int) $groups_template->group->total_member_count )
+			return apply_filters( 'bp_get_group_member_count', sprintf( __( '%s member', 'buddypress' ), bp_core_number_format( $groups_template->group->total_member_count ) ) );
+		else
+			return apply_filters( 'bp_get_group_member_count', sprintf( __( '%s members', 'buddypress' ), bp_core_number_format( $groups_template->group->total_member_count ) ) );
+	}
+
+function bp_group_forum_permalink() {
+	echo bp_get_group_forum_permalink();
+}
+	function bp_get_group_forum_permalink( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_forum_permalink', bp_get_group_permalink( $group ) . 'forum' );
+	}
+
+function bp_group_forum_topic_count( $args = '' ) {
+	echo bp_get_group_forum_topic_count( $args );
+}
+	function bp_get_group_forum_topic_count( $args = '' ) {
+		global $groups_template;
+
+		$defaults = array(
+			'showtext' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		if ( !$forum_id = groups_get_groupmeta( $groups_template->group->id, 'forum_id' ) )
+			return false;
+
+		if ( !bp_is_active( 'forums' ) )
+			return false;
+
+		if ( !$groups_template->group->forum_counts )
+			$groups_template->group->forum_counts = bp_forums_get_forum_topicpost_count( (int)$forum_id );
+
+		if ( (bool) $showtext ) {
+			if ( 1 == (int) $groups_template->group->forum_counts[0]->topics )
+				$total_topics = sprintf( __( '%d topic', 'buddypress' ), (int) $groups_template->group->forum_counts[0]->topics );
+			else
+				$total_topics = sprintf( __( '%d topics', 'buddypress' ), (int) $groups_template->group->forum_counts[0]->topics );
+		} else {
+			$total_topics = (int) $groups_template->group->forum_counts[0]->topics;
+		}
+
+		return apply_filters( 'bp_get_group_forum_topic_count', $total_topics, (bool)$showtext );
+	}
+
+function bp_group_forum_post_count( $args = '' ) {
+	echo bp_get_group_forum_post_count( $args );
+}
+	function bp_get_group_forum_post_count( $args = '' ) {
+		global $groups_template;
+
+		$defaults = array(
+			'showtext' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		if ( !$forum_id = groups_get_groupmeta( $groups_template->group->id, 'forum_id' ) )
+			return false;
+
+		if ( !bp_is_active( 'forums' ) )
+			return false;
+
+		if ( !$groups_template->group->forum_counts )
+			$groups_template->group->forum_counts = bp_forums_get_forum_topicpost_count( (int)$forum_id );
+
+		if ( (bool) $showtext ) {
+			if ( 1 == (int) $groups_template->group->forum_counts[0]->posts )
+				$total_posts = sprintf( __( '%d post', 'buddypress' ), (int) $groups_template->group->forum_counts[0]->posts );
+			else
+				$total_posts = sprintf( __( '%d posts', 'buddypress' ), (int) $groups_template->group->forum_counts[0]->posts );
+		} else {
+			$total_posts = (int) $groups_template->group->forum_counts[0]->posts;
+		}
+
+		return apply_filters( 'bp_get_group_forum_post_count', $total_posts, (bool)$showtext );
+	}
+
+function bp_group_is_forum_enabled( $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( bp_is_active( 'forums' ) ) {
+		if ( bp_forums_is_installed_correctly() ) {
+			if ( $group->enable_forum )
+				return true;
+
+			return false;
+		} else {
+			return false;
+		}
+	}
+
+	return false;
+}
+
+function bp_group_show_forum_setting( $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( $group->enable_forum )
+		echo ' checked="checked"';
+}
+
+function bp_group_show_status_setting( $setting, $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( $setting == $group->status )
+		echo ' checked="checked"';
+}
+
+/**
+ * Get the 'checked' value, if needed, for a given invite_status on the group create/admin screens
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ *
+ * @param str $setting The setting you want to check against ('members', 'mods', or 'admins')
+ * @param obj $group (optional) The group whose status you want to check
+ */
+function bp_group_show_invite_status_setting( $setting, $group = false ) {
+	$group_id = isset( $group->id ) ? $group->id : false;
+
+	$invite_status = bp_group_get_invite_status( $group_id );
+
+	if ( $setting == $invite_status )
+		echo ' checked="checked"';
+}
+
+/**
+ * Get the invite status of a group
+ *
+ * 'invite_status' became part of BuddyPress in BP 1.5. In order to provide backward compatibility,
+ * groups without a status set will default to 'members', ie all members in a group can send
+ * invitations. Filter 'bp_group_invite_status_fallback' to change this fallback behavior.
+ *
+ * This function can be used either in or out of the loop.
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ *
+ * @param int $group_id (optional) The id of the group whose status you want to check
+ * @return mixed Returns false when no group can be found. Otherwise returns the group invite
+ *    status, from among 'members', 'mods', and 'admins'
+ */
+function bp_group_get_invite_status( $group_id = false ) {
+	global $bp, $groups_template;
+
+	if ( !$group_id ) {
+		if ( isset( $bp->groups->current_group->id ) ) {
+			// Default to the current group first
+			$group_id = $bp->groups->current_group->id;
+		} else if ( isset( $groups_template->group->id ) ) {
+			// Then see if we're in the loop
+			$group_id = $groups_template->group->id;
+		} else {
+			return false;
+		}
+	}
+
+	$invite_status = groups_get_groupmeta( $group_id, 'invite_status' );
+
+	// Backward compatibility. When 'invite_status' is not set, fall back to a default value
+	if ( !$invite_status ) {
+		$invite_status = apply_filters( 'bp_group_invite_status_fallback', 'members' );
+	}
+
+	return apply_filters( 'bp_group_get_invite_status', $invite_status, $group_id );
+}
+
+/**
+ * Can the logged-in user send invitations in the specified group?
+ *
+ * @package BuddyPress
+ * @subpackage Groups Template
+ * @since 1.5
+ *
+ * @param int $group_id (optional) The id of the group whose status you want to check
+ * @return bool $can_send_invites
+ */
+function bp_groups_user_can_send_invites( $group_id = false ) {
+	global $bp;
+
+	$can_send_invites = false;
+	$invite_status    = false;
+
+	if ( is_user_logged_in() ) {
+		if ( is_super_admin() ) {
+			// Super admins can always send invitations
+			$can_send_invites = true;
+
+		} else {
+			// If no $group_id is provided, default to the current group id
+			if ( !$group_id )
+				$group_id = isset( $bp->groups->current_group->id ) ? $bp->groups->current_group->id : 0;
+
+			// If no group has been found, bail
+			if ( !$group_id )
+				return false;
+
+			$invite_status = bp_group_get_invite_status( $group_id );
+			if ( !$invite_status )
+				return false;
+
+			switch ( $invite_status ) {
+				case 'admins' :
+					if ( groups_is_user_admin( bp_loggedin_user_id(), $group_id ) )
+						$can_send_invites = true;
+					break;
+
+				case 'mods' :
+					if ( groups_is_user_mod( bp_loggedin_user_id(), $group_id ) || groups_is_user_admin( bp_loggedin_user_id(), $group_id ) )
+						$can_send_invites = true;
+					break;
+
+				case 'members' :
+					if ( groups_is_user_member( bp_loggedin_user_id(), $group_id ) )
+						$can_send_invites = true;
+					break;
+			}
+		}
+	}
+
+	return apply_filters( 'bp_groups_user_can_send_invites', $can_send_invites, $group_id, $invite_status );
+}
+
+/**
+ * Since BuddyPress 1.0, this generated the group settings admin/member screen.
+ * As of BuddyPress 1.5 (r4489), and because this function outputs HTML, it was moved into /bp-default/groups/single/admin.php.
+ *
+ * @deprecated 1.5
+ * @deprecated No longer used.
+ * @since 1.0
+ * @todo Remove in 1.4
+ */
+function bp_group_admin_memberlist( $admin_list = false, $group = false ) {
+	global $groups_template;
+
+	_deprecated_function( __FUNCTION__, '1.5', 'No longer used. See /bp-default/groups/single/admin.php' );
+
+	if ( empty( $group ) )
+		$group =& $groups_template->group;
+
+
+	if ( $admins = groups_get_group_admins( $group->id ) ) : ?>
+
+		<ul id="admins-list" class="item-list<?php if ( !empty( $admin_list ) ) : ?> single-line<?php endif; ?>">
+
+		<?php foreach ( (array)$admins as $admin ) { ?>
+
+			<?php if ( !empty( $admin_list ) ) : ?>
+
+			<li>
+
+				<?php echo bp_core_fetch_avatar( array( 'item_id' => $admin->user_id, 'type' => 'thumb', 'width' => 30, 'height' => 30, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+
+				<h5>
+
+					<?php echo bp_core_get_userlink( $admin->user_id ); ?>
+
+					<span class="small">
+						<a class="button confirm admin-demote-to-member" href="<?php bp_group_member_demote_link($admin->user_id) ?>"><?php _e( 'Demote to Member', 'buddypress' ) ?></a>
+					</span>
+				</h5>
+			</li>
+
+			<?php else : ?>
+
+			<li>
+
+				<?php echo bp_core_fetch_avatar( array( 'item_id' => $admin->user_id, 'type' => 'thumb', 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+
+				<h5><?php echo bp_core_get_userlink( $admin->user_id ) ?></h5>
+				<span class="activity">
+					<?php echo bp_core_get_last_activity( strtotime( $admin->date_modified ), __( 'joined %s', 'buddypress') ); ?>
+				</span>
+
+				<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+					<div class="action">
+
+						<?php bp_add_friend_button( $admin->user_id ); ?>
+
+					</div>
+
+				<?php endif; ?>
+
+			</li>
+
+			<?php endif;
+		} ?>
+
+		</ul>
+
+	<?php else : ?>
+
+		<div id="message" class="info">
+			<p><?php _e( 'This group has no administrators', 'buddypress' ); ?></p>
+		</div>
+
+	<?php endif;
+}
+
+function bp_group_mod_memberlist( $admin_list = false, $group = false ) {
+	global $groups_template, $group_mods;
+
+	if ( empty( $group ) )
+		$group =& $groups_template->group;
+
+	if ( $group_mods = groups_get_group_mods( $group->id ) ) { ?>
+
+		<ul id="mods-list" class="item-list<?php if ( $admin_list ) { ?> single-line<?php } ?>">
+
+		<?php foreach ( (array)$group_mods as $mod ) { ?>
+
+			<?php if ( !empty( $admin_list ) ) { ?>
+
+			<li>
+
+				<?php echo bp_core_fetch_avatar( array( 'item_id' => $mod->user_id, 'type' => 'thumb', 'width' => 30, 'height' => 30, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+
+				<h5>
+					<?php echo bp_core_get_userlink( $mod->user_id ); ?>
+
+					<span class="small">
+						<a href="<?php bp_group_member_promote_admin_link( array( 'user_id' => $mod->user_id ) ) ?>" class="button confirm mod-promote-to-admin" title="<?php _e( 'Promote to Admin', 'buddypress' ); ?>"><?php _e( 'Promote to Admin', 'buddypress' ); ?></a>
+						<a class="button confirm mod-demote-to-member" href="<?php bp_group_member_demote_link($mod->user_id) ?>"><?php _e( 'Demote to Member', 'buddypress' ) ?></a>
+					</span>
+				</h5>
+			</li>
+
+			<?php } else { ?>
+
+			<li>
+
+				<?php echo bp_core_fetch_avatar( array( 'item_id' => $mod->user_id, 'type' => 'thumb', 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+
+				<h5><?php echo bp_core_get_userlink( $mod->user_id ) ?></h5>
+
+				<span class="activity"><?php echo bp_core_get_last_activity( strtotime( $mod->date_modified ), __( 'joined %s', 'buddypress') ); ?></span>
+
+				<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+					<div class="action">
+						<?php bp_add_friend_button( $mod->user_id ) ?>
+					</div>
+
+				<?php endif; ?>
+
+			</li>
+
+			<?php } ?>
+		<?php } ?>
+
+		</ul>
+
+	<?php } else { ?>
+
+		<div id="message" class="info">
+			<p><?php _e( 'This group has no moderators', 'buddypress' ); ?></p>
+		</div>
+
+	<?php }
+}
+
+function bp_group_has_moderators( $group = false ) {
+	global $group_mods, $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	return apply_filters( 'bp_group_has_moderators', groups_get_group_mods( $group->id ) );
+}
+
+function bp_group_member_promote_mod_link( $args = '' ) {
+	echo bp_get_group_member_promote_mod_link( $args );
+}
+	function bp_get_group_member_promote_mod_link( $args = '' ) {
+		global $members_template, $groups_template, $bp;
+
+		$defaults = array(
+			'user_id' => $members_template->member->user_id,
+			'group'   => &$groups_template->group
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_group_member_promote_mod_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/promote/mod/' . $user_id, 'groups_promote_member' ) );
+	}
+
+function bp_group_member_promote_admin_link( $args = '' ) {
+	echo bp_get_group_member_promote_admin_link( $args );
+}
+	function bp_get_group_member_promote_admin_link( $args = '' ) {
+		global $members_template, $groups_template, $bp;
+
+		$defaults = array(
+			'user_id' => !empty( $members_template->member->user_id ) ? $members_template->member->user_id : false,
+			'group'   => &$groups_template->group
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_group_member_promote_admin_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/promote/admin/' . $user_id, 'groups_promote_member' ) );
+	}
+
+function bp_group_member_demote_link( $user_id = 0 ) {
+	global $members_template;
+
+	if ( !$user_id )
+		$user_id = $members_template->member->user_id;
+
+	echo bp_get_group_member_demote_link( $user_id );
+}
+	function bp_get_group_member_demote_link( $user_id = 0, $group = false ) {
+		global $members_template, $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( !$user_id )
+			$user_id = $members_template->member->user_id;
+
+		return apply_filters( 'bp_get_group_member_demote_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/demote/' . $user_id, 'groups_demote_member' ) );
+	}
+
+function bp_group_member_ban_link( $user_id = 0 ) {
+	global $members_template;
+
+	if ( !$user_id )
+		$user_id = $members_template->member->user_id;
+
+	echo bp_get_group_member_ban_link( $user_id );
+}
+	function bp_get_group_member_ban_link( $user_id = 0, $group = false ) {
+		global $members_template, $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_member_ban_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/ban/' . $user_id, 'groups_ban_member' ) );
+	}
+
+function bp_group_member_unban_link( $user_id = 0 ) {
+	global $members_template;
+
+	if ( !$user_id )
+		$user_id = $members_template->member->user_id;
+
+	echo bp_get_group_member_unban_link( $user_id );
+}
+	function bp_get_group_member_unban_link( $user_id = 0, $group = false ) {
+		global $members_template, $groups_template;
+
+		if ( !$user_id )
+			$user_id = $members_template->member->user_id;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_member_unban_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/unban/' . $user_id, 'groups_unban_member' ) );
+	}
+
+
+function bp_group_member_remove_link( $user_id = 0 ) {
+	global $members_template;
+
+	if ( !$user_id )
+		$user_id = $members_template->member->user_id;
+
+	echo bp_get_group_member_remove_link( $user_id );
+}
+	function bp_get_group_member_remove_link( $user_id = 0, $group = false ) {
+		global $members_template, $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_member_remove_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'admin/manage-members/remove/' . $user_id, 'groups_remove_member' ) );
+	}
+
+function bp_group_admin_tabs( $group = false ) {
+	global $bp, $groups_template;
+
+	if ( !$group )
+		$group = ( $groups_template->group ) ? $groups_template->group : $bp->groups->current_group;
+
+	$current_tab = bp_action_variable( 0 );
+?>
+	<?php if ( $bp->is_item_admin || $bp->is_item_mod ) { ?>
+		<li<?php if ( 'edit-details' == $current_tab || empty( $current_tab ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/edit-details"><?php _e( 'Details', 'buddypress' ); ?></a></li>
+	<?php } ?>
+
+	<?php
+		if ( !$bp->is_item_admin )
+			return false;
+	?>
+	<li<?php if ( 'group-settings' == $current_tab ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/group-settings"><?php _e( 'Settings', 'buddypress' ); ?></a></li>
+
+	<?php if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) : ?>
+		<li<?php if ( 'group-avatar'   == $current_tab ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/group-avatar"><?php _e( 'Avatar', 'buddypress' ); ?></a></li>
+	<?php endif; ?>
+
+	<li<?php if ( 'manage-members' == $current_tab ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/manage-members"><?php _e( 'Members', 'buddypress' ); ?></a></li>
+
+	<?php if ( $groups_template->group->status == 'private' ) : ?>
+		<li<?php if ( 'membership-requests' == $current_tab ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/membership-requests"><?php _e( 'Requests', 'buddypress' ); ?></a></li>
+	<?php endif; ?>
+
+	<?php do_action( 'groups_admin_tabs', $current_tab, $group->slug ) ?>
+
+	<li<?php if ( 'delete-group' == $current_tab ) : ?> class="current"<?php endif; ?>><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug ?>/admin/delete-group"><?php _e( 'Delete', 'buddypress' ); ?></a></li>
+<?php
+}
+
+function bp_group_total_for_member() {
+	echo bp_get_group_total_for_member();
+}
+	function bp_get_group_total_for_member() {
+		return apply_filters( 'bp_get_group_total_for_member', BP_Groups_Member::total_group_count() );
+	}
+
+function bp_group_form_action( $page ) {
+	echo bp_get_group_form_action( $page );
+}
+	function bp_get_group_form_action( $page, $group = false ) {
+		global $bp, $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_group_form_action', bp_get_group_permalink( $group ) . $page );
+	}
+
+function bp_group_admin_form_action( $page = false ) {
+	echo bp_get_group_admin_form_action( $page );
+}
+	function bp_get_group_admin_form_action( $page = false, $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( !$page )
+			$page = bp_action_variable( 0 );
+
+		return apply_filters( 'bp_group_admin_form_action', bp_get_group_permalink( $group ) . 'admin/' . $page );
+	}
+
+function bp_group_has_requested_membership( $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( groups_check_for_membership_request( bp_loggedin_user_id(), $group->id ) )
+		return true;
+
+	return false;
+}
+
+/**
+ * bp_group_is_member()
+ *
+ * Checks if current user is member of a group.
+ *
+ * @uses is_super_admin Check if current user is super admin
+ * @uses apply_filters Creates bp_group_is_member filter and passes $is_member
+ * @usedby groups/activity.php, groups/single/forum/edit.php, groups/single/forum/topic.php to determine template part visibility
+ * @global array $bp BuddyPress Master global
+ * @global object $groups_template Current Group (usually in template loop)
+ * @param object $group Group to check is_member
+ * @return bool If user is member of group or not
+ */
+function bp_group_is_member( $group = false ) {
+	global $bp, $groups_template;
+
+	// Site admins always have access
+	if ( $bp->loggedin_user->is_super_admin )
+		return true;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	return apply_filters( 'bp_group_is_member', !empty( $group->is_member ) );
+}
+
+/**
+ * Checks if a user is banned from a group.
+ *
+ * If this function is invoked inside the groups template loop (e.g. the group directory), then
+ * check $groups_template->group->is_banned instead of making another SQL query.
+ * However, if used in a single group's pages, we must use groups_is_user_banned().
+ *
+ * @global object $bp BuddyPress global settings
+ * @global BP_Groups_Template $groups_template Group template loop object
+ * @param object $group Group to check if user is banned from the group
+ * @param int $user_id
+ * @return bool If user is banned from the group or not
+ * @since 1.5
+ */
+function bp_group_is_user_banned( $group = false, $user_id = 0 ) {
+	global $bp, $groups_template;
+
+	// Site admins always have access
+	if ( $bp->loggedin_user->is_super_admin )
+		return false;
+
+	if ( !$group ) {
+		$group =& $groups_template->group;
+
+		if ( !$user_id && isset( $group->is_banned ) )
+			return apply_filters( 'bp_group_is_user_banned', $group->is_banned );
+	}
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	return apply_filters( 'bp_group_is_user_banned', groups_is_user_banned( $user_id, $group->id ) );
+}
+
+function bp_group_accept_invite_link() {
+	echo bp_get_group_accept_invite_link();
+}
+	function bp_get_group_accept_invite_link( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_accept_invite_link', wp_nonce_url( $bp->loggedin_user->domain . bp_get_groups_slug() . '/invites/accept/' . $group->id, 'groups_accept_invite' ) );
+	}
+
+function bp_group_reject_invite_link() {
+	echo bp_get_group_reject_invite_link();
+}
+	function bp_get_group_reject_invite_link( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_reject_invite_link', wp_nonce_url( $bp->loggedin_user->domain . bp_get_groups_slug() . '/invites/reject/' . $group->id, 'groups_reject_invite' ) );
+	}
+
+function bp_group_leave_confirm_link() {
+	echo bp_get_group_leave_confirm_link();
+}
+	function bp_get_group_leave_confirm_link( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_group_leave_confirm_link', wp_nonce_url( bp_get_group_permalink( $group ) . 'leave-group/yes', 'groups_leave_group' ) );
+	}
+
+function bp_group_leave_reject_link() {
+	echo bp_get_group_leave_reject_link();
+}
+	function bp_get_group_leave_reject_link( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_get_group_leave_reject_link', bp_get_group_permalink( $group ) );
+	}
+
+function bp_group_send_invite_form_action() {
+	echo bp_get_group_send_invite_form_action();
+}
+	function bp_get_group_send_invite_form_action( $group = false ) {
+		global $groups_template, $bp;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		return apply_filters( 'bp_group_send_invite_form_action', bp_get_group_permalink( $group ) . 'send-invites/send' );
+	}
+
+function bp_has_friends_to_invite( $group = false ) {
+	global $groups_template, $bp;
+
+	if ( !bp_is_active( 'friends' ) )
+		return false;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( !friends_check_user_has_friends( $bp->loggedin_user->id ) || !friends_count_invitable_friends( $bp->loggedin_user->id, $group->id ) )
+		return false;
+
+	return true;
+}
+
+function bp_group_new_topic_button( $group = false ) {
+	echo bp_get_group_new_topic_button();
+}
+	function bp_get_group_new_topic_button( $group = false ) {
+		global $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( !is_user_logged_in() || bp_group_is_user_banned() || !bp_is_group_forum() || bp_is_group_forum_topic() )
+			return false;
+
+		$button = bp_button( array (
+			'id'                => 'new_topic',
+			'component'         => 'groups',
+			'must_be_logged_in' => true,
+			'block_self'        => true,
+			'wrapper_class'     => 'group-button',
+			'link_href'         => '#post-new',
+			'link_class'        => 'group-button show-hide-new',
+			'link_id'           => 'new-topic-button',
+			'link_text'         => __( 'New Topic', 'buddypress' ),
+			'link_title'        => __( 'New Topic', 'buddypress' ),
+		) );
+
+		// Filter and return the HTML button
+		return bp_get_button( apply_filters( 'bp_get_group_new_topic_button', $button ) );
+	}
+
+function bp_group_join_button( $group = false ) {
+	echo bp_get_group_join_button( $group );
+}
+	function bp_get_group_join_button( $group = false ) {
+		global $bp, $groups_template;
+
+		if ( !$group )
+			$group =& $groups_template->group;
+
+		if ( !is_user_logged_in() || bp_group_is_user_banned( $group ) )
+			return false;
+
+		// Group creation was not completed or status is unknown
+		if ( !$group->status )
+			return false;
+
+		// Already a member
+		if ( $group->is_member ) {
+
+			// Stop sole admins from abandoning their group
+	 		$group_admins = groups_get_group_admins( $group->id );
+		 	if ( 1 == count( $group_admins ) && $group_admins[0]->user_id == $bp->loggedin_user->id )
+				return false;
+
+			$button = array(
+				'id'                => 'leave_group',
+				'component'         => 'groups',
+				'must_be_logged_in' => true,
+				'block_self'        => false,
+				'wrapper_class'     => 'group-button ' . $group->status,
+				'wrapper_id'        => 'groupbutton-' . $group->id,
+				'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'leave-group', 'groups_leave_group' ),
+				'link_text'         => __( 'Leave Group', 'buddypress' ),
+				'link_title'        => __( 'Leave Group', 'buddypress' ),
+				'link_class'        => 'group-button leave-group',
+			);
+
+		// Not a member
+		} else {
+
+			// Show different buttons based on group status
+			switch ( $group->status ) {
+				case 'hidden' :
+					return false;
+					break;
+
+				case 'public':
+					$button = array(
+						'id'                => 'join_group',
+						'component'         => 'groups',
+						'must_be_logged_in' => true,
+						'block_self'        => false,
+						'wrapper_class'     => 'group-button ' . $group->status,
+						'wrapper_id'        => 'groupbutton-' . $group->id,
+						'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'join', 'groups_join_group' ),
+						'link_text'         => __( 'Join Group', 'buddypress' ),
+						'link_title'        => __( 'Join Group', 'buddypress' ),
+						'link_class'        => 'group-button join-group',
+					);
+					break;
+
+				case 'private' :
+
+					// Member has not requested membership yet
+					if ( !bp_group_has_requested_membership( $group ) ) {
+						$button = array(
+							'id'                => 'request_membership',
+							'component'         => 'groups',
+							'must_be_logged_in' => true,
+							'block_self'        => false,
+							'wrapper_class'     => 'group-button ' . $group->status,
+							'wrapper_id'        => 'groupbutton-' . $group->id,
+							'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'request-membership', 'groups_request_membership' ),
+							'link_text'         => __( 'Request Membership', 'buddypress' ),
+							'link_title'        => __( 'Request Membership', 'buddypress' ),
+							'link_class'        => 'group-button request-membership',
+						);
+
+					// Member has requested membership already
+					} else {
+						$button = array(
+							'id'                => 'membership_requested',
+							'component'         => 'groups',
+							'must_be_logged_in' => true,
+							'block_self'        => false,
+							'wrapper_class'     => 'group-button pending ' . $group->status,
+							'wrapper_id'        => 'groupbutton-' . $group->id,
+							'link_href'         => bp_get_group_permalink( $group ),
+							'link_text'         => __( 'Request Sent', 'buddypress' ),
+							'link_title'        => __( 'Request Sent', 'buddypress' ),
+							'link_class'        => 'group-button pending membership-requested',
+						);
+					}
+
+					break;
+			}
+		}
+
+		// Filter and return the HTML button
+		return bp_get_button( apply_filters( 'bp_get_group_join_button', $button ) );
+	}
+
+function bp_group_status_message( $group = false ) {
+	global $groups_template;
+
+	if ( !$group )
+		$group =& $groups_template->group;
+
+	if ( 'private' == $group->status ) {
+		if ( !bp_group_has_requested_membership() )
+			if ( is_user_logged_in() )
+				_e( 'This is a private group and you must request group membership in order to join.', 'buddypress' );
+			else
+				_e( 'This is a private group. To join you must be a registered site member and request group membership.', 'buddypress' );
+		else
+			_e( 'This is a private group. Your membership request is awaiting approval from the group administrator.', 'buddypress' );
+	} else {
+		_e( 'This is a hidden group and only invited members can join.', 'buddypress' );
+	}
+}
+
+function bp_group_hidden_fields() {
+	if ( isset( $_REQUEST['s'] ) ) {
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['s'] ) . '" name="search_terms" />';
+	}
+
+	if ( isset( $_REQUEST['letter'] ) ) {
+		echo '<input type="hidden" id="selected_letter" value="' . esc_attr( $_REQUEST['letter'] ) . '" name="selected_letter" />';
+	}
+
+	if ( isset( $_REQUEST['groups_search'] ) ) {
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['groups_search'] ) . '" name="search_terms" />';
+	}
+}
+
+function bp_total_group_count() {
+	echo bp_get_total_group_count();
+}
+	function bp_get_total_group_count() {
+		return apply_filters( 'bp_get_total_group_count', groups_get_total_group_count() );
+	}
+
+function bp_total_group_count_for_user( $user_id = 0 ) {
+	echo bp_get_total_group_count_for_user( $user_id );
+}
+	function bp_get_total_group_count_for_user( $user_id = 0 ) {
+		return apply_filters( 'bp_get_total_group_count_for_user', groups_total_groups_for_user( $user_id ) );
+	}
+
+
+/***************************************************************************
+ * Group Members Template Tags
+ **/
+
+class BP_Groups_Group_Members_Template {
+	var $current_member = -1;
+	var $member_count;
+	var $members;
+	var $member;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_group_count;
+
+	function bp_groups_group_members_template( $group_id, $per_page, $max, $exclude_admins_mods, $exclude_banned, $exclude ) {
+		$this->__construct( $group_id, $per_page, $max, $exclude_admins_mods, $exclude_banned, $exclude );
+	}
+
+	function __construct( $group_id, $per_page, $max, $exclude_admins_mods, $exclude_banned, $exclude ) {
+		global $bp;
+
+		$this->pag_page = isset( $_REQUEST['mlpage'] ) ? intval( $_REQUEST['mlpage'] ) : 1;
+		$this->pag_num = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		$this->members = BP_Groups_Member::get_all_for_group( $group_id, $this->pag_num, $this->pag_page, $exclude_admins_mods, $exclude_banned, $exclude );
+
+		if ( !$max || $max >= (int)$this->members['count'] )
+			$this->total_member_count = (int)$this->members['count'];
+		else
+			$this->total_member_count = (int)$max;
+
+		$this->members = $this->members['members'];
+
+		if ( $max ) {
+			if ( $max >= count($this->members) )
+				$this->member_count = count($this->members);
+			else
+				$this->member_count = (int)$max;
+		} else {
+			$this->member_count = count($this->members);
+		}
+
+		$this->pag_links = paginate_links( array(
+			'base' => add_query_arg( 'mlpage', '%#%' ),
+			'format' => '',
+			'total' => ceil( $this->total_member_count / $this->pag_num ),
+			'current' => $this->pag_page,
+			'prev_text' => '&larr;',
+			'next_text' => '&rarr;',
+			'mid_size' => 1
+		));
+	}
+
+	function has_members() {
+		if ( $this->member_count )
+			return true;
+
+		return false;
+	}
+
+	function next_member() {
+		$this->current_member++;
+		$this->member = $this->members[$this->current_member];
+
+		return $this->member;
+	}
+
+	function rewind_members() {
+		$this->current_member = -1;
+		if ( $this->member_count > 0 ) {
+			$this->member = $this->members[0];
+		}
+	}
+
+	function members() {
+		if ( $this->current_member + 1 < $this->member_count ) {
+			return true;
+		} elseif ( $this->current_member + 1 == $this->member_count ) {
+			do_action('loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_members();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_member() {
+		global $member;
+
+		$this->in_the_loop = true;
+		$this->member = $this->next_member();
+
+		if ( 0 == $this->current_member ) // loop has just started
+			do_action('loop_start');
+	}
+}
+
+function bp_group_has_members( $args = '' ) {
+	global $bp, $members_template;
+
+	$defaults = array(
+		'group_id' => bp_get_current_group_id(),
+		'per_page' => 20,
+		'max' => false,
+		'exclude' => false,
+		'exclude_admins_mods' => 1,
+		'exclude_banned' => 1
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	$members_template = new BP_Groups_Group_Members_Template( $group_id, $per_page, $max, (int)$exclude_admins_mods, (int)$exclude_banned, $exclude );
+	return apply_filters( 'bp_group_has_members', $members_template->has_members(), $members_template );
+}
+
+function bp_group_members() {
+	global $members_template;
+
+	return $members_template->members();
+}
+
+function bp_group_the_member() {
+	global $members_template;
+
+	return $members_template->the_member();
+}
+
+function bp_group_member_avatar() {
+	echo bp_get_group_member_avatar();
+}
+	function bp_get_group_member_avatar() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_avatar', bp_core_fetch_avatar( array( 'item_id' => $members_template->member->user_id, 'type' => 'full', 'email' => $members_template->member->user_email, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) );
+	}
+
+function bp_group_member_avatar_thumb() {
+	echo bp_get_group_member_avatar_thumb();
+}
+	function bp_get_group_member_avatar_thumb() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_avatar_thumb', bp_core_fetch_avatar( array( 'item_id' => $members_template->member->user_id, 'type' => 'thumb', 'email' => $members_template->member->user_email, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) );
+	}
+
+function bp_group_member_avatar_mini( $width = 30, $height = 30 ) {
+	echo bp_get_group_member_avatar_mini( $width, $height );
+}
+	function bp_get_group_member_avatar_mini( $width = 30, $height = 30 ) {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_avatar_mini', bp_core_fetch_avatar( array( 'item_id' => $members_template->member->user_id, 'type' => 'thumb', 'width' => $width, 'height' => $height, 'email' => $members_template->member->user_email, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) );
+	}
+
+function bp_group_member_name() {
+	echo bp_get_group_member_name();
+}
+	function bp_get_group_member_name() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_name', $members_template->member->display_name );
+	}
+
+function bp_group_member_url() {
+	echo bp_get_group_member_url();
+}
+	function bp_get_group_member_url() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_url', bp_core_get_user_domain( $members_template->member->user_id, $members_template->member->user_nicename, $members_template->member->user_login ) );
+	}
+
+function bp_group_member_link() {
+	echo bp_get_group_member_link();
+}
+	function bp_get_group_member_link() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_link', '<a href="' . bp_core_get_user_domain( $members_template->member->user_id, $members_template->member->user_nicename, $members_template->member->user_login ) . '">' . $members_template->member->display_name . '</a>' );
+	}
+
+function bp_group_member_domain() {
+	echo bp_get_group_member_domain();
+}
+	function bp_get_group_member_domain() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_domain', bp_core_get_user_domain( $members_template->member->user_id, $members_template->member->user_nicename, $members_template->member->user_login ) );
+	}
+
+function bp_group_member_is_friend() {
+	echo bp_get_group_member_is_friend();
+}
+	function bp_get_group_member_is_friend() {
+		global $members_template;
+
+		if ( !isset( $members_template->member->is_friend ) )
+			$friend_status = 'not_friends';
+		else
+			$friend_status = ( 0 == $members_template->member->is_friend ) ? 'pending' : 'is_friend';
+
+		return apply_filters( 'bp_get_group_member_is_friend', $friend_status );
+	}
+
+function bp_group_member_is_banned() {
+	echo bp_get_group_member_is_banned();
+}
+	function bp_get_group_member_is_banned() {
+		global $members_template, $groups_template;
+
+		return apply_filters( 'bp_get_group_member_is_banned', $members_template->member->is_banned );
+	}
+
+function bp_group_member_css_class() {
+	global $members_template;
+
+	if ( $members_template->member->is_banned )
+		echo apply_filters( 'bp_group_member_css_class', 'banned-user' );
+}
+
+function bp_group_member_joined_since() {
+	echo bp_get_group_member_joined_since();
+}
+	function bp_get_group_member_joined_since() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_joined_since', bp_core_get_last_activity( $members_template->member->date_modified, __( 'joined %s', 'buddypress') ) );
+	}
+
+function bp_group_member_id() {
+	echo bp_get_group_member_id();
+}
+	function bp_get_group_member_id() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_group_member_id', $members_template->member->user_id );
+	}
+
+function bp_group_member_needs_pagination() {
+	global $members_template;
+
+	if ( $members_template->total_member_count > $members_template->pag_num )
+		return true;
+
+	return false;
+}
+
+function bp_group_pag_id() {
+	echo bp_get_group_pag_id();
+}
+	function bp_get_group_pag_id() {
+		global $bp;
+
+		return apply_filters( 'bp_get_group_pag_id', 'pag' );
+	}
+
+function bp_group_member_pagination() {
+	echo bp_get_group_member_pagination();
+	wp_nonce_field( 'bp_groups_member_list', '_member_pag_nonce' );
+}
+	function bp_get_group_member_pagination() {
+		global $members_template;
+		return apply_filters( 'bp_get_group_member_pagination', $members_template->pag_links );
+	}
+
+function bp_group_member_pagination_count() {
+	echo bp_get_group_member_pagination_count();
+}
+	function bp_get_group_member_pagination_count() {
+		global $members_template;
+
+		$start_num = intval( ( $members_template->pag_page - 1 ) * $members_template->pag_num ) + 1;
+		$from_num = bp_core_number_format( $start_num );
+		$to_num = bp_core_number_format( ( $start_num + ( $members_template->pag_num - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->pag_num - 1 ) );
+		$total = bp_core_number_format( $members_template->total_member_count );
+
+		return apply_filters( 'bp_get_group_member_pagination_count', sprintf( __( 'Viewing members %1$s to %2$s (of %3$s members)', 'buddypress' ), $from_num, $to_num, $total ) );
+	}
+
+function bp_group_member_admin_pagination() {
+	echo bp_get_group_member_admin_pagination();
+	wp_nonce_field( 'bp_groups_member_admin_list', '_member_admin_pag_nonce' );
+}
+	function bp_get_group_member_admin_pagination() {
+		global $members_template;
+
+		return $members_template->pag_links;
+	}
+
+
+/***************************************************************************
+ * Group Creation Process Template Tags
+ **/
+
+/**
+ * Determine if the current logged in user can create groups.
+ *
+ * @package BuddyPress Groups
+ * @since 1.5
+ *
+ * @uses apply_filters() To call 'bp_user_can_create_groups'.
+ * @uses bp_get_option() To retrieve value of 'bp_restrict_group_creation'. Defaults to 0.
+ * @uses is_super_admin() To determine if current user if super admin.
+ *
+ * @return bool True if user can create groups. False otherwise.
+ */
+function bp_user_can_create_groups() {
+	// Super admin can always create groups
+	if ( is_super_admin() )
+		return true;
+
+	// Get group creation option, default to 0 (allowed)
+	$restricted = (int) bp_get_option( 'bp_restrict_group_creation', 0 );
+
+	// Allow by default
+	$can_create = true;
+
+	// Are regular users restricted?
+	if ( $restricted )
+		$can_create = false;
+
+	return apply_filters( 'bp_user_can_create_groups', $can_create, $restricted );
+}
+
+function bp_group_creation_tabs() {
+	global $bp;
+
+	if ( !is_array( $bp->groups->group_creation_steps ) )
+		return false;
+
+	if ( !$bp->groups->current_create_step )
+		$bp->groups->current_create_step = array_shift( array_keys( $bp->groups->group_creation_steps ) );
+
+	$counter = 1;
+
+	foreach ( (array)$bp->groups->group_creation_steps as $slug => $step ) {
+		$is_enabled = bp_are_previous_group_creation_steps_complete( $slug ); ?>
+
+		<li<?php if ( $bp->groups->current_create_step == $slug ) : ?> class="current"<?php endif; ?>><?php if ( $is_enabled ) : ?><a href="<?php echo bp_get_root_domain() . '/' . bp_get_groups_root_slug() ?>/create/step/<?php echo $slug ?>/"><?php else: ?><span><?php endif; ?><?php echo $counter ?>. <?php echo $step['name'] ?><?php if ( $is_enabled ) : ?></a><?php else: ?></span><?php endif ?></li><?php
+		$counter++;
+	}
+
+	unset( $is_enabled );
+
+	do_action( 'groups_creation_tabs' );
+}
+
+function bp_group_creation_stage_title() {
+	global $bp;
+
+	echo apply_filters( 'bp_group_creation_stage_title', '<span>&mdash; ' . $bp->groups->group_creation_steps[$bp->groups->current_create_step]['name'] . '</span>' );
+}
+
+function bp_group_creation_form_action() {
+	echo bp_get_group_creation_form_action();
+}
+	function bp_get_group_creation_form_action() {
+		global $bp;
+
+		if ( !bp_action_variable( 1 ) )
+			$bp->action_variables[1] = array_shift( array_keys( $bp->groups->group_creation_steps ) );
+
+		return apply_filters( 'bp_get_group_creation_form_action', bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create/step/' . bp_action_variable( 1 ) );
+	}
+
+function bp_is_group_creation_step( $step_slug ) {
+	global $bp;
+
+	/* Make sure we are in the groups component */
+	if ( !bp_is_groups_component() || !bp_is_current_action( 'create' ) )
+		return false;
+
+	/* If this the first step, we can just accept and return true */
+	if ( !bp_action_variable( 1 ) && array_shift( array_keys( $bp->groups->group_creation_steps ) ) == $step_slug )
+		return true;
+
+	/* Before allowing a user to see a group creation step we must make sure previous steps are completed */
+	if ( !bp_is_first_group_creation_step() ) {
+		if ( !bp_are_previous_group_creation_steps_complete( $step_slug ) )
+			return false;
+	}
+
+	/* Check the current step against the step parameter */
+	if ( bp_is_action_variable( $step_slug ) )
+		return true;
+
+	return false;
+}
+
+function bp_is_group_creation_step_complete( $step_slugs ) {
+	global $bp;
+
+	if ( !isset( $bp->groups->completed_create_steps ) )
+		return false;
+
+	if ( is_array( $step_slugs ) ) {
+		$found = true;
+
+		foreach ( (array)$step_slugs as $step_slug ) {
+			if ( !in_array( $step_slug, $bp->groups->completed_create_steps ) )
+				$found = false;
+		}
+
+		return $found;
+	} else {
+		return in_array( $step_slugs, $bp->groups->completed_create_steps );
+	}
+
+	return true;
+}
+
+function bp_are_previous_group_creation_steps_complete( $step_slug ) {
+	global $bp;
+
+	/* If this is the first group creation step, return true */
+	if ( array_shift( array_keys( $bp->groups->group_creation_steps ) ) == $step_slug )
+		return true;
+
+	reset( $bp->groups->group_creation_steps );
+	unset( $previous_steps );
+
+	/* Get previous steps */
+	foreach ( (array)$bp->groups->group_creation_steps as $slug => $name ) {
+		if ( $slug == $step_slug )
+			break;
+
+		$previous_steps[] = $slug;
+	}
+
+	return bp_is_group_creation_step_complete( $previous_steps );
+}
+
+function bp_new_group_id() {
+	echo bp_get_new_group_id();
+}
+	function bp_get_new_group_id() {
+		global $bp;
+
+		if ( isset( $bp->groups->new_group_id ) )
+			$new_group_id = $bp->groups->new_group_id;
+		else
+			$new_group_id = 0;
+
+		return apply_filters( 'bp_get_new_group_id', $new_group_id );
+	}
+
+function bp_new_group_name() {
+	echo bp_get_new_group_name();
+}
+	function bp_get_new_group_name() {
+		global $bp;
+
+		if ( isset( $bp->groups->current_group->name ) )
+			$name = $bp->groups->current_group->name;
+		else
+			$name = '';
+
+		return apply_filters( 'bp_get_new_group_name', $name );
+	}
+
+function bp_new_group_description() {
+	echo bp_get_new_group_description();
+}
+	function bp_get_new_group_description() {
+		global $bp;
+
+		if ( isset( $bp->groups->current_group->description ) )
+			$description = $bp->groups->current_group->description;
+		else
+			$description = '';
+
+		return apply_filters( 'bp_get_new_group_description', $description );
+	}
+
+function bp_new_group_enable_forum() {
+	echo bp_get_new_group_enable_forum();
+}
+	function bp_get_new_group_enable_forum() {
+		global $bp;
+		return (int) apply_filters( 'bp_get_new_group_enable_forum', $bp->groups->current_group->enable_forum );
+	}
+
+function bp_new_group_status() {
+	echo bp_get_new_group_status();
+}
+	function bp_get_new_group_status() {
+		global $bp;
+		return apply_filters( 'bp_get_new_group_status', $bp->groups->current_group->status );
+	}
+
+function bp_new_group_avatar( $args = '' ) {
+	echo bp_get_new_group_avatar( $args );
+}
+	function bp_get_new_group_avatar( $args = '' ) {
+		global $bp;
+
+		$defaults = array(
+			'type' => 'full',
+			'width' => false,
+			'height' => false,
+			'class' => 'avatar',
+			'id' => 'avatar-crop-preview',
+			'alt' => __( 'Group avatar', 'buddypress' ),
+			'no_grav' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_new_group_avatar', bp_core_fetch_avatar( array( 'item_id' => $bp->groups->current_group->id, 'object' => 'group', 'type' => $type, 'avatar_dir' => 'group-avatars', 'alt' => $alt, 'width' => $width, 'height' => $height, 'class' => $class, 'no_grav' => $no_grav ) ) );
+	}
+
+function bp_group_creation_previous_link() {
+	echo bp_get_group_creation_previous_link();
+}
+	function bp_get_group_creation_previous_link() {
+		global $bp;
+
+		foreach ( (array)$bp->groups->group_creation_steps as $slug => $name ) {
+			if ( bp_is_action_variable( $slug ) )
+				break;
+
+			$previous_steps[] = $slug;
+		}
+
+		return apply_filters( 'bp_get_group_creation_previous_link', trailingslashit( bp_get_root_domain() ) . bp_get_groups_slug() . '/create/step/' . array_pop( $previous_steps ) );
+	}
+
+function bp_is_last_group_creation_step() {
+	global $bp;
+
+	$last_step = array_pop( array_keys( $bp->groups->group_creation_steps ) );
+
+	if ( $last_step == $bp->groups->current_create_step )
+		return true;
+
+	return false;
+}
+
+function bp_is_first_group_creation_step() {
+	global $bp;
+
+	$first_step = array_shift( array_keys( $bp->groups->group_creation_steps ) );
+
+	if ( $first_step == $bp->groups->current_create_step )
+		return true;
+
+	return false;
+}
+
+function bp_new_group_invite_friend_list() {
+	echo bp_get_new_group_invite_friend_list();
+}
+	function bp_get_new_group_invite_friend_list( $args = '' ) {
+		global $bp;
+
+		if ( !bp_is_active( 'friends' ) )
+			return false;
+
+		$defaults = array(
+			'group_id'  => false,
+			'separator' => 'li'
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		if ( empty( $group_id ) )
+			$group_id = !empty( $bp->groups->new_group_id ) ? $bp->groups->new_group_id : $bp->groups->current_group->id;
+
+		if ( $friends = friends_get_friends_invite_list( $bp->loggedin_user->id, $group_id ) ) {
+			$invites = groups_get_invites_for_group( $bp->loggedin_user->id, $group_id );
+
+			for ( $i = 0, $count = count( $friends ); $i < $count; ++$i ) {
+				$checked = '';
+
+				if ( !empty( $invites ) ) {
+					if ( in_array( $friends[$i]['id'], $invites ) )
+						$checked = ' checked="checked"';
+				}
+
+				$items[] = '<' . $separator . '><input' . $checked . ' type="checkbox" name="friends[]" id="f-' . $friends[$i]['id'] . '" value="' . esc_attr( $friends[$i]['id'] ) . '" /> ' . $friends[$i]['full_name'] . '</' . $separator . '>';
+			}
+		}
+
+		if ( !empty( $items ) )
+			return implode( "\n", (array)$items );
+
+		return false;
+	}
+
+function bp_directory_groups_search_form() {
+	global $bp;
+
+	$default_search_value = bp_get_search_default_text( 'groups' );
+	$search_value         = !empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : $default_search_value; ?>
+
+	<form action="" method="get" id="search-groups-form">
+		<label><input type="text" name="s" id="groups_search" value="<?php echo esc_attr( $search_value ) ?>"  onfocus="if (this.value == '<?php echo $default_search_value ?>') {this.value = '';}" onblur="if (this.value == '') {this.value = '<?php echo $default_search_value ?>';}" /></label>
+		<input type="submit" id="groups_search_submit" name="groups_search_submit" value="<?php _e( 'Search', 'buddypress' ) ?>" />
+	</form>
+
+<?php
+}
+
+/**
+ * Displays group header tabs
+ *
+ * @package BuddyPress
+ * @todo Deprecate?
+ */
+function bp_groups_header_tabs() {
+	global $create_group_step, $completed_to_step;
+?>
+	<li<?php if ( !bp_action_variable( 0 ) || bp_is_action_variable( 'recently-active', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/recently-active"><?php _e( 'Recently Active', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'recently-joined', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/recently-joined"><?php _e( 'Recently Joined', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'most-popular', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/most-popular"><?php _e( 'Most Popular', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'admin-of', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/admin-of"><?php _e( 'Administrator Of', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'mod-of', 0 ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/mod-of"><?php _e( 'Moderator Of', 'buddypress' ) ?></a></li>
+	<li<?php if ( bp_is_action_variable( 'alphabetically' ) ) : ?> class="current"<?php endif; ?>><a href="<?php echo $bp->displayed_user->domain . bp_get_groups_slug() ?>/my-groups/alphabetically"><?php _e( 'Alphabetically', 'buddypress' ) ?></a></li>
+<?php
+	do_action( 'groups_header_tabs' );
+}
+
+/**
+ * Displays group filter titles
+ *
+ * @package BuddyPress
+ * @todo Deprecate?
+ */
+function bp_groups_filter_title() {
+	$current_filter = bp_action_variable( 0 );
+
+	switch ( $current_filter ) {
+		case 'recently-active': default:
+			_e( 'Recently Active', 'buddypress' );
+			break;
+		case 'recently-joined':
+			_e( 'Recently Joined', 'buddypress' );
+			break;
+		case 'most-popular':
+			_e( 'Most Popular', 'buddypress' );
+			break;
+		case 'admin-of':
+			_e( 'Administrator Of', 'buddypress' );
+			break;
+		case 'mod-of':
+			_e( 'Moderator Of', 'buddypress' );
+			break;
+		case 'alphabetically':
+			_e( 'Alphabetically', 'buddypress' );
+		break;
+	}
+	do_action( 'bp_groups_filter_title' );
+}
+
+function bp_is_group_admin_screen( $slug ) {
+	if ( !bp_is_groups_component() || !bp_is_current_action( 'admin' ) )
+		return false;
+
+	if ( bp_is_action_variable( $slug ) )
+		return true;
+
+	return false;
+}
+
+/************************************************************************************
+ * Group Avatar Template Tags
+ **/
+
+function bp_group_current_avatar() {
+	global $bp;
+
+	if ( $bp->groups->current_group->avatar_full ) { ?>
+
+		<img src="<?php echo esc_attr( $bp->groups->current_group->avatar_full ) ?>" alt="<?php _e( 'Group Avatar', 'buddypress' ) ?>" class="avatar" />
+
+	<?php } else { ?>
+
+		<img src="<?php echo $bp->groups->image_base . '/none.gif' ?>" alt="<?php _e( 'No Group Avatar', 'buddypress' ) ?>" class="avatar" />
+
+	<?php }
+}
+
+function bp_get_group_has_avatar() {
+	global $bp;
+
+	if ( !empty( $_FILES ) || !bp_core_fetch_avatar( array( 'item_id' => $bp->groups->current_group->id, 'object' => 'group', 'no_grav' => true ) ) )
+		return false;
+
+	return true;
+}
+
+function bp_group_avatar_delete_link() {
+	echo bp_get_group_avatar_delete_link();
+}
+	function bp_get_group_avatar_delete_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_group_avatar_delete_link', wp_nonce_url( bp_get_group_permalink( $bp->groups->current_group ) . '/admin/group-avatar/delete', 'bp_group_avatar_delete' ) );
+	}
+
+function bp_group_avatar_edit_form() {
+	groups_avatar_upload();
+}
+
+function bp_custom_group_boxes() {
+	do_action( 'groups_custom_group_boxes' );
+}
+
+function bp_custom_group_admin_tabs() {
+	do_action( 'groups_custom_group_admin_tabs' );
+}
+
+function bp_custom_group_fields_editable() {
+	do_action( 'groups_custom_group_fields_editable' );
+}
+
+function bp_custom_group_fields() {
+	do_action( 'groups_custom_group_fields' );
+}
+
+
+/************************************************************************************
+ * Membership Requests Template Tags
+ **/
+
+class BP_Groups_Membership_Requests_Template {
+	var $current_request = -1;
+	var $request_count;
+	var $requests;
+	var $request;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_request_count;
+
+	function bp_groups_membership_requests_template( $group_id, $per_page, $max ) {
+		$this->__construct( $group_id, $per_page, $max );
+	}
+
+
+	function __construct( $group_id, $per_page, $max ) {
+
+		global $bp;
+
+		$this->pag_page = isset( $_REQUEST['mrpage'] ) ? intval( $_REQUEST['mrpage'] ) : 1;
+		$this->pag_num = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+
+		$this->requests = BP_Groups_Group::get_membership_requests( $group_id, $this->pag_num, $this->pag_page );
+
+		if ( !$max || $max >= (int)$this->requests['total'] )
+			$this->total_request_count = (int)$this->requests['total'];
+		else
+			$this->total_request_count = (int)$max;
+
+		$this->requests = $this->requests['requests'];
+
+		if ( $max ) {
+			if ( $max >= count($this->requests) )
+				$this->request_count = count($this->requests);
+			else
+				$this->request_count = (int)$max;
+		} else {
+			$this->request_count = count($this->requests);
+		}
+
+		$this->pag_links = paginate_links( array(
+			'base' => add_query_arg( 'mrpage', '%#%' ),
+			'format' => '',
+			'total' => ceil( $this->total_request_count / $this->pag_num ),
+			'current' => $this->pag_page,
+			'prev_text' => '&larr;',
+			'next_text' => '&rarr;',
+			'mid_size' => 1
+		) );
+	}
+
+	function has_requests() {
+		if ( $this->request_count )
+			return true;
+
+		return false;
+	}
+
+	function next_request() {
+		$this->current_request++;
+		$this->request = $this->requests[$this->current_request];
+
+		return $this->request;
+	}
+
+	function rewind_requests() {
+		$this->current_request = -1;
+
+		if ( $this->request_count > 0 )
+			$this->request = $this->requests[0];
+	}
+
+	function requests() {
+		if ( $this->current_request + 1 < $this->request_count ) {
+			return true;
+		} elseif ( $this->current_request + 1 == $this->request_count ) {
+			do_action('group_request_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_requests();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_request() {
+		global $request;
+
+		$this->in_the_loop = true;
+		$this->request = $this->next_request();
+
+		if ( 0 == $this->current_request ) // loop has just started
+			do_action('group_request_loop_start');
+	}
+}
+
+function bp_group_has_membership_requests( $args = '' ) {
+	global $requests_template, $groups_template;
+
+	$defaults = array(
+		'group_id' => $groups_template->group->id,
+		'per_page' => 10,
+		'max'      => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	$requests_template = new BP_Groups_Membership_Requests_Template( $group_id, $per_page, $max );
+	return apply_filters( 'bp_group_has_membership_requests', $requests_template->has_requests(), $requests_template );
+}
+
+function bp_group_membership_requests() {
+	global $requests_template;
+
+	return $requests_template->requests();
+}
+
+function bp_group_the_membership_request() {
+	global $requests_template;
+
+	return $requests_template->the_request();
+}
+
+function bp_group_request_user_avatar_thumb() {
+	global $requests_template;
+
+	echo apply_filters( 'bp_group_request_user_avatar_thumb', bp_core_fetch_avatar( array( 'item_id' => $requests_template->request->user_id, 'type' => 'thumb', 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) );
+}
+
+function bp_group_request_reject_link() {
+	echo bp_get_group_request_reject_link();
+}
+	function bp_get_group_request_reject_link() {
+		global $requests_template, $groups_template;
+
+		return apply_filters( 'bp_get_group_request_reject_link', wp_nonce_url( bp_get_group_permalink( $groups_template->group ) . '/admin/membership-requests/reject/' . $requests_template->request->id, 'groups_reject_membership_request' ) );
+	}
+
+function bp_group_request_accept_link() {
+	echo bp_get_group_request_accept_link();
+}
+	function bp_get_group_request_accept_link() {
+		global $requests_template, $groups_template;
+
+		return apply_filters( 'bp_get_group_request_accept_link', wp_nonce_url( bp_get_group_permalink( $groups_template->group ) . '/admin/membership-requests/accept/' . $requests_template->request->id, 'groups_accept_membership_request' ) );
+	}
+
+function bp_group_request_user_link() {
+	echo bp_get_group_request_user_link();
+}
+	function bp_get_group_request_user_link() {
+		global $requests_template;
+
+		return apply_filters( 'bp_get_group_request_user_link', bp_core_get_userlink( $requests_template->request->user_id ) );
+	}
+
+function bp_group_request_time_since_requested() {
+	global $requests_template;
+
+	echo apply_filters( 'bp_group_request_time_since_requested', sprintf( __( 'requested %s', 'buddypress' ), bp_core_time_since( strtotime( $requests_template->request->date_modified ) ) ) );
+}
+
+function bp_group_request_comment() {
+	global $requests_template;
+
+	echo apply_filters( 'bp_group_request_comment', strip_tags( stripslashes( $requests_template->request->comments ) ) );
+}
+
+/************************************************************************************
+ * Invite Friends Template Tags
+ **/
+
+class BP_Groups_Invite_Template {
+	var $current_invite = -1;
+	var $invite_count;
+	var $invites;
+	var $invite;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_invite_count;
+
+	function bp_groups_invite_template( $user_id, $group_id ) {
+		$this->__construct( $user_id, $group_id );
+	}
+
+	function __construct( $user_id, $group_id ) {
+
+		global $bp;
+
+		$this->invites = groups_get_invites_for_group( $user_id, $group_id );
+		$this->invite_count = count( $this->invites );
+
+	}
+
+	function has_invites() {
+		if ( $this->invite_count )
+			return true;
+
+		return false;
+	}
+
+	function next_invite() {
+		$this->current_invite++;
+		$this->invite = $this->invites[$this->current_invite];
+
+		return $this->invite;
+	}
+
+	function rewind_invites() {
+		$this->current_invite = -1;
+		if ( $this->invite_count > 0 )
+			$this->invite = $this->invites[0];
+	}
+
+	function invites() {
+		if ( $this->current_invite + 1 < $this->invite_count ) {
+			return true;
+		} elseif ( $this->current_invite + 1 == $this->invite_count ) {
+			do_action('loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_invites();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_invite() {
+		global $invite, $group_id;
+
+		$this->in_the_loop = true;
+		$user_id = $this->next_invite();
+
+		$this->invite = new stdClass;
+		$this->invite->user = new BP_Core_User( $user_id );
+		$this->invite->group_id = $group_id; // Globaled in bp_group_has_invites()
+
+		if ( 0 == $this->current_invite ) // loop has just started
+			do_action('loop_start');
+	}
+}
+
+function bp_group_has_invites( $args = '' ) {
+	global $bp, $invites_template, $group_id;
+
+	$defaults = array(
+		'group_id' => false,
+		'user_id' => $bp->loggedin_user->id
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( !$group_id ) {
+		// Backwards compatibility
+		if ( !empty( $bp->groups->current_group ) )
+			$group_id = $bp->groups->current_group->id;
+
+		if ( !empty( $bp->groups->new_group_id ) )
+			$group_id = $bp->groups->new_group_id;
+	}
+
+	if ( !$group_id )
+		return false;
+
+	$invites_template = new BP_Groups_Invite_Template( $user_id, $group_id );
+	return apply_filters( 'bp_group_has_invites', $invites_template->has_invites(), $invites_template );
+}
+
+function bp_group_invites() {
+	global $invites_template;
+
+	return $invites_template->invites();
+}
+
+function bp_group_the_invite() {
+	global $invites_template;
+
+	return $invites_template->the_invite();
+}
+
+function bp_group_invite_item_id() {
+	echo bp_get_group_invite_item_id();
+}
+	function bp_get_group_invite_item_id() {
+		global $invites_template;
+
+		return apply_filters( 'bp_get_group_invite_item_id', 'uid-' . $invites_template->invite->user->id );
+	}
+
+function bp_group_invite_user_avatar() {
+	echo bp_get_group_invite_user_avatar();
+}
+	function bp_get_group_invite_user_avatar() {
+		global $invites_template;
+
+		return apply_filters( 'bp_get_group_invite_user_avatar', $invites_template->invite->user->avatar_thumb );
+	}
+
+function bp_group_invite_user_link() {
+	echo bp_get_group_invite_user_link();
+}
+	function bp_get_group_invite_user_link() {
+		global $invites_template;
+
+		return apply_filters( 'bp_get_group_invite_user_link', bp_core_get_userlink( $invites_template->invite->user->id ) );
+	}
+
+function bp_group_invite_user_last_active() {
+	echo bp_get_group_invite_user_last_active();
+}
+	function bp_get_group_invite_user_last_active() {
+		global $invites_template;
+
+		return apply_filters( 'bp_get_group_invite_user_last_active', $invites_template->invite->user->last_active );
+	}
+
+function bp_group_invite_user_remove_invite_url() {
+	echo bp_get_group_invite_user_remove_invite_url();
+}
+	function bp_get_group_invite_user_remove_invite_url() {
+		global $invites_template;
+
+		return wp_nonce_url( site_url( bp_get_groups_slug() . '/' . $invites_template->invite->group_id . '/invites/remove/' . $invites_template->invite->user->id ), 'groups_invite_uninvite_user' );
+	}
+
+/***
+ * Groups RSS Feed Template Tags
+ */
+
+/**
+ * Hook group activity feed to <head>
+ *
+ * @since 1.5
+ */
+function bp_groups_activity_feed() {
+	if ( !bp_is_active( 'groups' ) || !bp_is_active( 'activity' ) || !bp_is_group() )
+		return; ?>
+
+	<link rel="alternate" type="application/rss+xml" title="<?php bloginfo( 'name' ) ?> | <?php bp_current_group_name() ?> | <?php _e( 'Group Activity RSS Feed', 'buddypress' ) ?>" href="<?php bp_group_activity_feed_link() ?>" />
+
+<?php
+}
+add_action( 'bp_head', 'bp_groups_activity_feed' );
+
+function bp_group_activity_feed_link() {
+	echo bp_get_group_activity_feed_link();
+}
+	function bp_get_group_activity_feed_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_group_activity_feed_link', bp_get_group_permalink( $bp->groups->current_group ) . 'feed/' );
+	}
+
+/**
+ * Echoes the output of bp_get_current_group_id()
+ *
+ * @package BuddyPress
+ * @since 1.5
+ */
+function bp_current_group_id() {
+	echo bp_get_current_group_id();
+}
+	/**
+	 * Returns the ID of the current group
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 * @uses apply_filters() Filter bp_get_current_group_id to modify this output
+	 *
+	 * @return int $current_group_id The id of the current group, if there is one
+	 */
+	function bp_get_current_group_id() {
+		$current_group = groups_get_current_group();
+
+		$current_group_id = isset( $current_group->id ) ? (int)$current_group->id : 0;
+
+		return apply_filters( 'bp_get_current_group_id', $current_group_id, $current_group );
+	}
+
+/**
+ * Echoes the output of bp_get_current_group_slug()
+ *
+ * @package BuddyPress
+ * @since 1.5
+ */
+function bp_current_group_slug() {
+	echo bp_get_current_group_slug();
+}
+	/**
+	 * Returns the slug of the current group
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 * @uses apply_filters() Filter bp_get_current_group_slug to modify this output
+	 *
+	 * @return str $current_group_slug The slug of the current group, if there is one
+	 */
+	function bp_get_current_group_slug() {
+		$current_group = groups_get_current_group();
+
+		$current_group_slug = isset( $current_group->slug ) ? $current_group->slug : '';
+
+		return apply_filters( 'bp_get_current_group_slug', $current_group_slug, $current_group );
+	}
+
+/**
+ * Echoes the output of bp_get_current_group_name()
+ *
+ * @package BuddyPress
+ */
+function bp_current_group_name() {
+	echo bp_get_current_group_name();
+}
+	/**
+	 * Returns the name of the current group
+	 *
+	 * @package BuddyPress
+	 * @since 1.5
+	 * @uses apply_filters() Filter bp_get_current_group_name to modify this output
+	 *
+	 * @return str The name of the current group, if there is one
+	 */
+	function bp_get_current_group_name() {
+		global $bp;
+
+		$name = apply_filters( 'bp_get_group_name', $bp->groups->current_group->name );
+		return apply_filters( 'bp_get_current_group_name', $name );
+	}
+
+function bp_groups_action_link( $action = '', $query_args = '', $nonce = false ) {
+	echo bp_get_groups_action_link( $action, $query_args, $nonce );
+}
+	function bp_get_groups_action_link( $action = '', $query_args = '', $nonce = false ) {
+		global $bp;
+
+		// Must be displayed user
+		if ( empty( $bp->groups->current_group->id ) )
+			return;
+
+		// Append $action to $url if there is no $type
+		if ( !empty( $action ) )
+			$url = bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $bp->groups->current_group->slug . '/' . $action;
+		else
+			$url = bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $bp->groups->current_group->slug;
+
+		// Add a slash at the end of our user url
+		$url = trailingslashit( $url );
+
+		// Add possible query arg
+		if ( !empty( $query_args ) && is_array( $query_args ) )
+			$url = add_query_arg( $query_args, $url );
+
+		// To nonce, or not to nonce...
+		if ( true === $nonce )
+			$url = wp_nonce_url( $url );
+		elseif ( is_string( $nonce ) )
+			$url = wp_nonce_url( $url, $nonce );
+
+		// Return the url, if there is one
+		if ( !empty( $url ) )
+			return $url;
+	}
+?>
Index: /tags/1.5-beta-3/bp-groups/bp-groups-widgets.php
===================================================================
--- /tags/1.5-beta-3/bp-groups/bp-groups-widgets.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/bp-groups-widgets.php	(revision 5057)
@@ -0,0 +1,193 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Register widgets for groups component */
+function groups_register_widgets() {
+	add_action('widgets_init', create_function('', 'return register_widget("BP_Groups_Widget");') );
+}
+add_action( 'bp_register_widgets', 'groups_register_widgets' );
+
+/*** GROUPS WIDGET *****************/
+
+class BP_Groups_Widget extends WP_Widget {
+	function bp_groups_widget() {
+		$this->_construct();
+	}
+
+	function __construct() {
+		$widget_ops = array( 'description' => __( 'A dynamic list of recently active, popular, and newest groups', 'buddypress' ) );
+		parent::__construct( false, __( 'Groups', 'buddypress' ), $widget_ops );
+
+		if ( is_active_widget( false, false, $this->id_base ) ) {
+			if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+				wp_enqueue_script( 'groups_widget_groups_list-js', BP_PLUGIN_URL . '/bp-groups/js/widget-groups.dev.js', array( 'jquery' ) );
+			else
+				wp_enqueue_script( 'groups_widget_groups_list-js', BP_PLUGIN_URL . '/bp-groups/js/widget-groups.js', array( 'jquery' ) );
+		}
+	}
+
+	function widget( $args, $instance ) {
+		global $bp;
+
+		$user_id = apply_filters( 'bp_group_widget_user_id', '0' );
+
+		extract( $args );
+
+		if ( empty( $instance['group_default'] ) )
+			$instance['group_default'] = 'popular';
+
+		if ( empty( $instance['title'] ) )
+			$instance['title'] = __( 'Groups', 'buddypress' );
+
+		echo $before_widget;
+		echo $before_title
+		   . $instance['title']
+		   . $after_title; ?>
+
+		<?php if ( bp_has_groups( 'user_id=' . $user_id . '&type=' . $instance['group_default'] . '&max=' . $instance['max_groups'] ) ) : ?>
+			<div class="item-options" id="groups-list-options">
+				<a href="<?php echo site_url( bp_get_groups_root_slug() ); ?>" id="newest-groups"<?php if ( $instance['group_default'] == 'newest' ) : ?> class="selected"<?php endif; ?>><?php _e("Newest", 'buddypress') ?></a> |
+				<a href="<?php echo site_url( bp_get_groups_root_slug() ); ?>" id="recently-active-groups"<?php if ( $instance['group_default'] == 'active' ) : ?> class="selected"<?php endif; ?>><?php _e("Active", 'buddypress') ?></a> |
+				<a href="<?php echo site_url( bp_get_groups_root_slug() ); ?>" id="popular-groups" <?php if ( $instance['group_default'] == 'popular' ) : ?> class="selected"<?php endif; ?>><?php _e("Popular", 'buddypress') ?></a>
+			</div>
+
+			<ul id="groups-list" class="item-list">
+				<?php while ( bp_groups() ) : bp_the_group(); ?>
+					<li>
+						<div class="item-avatar">
+							<a href="<?php bp_group_permalink() ?>" title="<?php bp_group_name() ?>"><?php bp_group_avatar_thumb() ?></a>
+						</div>
+
+						<div class="item">
+							<div class="item-title"><a href="<?php bp_group_permalink() ?>" title="<?php bp_group_name() ?>"><?php bp_group_name() ?></a></div>
+							<div class="item-meta">
+								<span class="activity">
+								<?php
+									if ( 'newest' == $instance['group_default'] )
+										printf( __( 'created %s', 'buddypress' ), bp_get_group_date_created() );
+									if ( 'active' == $instance['group_default'] )
+										printf( __( 'active %s', 'buddypress' ), bp_get_group_last_active() );
+									else if ( 'popular' == $instance['group_default'] )
+										bp_group_member_count();
+								?>
+								</span>
+							</div>
+						</div>
+					</li>
+
+				<?php endwhile; ?>
+			</ul>
+			<?php wp_nonce_field( 'groups_widget_groups_list', '_wpnonce-groups' ); ?>
+			<input type="hidden" name="groups_widget_max" id="groups_widget_max" value="<?php echo esc_attr( $instance['max_groups'] ); ?>" />
+
+		<?php else: ?>
+
+			<div class="widget-error">
+				<?php _e('There are no groups to display.', 'buddypress') ?>
+			</div>
+
+		<?php endif; ?>
+
+		<?php echo $after_widget; ?>
+	<?php
+	}
+
+	function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['title'] = strip_tags( $new_instance['title'] );
+		$instance['max_groups'] = strip_tags( $new_instance['max_groups'] );
+		$instance['group_default'] = strip_tags( $new_instance['group_default'] );
+
+		return $instance;
+	}
+
+	function form( $instance ) {
+		$defaults = array(
+			'title'         => __( 'Groups', 'buddypress' ),
+			'max_groups'    => 5,
+			'group_default' => 'active'
+		);
+		$instance = wp_parse_args( (array) $instance, $defaults );
+
+		$title = strip_tags( $instance['title'] );
+		$max_groups = strip_tags( $instance['max_groups'] );
+		$group_default = strip_tags( $instance['group_default'] );
+		?>
+
+		<p><label for="bp-groups-widget-title"><?php _e('Title:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" style="width: 100%" /></label></p>
+
+		<p><label for="bp-groups-widget-groups-max"><?php _e('Max groups to show:', 'buddypress'); ?> <input class="widefat" id="<?php echo $this->get_field_id( 'max_groups' ); ?>" name="<?php echo $this->get_field_name( 'max_groups' ); ?>" type="text" value="<?php echo esc_attr( $max_groups ); ?>" style="width: 30%" /></label></p>
+
+		<p>
+			<label for="bp-groups-widget-groups-default"><?php _e('Default groups to show:', 'buddypress'); ?>
+			<select name="<?php echo $this->get_field_name( 'group_default' ); ?>">
+				<option value="newest" <?php if ( $group_default == 'newest' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Newest', 'buddypress' ) ?></option>
+				<option value="active" <?php if ( $group_default == 'active' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Active', 'buddypress' ) ?></option>
+				<option value="popular"  <?php if ( $group_default == 'popular' ) : ?>selected="selected"<?php endif; ?>><?php _e( 'Popular', 'buddypress' ) ?></option>
+			</select>
+			</label>
+		</p>
+	<?php
+	}
+}
+
+function groups_ajax_widget_groups_list() {
+	global $bp;
+
+	check_ajax_referer('groups_widget_groups_list');
+
+	switch ( $_POST['filter'] ) {
+		case 'newest-groups':
+			$type = 'newest';
+		break;
+		case 'recently-active-groups':
+			$type = 'active';
+		break;
+		case 'popular-groups':
+			$type = 'popular';
+		break;
+	}
+
+	if ( bp_has_groups( 'type=' . $type . '&per_page=' . $_POST['max_groups'] . '&max=' . $_POST['max_groups'] ) ) : ?>
+		<?php echo "0[[SPLIT]]"; ?>
+
+		<ul id="groups-list" class="item-list">
+			<?php while ( bp_groups() ) : bp_the_group(); ?>
+				<li>
+					<div class="item-avatar">
+						<a href="<?php bp_group_permalink() ?>"><?php bp_group_avatar_thumb() ?></a>
+					</div>
+
+					<div class="item">
+						<div class="item-title"><a href="<?php bp_group_permalink() ?>" title="<?php bp_group_name() ?>"><?php bp_group_name() ?></a></div>
+						<div class="item-meta">
+							<span class="activity">
+								<?php
+								if ( 'newest-groups' == $_POST['filter'] ) {
+									printf( __( 'created %s', 'buddypress' ), bp_get_group_date_created() );
+								} else if ( 'recently-active-groups' == $_POST['filter'] ) {
+									printf( __( 'active %s', 'buddypress' ), bp_get_group_last_active() );
+								} else if ( 'popular-groups' == $_POST['filter'] ) {
+									bp_group_member_count();
+								}
+								?>
+							</span>
+						</div>
+					</div>
+				</li>
+
+			<?php endwhile; ?>
+		</ul>
+		<?php wp_nonce_field( 'groups_widget_groups_list', '_wpnonce-groups' ); ?>
+		<input type="hidden" name="groups_widget_max" id="groups_widget_max" value="<?php echo esc_attr( $_POST['max_groups'] ); ?>" />
+
+	<?php else: ?>
+
+		<?php echo "-1[[SPLIT]]<li>" . __("No groups matched the current filter.", 'buddypress'); ?>
+
+	<?php endif;
+
+}
+add_action( 'wp_ajax_widget_groups_list', 'groups_ajax_widget_groups_list' );
+?>
Index: /tags/1.5-beta-3/bp-groups/js/widget-groups.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-groups/js/widget-groups.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/js/widget-groups.dev.js	(revision 5057)
@@ -0,0 +1,49 @@
+jQuery(document).ready( function() {
+	jQuery(".widget div#groups-list-options a").live('click',
+		function() {
+			var link = this;
+			jQuery(link).addClass('loading');
+
+			jQuery(".widget div#groups-list-options a").removeClass("selected");
+			jQuery(this).addClass('selected');
+
+			jQuery.post( ajaxurl, {
+				action: 'widget_groups_list',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce': jQuery("input#_wpnonce-groups").val(),
+				'max_groups': jQuery("input#groups_widget_max").val(),
+				'filter': jQuery(this).attr('id')
+			},
+			function(response)
+			{
+				jQuery(link).removeClass('loading');
+				groups_wiget_response(response);
+			});
+
+			return false;
+		}
+	);
+});
+
+function groups_wiget_response(response) {
+	response = response.substr(0, response.length-1);
+	response = response.split('[[SPLIT]]');
+
+	if ( response[0] != "-1" ) {
+		jQuery(".widget ul#groups-list").fadeOut(200,
+			function() {
+				jQuery(".widget ul#groups-list").html(response[1]);
+				jQuery(".widget ul#groups-list").fadeIn(200);
+			}
+		);
+
+	} else {
+		jQuery(".widget ul#groups-list").fadeOut(200,
+			function() {
+				var message = '<p>' + response[1] + '</p>';
+				jQuery(".widget ul#groups-list").html(message);
+				jQuery(".widget ul#groups-list").fadeIn(200);
+			}
+		);
+	}
+}
Index: /tags/1.5-beta-3/bp-groups/js/widget-groups.js
===================================================================
--- /tags/1.5-beta-3/bp-groups/js/widget-groups.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-groups/js/widget-groups.js	(revision 5057)
@@ -0,0 +1,1 @@
+jQuery(document).ready(function(){jQuery(".widget div#groups-list-options a").live("click",function(){var a=this;jQuery(a).addClass("loading");jQuery(".widget div#groups-list-options a").removeClass("selected");jQuery(this).addClass("selected");jQuery.post(ajaxurl,{action:"widget_groups_list",cookie:encodeURIComponent(document.cookie),_wpnonce:jQuery("input#_wpnonce-groups").val(),max_groups:jQuery("input#groups_widget_max").val(),filter:jQuery(this).attr("id")},function(b){jQuery(a).removeClass("loading");groups_wiget_response(b)});return false})});function groups_wiget_response(a){a=a.substr(0,a.length-1);a=a.split("[[SPLIT]]");if(a[0]!="-1"){jQuery(".widget ul#groups-list").fadeOut(200,function(){jQuery(".widget ul#groups-list").html(a[1]);jQuery(".widget ul#groups-list").fadeIn(200)})}else{jQuery(".widget ul#groups-list").fadeOut(200,function(){var b="<p>"+a[1]+"</p>";jQuery(".widget ul#groups-list").html(b);jQuery(".widget ul#groups-list").fadeIn(200)})}};
Index: /tags/1.5-beta-3/bp-languages/buddypress.pot
===================================================================
--- /tags/1.5-beta-3/bp-languages/buddypress.pot	(revision 5057)
+++ /tags/1.5-beta-3/bp-languages/buddypress.pot	(revision 5057)
@@ -0,0 +1,4971 @@
+# Copyright (C) 2010 BuddyPress
+# This file is distributed under the same license as the BuddyPress package.
+msgid ""
+msgstr ""
+"Project-Id-Version: BuddyPress \n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2011-08-29 23:30:58+00:00\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+
+#: bp-blogs/bp-blogs-loader.php:24 bp-core/admin/bp-core-admin.php:340
+msgid "Site Tracking"
+msgstr ""
+
+#: bp-blogs/bp-blogs-loader.php:58
+msgid "Search sites..."
+msgstr ""
+
+#: bp-blogs/bp-blogs-loader.php:108
+msgid "Sites <span>%d</span>"
+msgstr ""
+
+#: bp-blogs/bp-blogs-loader.php:148 bp-core/admin/bp-core-update.php:487
+msgid "Sites"
+msgstr ""
+
+#: bp-blogs/bp-blogs-loader.php:155 bp-blogs/bp-blogs-loader.php:176
+#: bp-blogs/bp-blogs-buddybar.php:25 bp-core/bp-core-adminbar.php:105
+msgid "My Sites"
+msgstr ""
+
+#: bp-blogs/bp-blogs-activity.php:17
+msgid "New site created"
+msgstr ""
+
+#: bp-blogs/bp-blogs-activity.php:18
+msgid "New post published"
+msgstr ""
+
+#: bp-blogs/bp-blogs-activity.php:19
+msgid "New post comment posted"
+msgstr ""
+
+#: bp-blogs/bp-blogs-buddybar.php:37 bp-core/bp-core-adminbar.php:117
+#: bp-core/bp-core-buddybar.php:481
+msgid "Dashboard"
+msgstr ""
+
+#: bp-blogs/bp-blogs-buddybar.php:38 bp-core/bp-core-adminbar.php:120
+#: bp-core/bp-core-buddybar.php:485
+msgid "New Post"
+msgstr ""
+
+#: bp-blogs/bp-blogs-buddybar.php:39 bp-core/bp-core-buddybar.php:486
+msgid "Manage Posts"
+msgstr ""
+
+#: bp-blogs/bp-blogs-buddybar.php:40 bp-core/bp-core-adminbar.php:121
+#: bp-core/bp-core-buddybar.php:487
+msgid "Manage Comments"
+msgstr ""
+
+#: bp-blogs/bp-blogs-buddybar.php:53
+msgid "Create a Site!"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:135
+msgctxt "Blog pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:136
+msgctxt "Blog pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:258
+msgid "Viewing site %1$s to %2$s (of %3$s sites)"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:282 bp-activity/bp-activity-template.php:664
+msgid "Site authored by %s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:345 bp-core/bp-core-classes.php:175
+#: bp-groups/bp-groups-widgets.php:70 bp-groups/bp-groups-widgets.php:170
+#: bp-members/bp-members-template.php:559
+#: bp-members/bp-members-template.php:820
+#: bp-themes/bp-default/forums/single/forum-header.php:13
+#: bp-themes/bp-default/groups/groups-loop.php:47
+#: bp-themes/bp-default/groups/single/group-header.php:42
+msgid "active %s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:357
+msgid "Latest Post: %s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:421
+msgid "There was a problem, please correct the form below and try again."
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:424
+msgid "By filling out the form below, you can <strong>add a site to your account</strong>. There is no limit to the number of sites that you can have, so create to your heart's content, but blog responsibly!"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:426
+msgid "If you&#8217;re not going to use a great domain, leave it for a new user. Now have at it!"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:435
+msgid "Create Site"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:449
+msgid "Site Name:"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:451
+msgid "Site Domain:"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:465
+msgid "Your address will be "
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:468
+msgid "blogname"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:470
+msgid "domain."
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:473
+msgid "Must be at least 4 characters, letters and numbers only. It cannot be changed so choose carefully!)"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:479
+msgid "Site Title:"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:490
+msgid "Privacy:"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:491
+#: bp-themes/bp-default/registration/register.php:202
+msgid "I would like my site to appear in search engines, and in public listings around this network."
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:495 bp-core/admin/bp-core-admin.php:67
+#: bp-core/admin/bp-core-admin.php:77 bp-core/admin/bp-core-admin.php:85
+#: bp-core/admin/bp-core-admin.php:93 bp-core/admin/bp-core-admin.php:103
+#: bp-core/admin/bp-core-admin.php:115 bp-friends/bp-friends-screens.php:69
+#: bp-groups/bp-groups-screens.php:827 bp-messages/bp-messages-screens.php:150
+#: bp-themes/bp-default/registration/register.php:205
+#: bp-themes/bp-default/groups/single/admin.php:26
+#: bp-activity/bp-activity-screens.php:172
+msgid "Yes"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:499 bp-core/admin/bp-core-admin.php:68
+#: bp-core/admin/bp-core-admin.php:78 bp-core/admin/bp-core-admin.php:86
+#: bp-core/admin/bp-core-admin.php:94 bp-core/admin/bp-core-admin.php:104
+#: bp-core/admin/bp-core-admin.php:116 bp-friends/bp-friends-screens.php:70
+#: bp-groups/bp-groups-screens.php:828 bp-messages/bp-messages-screens.php:151
+#: bp-themes/bp-default/registration/register.php:206
+#: bp-themes/bp-default/groups/single/admin.php:27
+#: bp-activity/bp-activity-screens.php:173
+msgid "No"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:553
+msgid "Congratulations! You have successfully registered a new site."
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:555
+msgid "<a href=\"%1$s\">%2$s</a> is your new site.  <a href=\"%3$s\">Login</a> as \"%4$s\" using your existing password."
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:566 bp-core/bp-core-filters.php:313
+#: bp-themes/bp-default/blogs/index.php:23
+#: bp-themes/bp-default/blogs/create.php:21
+msgid "Create a Site"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:579
+msgid "%s's Sites"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:580
+msgid "%s's Recent Posts"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:581
+msgid "%s's Recent Comments"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:596 bp-core/deprecated/1.5.php:381
+#: bp-groups/bp-groups-template.php:2261 bp-forums/bp-forums-template.php:1258
+#: bp-members/bp-members-template.php:661 bp-themes/bp-default/header.php:35
+#: bp-themes/bp-default/searchform.php:5
+msgid "Search"
+msgstr ""
+
+#: bp-blogs/bp-blogs-template.php:629 bp-blogs/bp-blogs-template.php:630
+#: bp-core/bp-core-adminbar.php:124
+msgid "Visit Site"
+msgstr ""
+
+#: bp-blogs/bp-blogs-functions.php:115
+msgid "%s created the site %s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-functions.php:179
+msgid "%1$s wrote a new post, %2$s, on the site %3$s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-functions.php:181
+msgid "%1$s wrote a new post, %2$s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-functions.php:264
+msgid "%1$s commented on the post, %2$s, on the site %3$s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-functions.php:266
+msgid "%1$s commented on the post, %2$s"
+msgstr ""
+
+#: bp-blogs/bp-blogs-widgets.php:25
+msgid "Recent Networkwide Posts"
+msgstr ""
+
+#: bp-blogs/bp-blogs-widgets.php:68
+msgid "Sorry, there were no posts found. Why not write one?"
+msgstr ""
+
+#: bp-blogs/bp-blogs-widgets.php:88
+msgid "Max posts to show:"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:22
+msgid "A dynamic list of recently active, popular, and newest members"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:23 bp-core/bp-core-widgets.php:110
+#: bp-core/admin/bp-core-update.php:428 bp-core/bp-core-template.php:242
+#: bp-groups/bp-groups-template.php:1285 bp-members/bp-members-loader.php:24
+#: bp-themes/bp-default/groups/single/admin.php:229
+msgid "Members"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:48 bp-core/bp-core-widgets.php:128
+#: bp-friends/bp-friends-template.php:63 bp-friends/bp-friends-template.php:84
+#: bp-groups/bp-groups-widgets.php:50 bp-groups/bp-groups-widgets.php:125
+#: bp-themes/bp-default/members/single/blogs.php:22
+#: bp-themes/bp-default/blogs/index.php:58
+msgid "Newest"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:49 bp-core/bp-core-widgets.php:129
+#: bp-groups/bp-groups-widgets.php:51 bp-groups/bp-groups-widgets.php:126
+msgid "Active"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:53 bp-core/bp-core-widgets.php:130
+#: bp-groups/bp-groups-widgets.php:52 bp-groups/bp-groups-widgets.php:127
+msgid "Popular"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:90
+msgid "No one has signed up yet!"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:121 bp-core/bp-core-widgets.php:201
+#: bp-core/bp-core-widgets.php:270 bp-groups/bp-groups-widgets.php:118
+#: bp-themes/bp-default/forums/index.php:104
+#: bp-themes/bp-default/forums/single/edit.php:49
+#: bp-themes/bp-default/groups/single/forum/edit.php:49
+#: bp-themes/bp-default/groups/single/forum.php:71
+msgid "Title:"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:123
+msgid "Max members to show:"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:126
+msgid "Default members to show:"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:148
+msgid "Avatars of users who are currently online"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:149
+msgid "Who's Online Avatars"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:173
+msgid "There are no users currently online"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:192
+msgid "Who's Online"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:203 bp-core/bp-core-widgets.php:272
+msgid "Max Members to show:"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:217
+msgid "Avatars of recently active members"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:218
+msgid "Recently Active Member Avatars"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:242
+msgid "There are no recently active members"
+msgstr ""
+
+#: bp-core/bp-core-widgets.php:328
+msgid "There were no members found, please try another filter."
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:75
+msgid "Admin Dashboard"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:86
+msgid "Network Dashboard"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:112
+msgid "Blavatar"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:142
+msgid "Comments %s"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:156
+msgid "Appearance"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:162
+msgid "Themes"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:165
+msgid "Widgets"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:168
+msgid "Menus"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:171
+msgid "Background"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:174
+msgid "Header"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:207
+msgid "%d WordPress Update"
+msgstr ""
+
+#: bp-core/bp-core-adminbar.php:209
+msgid "%d Plugin Update"
+msgid_plural "%d Plugin Updates"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bp-core/bp-core-adminbar.php:211
+msgid "%d Theme Update"
+msgid_plural "%d Theme Updates"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bp-core/bp-core-adminbar.php:216
+msgid "Updates %s"
+msgstr ""
+
+#: bp-core/admin/bp-core-schema.php:208
+msgctxt "First XProfile group name"
+msgid "Base"
+msgstr ""
+
+#: bp-core/admin/bp-core-schema.php:209
+msgctxt "XProfile fullname field name"
+msgid "Name"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:11
+msgid "IMPORTANT: <a href=\"http://codex.buddypress.org/buddypress-site-administration/upgrading-buddypress/\">Read this before attempting to update BuddyPress</a>"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:41 bp-core/admin/bp-core-admin.php:208
+#: bp-core/admin/bp-core-admin.php:257 bp-core/admin/bp-core-update.php:72
+#: bp-core/admin/bp-core-update.php:88 bp-core/admin/bp-core-update.php:219
+#: bp-core/bp-core-functions.php:251 bp-forums/bp-forums-admin.php:27
+msgid "Components"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:42 bp-core/admin/bp-core-admin.php:209
+#: bp-core/admin/bp-core-admin.php:258 bp-core/admin/bp-core-update.php:73
+#: bp-core/admin/bp-core-update.php:89 bp-core/admin/bp-core-update.php:223
+#: bp-core/bp-core-functions.php:252 bp-forums/bp-forums-admin.php:28
+msgid "Pages"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:43 bp-core/admin/bp-core-admin.php:210
+#: bp-core/admin/bp-core-admin.php:259 bp-core/bp-core-functions.php:253
+#: bp-settings/bp-settings-loader.php:15 bp-settings/bp-settings-loader.php:72
+#: bp-settings/bp-settings-loader.php:142 bp-groups/bp-groups-loader.php:189
+#: bp-groups/bp-groups-template.php:1279 bp-forums/bp-forums-admin.php:29
+msgid "Settings"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:44 bp-core/admin/bp-core-admin.php:211
+#: bp-core/admin/bp-core-admin.php:260 bp-forums/bp-forums-admin.php:30
+msgid "Forum Setup"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:52 bp-core/admin/bp-core-admin.php:220
+#: bp-core/admin/bp-core-admin.php:269
+msgid "Settings Saved"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:65
+msgid "Disable BuddyPress to WordPress profile syncing?"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:75
+msgid "Hide admin bar for logged out users?"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:83
+msgid "Disable avatar uploads? (Gravatars will still work)"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:91
+msgid "Disable user account deletion?"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:101
+msgid "Disable activity stream commenting on blog and forum posts?"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:113
+msgid "Restrict group creation to Site Admins?"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:130 bp-core/admin/bp-core-admin.php:231
+msgid "Save Settings"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:280
+msgid "Save All"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:312 bp-xprofile/bp-xprofile-loader.php:25
+msgid "Extended Profiles"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:313
+msgid "Customize your community with fully editable profile fields that allow your users to describe themselves."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:316
+msgid "Account Settings"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:317
+msgid "Allow your users to modify their account and notification settings directly from within their profiles."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:320 bp-friends/bp-friends-loader.php:24
+msgid "Friend Connections"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:321
+msgid "Let your users make connections so they can track the activity of others and focus on the people they care about the most."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:324
+msgid "Private Messaging"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:325
+msgid "Allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, messages can be sent between any number of members."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:328 bp-activity/bp-activity-loader.php:28
+msgid "Activity Streams"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:329
+msgid "Global, personal, and group activity streams with threaded commenting, direct posting, favoriting and @mentions, all with full RSS feed and email notification support."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:332 bp-groups/bp-groups-loader.php:26
+msgid "User Groups"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:333
+msgid "Groups allow your users to organize themselves into specific public, private or hidden sections with separate activity streams and member listings."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:336 bp-forums/bp-forums-loader.php:24
+msgid "Discussion Forums"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:337
+msgid "Full-powered discussion forums built directly into groups allow for more conventional in-depth conversations. NOTE: This will require an extra (but easy) setup step."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:341
+msgid "Make BuddyPress aware of new posts and new comments from your site."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:346
+msgid "Make BuddyPress aware of new sites, new posts and new comments from across your entire network."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:370 bp-core/bp-core-loader.php:38
+msgid "BuddyPress Core"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:371
+msgid "It&#8216;s what makes <del>time travel</del> BuddyPress possible!"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:374
+msgid "Community Members"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:375
+msgid "Everything in a BuddyPress community revolves around its members."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:388
+msgid "Available Components"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:390
+msgid "Each component has a unique purpose, and your community may not need each one."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:420
+msgid "Required Components"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:422
+msgid "The following components are required by BuddyPress and cannot be turned off."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:490
+msgid "Directories"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:492
+msgid "Associate a WordPress Page with each BuddyPress component directory."
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:512 bp-core/admin/bp-core-admin.php:565
+msgid "- None -"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:516 bp-core/admin/bp-core-admin.php:569
+msgid "New Page"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:517 bp-core/admin/bp-core-admin.php:570
+#: bp-xprofile/bp-xprofile-classes.php:751
+msgid "Save"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:521 bp-core/admin/bp-core-admin.php:574
+#: bp-xprofile/bp-xprofile-loader.php:184
+#: bp-members/bp-members-template.php:588
+#: bp-themes/bp-default/functions.php:156
+#: bp-themes/bp-default/activity/entry.php:37
+#: bp-activity/bp-activity-template.php:1292
+msgid "View"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:543
+msgid "Sign-up"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:544
+msgid "Activation"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:547
+msgid "Registration"
+msgstr ""
+
+#: bp-core/admin/bp-core-admin.php:549
+msgid "Associate WordPress Pages with the following BuddyPress Registration pages."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:74 bp-core/admin/bp-core-update.php:227
+msgid "Permalinks"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:75 bp-core/admin/bp-core-update.php:231
+msgid "Theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:76 bp-core/admin/bp-core-update.php:92
+#: bp-core/admin/bp-core-update.php:188 bp-core/admin/bp-core-update.php:235
+#: bp-core/admin/bp-core-update.php:1186
+#: bp-themes/bp-default/groups/create.php:294
+msgid "Finish"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:82 bp-core/admin/bp-core-update.php:211
+msgid "Multisite Update"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:85 bp-core/admin/bp-core-update.php:207
+msgid "Database Update"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:145 bp-core/admin/bp-core-update.php:1215
+msgid "Update"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:145 bp-core/admin/bp-core-update.php:1213
+msgid "Setup"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:153
+msgid "BuddyPress %s"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:191
+msgid "Finish &amp; Activate"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:197
+msgid "Save &amp; Next"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:215 bp-themes/bp-default/blogs/index.php:23
+#: bp-themes/bp-default/blogs/create.php:21
+msgid "Site Directory"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:254
+msgid "Before you can continue using BuddyPress, a few minor adjustments need to be made. These changes are not destructive and will not remove or change any existing settings."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:295
+msgid "BuddyPress has detected a recent change to WordPress Multisite, which allows members of your community to have their own WordPress sites. You can enable or disable this feature at any time at <a href=\"%s\">Network Options</a>."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:297
+msgid "Please select the WordPress page you would like to use to display the site directory. You can either choose an existing page or let BuddyPress auto-create a page for you. If you'd like, you can go to manually create pages now, and return to this step when you are finished."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:299 bp-core/admin/bp-core-update.php:422
+msgid "Please Note:"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:299 bp-core/admin/bp-core-update.php:422
+msgid "If you have manually added BuddyPress navigation links in your theme you may need to remove these from your header.php to avoid duplicate links."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:301
+msgid "Would you like to enable site tracking, which tracks blog posts and comments from across your network?"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:306
+msgid "Enable Site Tracking?"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:312
+msgid "Track new sites, new posts and new comments across your entire network."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:320
+msgid "Select a WordPress page for the Sites directory."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:323 bp-core/admin/bp-core-update.php:432
+#: bp-core/admin/bp-core-update.php:445 bp-core/admin/bp-core-update.php:460
+#: bp-core/admin/bp-core-update.php:475 bp-core/admin/bp-core-update.php:491
+#: bp-core/admin/bp-core-update.php:504 bp-core/admin/bp-core-update.php:515
+msgid "Automatically create a page at:"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:324 bp-core/admin/bp-core-update.php:433
+#: bp-core/admin/bp-core-update.php:446 bp-core/admin/bp-core-update.php:461
+#: bp-core/admin/bp-core-update.php:476 bp-core/admin/bp-core-update.php:492
+#: bp-core/admin/bp-core-update.php:505 bp-core/admin/bp-core-update.php:516
+msgid "Use an existing page:"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:324 bp-core/admin/bp-core-update.php:433
+#: bp-core/admin/bp-core-update.php:446 bp-core/admin/bp-core-update.php:461
+#: bp-core/admin/bp-core-update.php:476 bp-core/admin/bp-core-update.php:492
+#: bp-core/admin/bp-core-update.php:505 bp-core/admin/bp-core-update.php:516
+msgid "- Select -"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:349
+msgid "BuddyPress bundles several individual social components together, each one adding a distinct feature. This first step decides which features are enabled on your site; all features are enabled by default. Don't worry, you can change your mind at any point in the future."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:388
+msgid "members"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:391
+#: bp-activity/bp-activity-template.php:1352
+msgid "groups"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:394
+msgid "activity"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:397
+msgid "forums"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:400
+msgid "blogs"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:403
+msgid "register"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:406
+msgid "activate"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:418
+msgid "BuddyPress now uses WordPress pages to display content. This allows you to easily change the names of pages or move them to a sub page."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:420
+msgid "Either choose an existing page or let BuddyPress auto-create pages for you. To manually create custom pages, come back to this step once you are finished."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:429
+msgid "Displays member profiles, and a directory of all site members."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:441
+#: bp-themes/bp-default/activity/index.php:23
+msgid "Site Activity"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:442
+msgid "Displays the activity for the entire site, a member's friends, groups and @mentions."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:456 bp-core/bp-core-template.php:245
+#: bp-groups/bp-groups-activity.php:135 bp-groups/bp-groups-activity.php:171
+#: bp-groups/bp-groups-activity.php:207 bp-groups/bp-groups-activity.php:243
+#: bp-groups/bp-groups-loader.php:395 bp-groups/bp-groups-widgets.php:20
+#: bp-groups/bp-groups-widgets.php:41 bp-groups/bp-groups-widgets.php:107
+#: bp-groups/bp-groups-screens.php:826 bp-activity/bp-activity-loader.php:169
+#: bp-activity/bp-activity-loader.php:249
+msgid "Groups"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:457
+msgid "Displays individual groups as well as a directory of groups."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:471 bp-core/bp-core-template.php:251
+#: bp-forums/bp-forums-admin.php:199 bp-forums/bp-forums-loader.php:111
+#: bp-forums/bp-forums-loader.php:194 bp-forums/bp-forums-loader.php:234
+msgid "Forums"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:472
+msgid "Displays a directory of public forum topics."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:488
+msgid "Displays a directory of the sites in your network."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:500 bp-core/bp-core-functions.php:412
+#: bp-members/bp-members-adminbar.php:63
+msgid "Register"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:501
+msgid "Displays a site registration page where users can create new accounts."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:511 bp-core/bp-core-functions.php:407
+#: bp-messages/bp-messages-template.php:498
+#: bp-themes/bp-default/registration/activate.php:36
+msgid "Activate"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:512
+msgid "The page users will visit to activate their account once they have registered."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:543
+msgid "Congratulations! You are already using pretty permalinks, which BuddyPress requires. If you'd like to change your settings, you may do so now. If you're happy with your current settings, click Save &amp; Next to continue."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:545
+msgid "To make sure the pages created in the previous step work correctly, pretty permalinks must be active on your site."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:551
+msgid "Please select the permalink setting you would like to use. For more advanced options please visit the <a href=\"%s\">permalink settings page</a> first, and complete this setup wizard later."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:555
+msgid "Day and name"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:559
+msgid "Month and name"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:563
+msgid "Numeric"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:628
+msgid "BuddyPress introduces a whole range of new screens to display content. To display these screens, you need to decide how you want to handle them in your current theme."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:633
+msgid "Use BuddyPress Default"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:634
+msgid "BuddyPress Default"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:637
+msgid "BuddyPress Default contains everything you need to get up and running out of the box. It supports all features and is highly customizable."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:638
+msgid "This is the best choice if you do not have an existing WordPress theme, or want to start using BuddyPress immediately."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:639
+msgid "Yes, please!"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:646
+msgid "Other themes"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:647
+msgid "A BuddyPress theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:650
+msgid "We've found that you already have some other BuddyPress-compatible themes available. To use one of those, pick it from this list."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:653
+msgid "Use this theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:669
+msgid "Manually update current theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:671
+msgid "Your existing theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:675
+msgid "The BuddyPress Template Pack plugin will guide you through the process of manually upgrading your existing WordPress theme. This usually involves following the step-by-step instructions and copying the BuddyPress template files into your theme. This option requires a working knowledge of CSS and HTML, as you will need to tweak the new templates to match your existing theme."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:679
+msgid "Install BuddyPress Template Pack"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:683
+msgid "Choose this option (go to Appearance &rarr; BP Compatibility after setup is complete)"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:684
+msgid "Plugin Installed"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:693
+msgid "Do not change theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:696
+msgid "You are happy with your current theme and plan on changing it later."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:697
+msgid "This is the best choice if you have a highly customized theme on your site already, and want to later manually integrate BuddyPress into your site."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:699
+msgid "Don't change my current theme"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:721
+msgid "setup"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:721
+msgid "update"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:723
+msgid "The BuddyPress %1$s is complete, and your site is ready to go!"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:903
+msgid "Oops, there was a problem creating a configuration file. "
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:909
+msgid "If your <code>web.config</code> file were <a href=\"http://codex.wordpress.org/Changing_File_Permissions\">writable</a>, we could do this automatically, but it isn&#8217;t so this is the url rewrite rule you should have in your <code>web.config</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all. Then insert this rule inside of the <code>/&lt;configuration&gt;/&lt;system.webServer&gt;/&lt;rewrite&gt;/&lt;rules&gt;</code> element in <code>web.config</code> file."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:918
+msgid "Permalink structure updated. Remove write access on web.config file now!"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:923
+msgid "If your <code>.htaccess</code> file were <a href=\"http://codex.wordpress.org/Changing_File_Permissions\">writable</a>, we could do this automatically, but it isn&#8217;t so these are the mod_rewrite rules you should have in your <code>.htaccess</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:935
+msgid "Paste all these rules into a new <code>.htaccess</code> file in the root of your WordPress installation and save the file. Once you're done, please hit the \"Save and Next\" button to continue."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:1159
+msgid "Installation was successful. The available options have now been updated, please continue with your selection."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:1218 bp-core/bp-core-functions.php:250
+#: bp-members/bp-members-signup.php:570
+msgid "BuddyPress"
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:1314
+msgid "BuddyPress has been updated! Please run the <a href=\"%s\">update wizard</a>."
+msgstr ""
+
+#: bp-core/admin/bp-core-update.php:1319
+msgid "BuddyPress was successfully installed! Please run the <a href=\"%s\">installation wizard</a>."
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:91
+msgid "Avatar Image"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:401
+msgid "There is no error, the file uploaded with success"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:402 bp-core/bp-core-avatars.php:403
+msgid "Your image was bigger than the maximum allowed file size of: "
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:404
+msgid "The uploaded file was only partially uploaded"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:405
+msgid "No file was uploaded"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:406
+msgid "Missing a temporary folder"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:410
+msgid "Your upload failed, please try again. Error was: %s"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:415
+msgid "The file you uploaded is too big. Please upload a file under %s"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:420
+msgid "Please upload only JPG, GIF or PNG photos."
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:431 bp-core/bp-core-avatars.php:444
+msgid "Upload Failed! Error was: %s"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:462
+msgid "Upload failed! Error was: %s"
+msgstr ""
+
+#: bp-core/bp-core-avatars.php:595
+msgid "Avatar of %s"
+msgstr ""
+
+#: bp-core/bp-core-template.php:61
+msgid "Options"
+msgstr ""
+
+#: bp-core/bp-core-template.php:187
+msgid "%1$s at %2$s"
+msgstr ""
+
+#: bp-core/bp-core-template.php:248
+msgid "Blogs"
+msgstr ""
+
+#: bp-core/bp-core-template.php:253
+#: bp-themes/bp-default/members/single/activity.php:27
+#: bp-themes/bp-default/activity/index.php:101
+#: bp-themes/bp-default/forums/forums-loop.php:40
+msgid "Posts"
+msgstr ""
+
+#: bp-core/bp-core-template.php:256
+msgid "Search these:"
+msgstr ""
+
+#: bp-core/bp-core-template.php:284
+msgid "Search anything..."
+msgstr ""
+
+#: bp-core/bp-core-template.php:378
+msgid " [&hellip;]"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:55
+msgid "noreply"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:161 bp-core/bp-core-filters.php:185
+msgid "[User Set]"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:204
+msgid ""
+"Thanks for registering! To complete the activation of your account and blog, please click the following link:\n"
+"\n"
+"%1$s\n"
+"\n"
+"\n"
+"\n"
+"After you activate, you can visit your blog here:\n"
+"\n"
+"%2$s"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:205
+msgid "Activate %s"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:237 bp-members/bp-members-signup.php:572
+msgid ""
+"Thanks for registering! To complete the activation of your account please click the following link:\n"
+"\n"
+"%s\n"
+"\n"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:238 bp-members/bp-members-signup.php:573
+msgid "Activate Your Account"
+msgstr ""
+
+#. translators: "displayed user's name | canonicalised component name"
+#. translators: "group name | group nav section name"
+
+#: bp-core/bp-core-filters.php:279 bp-core/bp-core-filters.php:285
+msgid "%1$s | %2$s"
+msgstr ""
+
+#. translators: "root component name | component item name | component nav
+#. section name"
+
+#: bp-core/bp-core-filters.php:290
+msgid "%1$s | %2$s | %3$s"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:295 bp-core/bp-core-filters.php:297
+msgid "%s Directory"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:301
+#: bp-themes/bp-default/registration/register.php:23
+msgid "Create an Account"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:305
+#: bp-themes/bp-default/registration/activate.php:24
+msgid "Activate your Account"
+msgstr ""
+
+#: bp-core/bp-core-filters.php:309 bp-themes/bp-default/groups/index.php:23
+#: bp-themes/bp-default/groups/create.php:18
+msgid "Create a Group"
+msgstr ""
+
+#: bp-core/bp-core-cssjs.php:49
+msgid "Are you sure?"
+msgstr ""
+
+#: bp-core/bp-core-classes.php:191
+msgid "%d group"
+msgid_plural "%d groups"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bp-core/deprecated/1.5.php:120 bp-xprofile/bp-xprofile-loader.php:217
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:8
+#: bp-themes/bp-default/activity/post-form.php:45
+msgid "My Profile"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:127 bp-themes/bp-default/functions.php:622
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:15
+msgid "Name"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:136
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:24
+msgid "About Me"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:145 bp-themes/bp-default/functions.php:626
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:33
+msgid "Website"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:154
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:42
+msgid "Jabber"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:163
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:51
+msgid "AOL Messenger"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:172
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:60
+msgid "Yahoo Messenger"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:272 bp-core/bp-core-buddybar.php:473
+#: bp-members/bp-members-adminbar.php:218
+#: bp-members/bp-members-template.php:721 bp-themes/bp-default/sidebar.php:18
+msgid "Log Out"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:319
+msgid "%1$s mentioned you in the group \"%2$s\""
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:321 bp-activity/bp-activity-notifications.php:41
+msgid ""
+"%1$s mentioned you in the group \"%2$s\":\n"
+"\n"
+"\"%3$s\"\n"
+"\n"
+"To view and respond to the message, log in and visit: %4$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-core/deprecated/1.5.php:331 bp-friends/bp-friends-notifications.php:36
+#: bp-friends/bp-friends-notifications.php:76
+#: bp-groups/bp-groups-notifications.php:32
+#: bp-groups/bp-groups-notifications.php:83
+#: bp-groups/bp-groups-notifications.php:139
+#: bp-groups/bp-groups-notifications.php:187
+#: bp-groups/bp-groups-notifications.php:241
+#: bp-messages/bp-messages-notifications.php:44
+#: bp-activity/bp-activity-notifications.php:62
+#: bp-activity/bp-activity-notifications.php:106
+#: bp-activity/bp-activity-notifications.php:151
+msgid "To disable these notifications please log in and go to: %s"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:369
+msgid "<strong>BuddyPress is almost ready</strong>. You must <a href=\"%s\">update your permalink structure</a> to something other than the default for it to work."
+msgstr ""
+
+#: bp-core/bp-core-functions.php:382
+msgid "You'll need to <a href='%s'>activate a <strong>BuddyPress-compatible theme</strong></a> to take advantage of all of BuddyPress's features. We've bundled a default theme, but you can always <a href='%s'>install some other compatible themes</a> or <a href='%s'>update your existing WordPress theme</a>."
+msgstr ""
+
+#: bp-core/bp-core-functions.php:424
+msgid "The following active BuddyPress Components do not have associated WordPress Pages: %2$s. <a href=\"%1$s\" class=\"button-secondary\">Repair</a>"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:449
+msgid "Each BuddyPress Component needs its own WordPress page. The following WordPress Pages have more than one component associated with them: %2$s. <a href=\"%1$s\" class=\"button-secondary\">Repair</a>"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:599
+msgid "sometime"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:600
+msgid "right now"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:601
+msgid "%s ago"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:605
+msgid "year"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:605
+msgid "years"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:606
+msgid "month"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:606
+msgid "months"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:607
+msgid "week"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:607
+msgid "weeks"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:608
+msgid "day"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:608
+msgid "days"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:609
+msgid "hour"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:609
+msgid "hours"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:610
+msgid "minute"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:610
+msgid "minutes"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:611
+msgid "second"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:611
+msgid "seconds"
+msgstr ""
+
+#: bp-core/bp-core-functions.php:668
+msgctxt "Separator in time since"
+msgid ","
+msgstr ""
+
+#: bp-core/bp-core-functions.php:731
+msgid "not recently active"
+msgstr ""
+
+#: bp-core/bp-core-catchuri.php:379
+msgid "This user has been marked as a spammer. Only site admins can view this profile."
+msgstr ""
+
+#: bp-core/bp-core-catchuri.php:402 bp-core/bp-core-catchuri.php:459
+msgid "You must log in to access the page you requested."
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:244
+msgid "You do not have access to this page."
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:415 bp-themes/bp-default/sidebar.php:53
+msgid "Log In"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:419
+msgid "Sign Up"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:431
+msgid "My Account"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:502
+msgid "Visit"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:504
+msgid "Random Member"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:508
+msgid "Random Group"
+msgstr ""
+
+#: bp-core/bp-core-buddybar.php:514
+msgid "Random Site"
+msgstr ""
+
+#: bp-core/bp-core-loader.php:210 bp-xprofile/bp-xprofile-loader.php:113
+#: bp-xprofile/bp-xprofile-loader.php:177 bp-members/bp-members-loader.php:137
+msgid "Profile"
+msgstr ""
+
+#: bp-core/bp-core-loader.php:221 bp-groups/bp-groups-template.php:520
+#: bp-xprofile/bp-xprofile-loader.php:125 bp-members/bp-members-loader.php:152
+msgid "Public"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:78
+msgid "Search Friends..."
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:96
+msgid "Friends <span>%d</span>"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:108 bp-friends/bp-friends-loader.php:168
+#: bp-friends/bp-friends-loader.php:194
+#: bp-themes/bp-default/members/single/activity.php:35
+#: bp-themes/bp-default/activity/index.php:122
+msgid "Friendships"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:118 bp-groups/bp-groups-template.php:1288
+msgid "Requests"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:150
+msgid "Friends <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:151
+msgid "Pending Requests <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:153 bp-friends/bp-friends-screens.php:68
+#: bp-activity/bp-activity-loader.php:156
+#: bp-activity/bp-activity-loader.php:240
+msgid "Friends"
+msgstr ""
+
+#: bp-friends/bp-friends-loader.php:154
+msgid "No Pending Requests"
+msgstr ""
+
+#: bp-friends/bp-friends-notifications.php:24
+msgid "New friendship request from %s"
+msgstr ""
+
+#: bp-friends/bp-friends-notifications.php:26
+msgid ""
+"%1$s wants to add you as a friend.\n"
+"\n"
+"To view all of your pending friendship requests: %2$s\n"
+"\n"
+"To view %3$s's profile: %4$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-friends/bp-friends-notifications.php:66
+#: bp-friends/bp-friends-activity.php:79
+msgid "%s accepted your friendship request"
+msgstr ""
+
+#: bp-friends/bp-friends-notifications.php:68
+msgid ""
+"%1$s accepted your friend request.\n"
+"\n"
+"To view %2$s's profile: %3$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:62 bp-friends/bp-friends-template.php:81
+#: bp-groups/bp-groups-template.php:2276 bp-groups/bp-groups-template.php:2297
+msgid "Recently Active"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:64 bp-friends/bp-friends-template.php:87
+#: bp-groups/bp-groups-template.php:2281 bp-groups/bp-groups-template.php:2312
+msgid "Alphabetically"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:101
+msgid "My Friends"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:101
+msgid "%s's Friends"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:101
+msgid "See All"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:121
+msgid "You haven't added any friend connections yet."
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:121
+msgid "%s hasn't created any friend connections yet."
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:190
+msgid "There aren't enough site members to show a random sample just yet."
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:201
+msgid "Filter Friends"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:238
+msgid "%d friend"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:240
+msgid "%d friends"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:323
+#: bp-friends/bp-friends-template.php:324
+#: bp-themes/bp-default/_inc/ajax.php:418
+msgid "Friendship Requested"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:338
+#: bp-friends/bp-friends-template.php:339
+msgid "Cancel Friendship"
+msgstr ""
+
+#: bp-friends/bp-friends-template.php:355
+#: bp-friends/bp-friends-template.php:356
+#: bp-themes/bp-default/_inc/ajax.php:408
+msgid "Add Friend"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:29 bp-themes/bp-default/_inc/ajax.php:416
+msgid "Friendship could not be requested."
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:31
+msgid "Friendship requested"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:35
+msgid "You are already friends with this user"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:37
+msgid "You already have a pending friendship request with this user"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:64 bp-themes/bp-default/_inc/ajax.php:406
+msgid "Friendship could not be canceled."
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:66
+msgid "Friendship canceled"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:70
+msgid "You are not yet friends with this user"
+msgstr ""
+
+#: bp-friends/bp-friends-actions.php:72
+msgid "You have a pending friendship request with this user"
+msgstr ""
+
+#: bp-friends/bp-friends-functions.php:94
+#: bp-friends/bp-friends-functions.php:103
+msgid "%1$s and %2$s are now friends"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:28
+msgid "Friendship accepted"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:30
+msgid "Friendship could not be accepted"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:39
+msgid "Friendship rejected"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:41
+msgid "Friendship could not be rejected"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:77
+msgid "A member sends you a friendship request"
+msgstr ""
+
+#: bp-friends/bp-friends-screens.php:83
+msgid "A member accepts your friendship request"
+msgstr ""
+
+#: bp-friends/bp-friends-activity.php:50
+msgid "New friendship created"
+msgstr ""
+
+#: bp-friends/bp-friends-activity.php:76
+msgid "%d friends accepted your friendship requests"
+msgstr ""
+
+#: bp-friends/bp-friends-activity.php:90
+msgid "You have %d pending friendship requests"
+msgstr ""
+
+#: bp-friends/bp-friends-activity.php:93
+msgid "You have a friendship request from %s"
+msgstr ""
+
+#: bp-settings/bp-settings-loader.php:84 bp-settings/bp-settings-loader.php:149
+msgid "General"
+msgstr ""
+
+#: bp-settings/bp-settings-loader.php:95 bp-settings/bp-settings-loader.php:156
+#: bp-members/bp-members-adminbar.php:167 bp-members/bp-members-buddybar.php:13
+msgid "Notifications"
+msgstr ""
+
+#: bp-settings/bp-settings-loader.php:107
+#: bp-settings/bp-settings-loader.php:164
+#: bp-members/bp-members-adminbar.php:145
+#: bp-themes/bp-default/members/single/settings/delete-account.php:50
+msgid "Delete Account"
+msgstr ""
+
+#: bp-settings/bp-settings-actions.php:96
+#: bp-settings/bp-settings-actions.php:135
+#: bp-xprofile/bp-xprofile-screens.php:113
+msgid "Changes saved."
+msgstr ""
+
+#: bp-settings/bp-settings-actions.php:99
+msgid "Your new passwords did not match."
+msgstr ""
+
+#: bp-settings/bp-settings-actions.php:102
+msgid "Your existing password is incorrect."
+msgstr ""
+
+#: bp-settings/bp-settings-actions.php:105
+msgid "Sorry, that email address is already used or is invalid."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:24
+msgid "Sorry, you are not allowed to create groups."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:46
+msgid "There was an error saving group details. Please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:68
+msgid "Please fill in all of the required fields"
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:75 bp-groups/bp-groups-actions.php:102
+msgid "There was an error saving group details, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:141
+msgid "%1$s created the group %2$s"
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:189
+msgid "There was an error saving the group avatar, please try uploading again."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:191
+msgid "The group avatar was uploaded successfully!"
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:215 bp-groups/bp-groups-actions.php:222
+msgid "There was an error joining the group."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:224
+msgid "You joined the group!"
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:247
+msgid "There was an error leaving the group."
+msgstr ""
+
+#: bp-groups/bp-groups-actions.php:249 bp-groups/bp-groups-functions.php:280
+msgid "You successfully left the group."
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:21
+msgid "Created a group"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:22
+msgid "Joined a group"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:23
+msgid "New group forum topic"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:24
+msgid "New group forum post"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:93
+msgid "%1$d new membership requests for the group \"%2$s\""
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:98
+msgid "Group Membership Requests"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:107 bp-groups/bp-groups-activity.php:112
+msgid "%s requests group membership"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:130
+msgid "%d accepted group membership requests"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:143
+msgid "Membership for group \"%s\" accepted"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:166
+msgid "%d rejected group membership requests"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:179
+msgid "Membership for group \"%s\" rejected"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:202
+msgid "You were promoted to an admin in %d groups"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:215
+msgid "You were promoted to an admin in the group \"%s\""
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:238
+msgid "You were promoted to a mod in %d groups"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:251
+msgid "You were promoted to a mod in the group \"%s\""
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:275
+msgid "You have %d new group invitations"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:279
+msgid "Group Invites"
+msgstr ""
+
+#: bp-groups/bp-groups-activity.php:287
+msgid "You have an invitation to the group: %s"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:84
+msgid "Search Groups..."
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:172
+msgid "You do not have access to this group."
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:185 bp-groups/bp-groups-template.php:1272
+msgid "Details"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:197 bp-groups/bp-groups-template.php:1282
+msgid "Avatar"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:205
+msgid "Invites"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:231
+msgid "Groups <span>%d</span>"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:243 bp-groups/bp-groups-loader.php:271
+#: bp-groups/bp-groups-loader.php:410 bp-groups/bp-groups-loader.php:437
+msgid "Memberships"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:254
+msgid "Invitations"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:283
+msgctxt "Group home navigation title"
+msgid "Home"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:295
+msgid "Admin"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:314 bp-groups/bp-groups-template.php:1580
+#: bp-groups/bp-groups-template.php:1581 bp-themes/bp-default/_inc/ajax.php:493
+msgid "Request Membership"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:326
+msgid "Forum"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:338
+msgid "Members <span>%s</span>"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:350
+#: bp-themes/bp-default/groups/single/send-invites.php:60
+msgid "Send Invites"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:392
+msgid "Groups <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:393
+msgid "Pending Invites <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:396
+msgid "No Pending Invites"
+msgstr ""
+
+#: bp-groups/bp-groups-loader.php:456 bp-groups/bp-groups-buddybar.php:25
+#: bp-groups/bp-groups-template.php:2337
+msgid "Group Avatar"
+msgstr ""
+
+#: bp-groups/bp-groups-classes.php:752
+msgid "Group Mod"
+msgstr ""
+
+#: bp-groups/bp-groups-classes.php:758 bp-groups/bp-groups-functions.php:98
+msgid "Group Admin"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:10
+msgid "Group Details Updated"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:24
+msgid ""
+"Group details for the group \"%1$s\" were updated:\n"
+"\n"
+"To view the group: %2$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:68
+msgid "Membership request for group: %s"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:70
+msgid ""
+"%1$s wants to join the group \"%2$s\".\n"
+"\n"
+"Because you are the administrator of this group, you must either accept or reject the membership request.\n"
+"\n"
+"To view all pending membership requests for this group, please visit:\n"
+"%3$s\n"
+"\n"
+"To view %4$s's profile: %5$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:119
+msgid "Membership request for group \"%s\" accepted"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:120
+msgid ""
+"Your membership request for the group \"%1$s\" has been accepted.\n"
+"\n"
+"To view the group please login and visit: %2$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:129
+msgid "Membership request for group \"%s\" rejected"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:130
+msgid ""
+"Your membership request for the group \"%1$s\" has been rejected.\n"
+"\n"
+"To submit another request please log in and visit: %2$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:155
+msgid "an administrator"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:158
+msgid "a moderator"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:177
+msgid "You have been promoted in the group: \"%s\""
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:179
+msgid ""
+"You have been promoted to %1$s for the group: \"%2$s\".\n"
+"\n"
+"To view the group please visit: %3$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:227
+msgid "You have an invitation to the group: \"%s\""
+msgstr ""
+
+#: bp-groups/bp-groups-notifications.php:229
+msgid ""
+"One of your friends %1$s has invited you to the group: \"%2$s\".\n"
+"\n"
+"To view your group invites visit: %3$s\n"
+"\n"
+"To view the group visit: %4$s\n"
+"\n"
+"To view %5$s's profile visit: %6$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:57 bp-groups/bp-groups-buddybar.php:19
+msgid "Edit Details"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:65
+msgid "Edit Settings"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:74 bp-members/bp-members-adminbar.php:118
+msgid "Edit Avatar"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:84 bp-groups/bp-groups-buddybar.php:31
+msgid "Manage Invitations"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:93 bp-groups/bp-groups-buddybar.php:35
+msgid "Manage Members"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:102 bp-groups/bp-groups-buddybar.php:39
+msgid "Membership Requests"
+msgstr ""
+
+#: bp-groups/bp-groups-adminbar.php:111 bp-groups/bp-groups-buddybar.php:43
+#: bp-xprofile/bp-xprofile-admin.php:125
+#: bp-themes/bp-default/groups/single/admin.php:358
+msgid "Delete Group"
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:19
+msgid "A dynamic list of recently active, popular, and newest groups"
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:68 bp-groups/bp-groups-widgets.php:168
+msgid "created %s"
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:87
+msgid "There are no groups to display."
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:120
+msgid "Max groups to show:"
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:123
+msgid "Default groups to show:"
+msgstr ""
+
+#: bp-groups/bp-groups-widgets.php:187
+msgid "No groups matched the current filter."
+msgstr ""
+
+#: bp-groups/bp-groups-forums.php:56 bp-groups/bp-groups-forums.php:160
+msgid "%1$s replied to the forum topic %2$s in the group %3$s"
+msgstr ""
+
+#: bp-groups/bp-groups-forums.php:95 bp-groups/bp-groups-forums.php:127
+msgid "%1$s started the forum topic %2$s in the group %3$s"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:49
+msgid "Group invite could not be accepted"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:51
+msgid "Group invite accepted"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:57 bp-groups/bp-groups-functions.php:324
+#: bp-groups/bp-groups-functions.php:809
+msgid "%1$s joined the group %2$s"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:71
+msgid "Group invite could not be rejected"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:73
+msgid "Group invite rejected"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:149
+msgid "There was an error when replying to that topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:151
+msgid "Your reply was posted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:165
+msgid "There was an error when making that topic a sticky"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:167
+msgid "The topic was made sticky successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:179
+msgid "There was an error when unsticking that topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:181
+msgid "The topic was unstuck successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:193
+msgid "There was an error when closing that topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:195
+msgid "The topic was closed successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:207
+msgid "There was an error when opening that topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:209
+msgid "The topic was opened successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:230
+msgid "There was an error deleting the topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:232
+msgid "The topic was deleted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:254
+msgid "There was an error when editing that topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:256
+msgid "The topic was edited successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:280
+msgid "There was an error deleting that post"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:282
+msgid "The post was deleted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:304
+msgid "There was an error when editing that post"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:306
+msgid "The post was edited successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:321 bp-groups/bp-groups-screens.php:338
+msgid "You have been banned from this group."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:346 bp-forums/bp-forums-screens.php:36
+msgid "Please provide a title for your forum topic."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:348 bp-forums/bp-forums-screens.php:38
+msgid "Forum posts cannot be empty. Please enter some text."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:351
+msgid "This group does not have a forum setup yet."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:358 bp-forums/bp-forums-screens.php:45
+msgid "There was an error when creating the topic"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:361 bp-forums/bp-forums-screens.php:48
+msgid "The topic was created successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:405
+msgid "Group invites sent."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:433
+msgid "There was an error sending your group membership request, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:435
+msgid "Your membership request was sent to the group administrator successfully. You will be notified when the group administrator responds to your request."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:482
+msgid "There was an error updating group details, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:484
+msgid "Group details were successfully updated."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:525
+msgid "There was an error updating group settings, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:527
+msgid "Group settings were successfully updated."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:558 bp-xprofile/bp-xprofile-actions.php:36
+msgid "Your avatar was deleted successfully!"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:560 bp-xprofile/bp-xprofile-actions.php:38
+msgid "There was a problem deleting that avatar, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:588
+msgid "There was a problem cropping the avatar, please try uploading it again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:590
+msgid "The new group avatar was uploaded successfully!"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:625
+msgid "There was an error when promoting that user, please try again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:627
+msgid "User promoted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:645
+msgid "There was an error when demoting that user, please try again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:647
+msgid "User demoted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:663
+msgid "There was an error when banning that user, please try again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:665
+msgid "User banned successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:681
+msgid "There was an error when unbanning that user, please try again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:683
+msgid "User ban removed successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:699
+msgid "There was an error removing that user from the group, please try again"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:701
+msgid "User removed successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:739
+msgid "There was an error accepting the membership request, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:741
+msgid "Group membership request accepted"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:750
+msgid "There was an error rejecting the membership request, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:752
+msgid "Group membership request rejected"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:782
+msgid "There was an error deleting the group, please try again."
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:784
+msgid "The group was deleted successfully"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:835
+msgid "A member invites you to join a group"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:841
+msgid "Group information is updated"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:847
+msgid "You are promoted to a group administrator or moderator"
+msgstr ""
+
+#: bp-groups/bp-groups-screens.php:853
+msgid "A member requests to join a private group for which you are an admin"
+msgstr ""
+
+#: bp-groups/bp-groups-buddybar.php:16 bp-members/bp-members-buddybar.php:103
+msgid "Admin Options"
+msgstr ""
+
+#: bp-groups/bp-groups-buddybar.php:21
+msgid "Group Settings"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:171
+msgctxt "Group pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:172
+msgctxt "Group pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:353
+msgid "Public Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:355
+msgid "Hidden Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:357
+msgid "Private Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:359
+msgid "Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:389
+#: bp-activity/bp-activity-template.php:656
+msgid "Group logo of %s"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:431
+msgid "not yet active"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:522
+msgid "Private"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:572 bp-groups/bp-groups-template.php:595
+#: bp-groups/bp-groups-template.php:1034 bp-groups/bp-groups-template.php:1050
+#: bp-groups/bp-groups-template.php:1099 bp-groups/bp-groups-template.php:1115
+#: bp-groups/bp-groups-template.php:1795 bp-groups/bp-groups-template.php:1804
+#: bp-groups/bp-groups-template.php:1813 bp-groups/bp-groups-template.php:2519
+#: bp-forums/bp-forums-template.php:482 bp-forums/bp-forums-template.php:596
+#: bp-forums/bp-forums-template.php:1137 bp-members/bp-members-template.php:484
+#: bp-members/bp-members-template.php:767
+#: bp-members/bp-members-template.php:787
+#: bp-themes/bp-default/groups/single/admin.php:185
+#: bp-themes/bp-default/groups/single/admin.php:210
+#: bp-activity/bp-activity-template.php:572
+#: bp-activity/bp-activity-template.php:672
+#: bp-activity/bp-activity-template.php:681
+msgid "Profile picture of %s"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:577
+msgid "No Admins"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:604
+msgid "No Mods"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:686
+msgid "Filter Groups"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:736
+msgid "Viewing group %1$s to %2$s (of %3$s groups)"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:764
+msgid "%s member"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:766
+#: bp-themes/bp-default/members/single/groups/invites.php:14
+msgid "%s members"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:805
+msgid "%d topic"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:807
+msgid "%d topics"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:839 bp-forums/bp-forums-template.php:675
+msgid "%d post"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:841 bp-forums/bp-forums-template.php:677
+msgid "%d posts"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1041 bp-groups/bp-groups-template.php:1106
+#: bp-themes/bp-default/groups/single/admin.php:189
+#: bp-themes/bp-default/groups/single/admin.php:215
+msgid "Demote to Member"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1054 bp-groups/bp-groups-template.php:1119
+#: bp-groups/bp-groups-template.php:1888
+msgid "joined %s"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1077
+msgid "This group has no administrators"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1105
+#: bp-themes/bp-default/groups/single/admin.php:214
+#: bp-themes/bp-default/groups/single/admin.php:270
+msgid "Promote to Admin"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1139
+msgid "This group has no moderators"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1293 bp-xprofile/bp-xprofile-admin.php:464
+#: bp-forums/bp-forums-template.php:1208
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:50
+#: bp-themes/bp-default/members/single/messages/single.php:24
+#: bp-themes/bp-default/activity/comment.php:43
+#: bp-activity/bp-activity-template.php:1272
+msgid "Delete"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1499 bp-groups/bp-groups-template.php:1500
+#: bp-themes/bp-default/forums/index.php:23
+#: bp-themes/bp-default/forums/single/forum.php:19
+#: bp-themes/bp-default/groups/single/forum.php:17
+msgid "New Topic"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1539 bp-groups/bp-groups-template.php:1540
+#: bp-themes/bp-default/_inc/ajax.php:469
+msgid "Leave Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1562 bp-groups/bp-groups-template.php:1563
+#: bp-themes/bp-default/_inc/ajax.php:491
+msgid "Join Group"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1595 bp-groups/bp-groups-template.php:1596
+msgid "Request Sent"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1618
+msgid "This is a private group and you must request group membership in order to join."
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1620
+msgid "This is a private group. To join you must be a registered site member and request group membership."
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1622
+msgid "This is a private group. Your membership request is awaiting approval from the group administrator."
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1624
+msgid "This is a hidden group and only invited members can join."
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:1938
+msgid "Viewing members %1$s to %2$s (of %3$s members)"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2164
+msgid "Group avatar"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2277 bp-groups/bp-groups-template.php:2300
+msgid "Recently Joined"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2278 bp-groups/bp-groups-template.php:2303
+msgid "Most Popular"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2279 bp-groups/bp-groups-template.php:2306
+msgid "Administrator Of"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2280 bp-groups/bp-groups-template.php:2309
+msgid "Moderator Of"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2341
+msgid "No Group Avatar"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2552
+msgid "requested %s"
+msgstr ""
+
+#: bp-groups/bp-groups-template.php:2736
+msgid "Group Activity RSS Feed"
+msgstr ""
+
+#: bp-groups/bp-groups-functions.php:255
+msgid "As the only Admin, you cannot leave the group."
+msgstr ""
+
+#: bp-groups/bp-groups-functions.php:513
+msgid "%1$s posted an update in the group %2$s"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-loader.php:135
+#: bp-xprofile/bp-xprofile-loader.php:191 bp-xprofile/bp-xprofile-admin.php:463
+#: bp-forums/bp-forums-template.php:1207 bp-themes/bp-default/functions.php:483
+msgid "Edit"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-loader.php:145
+#: bp-xprofile/bp-xprofile-loader.php:198
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:1
+msgid "Change Avatar"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-screens.php:85
+msgid "Please make sure you fill in all required fields in this profile field group before saving."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-screens.php:111
+msgid "There was a problem updating some of your profile information, please try again."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-screens.php:165
+msgid "There was a problem cropping your avatar, please try uploading it again"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-screens.php:167
+msgid "Your new avatar was uploaded successfully!"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:237
+msgid "Please make sure you give the group a name."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:261
+msgid "Add New Field Group"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:263
+msgid "Create Field Group"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:265
+msgid "Edit Field Group"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:267
+#: bp-themes/bp-default/members/single/settings/notifications.php:62
+#: bp-themes/bp-default/members/single/settings/general.php:69
+#: bp-themes/bp-default/members/single/profile/edit.php:126
+#: bp-themes/bp-default/forums/single/edit.php:57
+#: bp-themes/bp-default/forums/single/edit.php:73
+#: bp-themes/bp-default/groups/single/forum/edit.php:60
+#: bp-themes/bp-default/groups/single/forum/edit.php:76
+#: bp-themes/bp-default/groups/single/admin.php:32
+#: bp-themes/bp-default/groups/single/admin.php:117
+msgid "Save Changes"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:275
+#: bp-xprofile/bp-xprofile-classes.php:695
+msgid "Fields marked * are required"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:286
+msgid "Field Group Title"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:294
+msgid "Group Description"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:304
+#: bp-xprofile/bp-xprofile-classes.php:752
+msgid "or"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:304
+#: bp-xprofile/bp-xprofile-classes.php:752
+#: bp-themes/bp-default/forums/index.php:134
+msgid "Cancel"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:612
+msgid "Please enter options for this Field:"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:613
+#: bp-themes/bp-default/members/index.php:55
+#: bp-themes/bp-default/members/single/friends.php:20
+#: bp-themes/bp-default/members/single/groups.php:20
+#: bp-themes/bp-default/members/single/forums.php:18
+#: bp-themes/bp-default/members/single/blogs.php:19
+#: bp-themes/bp-default/blogs/index.php:55
+#: bp-themes/bp-default/forums/index.php:60
+#: bp-themes/bp-default/forums/single/forum.php:34
+#: bp-themes/bp-default/groups/index.php:57
+#: bp-themes/bp-default/groups/single/members.php:12
+#: bp-themes/bp-default/groups/single/forum.php:32
+msgid "Order By:"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:615
+msgid "Order Entered"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:616
+msgid "Name - Ascending"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:617
+msgid "Name - Descending"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:644
+#: bp-xprofile/bp-xprofile-classes.php:661
+msgid "Option"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:646
+#: bp-xprofile/bp-xprofile-classes.php:662
+msgid "Default Value"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:668
+msgid "Add Another Option"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:676
+msgid "Add Field"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:688
+msgid "Edit Field"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:708
+msgid "Field Title"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:717
+msgid "Field Description"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:724
+msgid "Is This Field Required?"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:726
+msgid "Not Required"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:727
+msgid "Required"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:732
+msgid "Field Type"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:734
+msgid "Text Box"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:735
+msgid "Multi-line Text Box"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:736
+msgid "Date Selector"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:737
+msgid "Radio Buttons"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:738
+msgid "Drop Down Select Box"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:739
+msgid "Multi Select Box"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:740
+msgid "Checkboxes"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:770
+msgid "Please make sure you fill out all required fields."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:773
+msgid "Radio button field types require at least one option. Please add options below."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:776
+#: bp-xprofile/bp-xprofile-classes.php:779
+msgid "Select box field types require at least one option. Please add options below."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-classes.php:782
+msgid "Checkbox field types require at least one option. Please add options below."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:25
+msgid "Profile Fields"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:74
+msgid "Extended Profile Fields"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:76 bp-xprofile/bp-xprofile-admin.php:178
+msgid "Add New Group"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:79
+msgid "Your users will distinguish themselves through their profile page. You must give them profile fields that allow them to describe themselves in a way that is relevant to the theme of your social network."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:80
+msgid "NOTE: Any fields in the \"%s\" group will appear on the signup page."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:103 bp-xprofile/bp-xprofile-admin.php:399
+msgid "(Primary)"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:120
+msgid "Add New Field"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:121
+msgid "Edit Group"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:160
+msgid "There are no fields in this group."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:177
+msgid "You have no groups."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:209
+msgid "There was an error saving the group. Please try again"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:212
+msgid "The group was saved successfully."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:244
+msgid "There was an error deleting the group. Please try again"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:247
+msgid "The group was deleted successfully."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:288
+msgid "There was an error saving the field. Please try again"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:294
+msgid "The field was saved successfully."
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:324
+msgid "field"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:326
+msgid "option"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:331
+msgid "There was an error deleting the %s. Please try again"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:334
+msgid "The %s was deleted successfully!"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:399
+msgid "(Required)"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-admin.php:432
+#: bp-themes/bp-default/registration/register.php:119
+#: bp-themes/bp-default/members/single/profile/edit.php:58
+#: bp-themes/bp-default/members/single/profile/edit.php:73
+msgid "Clear"
+msgstr ""
+
+#. translators: no option picked in select box
+
+#: bp-xprofile/bp-xprofile-template.php:432
+#: bp-themes/bp-default/forums/index.php:116
+msgid "----"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:605
+msgid "January"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:605
+msgid "February"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:605
+msgid "March"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:606
+msgid "April"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:606
+msgid "May"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:606
+msgid "June"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:607
+msgid "July"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:607
+msgid "August"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:607
+msgid "September"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:608
+msgid "October"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:608
+msgid "November"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:608
+msgid "December"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:730
+msgid "Avatar uploads are currently disabled. Why not use a <a href=\"http://gravatar.com\" target=\"_blank\">gravatar</a> instead?"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:739
+msgid "Profile not recently updated"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:750
+msgid "Profile updated %s"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-template.php:793
+#: bp-xprofile/bp-xprofile-template.php:794
+#: bp-members/bp-members-adminbar.php:110
+msgid "Edit Profile"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-activity.php:22
+msgid "New member registered"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-activity.php:23
+msgid "Updated Profile"
+msgstr ""
+
+#: bp-xprofile/bp-xprofile-activity.php:128
+msgid "%s changed their profile picture"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:12
+msgid "Forums Setup"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:38
+msgid "Settings Saved."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:51
+msgid "bbPress forum integration in BuddyPress has been set up correctly. If you are having problems you can <a href=\"%s\" title=\"Reinstall bbPress\">re-install</a>."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:52
+msgid "NOTE: The forums directory will only work if your bbPress tables are in the same database as your WordPress tables. If you are not using an existing bbPress install you can ignore this message."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:69
+msgid "The bb-config.php file was not found at that location, please try again."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:72
+msgid "Forums were set up correctly using your existing bbPress install!"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:73
+msgid "BuddyPress will now use its internal copy of bbPress to run the forums on your site. If you wish, you can remove your old bbPress installation files, as long as you keep the bb-config.php file in the same location."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:78
+msgid "Existing bbPress Installation"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:79
+msgid "BuddyPress can make use of your existing bbPress install. Just provide the location of your <code>bb-config.php</code> file, and BuddyPress will do the rest."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:81 bp-forums/bp-forums-admin.php:113
+msgid "Complete Installation"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:97
+msgid "<p>All done! Configuration settings have been saved to the file <code>bb-config.php</code> in the root of your WordPress install.</p>"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:101
+msgid "<p>A configuration file could not be created. No problem, but you will need to save the text shown below into a file named <code>bb-config.php</code> in the root directory of your WordPress installation before you can start using the forum functionality.</p>"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:110
+msgid "New bbPress Installation"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:111
+msgid ""
+"You've decided to set up a new installation of bbPress for forum management in BuddyPress. This is very simple and is usually just a one click\n"
+"\t\t\t\tprocess. When you're ready, hit the link below."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:123
+msgid "bbPress files were not found. To install the forums component you must download a copy of bbPress and make sure it is in the folder: \"%s\""
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:128
+msgid "Forums in BuddyPress make use of a bbPress installation to function. You can choose to either let BuddyPress set up a new bbPress install, or use an already existing bbPress install. Please choose one of the options below."
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:130
+msgid "Set up a new bbPress installation"
+msgstr ""
+
+#: bp-forums/bp-forums-admin.php:131
+msgid "Use an existing bbPress installation"
+msgstr ""
+
+#: bp-forums/bp-forums-loader.php:61
+msgid "Search Forums..."
+msgstr ""
+
+#: bp-forums/bp-forums-loader.php:135 bp-forums/bp-forums-loader.php:201
+msgid "Topics Started"
+msgstr ""
+
+#: bp-forums/bp-forums-loader.php:146
+msgid "Replied To"
+msgstr ""
+
+#: bp-forums/bp-forums-loader.php:208
+msgid "Replies"
+msgstr ""
+
+#: bp-forums/bp-forums-loader.php:215
+msgid "Favorite Topics"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:183 bp-forums/bp-forums-template.php:399
+msgctxt "Forum topic pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:184 bp-forums/bp-forums-template.php:400
+msgctxt "Forum topic pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:500 bp-forums/bp-forums-template.php:559
+#: bp-forums/bp-forums-template.php:1153
+msgid "Deleted User"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:577
+msgid "Group logo for %s"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:773
+msgid "Edit Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:777
+msgid "Sticky Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:779
+msgid "Un-stick Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:782
+msgid "Open Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:784
+msgid "Close Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:786
+msgid "Delete Topic"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:905
+msgid " matching tag \"%s\""
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:907
+msgid "Viewing topic %s to %s (of %s total topics%s)"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:989
+msgctxt "Forum thread pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:990
+msgctxt "Forum thread pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-forums/bp-forums-template.php:1238
+msgid "Viewing post %1$s to %2$s (%3$s total posts)"
+msgstr ""
+
+#: bp-forums/bp-forums-screens.php:13
+msgid "The forums component has not been set up yet."
+msgstr ""
+
+#: bp-forums/bp-forums-screens.php:56 bp-forums/bp-forums-screens.php:61
+msgid "Please pick the group forum where you would like to post this topic."
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:24
+msgid "Private Messages"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:79
+msgid "Search Messages..."
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:96 bp-messages/bp-messages-actions.php:41
+msgid "Messages <span>%s</span>"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:114
+#: bp-messages/bp-messages-loader.php:182
+#: bp-messages/bp-messages-functions.php:237
+msgid "Inbox"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:124
+#: bp-messages/bp-messages-loader.php:203
+msgid "Sent"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:134
+#: bp-messages/bp-messages-loader.php:210
+msgid "Compose"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:145
+msgid "Notices"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:178
+msgid "Messages <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:179
+msgid "Inbox <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:181
+#: bp-messages/bp-messages-screens.php:149
+msgid "Messages"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:218
+msgid "All Member Notices"
+msgstr ""
+
+#: bp-messages/bp-messages-loader.php:237
+msgid "My Messages"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:75
+msgctxt "Message pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:76
+msgctxt "Message pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:343
+msgid "Viewing message %1$s to %2$s (of %3$s messages)"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:401
+msgid "Select:"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:404
+msgid "Read"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:405
+msgid "Unread"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:406
+msgid "All"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:409
+msgid "Mark as Read"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:410
+msgid "Mark as Unread"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:412
+msgid "Delete Selected"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:421
+msgid "Currently Active"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:496
+msgid "Deactivate"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:547
+#: bp-themes/bp-default/functions.php:155
+msgid "Close"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:591
+msgid "Send a private message to this user."
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:592
+msgid "Private Message"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:780
+msgid "%d Recipients"
+msgstr ""
+
+#: bp-messages/bp-messages-template.php:855
+#: bp-themes/bp-default/_inc/ajax.php:530
+msgid "Sent %s"
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:30
+msgid "Your reply was sent successfully"
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:32
+msgid "There was a problem sending your reply, please try again"
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:48
+msgid "From: %s"
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:78
+msgid "There was an error deleting that message."
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:80
+msgid "Message deleted."
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:102
+msgid "There was an error deleting messages."
+msgstr ""
+
+#: bp-messages/bp-messages-actions.php:104
+#: bp-themes/bp-default/_inc/ajax.php:596
+msgid "Messages deleted."
+msgstr ""
+
+#: bp-messages/bp-messages-classes.php:203
+msgid " Recipients"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:54
+#: bp-messages/bp-messages-screens.php:76
+msgid "There was an error sending that message, please try again"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:59
+msgid "Notice sent successfully!"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:62
+msgid "There was an error sending that notice, please try again"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:73
+msgid "Message sent successfully!"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:100
+msgid "There was a problem deactivating that notice."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:102
+msgid "Notice deactivated."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:106
+msgid "There was a problem activating that notice."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:108
+msgid "Notice activated."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:112
+msgid "There was a problem deleting that notice."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:114
+msgid "Notice deleted."
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:158
+msgid "A member sends you a new message"
+msgstr ""
+
+#: bp-messages/bp-messages-screens.php:164
+msgid "A new site notice is posted"
+msgstr ""
+
+#: bp-messages/bp-messages-notifications.php:30
+msgid "New message from %s"
+msgstr ""
+
+#: bp-messages/bp-messages-notifications.php:32
+msgid ""
+"%s sent you a new message:\n"
+"\n"
+"Subject: %s\n"
+"\n"
+"\"%s\"\n"
+"\n"
+"To view and read your messages please log in and visit: %s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-messages/bp-messages-functions.php:48
+msgid "Re: %s"
+msgstr ""
+
+#: bp-messages/bp-messages-functions.php:56
+msgid "No Subject"
+msgstr ""
+
+#: bp-messages/bp-messages-functions.php:240
+msgid "You have %d new messages"
+msgstr ""
+
+#: bp-messages/bp-messages-functions.php:243
+msgid "You have %d new message"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:55
+msgid "Log in"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:127
+#: bp-members/bp-members-buddybar.php:116
+msgid "Mark as Spammer"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:129
+msgid "Are you sure you want to mark this user as a spammer?"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:135
+#: bp-members/bp-members-buddybar.php:120
+msgid "Not a Spammer"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:137
+msgid "Are you sure you want to mark this user as not a spammer?"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:147
+msgid "Are you sure you want to delete this user's account?"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:165
+msgid "Notifications <span id=\"ab-pending-notifications\" class=\"pending-count\">%s</span>"
+msgstr ""
+
+#: bp-members/bp-members-adminbar.php:190
+msgid "No new notifications"
+msgstr ""
+
+#: bp-members/bp-members-actions.php:87
+msgid "User marked as spammer. Spam users are visible only to site admins."
+msgstr ""
+
+#: bp-members/bp-members-actions.php:89
+msgid "User removed as spammer."
+msgstr ""
+
+#: bp-members/bp-members-actions.php:136
+msgid "%s has been deleted from the system."
+msgstr ""
+
+#: bp-members/bp-members-actions.php:138
+msgid "There was an error deleting %s from the system. Please try again."
+msgstr ""
+
+#: bp-members/bp-members-buddybar.php:34
+msgid "No new notifications."
+msgstr ""
+
+#: bp-members/bp-members-buddybar.php:62
+msgid "Blog Authors"
+msgstr ""
+
+#: bp-members/bp-members-buddybar.php:108
+msgid "Edit %s's Profile"
+msgstr ""
+
+#: bp-members/bp-members-buddybar.php:112
+msgid "Edit %s's Avatar"
+msgstr ""
+
+#: bp-members/bp-members-buddybar.php:124
+msgid "Delete %s's Account"
+msgstr ""
+
+#: bp-members/bp-members-loader.php:70
+msgid "Search Members..."
+msgstr ""
+
+#: bp-members/bp-members-loader.php:173
+msgid "You"
+msgstr ""
+
+#: bp-members/bp-members-functions.php:671
+msgid "<strong>ERROR</strong>: Your account has been marked as a spammer."
+msgstr ""
+
+#: bp-members/bp-members-signup.php:57
+msgid "Please make sure you enter your password twice"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:61
+msgid "The passwords you entered do not match."
+msgstr ""
+
+#: bp-members/bp-members-signup.php:84
+msgid "This is a required field"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:197
+msgid "Your account is now active!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:306
+msgid "Please enter a username"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:317
+msgid "Only lowercase letters and numbers allowed"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:320
+msgid "Username must be at least 4 characters"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:323
+msgid "Sorry, usernames may not contain the character \"_\"!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:330
+msgid "Sorry, usernames must have letters too!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:333
+msgid "Please check your email address."
+msgstr ""
+
+#: bp-members/bp-members-signup.php:336 bp-members/bp-members-signup.php:344
+msgid "Sorry, that email address is not allowed!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:349
+msgid "Sorry, that username already exists!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:353
+msgid "Sorry, that email address is already used!"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:392
+msgid "<strong>ERROR</strong>: Couldn&#8217;t register you... please contact the <a href=\"mailto:%s\">webmaster</a> !"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:477 bp-members/bp-members-signup.php:481
+msgid "Invalid activation key"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:521
+msgid "%s became a registered member"
+msgstr ""
+
+#: bp-members/bp-members-signup.php:595
+msgid "<strong>ERROR</strong>: Your account has not been activated. Check your email for the activation link."
+msgstr ""
+
+#: bp-members/bp-members-template.php:212
+msgctxt "Member pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-members/bp-members-template.php:213
+msgctxt "Member pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-members/bp-members-template.php:354
+msgid "Viewing member %1$s to %2$s (of %3$s active members)"
+msgstr ""
+
+#: bp-members/bp-members-template.php:356
+msgid "Viewing member %1$s to %2$s (of %3$s members with friends)"
+msgstr ""
+
+#: bp-members/bp-members-template.php:358
+msgid "Viewing member %1$s to %2$s (of %3$s members online)"
+msgstr ""
+
+#: bp-members/bp-members-template.php:360
+msgid "Viewing member %1$s to %2$s (of %3$s members)"
+msgstr ""
+
+#: bp-members/bp-members-template.php:561
+msgid "Never active"
+msgstr ""
+
+#: bp-members/bp-members-template.php:583
+msgctxt "member latest update in member directory"
+msgid "- &quot;%s &quot;"
+msgstr ""
+
+#: bp-members/bp-members-template.php:627
+msgid "registered %s"
+msgstr ""
+
+#: bp-members/bp-members-template.php:1078
+msgid "Your Avatar"
+msgstr ""
+
+#: bp-members/bp-members-template.php:1146
+msgid "Activity RSS Feed"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:24 bp-themes/bp-default/attachment.php:18
+#: bp-themes/bp-default/search.php:26 bp-themes/bp-default/archive.php:24
+#: bp-themes/bp-default/single.php:16
+msgctxt "Post written by..."
+msgid "by %s"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:27
+msgctxt "Sticky post"
+msgid "Featured"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:32 bp-themes/bp-default/attachment.php:22
+#: bp-themes/bp-default/search.php:30 bp-themes/bp-default/archive.php:28
+msgid "Permanent Link to"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:34 bp-themes/bp-default/search.php:32
+#: bp-themes/bp-default/archive.php:30 bp-themes/bp-default/single.php:23
+msgid "%1$s <span>in %2$s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:37 bp-themes/bp-default/search.php:35
+#: bp-themes/bp-default/archive.php:33 bp-themes/bp-default/single.php:28
+msgid "Read the rest of this entry &rarr;"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:38 bp-themes/bp-default/page.php:20
+#: bp-themes/bp-default/onecolumn-page.php:31
+#: bp-themes/bp-default/archive.php:34 bp-themes/bp-default/single.php:30
+msgid "Pages: "
+msgstr ""
+
+#: bp-themes/bp-default/index.php:41 bp-themes/bp-default/search.php:38
+#: bp-themes/bp-default/archive.php:37 bp-themes/bp-default/single.php:33
+msgid "Tags: "
+msgstr ""
+
+#: bp-themes/bp-default/index.php:41 bp-themes/bp-default/search.php:38
+#: bp-themes/bp-default/archive.php:37
+msgid "No Comments &#187;"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:41 bp-themes/bp-default/search.php:38
+#: bp-themes/bp-default/archive.php:37
+msgid "1 Comment &#187;"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:41 bp-themes/bp-default/search.php:38
+#: bp-themes/bp-default/archive.php:37
+msgid "% Comments &#187;"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:54 bp-themes/bp-default/archive.php:50
+msgid "Not Found"
+msgstr ""
+
+#: bp-themes/bp-default/index.php:55
+msgid "Sorry, but you are looking for something that isn't here."
+msgstr ""
+
+#: bp-themes/bp-default/registration/activate.php:12
+msgid "Account Activated"
+msgstr ""
+
+#: bp-themes/bp-default/registration/activate.php:17
+msgid "Your account was activated successfully! Your account details have been sent to you in a separate email."
+msgstr ""
+
+#: bp-themes/bp-default/registration/activate.php:19
+msgid "Your account was activated successfully! You can now log in with the username and password you provided when you signed up."
+msgstr ""
+
+#: bp-themes/bp-default/registration/activate.php:28
+msgid "Please provide a valid activation key."
+msgstr ""
+
+#: bp-themes/bp-default/registration/activate.php:32
+msgid "Activation Key:"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:16
+msgid "User registration is currently not allowed."
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:27
+msgid "Registering for this site is easy, just fill in the fields below and we'll get a new account set up for you in no time."
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:35
+msgid "Account Details"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:37
+#: bp-themes/bp-default/sidebar.php:44
+msgid "Username"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:37
+#: bp-themes/bp-default/registration/register.php:41
+#: bp-themes/bp-default/registration/register.php:45
+#: bp-themes/bp-default/registration/register.php:49
+#: bp-themes/bp-default/registration/register.php:76
+#: bp-themes/bp-default/registration/register.php:84
+#: bp-themes/bp-default/registration/register.php:92
+#: bp-themes/bp-default/registration/register.php:102
+#: bp-themes/bp-default/registration/register.php:113
+#: bp-themes/bp-default/registration/register.php:128
+#: bp-themes/bp-default/registration/register.php:139
+#: bp-themes/bp-default/registration/register.php:189
+#: bp-themes/bp-default/registration/register.php:198
+#: bp-themes/bp-default/members/single/profile/edit.php:26
+#: bp-themes/bp-default/members/single/profile/edit.php:33
+#: bp-themes/bp-default/members/single/profile/edit.php:40
+#: bp-themes/bp-default/members/single/profile/edit.php:49
+#: bp-themes/bp-default/members/single/profile/edit.php:67
+#: bp-themes/bp-default/members/single/profile/edit.php:83
+#: bp-themes/bp-default/members/single/profile/edit.php:93
+msgid "(required)"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:41
+msgid "Email Address"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:45
+msgid "Choose a Password"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:49
+msgid "Confirm Password"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:65
+msgid "Profile Details"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:183
+msgid "Blog Details"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:185
+msgid "Yes, I'd like to create a new site"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:189
+msgid "Blog URL"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:198
+msgid "Site Title"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:219
+msgid "Complete Sign Up"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:230
+msgid "Sign Up Complete!"
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:236
+msgid "You have successfully created your account! To begin using this site you will need to activate your account via the email we have just sent to your address."
+msgstr ""
+
+#: bp-themes/bp-default/registration/register.php:238
+msgid "You have successfully created your account! Please log in using the username and password you have just created."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:147
+#: bp-activity/bp-activity-actions.php:143
+msgid "Please enter some content to post."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:164
+msgid "There was a problem posting your update, please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:189
+#: bp-activity/bp-activity-actions.php:186
+msgid "Please do not leave the comment area blank."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:194
+#: bp-themes/bp-default/_inc/ajax.php:205
+#: bp-activity/bp-activity-actions.php:199
+msgid "There was an error posting that reply, please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:262
+#: bp-themes/bp-default/_inc/ajax.php:297
+msgid "There was a problem when deleting. Please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:312
+#: bp-themes/bp-default/activity/entry.php:72
+msgid "Remove Favorite"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:321
+#: bp-themes/bp-default/activity/entry.php:68
+msgid "Favorite"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:380
+#: bp-themes/bp-default/groups/single/send-invites.php:42
+#: bp-themes/bp-default/groups/create.php:234
+msgid "Remove Invite"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:421
+msgid "Request Pending"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:433
+msgid "There was a problem accepting that request. Please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:444
+msgid "There was a problem rejecting that request. Please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:467
+msgid "Error joining group"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:477
+msgid "Error requesting membership"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:479
+msgid "Membership Requested"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:488
+msgid "Error leaving group"
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:505
+msgid "There was a problem closing the notice."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:547
+msgid "There was a problem sending that reply. Please try again."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:557
+msgid "There was a problem marking messages as unread."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:573
+msgid "There was a problem marking messages as read."
+msgstr ""
+
+#: bp-themes/bp-default/_inc/ajax.php:589
+msgid "There was a problem deleting messages."
+msgstr ""
+
+#: bp-themes/bp-default/footer.php:15
+msgid "Proudly powered by <a href=\"%1$s\">WordPress</a> and <a href=\"%2$s\">BuddyPress</a>."
+msgstr ""
+
+#: bp-themes/bp-default/sidebar.php:37
+msgid "Please <a href=\"%s\" title=\"Create an account\">create an account</a> to get started."
+msgstr ""
+
+#: bp-themes/bp-default/sidebar.php:47
+msgid "Password"
+msgstr ""
+
+#: bp-themes/bp-default/sidebar.php:50
+msgid "Remember Me"
+msgstr ""
+
+#: bp-themes/bp-default/sidebar.php:64
+msgid "Forum Topic Tags"
+msgstr ""
+
+#: bp-themes/bp-default/comments.php:3
+msgid "Password Protected"
+msgstr ""
+
+#: bp-themes/bp-default/comments.php:4
+msgid "Enter the password to view comments."
+msgstr ""
+
+#: bp-themes/bp-default/comments.php:24
+msgid "1 response to %2$s"
+msgid_plural "%1$s responses to %2$s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bp-themes/bp-default/comments.php:46
+msgid "Comments are closed, but <a href=\"%1$s\" title=\"Trackback URL for this post\">trackbacks</a> and pingbacks are open."
+msgstr ""
+
+#: bp-themes/bp-default/comments.php:50
+msgid "Comments are closed."
+msgstr ""
+
+#: bp-themes/bp-default/comments.php:62
+msgid "1 trackback"
+msgid_plural "%d trackbacks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bp-themes/bp-default/404.php:7
+msgid "Page not found"
+msgstr ""
+
+#: bp-themes/bp-default/404.php:9
+msgid "We're sorry, but we can't find the page that you're looking for. Perhaps searching will help."
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:81
+msgid "Primary Navigation"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:149
+msgid "My Favorites"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:150
+msgid "Accepted"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:151
+msgid "Rejected"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:152
+msgid "Show all comments for this thread"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:153
+msgid "Show all"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:154
+msgid "comments"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:365
+msgid "The sidebar widget area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:374
+msgid "First Footer Widget Area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:376
+msgid "The first footer widget area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:385
+msgid "Second Footer Widget Area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:387
+msgid "The second footer widget area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:396
+msgid "Third Footer Widget Area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:398
+msgid "The third footer widget area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:407
+msgid "Fourth Footer Widget Area"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:409
+msgid "The fourth footer widget area"
+msgstr ""
+
+#. translators: 1: comment author url, 2: comment author name, 3: comment
+#. permalink, 4: comment date/timestamp
+
+#: bp-themes/bp-default/functions.php:464
+msgid "<a href=\"%1$s\" rel=\"nofollow\">%2$s</a> said on <a href=\"%3$s\"><span class=\"time-since\">%4$s</span></a>"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:471
+msgid "Your comment is awaiting moderation."
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:483
+msgid "Edit comment"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:553
+msgid "Theme activated! This theme contains <a href=\"%s\">custom header image</a> support and <a href=\"%s\">sidebar widgets</a>."
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:624
+msgid "Email"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:634
+msgid "You must be <a href=\"%1$s\">logged in</a> to post a comment."
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:635
+msgid "Leave a reply"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:719
+msgid "&larr; Previous Entries"
+msgstr ""
+
+#: bp-themes/bp-default/functions.php:720
+msgid "Next Entries &rarr;"
+msgstr ""
+
+#: bp-themes/bp-default/page.php:18 bp-themes/bp-default/onecolumn-page.php:29
+msgid "<p class=\"serif\">Read the rest of this page &rarr;</p>"
+msgstr ""
+
+#: bp-themes/bp-default/page.php:21 bp-themes/bp-default/onecolumn-page.php:32
+msgid "Edit this page."
+msgstr ""
+
+#: bp-themes/bp-default/header.php:27
+msgctxt "Home page banner link title"
+msgid "Home"
+msgstr ""
+
+#: bp-themes/bp-default/header.php:30
+msgid "Search for:"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:23
+msgid "Members Directory"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:35
+#: bp-themes/bp-default/activity/index.php:41
+msgid "All Members <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:39
+#: bp-themes/bp-default/activity/index.php:51
+msgid "My Friends <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:57
+#: bp-themes/bp-default/members/single/friends.php:22
+#: bp-themes/bp-default/members/single/groups.php:22
+#: bp-themes/bp-default/members/single/forums.php:20
+#: bp-themes/bp-default/members/single/blogs.php:21
+#: bp-themes/bp-default/blogs/index.php:57
+#: bp-themes/bp-default/forums/index.php:62
+#: bp-themes/bp-default/forums/single/forum.php:36
+#: bp-themes/bp-default/groups/index.php:59
+#: bp-themes/bp-default/groups/single/members.php:14
+#: bp-themes/bp-default/groups/single/forum.php:34
+msgid "Last Active"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:58
+#: bp-themes/bp-default/members/single/friends.php:23
+#: bp-themes/bp-default/groups/single/members.php:15
+msgid "Newest Registered"
+msgstr ""
+
+#: bp-themes/bp-default/members/index.php:62
+#: bp-themes/bp-default/members/single/friends.php:24
+#: bp-themes/bp-default/members/single/groups.php:25
+#: bp-themes/bp-default/members/single/blogs.php:23
+#: bp-themes/bp-default/blogs/index.php:59
+#: bp-themes/bp-default/groups/index.php:62
+#: bp-themes/bp-default/groups/single/members.php:19
+msgid "Alphabetical"
+msgstr ""
+
+#: bp-themes/bp-default/members/members-loop.php:108
+msgid "Sorry, no members were found."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/notifications.php:50
+msgid "Email Notification"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/notifications.php:55
+msgid "Send a notification by email when:"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/delete-account.php:55
+msgid "WARNING: Deleting your account will completely remove ALL content associated with it. There is no way back, please be careful with this option."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/delete-account.php:58
+msgid "I understand the consequences of deleting my account."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/delete-account.php:63
+msgid "Delete My Account"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:50
+msgid "General Settings"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:56
+msgid "Current Password <span>(required to update email or change current password)</span>"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:57
+msgid "Password Lost and Found"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:57
+msgid "Lost your password?"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:59
+msgid "Account Email"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:62
+msgid "Change Password <span>(leave blank for no change)</span>"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:63
+msgid "New Password"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/settings/general.php:64
+msgid "Repeat New Password"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/profile-wp.php:8
+msgid "%s's Profile"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:7
+msgid "Your avatar will be used on your profile and throughout the site. If there is a <a href=\"http://gravatar.com\">Gravatar</a> associated with your account email we will use that, or you can upload an image from your computer."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:14
+msgid "Click below to select a JPG, GIF or PNG format photo from your computer and then click 'Upload Image' to proceed."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:18
+#: bp-themes/bp-default/groups/single/admin.php:131
+#: bp-themes/bp-default/groups/create.php:158
+msgid "Upload Image"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:23
+msgid "If you'd like to delete your current avatar but not upload a new one, please use the delete avatar button."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:24
+#: bp-themes/bp-default/groups/single/admin.php:139
+msgid "Delete Avatar"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:24
+msgid "Delete My Avatar"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:31
+msgid "Crop Your New Avatar"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:33
+#: bp-themes/bp-default/groups/single/admin.php:151
+#: bp-themes/bp-default/groups/create.php:171
+msgid "Avatar to crop"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:36
+#: bp-themes/bp-default/groups/single/admin.php:154
+#: bp-themes/bp-default/groups/create.php:174
+msgid "Avatar preview"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:39
+#: bp-themes/bp-default/groups/single/admin.php:157
+#: bp-themes/bp-default/groups/create.php:177
+msgid "Crop Image"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/change-avatar.php:55
+msgid "Your avatar will be used on your profile and throughout the site. To change your avatar, please create an account with <a href=\"http://gravatar.com\">Gravatar</a> using the same email address as you used to register with this site."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/profile/edit.php:10
+msgid "Editing '%s' Profile Group"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/groups.php:23
+#: bp-themes/bp-default/groups/index.php:60
+msgid "Most Members"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/groups.php:24
+#: bp-themes/bp-default/groups/index.php:61
+msgid "Newly Created"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/forums.php:21
+#: bp-themes/bp-default/forums/index.php:63
+#: bp-themes/bp-default/forums/single/forum.php:37
+#: bp-themes/bp-default/groups/single/forum.php:35
+msgid "Most Posts"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/forums.php:22
+#: bp-themes/bp-default/forums/index.php:64
+#: bp-themes/bp-default/forums/single/forum.php:38
+#: bp-themes/bp-default/groups/single/forum.php:36
+msgid "Unreplied"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/compose.php:5
+msgid "Send To (Username or Friend's Name)"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/compose.php:14
+msgid "This is a notice to all users."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/compose.php:17
+msgid "Subject"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/compose.php:20
+msgid "Message"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/compose.php:28
+msgid "Send Message"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/notices-loop.php:31
+msgid "Sent:"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/notices-loop.php:38
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:50
+#: bp-themes/bp-default/members/single/messages/single.php:24
+msgid "Delete Message"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/notices-loop.php:49
+msgid "Sorry, no notices were found."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:31
+msgid "From:"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:36
+msgid "To:"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:42
+msgid "View Message"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/messages-loop.php:68
+msgid "Sorry, no messages were found."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/single.php:14
+msgid "You are alone in this conversation."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/single.php:18
+msgid "Conversation between %s and you."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/single.php:75
+msgid "Send a Reply"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/messages/single.php:91
+msgid "Send Reply"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/groups/invites.php:23
+#: bp-themes/bp-default/members/single/friends/requests.php:37
+#: bp-themes/bp-default/groups/single/admin.php:318
+msgid "Accept"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/groups/invites.php:24
+#: bp-themes/bp-default/members/single/friends/requests.php:38
+#: bp-themes/bp-default/groups/single/admin.php:320
+msgid "Reject"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/groups/invites.php:37
+msgid "You have no outstanding group invites."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/friends/requests.php:68
+msgid "You have no pending friendship requests."
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:18
+#: bp-themes/bp-default/activity/index.php:94
+#: bp-themes/bp-default/groups/single/activity.php:8
+msgid "Show:"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:20
+#: bp-themes/bp-default/activity/index.php:96
+#: bp-themes/bp-default/groups/single/activity.php:10
+msgid "Everything"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:21
+#: bp-themes/bp-default/activity/index.php:97
+#: bp-themes/bp-default/groups/single/activity.php:11
+msgid "Updates"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:28
+#: bp-themes/bp-default/activity/index.php:102
+msgid "Comments"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:43
+#: bp-themes/bp-default/activity/index.php:108
+#: bp-themes/bp-default/groups/single/activity.php:14
+msgid "Forum Topics"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:44
+#: bp-themes/bp-default/activity/index.php:109
+#: bp-themes/bp-default/groups/single/activity.php:15
+msgid "Forum Replies"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:50
+#: bp-themes/bp-default/activity/index.php:115
+msgid "New Groups"
+msgstr ""
+
+#: bp-themes/bp-default/members/single/activity.php:51
+#: bp-themes/bp-default/activity/index.php:116
+#: bp-themes/bp-default/groups/single/activity.php:18
+msgid "Group Memberships"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:41
+msgid "The public activity for everyone on this site."
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:51
+msgid "The activity of my friends only."
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:63
+msgid "The activity of groups I am a member of."
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:63
+#: bp-themes/bp-default/groups/index.php:41
+msgid "My Groups <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:73
+msgid "The activity I've marked as a favorite."
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:73
+msgid "My Favorites <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:79
+msgid "Activity that I have been mentioned in."
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:79
+msgid "Public Mentions"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:79
+msgid "<span>%s new</span>"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:89
+#: bp-themes/bp-default/groups/single/activity.php:3
+msgid "RSS Feed"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:89
+#: bp-themes/bp-default/groups/single/activity.php:3
+msgid "RSS"
+msgstr ""
+
+#: bp-themes/bp-default/activity/index.php:126
+msgid "New Members"
+msgstr ""
+
+#. translators: 1: user profile link, 2: user name, 3: activity permalink, 3:
+#. activity timestamp
+
+#: bp-themes/bp-default/activity/comment.php:27
+msgid "<a href=\"%1$s\">%2$s</a> replied <a href=\"%3$s\" class=\"activity-time-since\"><span class=\"time-since\">%4$s</span></a>"
+msgstr ""
+
+#: bp-themes/bp-default/activity/comment.php:37
+#: bp-themes/bp-default/forums/single/edit.php:10
+#: bp-themes/bp-default/groups/single/forum/edit.php:10
+msgid "Reply"
+msgstr ""
+
+#: bp-themes/bp-default/activity/post-form.php:23
+msgid "What's new in %s, %s?"
+msgstr ""
+
+#: bp-themes/bp-default/activity/post-form.php:25
+msgid "What's new, %s?"
+msgstr ""
+
+#: bp-themes/bp-default/activity/post-form.php:35
+msgid "Post Update"
+msgstr ""
+
+#: bp-themes/bp-default/activity/post-form.php:42
+msgid "Post in"
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:37
+msgid "In reply to: "
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:37
+msgid "View Thread / Permalink"
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:60
+msgid "Comment <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:68
+msgid "Mark as Favorite"
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:104
+msgid "Post"
+msgstr ""
+
+#: bp-themes/bp-default/activity/entry.php:104
+msgid "or press esc to cancel."
+msgstr ""
+
+#: bp-themes/bp-default/activity/activity-loop.php:41
+msgid "Load More"
+msgstr ""
+
+#: bp-themes/bp-default/activity/activity-loop.php:55
+msgid "Sorry, there was no activity found. Please try a different filter."
+msgstr ""
+
+#: bp-themes/bp-default/blogs/index.php:35
+msgid "All Sites <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/blogs/index.php:39
+msgid "My Sites <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/blogs/blogs-loop.php:90
+msgid "Sorry, there were no sites found."
+msgstr ""
+
+#: bp-themes/bp-default/blogs/create.php:32
+msgid "Site registration is currently disabled"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:23
+msgid "Forums Directory"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:40
+msgid "All Topics <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:44
+msgid "My Topics <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:100
+msgid "Create New Topic:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:107
+#: bp-themes/bp-default/forums/single/edit.php:52
+#: bp-themes/bp-default/groups/single/forum/edit.php:52
+#: bp-themes/bp-default/groups/single/forum.php:74
+msgid "Content:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:110
+#: bp-themes/bp-default/groups/single/forum/edit.php:55
+#: bp-themes/bp-default/groups/single/forum.php:77
+msgid "Tags (comma separated):"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:113
+msgid "Post In Group Forum:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:133
+#: bp-themes/bp-default/groups/single/forum.php:83
+msgid "Post Topic"
+msgstr ""
+
+#: bp-themes/bp-default/forums/index.php:145
+msgid "You are not a member of any groups so you don't have any group forums you can post in. To start posting, first find a group that matches the topic subject you'd like to start. If this group does not exist, why not <a href='%s'>create a new group</a>? Once you have joined or created the group you can post your topic in that group's forum."
+msgstr ""
+
+#: bp-themes/bp-default/forums/forums-loop.php:39
+msgid "Topic"
+msgstr ""
+
+#: bp-themes/bp-default/forums/forums-loop.php:41
+msgid "Freshness"
+msgstr ""
+
+#: bp-themes/bp-default/forums/forums-loop.php:54
+msgid "Permalink"
+msgstr ""
+
+#. translators: "started by [poster] in [forum]"
+
+#: bp-themes/bp-default/forums/forums-loop.php:61
+msgid "Started by %1$s"
+msgstr ""
+
+#. translators: "started by [poster] in [forum]"
+
+#: bp-themes/bp-default/forums/forums-loop.php:72
+msgid "in %1$s"
+msgstr ""
+
+#: bp-themes/bp-default/forums/forums-loop.php:122
+msgid "Sorry, there were no forum topics found."
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/forum.php:25
+#: bp-themes/bp-default/forums/single/edit.php:16
+#: bp-themes/bp-default/forums/single/topic.php:43
+#: bp-themes/bp-default/groups/single/forum/edit.php:16
+#: bp-themes/bp-default/groups/single/forum/topic.php:16
+#: bp-themes/bp-default/groups/single/forum.php:23
+msgid "Forum Directory"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/edit.php:25
+#: bp-themes/bp-default/groups/single/forum/edit.php:25
+msgid "Edit:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/edit.php:88
+#: bp-themes/bp-default/groups/single/forum/edit.php:91
+msgid "This topic does not exist."
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:37
+#: bp-themes/bp-default/groups/single/forum/topic.php:10
+msgid "New Reply"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:97
+msgid "%s said %s ago:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:117
+#: bp-themes/bp-default/groups/single/forum/topic.php:91
+msgid "Permanent link to this post"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:153
+#: bp-themes/bp-default/groups/single/forum/topic.php:127
+msgid "Add a reply:"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:158
+#: bp-themes/bp-default/groups/single/forum/topic.php:132
+msgid "Post Reply"
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:169
+#: bp-themes/bp-default/groups/single/forum/topic.php:143
+msgid "This topic is closed, replies are no longer accepted."
+msgstr ""
+
+#: bp-themes/bp-default/forums/single/topic.php:184
+#: bp-themes/bp-default/groups/single/forum/topic.php:157
+msgid "There are no posts for this topic."
+msgstr ""
+
+#: bp-themes/bp-default/groups/index.php:23
+#: bp-themes/bp-default/groups/create.php:18
+msgid "Groups Directory"
+msgstr ""
+
+#: bp-themes/bp-default/groups/index.php:37
+msgid "All Groups <span>%s</span>"
+msgstr ""
+
+#: bp-themes/bp-default/groups/groups-loop.php:95
+msgid "There were no groups found."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/members.php:104
+#: bp-themes/bp-default/groups/single/admin.php:288
+msgid "This group has no members."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/forum/topic.php:31
+msgid "Topic tags:"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/forum/topic.php:77
+msgid "%1$s said %2$s:"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/forum/topic.php:122
+msgid "You will auto join this group when you reply to this topic."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/request-membership.php:4
+msgid "You are requesting to become a member of the group '%s'."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/request-membership.php:7
+msgid "Comments (optional)"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/request-membership.php:12
+msgid "Send Request"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/send-invites.php:22
+#: bp-themes/bp-default/groups/create.php:216
+msgid "Select people to invite from your friends list."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/send-invites.php:73
+#: bp-themes/bp-default/groups/create.php:251
+msgid "Once you have built up friend connections you will be able to invite others to your group. You can send invites any time in the future by selecting the \"Send Invites\" option when viewing your new group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/forum.php:65
+msgid "You will auto join this group when you start a new topic."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/forum.php:69
+msgid "Post a New Topic:"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/group-header.php:11
+msgid "Group Admins"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/group-header.php:20
+msgid "Group Mods"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:16
+#: bp-themes/bp-default/groups/create.php:39
+msgid "Group Name (required)"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:19
+#: bp-themes/bp-default/groups/create.php:42
+msgid "Group Description (required)"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:25
+msgid "Notify group members of changes via email"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:47
+#: bp-themes/bp-default/groups/create.php:62
+msgid "Enable discussion forum"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:56
+#: bp-themes/bp-default/groups/create.php:78
+msgid "Privacy Options"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:61
+#: bp-themes/bp-default/groups/create.php:82
+msgid "This is a public group"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:63
+#: bp-themes/bp-default/groups/create.php:84
+msgid "Any site member can join this group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:64
+#: bp-themes/bp-default/groups/single/admin.php:74
+#: bp-themes/bp-default/groups/create.php:85
+#: bp-themes/bp-default/groups/create.php:94
+msgid "This group will be listed in the groups directory and in search results."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:65
+#: bp-themes/bp-default/groups/create.php:86
+msgid "Group content and activity will be visible to any site member."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:71
+#: bp-themes/bp-default/groups/create.php:91
+msgid "This is a private group"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:73
+#: bp-themes/bp-default/groups/create.php:93
+msgid "Only users who request membership and are accepted can join the group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:75
+#: bp-themes/bp-default/groups/single/admin.php:85
+#: bp-themes/bp-default/groups/create.php:95
+#: bp-themes/bp-default/groups/create.php:104
+msgid "Group content and activity will only be visible to members of the group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:81
+#: bp-themes/bp-default/groups/create.php:100
+msgid "This is a hidden group"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:83
+#: bp-themes/bp-default/groups/create.php:102
+msgid "Only users who are invited can join the group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:84
+#: bp-themes/bp-default/groups/create.php:103
+msgid "This group will not be listed in the groups directory or search results."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:92
+#: bp-themes/bp-default/groups/create.php:111
+msgid "Group Invitations"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:94
+#: bp-themes/bp-default/groups/create.php:113
+msgid "Which members of this group are allowed to invite others?"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:99
+#: bp-themes/bp-default/groups/create.php:118
+msgid "All group members"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:104
+#: bp-themes/bp-default/groups/create.php:123
+msgid "Group admins and mods only"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:109
+#: bp-themes/bp-default/groups/create.php:128
+msgid "Group admins only"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:127
+#: bp-themes/bp-default/groups/create.php:154
+msgid "Upload an image to use as an avatar for this group. The image will be shown on the main group page, and in search results."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:137
+msgid "If you'd like to remove the existing avatar but not upload a new one, please use the delete avatar button."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:149
+msgid "Crop Avatar"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:177
+msgid "Administrators"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:203
+msgid "Moderators"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:258
+msgid "(banned)"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:264
+msgid "Unban this member"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:264
+msgid "Remove Ban"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:268
+msgid "Kick and ban this member"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:268
+msgid "Kick &amp; Ban"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:269
+msgid "Promote to Mod"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:274
+msgid "Remove this member"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:274
+msgid "Remove from group"
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:333
+msgid "There are no pending membership requests."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:350
+msgid "WARNING: Deleting this group will completely remove ALL content associated with it. There is no way back, please be careful with this option."
+msgstr ""
+
+#: bp-themes/bp-default/groups/single/admin.php:353
+msgid "I understand the consequences of deleting this group."
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:69
+msgid "<strong>Attention Site Admin:</strong> Group forums require the <a href=\"%s\">correct setup and configuration</a> of a bbPress installation."
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:162
+msgid "To skip the avatar upload process, hit the \"Next Step\" button."
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:169
+msgid "Crop Group Avatar"
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:273
+msgid "Back to Previous Step"
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:280
+msgid "Next Step"
+msgstr ""
+
+#: bp-themes/bp-default/groups/create.php:287
+msgid "Create Group and Continue"
+msgstr ""
+
+#: bp-themes/bp-default/attachment.php:26 bp-themes/bp-default/single.php:24
+msgid "Edit this entry"
+msgstr ""
+
+#: bp-themes/bp-default/attachment.php:40
+msgid "Full size is %s pixels"
+msgstr ""
+
+#: bp-themes/bp-default/attachment.php:43
+msgid "Link to full size image"
+msgstr ""
+
+#: bp-themes/bp-default/attachment.php:62
+msgid "Sorry, no attachments matched your criteria."
+msgstr ""
+
+#: bp-themes/bp-default/search.php:10
+msgid "Site"
+msgstr ""
+
+#: bp-themes/bp-default/search.php:14
+msgid "Search Results"
+msgstr ""
+
+#: bp-themes/bp-default/search.php:51
+msgid "No posts found. Try a different search?"
+msgstr ""
+
+#: bp-themes/bp-default/archive.php:10
+msgid "You are browsing the archive for %1$s."
+msgstr ""
+
+#: bp-themes/bp-default/links.php:16
+msgid "Links"
+msgstr ""
+
+#: bp-themes/bp-default/single.php:35
+msgctxt "Previous post link"
+msgid "&larr;"
+msgstr ""
+
+#: bp-themes/bp-default/single.php:36
+msgctxt "Next post link"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-themes/bp-default/single.php:45
+msgid "Sorry, no posts matched your criteria."
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:177
+msgctxt "Activity pagination previous text"
+msgid "&larr;"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:178
+msgctxt "Activity pagination next text"
+msgid "&rarr;"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:404
+msgid "Viewing item %1$s to %2$s (of %3$s items)"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:762
+msgid "View Discussion"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1352
+msgid "xprofile"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1352
+msgid "friends"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1352
+msgid "status"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1352
+msgid "sites"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1360
+msgid "Clear Filter"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1426
+msgid "a user"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1457
+msgid "Send a public message on your activity stream."
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1458
+msgid "Public Message"
+msgstr ""
+
+#: bp-activity/bp-activity-template.php:1586
+msgid "Site Wide Activity RSS Feed"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:80
+msgid "Search Activity..."
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:98 bp-activity/bp-activity-loader.php:211
+#: bp-activity/feeds/bp-activity-personal-feed.php:25
+#: bp-activity/bp-activity-screens.php:171
+msgid "Activity"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:123
+#: bp-activity/bp-activity-loader.php:225
+msgid "Personal"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:133
+#: bp-activity/bp-activity-loader.php:204
+#: bp-activity/feeds/bp-activity-mentions-feed.php:25
+#: bp-activity/feeds/bp-activity-mentions-feed.php:28
+msgid "Mentions"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:144
+#: bp-activity/bp-activity-loader.php:232
+msgid "Favorites"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:202
+msgid "Mentions <span class=\"count\">%s</span>"
+msgstr ""
+
+#: bp-activity/bp-activity-loader.php:269
+msgid "My Activity"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:38
+msgid "%s mentioned you in an update"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:51
+msgid ""
+"%1$s mentioned you in an update:\n"
+"\n"
+"\"%2$s\"\n"
+"\n"
+"To view and respond to the message, log in and visit: %3$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:94
+msgid "%s replied to one of your updates"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:96
+msgid ""
+"%1$s replied to one of your updates:\n"
+"\n"
+"\"%2$s\"\n"
+"\n"
+"To view your original update and all comments, log in and visit: %3$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:136
+msgid "%s replied to one of your comments"
+msgstr ""
+
+#: bp-activity/bp-activity-notifications.php:141
+msgid ""
+"%1$s replied to one of your comments:\n"
+"\n"
+"\"%2$s\"\n"
+"\n"
+"To view the original activity, your comment and all replies, log in and visit: %3$s\n"
+"\n"
+"---------------------\n"
+msgstr ""
+
+#: bp-activity/bp-activity-filters.php:189
+msgid "[Read more]"
+msgstr ""
+
+#: bp-activity/bp-activity-filters.php:194
+msgid "&hellip;"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:131
+msgid "@%s Mentions"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:134
+msgid "You have %1$d new activity mentions"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:138
+msgid "%1$s mentioned you in an activity update"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:550
+msgid "Posted an update"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:700
+msgid "%s posted an update"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:749
+msgid "%s posted a new activity comment"
+msgstr ""
+
+#: bp-activity/bp-activity-functions.php:989
+msgid "Thumbnail"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-friends-feed.php:25
+msgid "Friends Activity"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-friends-feed.php:28
+msgid "%s - Friends Activity Feed"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-friends-feed.php:48
+#: bp-activity/feeds/bp-activity-mentions-feed.php:47
+#: bp-activity/feeds/bp-activity-personal-feed.php:47
+#: bp-activity/feeds/bp-activity-favorites-feed.php:54
+#: bp-activity/feeds/bp-activity-group-feed.php:47
+#: bp-activity/feeds/bp-activity-sitewide-feed.php:47
+#: bp-activity/feeds/bp-activity-mygroups-feed.php:52
+msgid "Comments: %s"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-friends-feed.php:52
+#: bp-activity/feeds/bp-activity-mentions-feed.php:51
+#: bp-activity/feeds/bp-activity-personal-feed.php:51
+#: bp-activity/feeds/bp-activity-favorites-feed.php:58
+msgid "In reply to"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-personal-feed.php:28
+msgid "%s - Activity Feed"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-favorites-feed.php:25
+#: bp-activity/feeds/bp-activity-favorites-feed.php:28
+msgid "Favorite Activity"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-group-feed.php:25
+msgid "Group Activity"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-group-feed.php:28
+msgid "%s - Group Activity Feed"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-sitewide-feed.php:25
+msgid "Site Wide Activity"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-sitewide-feed.php:28
+msgid "Site Wide Activity Feed"
+msgstr ""
+
+#: bp-activity/feeds/bp-activity-mygroups-feed.php:25
+#: bp-activity/feeds/bp-activity-mygroups-feed.php:28
+msgid "My Groups - Public Activity"
+msgstr ""
+
+#: bp-activity/bp-activity-screens.php:146
+msgid "You do not have access to this activity."
+msgstr ""
+
+#: bp-activity/bp-activity-screens.php:180
+msgid "A member mentions you in an update using \"@%s\""
+msgstr ""
+
+#: bp-activity/bp-activity-screens.php:186
+msgid "A member replies to an update or comment you've posted"
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:112
+msgid "Activity deleted successfully"
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:114
+msgid "There was an error when deleting that activity"
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:164
+msgid "Update Posted!"
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:166
+msgid "There was an error when posting your update, please try again."
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:197
+msgid "Reply Posted!"
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:215
+msgid "Activity marked as favorite."
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:217
+msgid "There was an error marking that activity as a favorite, please try again."
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:233
+msgid "Activity removed as favorite."
+msgstr ""
+
+#: bp-activity/bp-activity-actions.php:235
+msgid "There was an error removing that activity as a favorite, please try again."
+msgstr ""
Index: /tags/1.5-beta-3/bp-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-loader.php	(revision 5057)
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Plugin Name: BuddyPress
+ * Plugin URI:  http://buddypress.org
+ * Description: Social networking in a box. Build a social network for your company, school, sports team or niche community all based on the power and flexibility of WordPress.
+ * Author:      The BuddyPress Community
+ * Version:     1.5-beta-3
+ * Author URI:  http://buddypress.org/community/members/
+ * Network:     true
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/** Constants *****************************************************************/
+global $wpdb;
+
+// Define the BuddyPress version
+if ( !defined( 'BP_VERSION' ) )
+	define( 'BP_VERSION', '1.5-beta-3' );
+
+// Define the database version
+if ( !defined( 'BP_DB_VERSION' ) )
+	define( 'BP_DB_VERSION', 3816 );
+
+// Place your custom code (actions/filters) in a file called
+// '/plugins/bp-custom.php' and it will be loaded before anything else.
+if ( file_exists( WP_PLUGIN_DIR . '/bp-custom.php' ) )
+	require( WP_PLUGIN_DIR . '/bp-custom.php' );
+
+// Define on which blog ID BuddyPress should run
+if ( !defined( 'BP_ROOT_BLOG' ) ) {
+
+	// Root blog is the main site on this network
+	if ( is_multisite() && !defined( 'BP_ENABLE_MULTIBLOG' ) ) {
+		$current_site = get_current_site();
+		$root_blog_id = $current_site->blog_id;
+
+	// Root blog is every site on this network
+	} elseif ( is_multisite() && defined( 'BP_ENABLE_MULTIBLOG' ) ) {
+		$root_blog_id = get_current_blog_id();
+
+	// Root blog is the only blog on this network
+	} elseif( !is_multisite() ) {
+		$root_blog_id = 1;
+	}
+
+	define( 'BP_ROOT_BLOG', $root_blog_id );
+}
+
+// Path and URL
+if ( !defined( 'BP_PLUGIN_DIR' ) )
+	define( 'BP_PLUGIN_DIR', WP_PLUGIN_DIR . '/buddypress' );
+
+if ( !defined( 'BP_PLUGIN_URL' ) )
+	define( 'BP_PLUGIN_URL', plugins_url( 'buddypress' ) );
+
+// The search slug has to be defined nice and early because of the way search requests are loaded
+if ( !defined( 'BP_SEARCH_SLUG' ) )
+	define( 'BP_SEARCH_SLUG', 'search' );
+
+// Setup the BuddyPress theme directory
+register_theme_directory( WP_PLUGIN_DIR . '/buddypress/bp-themes' );
+
+/** Loader ********************************************************************/
+
+// Load the WP abstraction file so BuddyPress can run on all WordPress setups.
+require( BP_PLUGIN_DIR . '/bp-core/bp-core-wpabstraction.php' );
+
+// Test to see whether this is a new installation or an upgraded version of BuddyPress
+if ( !$bp->database_version = get_site_option( 'bp-db-version' ) ) {
+	if ( $bp->database_version = get_option( 'bp-db-version' ) ) {
+		$bp->is_network_activate = 1;
+	} else {
+		$bp->database_version = get_site_option( 'bp-core-db-version' );  // BP 1.2 option
+	}
+}
+
+// This is a new installation.
+if ( empty( $bp->database_version ) ) {
+	$bp->maintenance_mode = 'install';
+	require( WP_PLUGIN_DIR . '/buddypress/bp-core/admin/bp-core-update.php' );
+
+// There is a previous installation
+} else {
+	// Load core
+	require( WP_PLUGIN_DIR . '/buddypress/bp-core/bp-core-loader.php' );
+
+	// Check if an update is required
+	if ( (int)$bp->database_version < (int)constant( 'BP_DB_VERSION' ) || isset( $bp->is_network_activate ) ) {
+		$bp->maintenance_mode = 'update';
+		require( WP_PLUGIN_DIR . '/buddypress/bp-core/admin/bp-core-update.php' );
+	}
+}
+
+/** Activation ****************************************************************/
+
+if ( !function_exists( 'bp_loader_activate' ) ) :
+/**
+ * Defines BP's activation routine.
+ *
+ * Most of BP's crucial setup is handled by the setup wizard. This function takes care of some
+ * issues with incompatible legacy themes, and provides a hook for other functions to know that
+ * BP has been activated.
+ *
+ * @package BuddyPress Core
+*/
+function bp_loader_activate() {
+	// Force refresh theme roots.
+	delete_site_transient( 'theme_roots' );
+
+	if ( !function_exists( 'get_blog_option' ) )
+		require ( WP_PLUGIN_DIR . '/buddypress/bp-core/bp-core-wpabstraction.php' );
+
+	if ( !function_exists( 'bp_get_root_blog_id' ) )
+		require ( WP_PLUGIN_DIR . '/buddypress/bp-core/bp-core-functions.php' );
+
+	// Switch the user to the new bp-default if they are using the old
+	// bp-default on activation.
+	if ( 'bp-sn-parent' == get_blog_option( bp_get_root_blog_id(), 'template' ) && 'bp-default' == get_blog_option( bp_get_root_blog_id(), 'stylesheet' ) )
+		switch_theme( 'bp-default', 'bp-default' );
+
+	do_action( 'bp_loader_activate' );
+}
+register_activation_hook( 'buddypress/bp-loader.php', 'bp_loader_activate' );
+endif;
+
+if ( !function_exists( 'bp_loader_deactivate' ) ) :
+// Deactivation Function
+function bp_loader_deactivate() {
+	do_action( 'bp_loader_deactivate' );
+}
+register_deactivation_hook( 'buddypress/bp-loader.php', 'bp_loader_deactivate' );
+endif;
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-actions.php	(revision 5057)
@@ -0,0 +1,169 @@
+<?php
+/*******************************************************************************
+ * Action Functions
+ *
+ * Action functions are exactly the same as screen functions, however they do not
+ * have a template screen associated with them. Usually they will send the user
+ * back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Listens to the $bp component and action variables to determine if the user is viewing the members
+ * directory page. If they are, it will set up the directory and load the members directory template.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @uses wp_enqueue_script() Loads a JS script into the header of the page.
+ * @uses bp_core_load_template() Loads a specific template file.
+ */
+/**
+ * When a site admin selects "Mark as Spammer/Not Spammer" from the admin menu
+ * this action will fire and mark or unmark the user and their blogs as spam.
+ * Must be a site admin for this function to run.
+ *
+ * @package BuddyPress Core
+ * @param int $user_id Optional user ID to mark as spam
+ * @global object $wpdb Global WordPress Database object
+ */
+function bp_core_action_set_spammer_status( $user_id = 0 ) {
+	global $wpdb;
+
+	// Only super admins can currently spam users
+	if ( !is_super_admin() || bp_is_my_profile() )
+		return;
+
+	// Use displayed user if it's not yourself
+	if ( empty( $user_id ) && bp_is_user() )
+		$user_id = bp_displayed_user_id();
+
+	// Bail if no user ID
+	if ( empty( $user_id ) )
+		return;
+
+	// Bail if user ID is super admin
+	if ( is_super_admin( $user_id ) )
+		return;
+
+	if ( bp_is_current_component( 'admin' ) && ( in_array( bp_current_action(), array( 'mark-spammer', 'unmark-spammer' ) ) ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'mark-unmark-spammer' );
+
+		// Get the functions file
+		if ( is_multisite() ) {
+			require( ABSPATH . 'wp-admin/includes/ms.php' );
+		}
+
+		// To spam or not to spam
+		$is_spam = bp_is_current_action( 'mark-spammer' ) ? 1 : 0;
+
+		// Get the blogs for the user
+		$blogs = get_blogs_of_user( $user_id, true );
+
+		foreach ( (array) $blogs as $key => $details ) {
+
+			// Do not mark the main or current root blog as spam
+			if ( 1 == $details->userblog_id || bp_get_root_blog_id() == $details->userblog_id ) {
+				continue;
+			}
+
+			// Update the blog status
+			update_blog_status( $details->userblog_id, 'spam', $is_spam );
+		}
+
+		// Finally, mark this user as a spammer
+		if ( is_multisite() ) {
+			update_user_status( $user_id, 'spam', $is_spam );
+		}
+
+		// Always set single site status
+		$wpdb->update( $wpdb->users, array( 'user_status' => $is_spam ), array( 'ID' => $user_id ) );
+
+		// Add feedback message
+		if ( $is_spam ) {
+			bp_core_add_message( __( 'User marked as spammer. Spam users are visible only to site admins.', 'buddypress' ) );
+		} else {
+			bp_core_add_message( __( 'User removed as spammer.', 'buddypress' ) );
+		}
+
+		// Hide this user's activity
+		if ( $is_spam && bp_is_active( 'activity' ) ) {
+			bp_activity_hide_user_activity( $user_id );
+		}
+
+		// We need a special hook for is_spam so that components can delete data at spam time
+		$bp_action = $is_spam ? 'bp_make_spam_user' : 'bp_make_ham_user';
+		do_action( $bp_action, bp_displayed_user_id() );
+
+		// Call multisite actions in single site mode for good measure
+		if ( !is_multisite() ) {
+			$wp_action = $is_spam ? 'make_spam_user' : 'make_ham_user';
+			do_action( $wp_action, bp_displayed_user_id() );
+		}
+
+		// Allow plugins to do neat things
+		do_action( 'bp_core_action_set_spammer_status', bp_displayed_user_id(), $is_spam );
+
+		// Redirect back to where we came from
+		bp_core_redirect( wp_get_referer() );
+	}
+}
+add_action( 'bp_actions', 'bp_core_action_set_spammer_status' );
+
+/**
+ * Allows a site admin to delete a user from the adminbar menu.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_action_delete_user() {
+	global $bp;
+
+	if ( !is_super_admin() || bp_is_my_profile() || !$bp->displayed_user->id )
+		return false;
+
+	if ( 'admin' == $bp->current_component && 'delete-user' == $bp->current_action ) {
+		// Check the nonce
+		check_admin_referer( 'delete-user' );
+
+		$errors = false;
+		do_action( 'bp_core_before_action_delete_user', $errors );
+
+		if ( bp_core_delete_account( $bp->displayed_user->id ) ) {
+			bp_core_add_message( sprintf( __( '%s has been deleted from the system.', 'buddypress' ), $bp->displayed_user->fullname ) );
+		} else {
+			bp_core_add_message( sprintf( __( 'There was an error deleting %s from the system. Please try again.', 'buddypress' ), $bp->displayed_user->fullname ), 'error' );
+			$errors = true;
+		}
+
+		do_action( 'bp_core_action_delete_user', $errors );
+
+		if ( $errors )
+			bp_core_redirect( $bp->displayed_user->domain );
+		else
+			bp_core_redirect( $bp->loggedin_user->domain );
+	}
+}
+add_action( 'bp_actions', 'bp_core_action_delete_user' );
+
+/**
+ * Returns the user_id for a user based on their username.
+ *
+ * @package BuddyPress Core
+ * @param $username str Username to check.
+ * @return false on no match
+ * @return int the user ID of the matched user.
+ */
+function bp_core_get_random_member() {
+	global $bp;
+
+	if ( isset( $_GET['random-member'] ) ) {
+		$user = bp_core_get_users( array( 'type' => 'random', 'per_page' => 1 ) );
+		bp_core_redirect( bp_core_get_user_domain( $user['users'][0]->id ) );
+	}
+}
+add_action( 'bp_actions', 'bp_core_get_random_member' );
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-adminbar.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-adminbar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-adminbar.php	(revision 5057)
@@ -0,0 +1,225 @@
+<?php
+/**
+ * BuddyPress Members Admin Bar
+ *
+ * Handles the member functions related to the WordPress Admin Bar
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Add the "My Account" menu and all submenus.
+ *
+ * @since BuddyPress (r4151)
+ */
+function bp_members_admin_bar_my_account_menu() {
+	global $bp, $wp_admin_bar;
+
+	// Bail if this is an ajax request
+	if ( defined( 'DOING_AJAX' ) )
+		return;
+
+	// Logged in user
+	if ( is_user_logged_in() ) {
+
+		// User avatar
+		$avatar = bp_core_fetch_avatar( array(
+			'item_id' => $bp->loggedin_user->id,
+			'email'   => $bp->loggedin_user->userdata->user_email,
+			'width'   => 16,
+			'height'  => 16
+		) );
+
+		// Unique ID for the 'My Account' menu
+		$bp->my_account_menu_id = ( ! empty( $avatar ) ) ? 'my-account-with-avatar' : 'my-account';
+
+		// Create the main 'My Account' menu
+		$wp_admin_bar->add_menu( array(
+			'id'    => $bp->my_account_menu_id,
+			'title' => $avatar . bp_get_loggedin_user_fullname(),
+			'href'  => $bp->loggedin_user->domain
+		) );
+
+	// Show login and sign-up links
+	} elseif ( !empty( $wp_admin_bar ) ) {
+
+		add_filter ( 'show_admin_bar', '__return_true' );
+
+		// Create the main 'My Account' menu
+		$wp_admin_bar->add_menu( array(
+			'id'    => 'bp-login',
+			'title' => __( 'Log in', 'buddypress' ),
+			'href'  => wp_login_url()
+		) );
+
+		// Sign up
+		if ( bp_get_signup_allowed() ) {
+			$wp_admin_bar->add_menu( array(
+				'id'    => 'bp-register',
+				'title' => __( 'Register', 'buddypress' ),
+				'href'  => bp_get_signup_page()
+			) );
+		}
+	}
+}
+add_action( 'bp_setup_admin_bar', 'bp_members_admin_bar_my_account_menu', 4 );
+
+/**
+ * Adds the User Admin top-level menu to user pages
+ *
+ * @package BuddyPress
+ * @since 1.5
+ */
+function bp_members_admin_bar_user_admin_menu() {
+	global $bp, $wp_admin_bar;
+
+	// Only show if viewing a user
+	if ( !bp_is_user() )
+		return false;
+
+	// Don't show this menu to non site admins or if you're viewing your own profile
+	if ( !current_user_can( 'edit_users' ) || bp_is_my_profile() )
+		return false;
+
+	// User avatar
+	$avatar = bp_core_fetch_avatar( array(
+		'item_id' => $bp->displayed_user->id,
+		'email'   => $bp->displayed_user->userdata->user_email,
+		'width'   => 16,
+		'height'  => 16
+	) );
+
+	// Unique ID for the 'My Account' menu
+	$bp->user_admin_menu_id = ( ! empty( $avatar ) ) ? 'user-admin-with-avatar' : 'user-admin';
+
+	// Add the top-level User Admin button
+	$wp_admin_bar->add_menu( array(
+		'id'    => $bp->user_admin_menu_id,
+		'title' => $avatar . bp_get_displayed_user_fullname(),
+		'href'  => bp_displayed_user_domain()
+	) );
+
+	// User Admin > Edit this user's profile
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->user_admin_menu_id,
+		'id'     => 'edit-profile',
+		'title'  => __( "Edit Profile", 'buddypress' ),
+		'href'   => bp_get_members_component_link( 'profile', 'edit' )
+	) );
+
+	// User Admin > Edit this user's avatar
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->user_admin_menu_id,
+		'id'     => 'change-avatar',
+		'title'  => __( "Edit Avatar", 'buddypress' ),
+		'href'   => bp_get_members_component_link( 'profile', 'change-avatar' )
+	) );
+
+	// User Admin > Spam/unspam
+	if ( !bp_core_is_user_spammer( bp_displayed_user_id() ) ) {
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->user_admin_menu_id,
+			'id'     => 'spam-user',
+			'title'  => __( 'Mark as Spammer', 'buddypress' ),
+			'href'   => wp_nonce_url( bp_displayed_user_domain() . 'admin/mark-spammer/', 'mark-unmark-spammer' ),
+			'meta'   => array( 'onclick' => 'confirm(" ' . __( 'Are you sure you want to mark this user as a spammer?', 'buddypress' ) . '");' )
+		) );
+	} else {
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->user_admin_menu_id,
+			'id'     => 'unspam-user',
+			'title'  => __( 'Not a Spammer', 'buddypress' ),
+			'href'   => wp_nonce_url( bp_displayed_user_domain() . 'admin/unmark-spammer/', 'mark-unmark-spammer' ),
+			'meta'   => array( 'onclick' => 'confirm(" ' . __( 'Are you sure you want to mark this user as not a spammer?', 'buddypress' ) . '");' )
+		) );
+	}
+
+	// User Admin > Delete Account
+	$wp_admin_bar->add_menu( array(
+		'parent' => $bp->user_admin_menu_id,
+		'id'     => 'delete-user',
+		'title'  => __( 'Delete Account', 'buddypress' ),
+		'href'   => wp_nonce_url( bp_displayed_user_domain() . 'admin/delete-user/', 'delete-user' ),
+		'meta'   => array( 'onclick' => 'confirm(" ' . __( "Are you sure you want to delete this user's account?", 'buddypress' ) . '");' )
+	) );
+}
+add_action( 'bp_setup_admin_bar', 'bp_members_admin_bar_user_admin_menu', 99 );
+
+/**
+ * Build the "Notifications" dropdown
+ *
+ * @package Buddypress
+ * @since 1.5
+ */
+function bp_members_admin_bar_notifications_menu() {
+	global $bp, $wp_admin_bar;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+	if ( $notifications = bp_core_get_notifications_for_user( bp_loggedin_user_id(), 'object' ) ) {
+		$menu_title = sprintf( __( 'Notifications <span id="ab-pending-notifications" class="pending-count">%s</span>', 'buddypress' ), count( $notifications ) );
+	} else {
+		$menu_title = __( 'Notifications', 'buddypress' );
+	}
+
+	// Add the top-level Notifications button
+	$wp_admin_bar->add_menu( array(
+		'id'    => 'bp-notifications',
+		'title' => $menu_title,
+		'href'  => bp_loggedin_user_domain()
+	) );
+
+	if ( !empty( $notifications ) ) {
+		foreach ( (array)$notifications as $notification ) {
+			$wp_admin_bar->add_menu( array(
+				'parent' => 'bp-notifications',
+				'id'     => 'notification-' . $notification->id,
+				'title'  => $notification->content,
+				'href'   => $notification->href
+			) );
+		}
+	} else {
+		$wp_admin_bar->add_menu( array(
+			'parent' => 'bp-notifications',
+			'id'     => 'no-notifications',
+			'title'  => __( 'No new notifications', 'buddypress' ),
+			'href'   => bp_loggedin_user_domain()
+		) );
+	}
+
+	return;
+}
+add_action( 'bp_setup_admin_bar', 'bp_members_admin_bar_notifications_menu', 999 );
+
+/**
+ * Make sure the logout link is at the bottom of the "My Account" menu
+ *
+ * @since BuddyPress (r4151)
+ *
+ * @global obj $bp
+ * @global obj $wp_admin_bar
+ */
+function bp_members_admin_bar_my_account_logout() {
+	global $bp, $wp_admin_bar;
+
+	// Bail if this is an ajax request
+	if ( defined( 'DOING_AJAX' ) )
+		return;
+
+	if ( is_user_logged_in() ) {
+		// Log out
+		$wp_admin_bar->add_menu( array(
+			'parent' => $bp->my_account_menu_id,
+			'title'  => __( 'Log Out', 'buddypress' ),
+			'href'   => wp_logout_url()
+		) );
+	}
+}
+add_action( 'bp_setup_admin_bar', 'bp_members_admin_bar_my_account_logout', 9999 );
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-buddybar.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-buddybar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-buddybar.php	(revision 5057)
@@ -0,0 +1,135 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// **** "Notifications" Menu *********
+function bp_adminbar_notifications_menu() {
+	global $bp;
+
+	if ( !is_user_logged_in() )
+		return false;
+
+	echo '<li id="bp-adminbar-notifications-menu"><a href="' . $bp->loggedin_user->domain . '">';
+	_e( 'Notifications', 'buddypress' );
+
+	if ( $notifications = bp_core_get_notifications_for_user( $bp->loggedin_user->id ) ) { ?>
+		<span><?php echo count( $notifications ) ?></span>
+	<?php
+	}
+
+	echo '</a>';
+	echo '<ul>';
+
+	if ( $notifications ) {
+		$counter = 0;
+		for ( $i = 0, $count = count( $notifications ); $i < $count; ++$i ) {
+			$alt = ( 0 == $counter % 2 ) ? ' class="alt"' : ''; ?>
+
+			<li<?php echo $alt ?>><?php echo $notifications[$i] ?></li>
+
+			<?php $counter++;
+		}
+	} else { ?>
+
+		<li><a href="<?php echo $bp->loggedin_user->domain ?>"><?php _e( 'No new notifications.', 'buddypress' ); ?></a></li>
+
+	<?php
+	}
+
+	echo '</ul>';
+	echo '</li>';
+}
+add_action( 'bp_adminbar_menus', 'bp_adminbar_notifications_menu', 8 );
+
+// **** "Blog Authors" Menu (visible when not logged in) ********
+function bp_adminbar_authors_menu() {
+	global $bp, $wpdb;
+
+	// Only for multisite
+	if ( !is_multisite() )
+		return false;
+
+	// Hide on root blog
+	if ( $wpdb->blogid == bp_get_root_blog_id() || !bp_is_active( 'blogs' ) )
+		return false;
+
+	$blog_prefix = $wpdb->get_blog_prefix( $wpdb->blogid );
+	$authors     = $wpdb->get_results( "SELECT user_id, user_login, user_nicename, display_name, user_email, meta_value as caps FROM $wpdb->users u, $wpdb->usermeta um WHERE u.ID = um.user_id AND meta_key = '{$blog_prefix}capabilities' ORDER BY um.user_id" );
+
+	if ( !empty( $authors ) ) {
+		// This is a blog, render a menu with links to all authors
+		echo '<li id="bp-adminbar-authors-menu"><a href="/">';
+		_e('Blog Authors', 'buddypress');
+		echo '</a>';
+
+		echo '<ul class="author-list">';
+		foreach( (array)$authors as $author ) {
+			$caps = maybe_unserialize( $author->caps );
+			if ( isset( $caps['subscriber'] ) || isset( $caps['contributor'] ) ) continue;
+
+			echo '<li>';
+			echo '<a href="' . bp_core_get_user_domain( $author->user_id, $author->user_nicename, $author->user_login ) . '">';
+			echo bp_core_fetch_avatar( array( 'item_id' => $author->user_id, 'email' => $author->user_email, 'width' => 15, 'height' => 15 ) ) ;
+ 			echo ' ' . $author->display_name . '</a>';
+			echo '<div class="admin-bar-clear"></div>';
+			echo '</li>';
+		}
+		echo '</ul>';
+		echo '</li>';
+	}
+}
+add_action( 'bp_adminbar_menus', 'bp_adminbar_authors_menu', 12 );
+
+/**
+ * Adds an admin bar menu to any profile page providing site moderator actions
+ * that allow capable users to clean up a users account.
+ *
+ * @package BuddyPress XProfile
+ * @global $bp BuddyPress
+ */
+function bp_members_adminbar_admin_menu() {
+	global $bp;
+
+	// Only show if viewing a user
+	if ( !$bp->displayed_user->id )
+		return false;
+
+	// Don't show this menu to non site admins or if you're viewing your own profile
+	if ( !current_user_can( 'edit_users' ) || bp_is_my_profile() )
+		return false; ?>
+
+	<li id="bp-adminbar-adminoptions-menu">
+
+		<a href=""><?php _e( 'Admin Options', 'buddypress' ) ?></a>
+
+		<ul>
+			<?php if ( bp_is_active( 'xprofile' ) ) : ?>
+
+				<li><a href="<?php bp_members_component_link( 'profile', 'edit' ); ?>"><?php printf( __( "Edit %s's Profile", 'buddypress' ), esc_attr( $bp->displayed_user->fullname ) ) ?></a></li>
+
+			<?php endif ?>
+
+			<li><a href="<?php bp_members_component_link( 'profile', 'change-avatar' ); ?>"><?php printf( __( "Edit %s's Avatar", 'buddypress' ), esc_attr( $bp->displayed_user->fullname ) ) ?></a></li>
+
+			<?php if ( !bp_core_is_user_spammer( $bp->displayed_user->id ) ) : ?>
+
+				<li><a href="<?php echo wp_nonce_url( $bp->displayed_user->domain . 'admin/mark-spammer/', 'mark-unmark-spammer' ) ?>" class="confirm"><?php printf( __( "Mark as Spammer", 'buddypress' ), esc_attr( $bp->displayed_user->fullname ) ); ?></a></li>
+
+			<?php else : ?>
+
+				<li><a href="<?php echo wp_nonce_url( $bp->displayed_user->domain . 'admin/unmark-spammer/', 'mark-unmark-spammer' ) ?>" class="confirm"><?php _e( "Not a Spammer", 'buddypress' ) ?></a></li>
+
+			<?php endif; ?>
+
+			<li><a href="<?php echo wp_nonce_url( $bp->displayed_user->domain . 'admin/delete-user/', 'delete-user' ) ?>" class="confirm"><?php printf( __( "Delete %s's Account", 'buddypress' ), esc_attr( $bp->displayed_user->fullname ) ); ?></a></li>
+
+			<?php do_action( 'bp_members_adminbar_admin_menu' ) ?>
+
+		</ul>
+	</li>
+
+	<?php
+}
+add_action( 'bp_adminbar_menus', 'bp_members_adminbar_admin_menu', 20 );
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-filters.php	(revision 5057)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * BuddyPress Members Filters
+ *
+ * Member specific filters
+ *
+ * @package BuddyPress
+ * @subpackage Member Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Load additional sign-up sanitization filters on bp_loaded. These are used
+ * to prevent XSS in the BuddyPress sign-up process. You can unhook these to
+ * allow for customization of your registration fields, however it is highly
+ * recommended that you leave these in place for the safety of your network.
+ *
+ * @since BuddyPress (r4079)
+ * @uses add_filter()
+ */
+function bp_members_signup_sanitization() {
+
+	// Filters on sign-up fields
+	$fields = array (
+		'bp_get_signup_username_value',
+		'bp_get_signup_email_value',
+		'bp_get_signup_with_blog_value',
+		'bp_get_signup_blog_url_value',
+		'bp_get_signup_blog_title_value',
+		'bp_get_signup_blog_privacy_value',
+		'bp_get_signup_avatar_dir_value',
+	);
+
+	// Add the filters to each field
+	foreach( $fields as $filter ) {
+		add_filter( $filter, 'esc_html',       1 );
+		add_filter( $filter, 'wp_filter_kses', 2 );
+		add_filter( $filter, 'stripslashes',   3 );
+	}
+
+	// Sanitize email
+	add_filter( 'bp_get_signup_email_value', 'sanitize_email' );
+}
+add_action( 'bp_loaded', 'bp_members_signup_sanitization' );
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-functions.php	(revision 5057)
@@ -0,0 +1,705 @@
+<?php
+/**
+ * BuddyPress Member Functions
+ *
+ * Functions specific to the members component.
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Checks $bp pages global and looks for directory page
+ *
+ * @since 1.5
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @return bool True if set, False if empty
+ */
+function bp_members_has_directory() {
+	global $bp;
+
+	return (bool) !empty( $bp->pages->members->id );
+}
+
+/**
+ * Define the slugs used for BuddyPress pages, based on the slugs of the WP pages used.
+ * These can be overridden manually by defining these slugs in wp-config.php.
+ *
+ * The fallback values are only used during initial BP page creation, when no slugs have been
+ * explicitly defined.
+ *
+ * @package BuddyPress Core Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_core_define_slugs() {
+	global $bp;
+
+	// No custom members slug
+	if ( !defined( 'BP_MEMBERS_SLUG' ) )
+		if ( !empty( $bp->pages->members ) )
+			define( 'BP_MEMBERS_SLUG', $bp->pages->members->slug );
+		else
+			define( 'BP_MEMBERS_SLUG', 'members' );
+
+	// No custom registration slug
+	if ( !defined( 'BP_REGISTER_SLUG' ) )
+		if ( !empty( $bp->pages->register ) )
+			define( 'BP_REGISTER_SLUG', $bp->pages->register->slug );
+		else
+			define( 'BP_REGISTER_SLUG', 'register' );
+
+	// No custom activation slug
+	if ( !defined( 'BP_ACTIVATION_SLUG' ) )
+		if ( !empty( $bp->pages->activate ) )
+			define( 'BP_ACTIVATION_SLUG', $bp->pages->activate->slug );
+		else
+			define( 'BP_ACTIVATION_SLUG', 'activate' );
+
+}
+add_action( 'bp_setup_globals', 'bp_core_define_slugs' );
+
+/**
+ * Return an array of users IDs based on the parameters passed.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_get_users( $args = '' ) {
+	global $bp;
+
+	$defaults = array(
+		'type'            => 'active', // active, newest, alphabetical, random or popular
+		'user_id'         => false,    // Pass a user_id to limit to only friend connections for this user
+		'exclude'         => false,    // Users to exclude from results
+		'search_terms'    => false,    // Limit to users that match these search terms
+		'meta_key'        => false,    // Limit to users who have this piece of usermeta
+		'meta_value'      => false,    // With meta_key, limit to users where usermeta matches this value
+
+		'include'         => false,    // Pass comma separated list of user_ids to limit to only these users
+		'per_page'        => 20,       // The number of results to return per page
+		'page'            => 1,        // The page to return if limiting per page
+		'populate_extras' => true,     // Fetch the last active, where the user is a friend, total friend count, latest update
+	);
+
+	$params = wp_parse_args( $args, $defaults );
+	extract( $params, EXTR_SKIP );
+
+	return apply_filters( 'bp_core_get_users', BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value ), $params );
+}
+
+/**
+ * Returns the domain for the passed user: e.g. http://domain.com/members/andy/
+ *
+ * @package BuddyPress Core
+ * @global $current_user WordPress global variable containing current logged in user information
+ * @param user_id The ID of the user.
+ */
+function bp_core_get_user_domain( $user_id, $user_nicename = false, $user_login = false ) {
+	global $bp;
+
+	if ( empty( $user_id ) )
+		return;
+
+	if ( !$domain = wp_cache_get( 'bp_user_domain_' . $user_id, 'bp' ) ) {
+		$username = bp_core_get_username( $user_id, $user_nicename, $user_login );
+
+		if ( bp_is_username_compatibility_mode() )
+			$username = rawurlencode( $username );
+
+		// If we are using a members slug, include it.
+		if ( !defined( 'BP_ENABLE_ROOT_PROFILES' ) )
+			$domain = bp_get_root_domain() . '/' . bp_get_members_root_slug() . '/' . $username;
+		else
+			$domain = bp_get_root_domain() . '/' . $username;
+
+		// Add a slash at the end, and filter before caching
+		$domain = apply_filters( 'bp_core_get_user_domain_pre_cache', trailingslashit( $domain ), $user_id, $user_nicename, $user_login );
+
+		// Cache the link
+		if ( !empty( $domain ) )
+			wp_cache_set( 'bp_user_domain_' . $user_id, $domain, 'bp' );
+	}
+
+	return apply_filters( 'bp_core_get_user_domain', $domain, $user_id, $user_nicename, $user_login );
+}
+
+/**
+ * Fetch everything in the wp_users table for a user, without any usermeta.
+ *
+ * @package BuddyPress Core
+ * @param user_id The ID of the user.
+ * @uses BP_Core_User::get_core_userdata() Performs the query.
+ */
+function bp_core_get_core_userdata( $user_id ) {
+	if ( empty( $user_id ) )
+		return false;
+
+	if ( !$userdata = wp_cache_get( 'bp_core_userdata_' . $user_id, 'bp' ) ) {
+		$userdata = BP_Core_User::get_core_userdata( $user_id );
+		wp_cache_set( 'bp_core_userdata_' . $user_id, $userdata, 'bp' );
+	}
+	return apply_filters( 'bp_core_get_core_userdata', $userdata );
+}
+
+/**
+ * Returns the user id for the user that is currently being displayed.
+ * eg: http://andy.domain.com/ or http://domain.com/andy/
+ *
+ * @package BuddyPress Core
+ * @uses bp_core_get_userid_from_user_login() Returns the user id for the username passed
+ * @return The user id for the user that is currently being displayed, return zero if this is not a user home and just a normal blog.
+ */
+function bp_core_get_displayed_userid( $user_login ) {
+	return apply_filters( 'bp_core_get_displayed_userid', bp_core_get_userid( $user_login ) );
+}
+
+/**
+ * Returns the user_id for a user based on their username.
+ *
+ * @package BuddyPress Core
+ * @param $username str Username to check.
+ * @global $wpdb WordPress DB access object.
+ * @return false on no match
+ * @return int the user ID of the matched user.
+ */
+function bp_core_get_userid( $username ) {
+	global $wpdb;
+
+	if ( empty( $username ) )
+		return false;
+
+	return apply_filters( 'bp_core_get_userid', $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_login = %s", $username ) ) );
+}
+
+/**
+ * Returns the user_id for a user based on their user_nicename.
+ *
+ * @package BuddyPress Core
+ * @param $username str Username to check.
+ * @global $wpdb WordPress DB access object.
+ * @return false on no match
+ * @return int the user ID of the matched user.
+ */
+function bp_core_get_userid_from_nicename( $user_nicename ) {
+	global $wpdb;
+
+	if ( empty( $user_nicename ) )
+		return false;
+
+	return apply_filters( 'bp_core_get_userid_from_nicename', $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s", $user_nicename ) ) );
+}
+
+/**
+ * Returns the username for a user based on their user id.
+ *
+ * @package BuddyPress Core
+ * @param $uid int User ID to check.
+ * @global $userdata WordPress user data for the current logged in user.
+ * @uses get_userdata() WordPress function to fetch the userdata for a user ID
+ * @return false on no match
+ * @return str the username of the matched user.
+ */
+function bp_core_get_username( $user_id, $user_nicename = false, $user_login = false ) {
+	global $bp;
+
+	if ( !$username = wp_cache_get( 'bp_user_username_' . $user_id, 'bp' ) ) {
+		// Cache not found so prepare to update it
+		$update_cache = true;
+
+		// Nicename and login were not passed
+		if ( empty( $user_nicename ) && empty( $user_login ) ) {
+
+			// User ID matches logged in user
+			if ( isset( $bp->loggedin_user->id ) && $bp->loggedin_user->id == $user_id ) {
+				$userdata = &$bp->loggedin_user->userdata;
+
+			// User ID matches displayed in user
+			} elseif ( isset( $bp->displayed_user->id ) && $bp->displayed_user->id == $user_id ) {
+				$userdata = &$bp->displayed_user->userdata;
+
+			// No user ID match
+			} else {
+				$userdata = false;
+			}
+
+			// No match so go dig
+			if ( empty( $userdata ) ) {
+
+				// User not found so return false
+				if ( !$userdata = bp_core_get_core_userdata( $user_id ) ) {
+					return false;
+				}
+			}
+
+			// Update the $user_id for later
+			$user_id       = $userdata->ID;
+
+			// Two possible options
+			$user_nicename = $userdata->user_nicename;
+			$user_login    = $userdata->user_login;
+		}
+
+		// Pull an audible and use the login over the nicename
+		if ( bp_is_username_compatibility_mode() )
+			$username = $user_login;
+		else
+			$username = $user_nicename;
+
+	// Username found in cache so don't update it again
+	} else {
+		$update_cache = false;
+	}
+
+	// Check $username for empty spaces and default to nicename if found
+	if ( strstr( $username, ' ' ) )
+		$username = bp_members_get_user_nicename( $user_id );
+
+	// Add this to cache
+	if ( ( true == $update_cache ) && !empty( $username ) )
+		wp_cache_set( 'bp_user_username_' . $user_id, $username, 'bp' );
+
+	return apply_filters( 'bp_core_get_username', $username );
+}
+
+/**
+ * Returns the user_nicename for a user based on their user_id. This should be
+ * used for linking to user profiles and anywhere else a sanitized and unique
+ * slug to a user is needed.
+ *
+ * @since BuddyPress (1.5)
+ *
+ * @package BuddyPress Core
+ * @param $uid int User ID to check.
+ * @global $userdata WordPress user data for the current logged in user.
+ * @uses get_userdata() WordPress function to fetch the userdata for a user ID
+ * @return false on no match
+ * @return str the username of the matched user.
+ */
+function bp_members_get_user_nicename( $user_id ) {
+	global $bp;
+
+	if ( !$user_nicename = wp_cache_get( 'bp_members_user_nicename_' . $user_id, 'bp' ) ) {
+		$update_cache = true;
+
+		// User ID matches logged in user
+		if ( isset( $bp->loggedin_user->id ) && $bp->loggedin_user->id == $user_id ) {
+			$userdata = &$bp->loggedin_user->userdata;
+
+		// User ID matches displayed in user
+		} elseif ( isset( $bp->displayed_user->id ) && $bp->displayed_user->id == $user_id ) {
+			$userdata = &$bp->displayed_user->userdata;
+
+		// No user ID match
+		} else {
+			$userdata = false;
+		}
+
+		// No match so go dig
+		if ( empty( $userdata ) ) {
+
+			// User not found so return false
+			if ( !$userdata = bp_core_get_core_userdata( $user_id ) ) {
+				return false;
+			}
+		}
+
+		// User nicename found
+		$user_nicename = $userdata->user_nicename;
+
+	// Nicename found in cache so don't update it again
+	} else {
+		$update_cache = false;
+	}
+
+	// Add this to cache
+	if ( true == $update_cache && !empty( $user_nicename ) )
+		wp_cache_set( 'bp_members_user_nicename_' . $user_id, $user_nicename, 'bp' );
+
+	return apply_filters( 'bp_members_get_user_nicename', $user_nicename );
+}
+
+/**
+ * Returns the email address for the user based on user ID
+ *
+ * @package BuddyPress Core
+ * @param $uid int User ID to check.
+ * @uses get_userdata() WordPress function to fetch the userdata for a user ID
+ * @return false on no match
+ * @return str The email for the matched user.
+ */
+function bp_core_get_user_email( $uid ) {
+	if ( !$email = wp_cache_get( 'bp_user_email_' . $uid, 'bp' ) ) {
+		// User exists
+		if ( $ud = bp_core_get_core_userdata( $uid ) )
+			$email = $ud->user_email;
+
+		// User was deleted
+		else
+			$email = '';
+
+		wp_cache_set( 'bp_user_email_' . $uid, $email, 'bp' );
+	}
+
+	return apply_filters( 'bp_core_get_user_email', $email );
+}
+
+/**
+ * Returns a HTML formatted link for a user with the user's full name as the link text.
+ * eg: <a href="http://andy.domain.com/">Andy Peatling</a>
+ * Optional parameters will return just the name or just the URL.
+ *
+ * @param int $user_id User ID to check.
+ * @param $no_anchor bool Disable URL and HTML and just return full name. Default false.
+ * @param $just_link bool Disable full name and HTML and just return the URL text. Default false.
+ * @return false on no match
+ * @return str The link text based on passed parameters.
+ * @todo This function needs to be cleaned up or split into separate functions
+ */
+function bp_core_get_userlink( $user_id, $no_anchor = false, $just_link = false ) {
+	$display_name = bp_core_get_user_displayname( $user_id );
+
+	if ( empty( $display_name ) )
+		return false;
+
+	if ( $no_anchor )
+		return $display_name;
+
+	if ( !$url = bp_core_get_user_domain( $user_id ) )
+		return false;
+
+	if ( $just_link )
+		return $url;
+
+	return apply_filters( 'bp_core_get_userlink', '<a href="' . $url . '" title="' . $display_name . '">' . $display_name . '</a>', $user_id );
+}
+
+
+/**
+ * Fetch the display name for a user. This will use the "Name" field in xprofile if it is installed.
+ * Otherwise, it will fall back to the normal WP display_name, or user_nicename, depending on what has been set.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @uses wp_cache_get() Will try and fetch the value from the cache, rather than querying the DB again.
+ * @uses get_userdata() Fetches the WP userdata for a specific user.
+ * @uses xprofile_set_field_data() Will update the field data for a user based on field name and user id.
+ * @uses wp_cache_set() Adds a value to the cache.
+ * @return str The display name for the user in question.
+ */
+function bp_core_get_user_displayname( $user_id_or_username ) {
+	global $bp;
+
+	$fullname = '';
+
+	if ( !$user_id_or_username )
+		return false;
+
+	if ( !is_numeric( $user_id_or_username ) )
+		$user_id = bp_core_get_userid( $user_id_or_username );
+	else
+		$user_id = $user_id_or_username;
+
+	if ( !$user_id )
+		return false;
+
+	if ( !$fullname = wp_cache_get( 'bp_user_fullname_' . $user_id, 'bp' ) ) {
+		if ( bp_is_active( 'xprofile' ) ) {
+			$fullname = xprofile_get_field_data( stripslashes( $bp->site_options['bp-xprofile-fullname-field-name'] ), $user_id );
+
+			if ( empty($fullname) ) {
+				$ud = bp_core_get_core_userdata( $user_id );
+
+				if ( !empty( $ud->display_name ) )
+					$fullname = $ud->display_name;
+				elseif ( !empty( $ud->user_nicename ) )
+					$fullname = $ud->user_nicename;
+
+				xprofile_set_field_data( 1, $user_id, $fullname );
+			}
+		} else {
+			$ud = bp_core_get_core_userdata($user_id);
+
+			if ( !empty( $ud->display_name ) )
+				$fullname = $ud->display_name;
+			elseif ( !empty( $ud->user_nicename ) )
+				$fullname = $ud->user_nicename;
+		}
+
+		if ( !empty( $fullname ) )
+			wp_cache_set( 'bp_user_fullname_' . $user_id, $fullname, 'bp' );
+	}
+
+	return apply_filters( 'bp_core_get_user_displayname', $fullname, $user_id );
+}
+add_filter( 'bp_core_get_user_displayname', 'strip_tags', 1 );
+add_filter( 'bp_core_get_user_displayname', 'trim'          );
+add_filter( 'bp_core_get_user_displayname', 'stripslashes'  );
+
+
+/**
+ * Returns the user link for the user based on user email address
+ *
+ * @package BuddyPress Core
+ * @param $email str The email address for the user.
+ * @uses bp_core_get_userlink() BuddyPress function to get a userlink by user ID.
+ * @uses get_user_by_email() WordPress function to get userdata via an email address
+ * @return str The link to the users home base. False on no match.
+ */
+function bp_core_get_userlink_by_email( $email ) {
+	$user = get_user_by_email( $email );
+	return apply_filters( 'bp_core_get_userlink_by_email', bp_core_get_userlink( $user->ID, false, false, true ) );
+}
+
+/**
+ * Returns the user link for the user based on the supplied identifier
+ *
+ * @param $username str If BP_ENABLE_USERNAME_COMPATIBILITY_MODE is set, this will be user_login, otherwise it will be user_nicename.
+ * @return str The link to the users home base. False on no match.
+ */
+function bp_core_get_userlink_by_username( $username ) {
+	if ( bp_is_username_compatibility_mode() )
+		$user_id = bp_core_get_userid( $username );
+	else
+		$user_id = bp_core_get_userid_from_nicename( $username );
+
+	return apply_filters( 'bp_core_get_userlink_by_username', bp_core_get_userlink( $user_id, false, false, true ) );
+}
+
+/**
+ * Returns the total number of members for the installation.
+ *
+ * @package BuddyPress Core
+ * @return int The total number of members.
+ */
+function bp_core_get_total_member_count() {
+	global $wpdb, $bp;
+
+	if ( !$count = wp_cache_get( 'bp_total_member_count', 'bp' ) ) {
+		$status_sql = bp_core_get_status_sql();
+		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->users WHERE {$status_sql}" ) );
+		wp_cache_set( 'bp_total_member_count', $count, 'bp' );
+	}
+
+	return apply_filters( 'bp_core_get_total_member_count', $count );
+}
+
+/**
+ * Checks if the user has been marked as a spammer.
+ *
+ * @package BuddyPress Core
+ * @param int $user_id int The id for the user.
+ * @return bool True if spammer, False if not.
+ */
+function bp_core_is_user_spammer( $user_id = 0 ) {
+	global $wpdb;
+
+	// No user to check
+	if ( empty( $user_id ) )
+		return false;
+
+	// Assume user is not spam
+	$is_spammer = false;
+
+	// Get user data
+	$user = get_userdata( $user_id );
+
+	// No user found
+	if ( empty( $user ) ) {
+		$is_spammer = false;
+
+	// User found
+	} else {
+
+		// Check if spam
+		if ( !empty( $user->spam ) )
+			$is_spammer = true;
+
+		if ( 1 == $user->user_status )
+			$is_spammer = true;
+	}
+
+	return apply_filters( 'bp_core_is_user_spammer', (bool) $is_spammer );
+}
+
+/**
+ * Checks if the user has been marked as deleted.
+ *
+ * @package BuddyPress Core
+ * @param int $user_id int The id for the user.
+ * @return bool True if deleted, False if not.
+ */
+function bp_core_is_user_deleted( $user_id = 0 ) {
+	global $wpdb;
+
+	// No user to check
+	if ( empty( $user_id ) )
+		return false;
+
+	// Assume user is not deleted
+	$is_deleted = false;
+
+	// Get user data
+	$user = get_userdata( $user_id );
+
+	// No user found
+	if ( empty( $user ) ) {
+		$is_deleted = true;
+
+	// User found
+	} else {
+
+		// Check if deleted
+		if ( !empty( $user->deleted ) )
+			$is_deleted = true;
+
+		if ( 2 == $user->user_status )
+			$is_deleted = true;
+
+	}
+
+	return apply_filters( 'bp_core_is_user_deleted', (bool) $is_deleted );
+}
+
+/**
+ * Fetch every post that is authored by the given user for the current blog.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @global $wpdb WordPress user data for the current logged in user.
+ * @return array of post ids.
+ */
+function bp_core_get_all_posts_for_user( $user_id = 0 ) {
+	global $bp, $wpdb;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->displayed_user->id;
+
+	return apply_filters( 'bp_core_get_all_posts_for_user', $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d AND post_status = 'publish' AND post_type = 'post'", $user_id ) ) );
+}
+
+/**
+ * Allows a user to completely remove their account from the system
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ * @uses is_super_admin() Checks to see if the user is a site administrator.
+ * @uses wpmu_delete_user() Deletes a user from the system on multisite installs.
+ * @uses wp_delete_user() Deletes a user from the system on singlesite installs.
+ */
+function bp_core_delete_account( $user_id = 0 ) {
+	global $bp, $wp_version;
+
+	if ( !$user_id )
+		$user_id = $bp->loggedin_user->id;
+
+	// Make sure account deletion is not disabled
+	if ( !empty( $bp->site_options['bp-disable-account-deletion'] ) && !$bp->loggedin_user->is_super_admin )
+		return false;
+
+	// Site admins cannot be deleted
+	if ( is_super_admin( bp_core_get_username( $user_id ) ) )
+		return false;
+
+	// Specifically handle multi-site environment
+	if ( is_multisite() ) {
+		if ( $wp_version >= '3.0' )
+			require( ABSPATH . '/wp-admin/includes/ms.php' );
+		else
+			require( ABSPATH . '/wp-admin/includes/mu.php' );
+
+		require( ABSPATH . '/wp-admin/includes/user.php' );
+
+		return wpmu_delete_user( $user_id );
+
+	// Single site user deletion
+	} else {
+		require( ABSPATH . '/wp-admin/includes/user.php' );
+		return wp_delete_user( $user_id );
+	}
+}
+
+/**
+ * Localization safe ucfirst() support.
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_ucfirst( $str ) {
+	if ( function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) ) {
+		$fc = mb_strtoupper( mb_substr( $str, 0, 1 ) );
+		return $fc.mb_substr( $str, 1 );
+	} else {
+		return ucfirst( $str );
+	}
+}
+
+/**
+ * Strips spaces from usernames that are created using add_user() and wp_insert_user()
+ *
+ * @package BuddyPress Core
+ */
+function bp_core_strip_username_spaces( $username ) {
+	// Don't alter the user_login of existing users, as it causes user_nicename problems.
+	// See http://trac.buddypress.org/ticket/2642
+	if ( username_exists( $username ) && ( !bp_is_username_compatibility_mode() ) )
+		return $username;
+
+	return str_replace( ' ', '-', $username );
+}
+add_action( 'pre_user_login', 'bp_core_strip_username_spaces' );
+
+/**
+ * When a user logs in, check if they have been marked as a spammer. If yes then simply
+ * redirect them to the home page and stop them from logging in.
+ *
+ * @package BuddyPress Core
+ * @param $auth_obj The WP authorization object
+ * @param $username The username of the user logging in.
+ * @uses get_userdatabylogin() Get the userdata object for a user based on their username
+ * @uses bp_core_redirect() Safe redirect to a page
+ * @return $auth_obj If the user is not a spammer, return the authorization object
+ */
+function bp_core_boot_spammer( $auth_obj, $username ) {
+	global $bp;
+
+	if ( !$user = get_userdatabylogin( $username ) )
+		return $auth_obj;
+
+	if ( ( is_multisite() && (int)$user->spam ) || 1 == (int)$user->user_status )
+		return new WP_Error( 'invalid_username', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.', 'buddypress' ) );
+	else
+		return $auth_obj;
+}
+add_filter( 'authenticate', 'bp_core_boot_spammer', 30, 2 );
+
+/**
+ * Deletes usermeta for the user when the user is deleted.
+ *
+ * @package BuddyPress Core
+ * @param $user_id The user id for the user to delete usermeta for
+ * @uses bp_delete_user_meta() deletes a row from the wp_usermeta table based on meta_key
+ */
+function bp_core_remove_data( $user_id ) {
+	// Remove usermeta
+	bp_delete_user_meta( $user_id, 'last_activity' );
+
+	// Flush the cache to remove the user from all cached objects
+	wp_cache_flush();
+}
+add_action( 'wpmu_delete_user',  'bp_core_remove_data' );
+add_action( 'delete_user',       'bp_core_remove_data' );
+add_action( 'bp_make_spam_user', 'bp_core_remove_data' );
+
+function bp_core_can_edit_settings() {
+	if ( bp_is_my_profile() )
+		return true;
+
+	if ( is_super_admin() || current_user_can( 'edit_users' ) )
+		return true;
+
+	return false;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-loader.php	(revision 5057)
@@ -0,0 +1,189 @@
+<?php
+/**
+ * BuddyPress Member Loader
+ *
+ * A members component to help contain all of the user specific slugs
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Members_Component extends BP_Component {
+
+	/**
+	 * Start the members component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'members',
+			__( 'Members', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 *
+	 * @global obj $bp
+	 */
+	function includes() {
+		$includes = array(
+			'signup',
+			'actions',
+			'filters',
+			'screens',
+			'template',
+			'buddybar',
+			'adminbar',
+			'functions',
+			'notifications',
+		);
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_MEMBERS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp, $current_user, $displayed_user_id;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_MEMBERS_SLUG' ) )
+			define( 'BP_MEMBERS_SLUG', 'members' );
+
+		$globals = array(
+			'path'          => BP_PLUGIN_DIR,
+			'slug'          => BP_MEMBERS_SLUG,
+			'root_slug'     => isset( $bp->pages->members->slug ) ? $bp->pages->members->slug : BP_MEMBERS_SLUG,
+			'has_directory' => true,
+			'search_string' => __( 'Search Members...', 'buddypress' ),
+		);
+
+		parent::setup_globals( $globals );
+
+		/** Logged in user ****************************************************/
+
+		// Fetch the full name for the logged in user
+		$bp->loggedin_user->fullname       = bp_core_get_user_displayname( $bp->loggedin_user->id );
+
+		// Hits the DB on single WP installs so get this separately
+		$bp->loggedin_user->is_super_admin = $bp->loggedin_user->is_site_admin = is_super_admin();
+
+		// The domain for the user currently logged in. eg: http://domain.com/members/andy
+		$bp->loggedin_user->domain         = bp_core_get_user_domain( $bp->loggedin_user->id );
+
+		// The core userdata of the user who is currently logged in.
+		$bp->loggedin_user->userdata       = bp_core_get_core_userdata( $bp->loggedin_user->id );
+
+		/** Displayed user ****************************************************/
+
+		// The user id of the user currently being viewed:
+		// $bp->displayed_user->id is set in /bp-core/bp-core-catchuri.php
+		if ( empty( $bp->displayed_user->id ) )
+			$bp->displayed_user->id = 0;
+
+		// The domain for the user currently being displayed
+		$bp->displayed_user->domain   = bp_core_get_user_domain( $bp->displayed_user->id );
+
+		// The core userdata of the user who is currently being displayed
+		$bp->displayed_user->userdata = bp_core_get_core_userdata( $bp->displayed_user->id );
+
+		// Fetch the full name displayed user
+		$bp->displayed_user->fullname = bp_core_get_user_displayname( $bp->displayed_user->id );
+
+		/** Profiles Fallback *************************************************/
+		if ( !bp_is_active( 'xprofile' ) ) {
+			$bp->profile->slug = 'profile';
+			$bp->profile->id   = 'profile';
+		}
+
+		/** Default Profile Component *****************************************/
+		if ( !defined( 'BP_DEFAULT_COMPONENT' ) ) {
+			if ( bp_is_active( 'activity' ) && isset( $bp->pages->activity ) )
+				$bp->default_component = bp_get_activity_slug();
+			else
+				$bp->default_component = ( 'xprofile' == $bp->profile->id ) ? 'profile' : $bp->profile->id;
+
+		} else {
+			$bp->default_component = BP_DEFAULT_COMPONENT;
+		}
+
+		if ( !$bp->current_component && $bp->displayed_user->id )
+			$bp->current_component = $bp->default_component;
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add 'Profile' to the main navigation
+		if ( !bp_is_active( 'xprofile' ) ) {
+			$main_nav = array(
+				'name'                => __( 'Profile', 'buddypress' ),
+				'slug'                => $bp->profile->slug,
+				'position'            => 20,
+				'screen_function'     => 'bp_members_screen_display_profile',
+				'default_subnav_slug' => 'public',
+				'item_css_id'         => $bp->profile->id
+			);
+
+			// User links
+			$user_domain   = ( isset( $bp->displayed_user->domain ) )               ? $bp->displayed_user->domain               : $bp->loggedin_user->domain;
+			$user_login    = ( isset( $bp->displayed_user->userdata->user_login ) ) ? $bp->displayed_user->userdata->user_login : $bp->loggedin_user->userdata->user_login;
+			$profile_link  = trailingslashit( $user_domain . $bp->profile->slug );
+
+			// Add the subnav items to the profile
+			$sub_nav[] = array(
+				'name'            => __( 'Public', 'buddypress' ),
+				'slug'            => 'public',
+				'parent_url'      => $profile_link,
+				'parent_slug'     => $bp->profile->slug,
+				'screen_function' => 'bp_members_screen_display_profile',
+				'position'        => 10
+			);
+
+			parent::setup_nav( $main_nav, $sub_nav );
+		}
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		if ( bp_is_my_profile() ) {
+			$bp->bp_options_title = __( 'You', 'buddypress' );
+		} elseif( bp_is_user() ) {
+			$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+				'item_id' => $bp->displayed_user->id,
+				'type'    => 'thumb'
+			) );
+			$bp->bp_options_title  = $bp->displayed_user->fullname;
+		}
+
+		parent::setup_title();
+	}
+
+}
+// Create the users component
+$bp->members = new BP_Members_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-notifications.php	(revision 5057)
@@ -0,0 +1,131 @@
+<?php
+/**
+ * BuddyPress Member Notifications
+ *
+ * Functions and filters used for member notification
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_core_add_notification( $item_id, $user_id, $component_name, $component_action, $secondary_item_id = 0, $date_notified = false ) {
+	global $bp;
+
+	if ( empty( $date_notified ) )
+		$date_notified = bp_core_current_time();
+
+	$notification                        = new BP_Core_Notification;
+	$notification->item_id               = $item_id;
+	$notification->user_id               = $user_id;
+	$notification->component_name        = $component_name;
+	$notification->component_action      = $component_action;
+	$notification->date_notified         = $date_notified;
+	$notification->is_new                = 1;
+
+	if ( !empty( $secondary_item_id ) )
+		$notification->secondary_item_id = $secondary_item_id;
+
+	if ( !$notification->save() )
+		return false;
+
+	return true;
+}
+
+function bp_core_delete_notification( $id ) {
+	if ( !bp_core_check_notification_access( $bp->loggedin_user->id, $id ) )
+		return false;
+
+	return BP_Core_Notification::delete( $id );
+}
+
+function bp_core_get_notification( $id ) {
+	return new BP_Core_Notification( $id );
+}
+
+function bp_core_get_notifications_for_user( $user_id, $format = 'simple' ) {
+	global $bp;
+
+	$notifications = BP_Core_Notification::get_all_for_user( $user_id );
+
+	// Group notifications by component and component_action and provide totals
+	for ( $i = 0, $count = count( $notifications ); $i < $count; ++$i ) {
+		$notification = $notifications[$i];
+		$grouped_notifications[$notification->component_name][$notification->component_action][] = $notification;
+	}
+
+	if ( empty( $grouped_notifications ) )
+		return false;
+
+	$renderable = array();
+
+	// Calculate a renderable output for each notification type
+	foreach ( (array)$grouped_notifications as $component_name => $action_arrays ) {
+		if ( !$action_arrays )
+			continue;
+
+		foreach ( (array)$action_arrays as $component_action_name => $component_action_items ) {
+			$action_item_count = count($component_action_items);
+
+			if ( $action_item_count < 1 )
+				continue;
+
+			// @deprecated format_notification_function - 1.5
+			if ( isset( $bp->{$component_name}->format_notification_function ) && function_exists( $bp->{$component_name}->format_notification_function ) ) {
+				$renderable[] = call_user_func( $bp->{$component_name}->format_notification_function, $component_action_name, $component_action_items[0]->item_id, $component_action_items[0]->secondary_item_id, $action_item_count );
+			} elseif ( isset( $bp->{$component_name}->notification_callback ) && function_exists( $bp->{$component_name}->notification_callback ) ) {
+				if ( 'object' == $format ) {
+					$content = call_user_func( $bp->{$component_name}->notification_callback, $component_action_name, $component_action_items[0]->item_id, $component_action_items[0]->secondary_item_id, $action_item_count, 'array' );
+
+					$notification_object = new stdClass;
+
+					// Minimal backpat with non-compatible notification
+					// callback functions
+					if ( is_string( $content ) ) {
+						$notification_object->content = $content;
+						$notification_object->href    = bp_loggedin_user_domain();
+					} else {
+						$notification_object->content = $content['text'];
+						$notification_object->href    = $content['link'];
+					}
+
+					$notification_object->id = $component_action_items[0]->id;
+
+					$renderable[] 	  = $notification_object;
+				} else {
+					$content = call_user_func( $bp->{$component_name}->notification_callback, $component_action_name, $component_action_items[0]->item_id, $component_action_items[0]->secondary_item_id, $action_item_count );
+					$renderable[] = $content;
+				}
+			}
+		}
+	}
+
+	return isset( $renderable ) ? $renderable : false;
+}
+
+function bp_core_delete_notifications_by_type( $user_id, $component_name, $component_action ) {
+	return BP_Core_Notification::delete_for_user_by_type( $user_id, $component_name, $component_action );
+}
+
+function bp_core_delete_notifications_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id = false ) {
+	return BP_Core_Notification::delete_for_user_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id );
+}
+
+function bp_core_delete_all_notifications_by_type( $item_id, $component_name, $component_action = false, $secondary_item_id = false ) {
+	return BP_Core_Notification::delete_all_by_type( $item_id, $component_name, $component_action, $secondary_item_id );
+}
+
+function bp_core_delete_notifications_from_user( $user_id, $component_name, $component_action ) {
+	return BP_Core_Notification::delete_from_user_by_type( $user_id, $component_name, $component_action );
+}
+
+function bp_core_check_notification_access( $user_id, $notification_id ) {
+	if ( !BP_Core_Notification::check_access( $user_id, $notification_id ) )
+		return false;
+
+	return true;
+}
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-screens.php	(revision 5057)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * BuddyPress Member Screens
+ *
+ * Handlers for member screens that aren't handled elsewhere
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Handles the display of the profile page by loading the correct template file.
+ *
+ * @package BuddyPress Members
+ * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
+ */
+function bp_members_screen_display_profile() {
+	do_action( 'bp_members_screen_display_profile' );
+	bp_core_load_template( apply_filters( 'bp_members_screen_display_profile', 'members/single/home' ) );
+}
+
+/**
+ * Handles the display of the members directory index
+ *
+ * @global object $bp
+ *
+ * @uses bp_is_user()
+ * @uses bp_is_current_component()
+ * @uses do_action()
+ * @uses bp_core_load_template()
+ * @uses apply_filters()
+ */
+function bp_members_screen_index() {
+	if ( !bp_is_user() && bp_is_current_component( 'members' ) ) {
+		bp_update_is_directory( true, 'members' );
+
+		do_action( 'bp_members_screen_index' );
+
+		bp_core_load_template( apply_filters( 'bp_members_screen_index', 'members/index' ) );
+	}
+}
+add_action( 'bp_screens', 'bp_members_screen_index' );
+
+
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-signup.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-signup.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-signup.php	(revision 5057)
@@ -0,0 +1,614 @@
+<?php
+/**
+ * BuddyPress Member Sign-up
+ *
+ * Functions and filters specific to the member sign-up process
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function bp_core_screen_signup() {
+	global $bp, $wpdb;
+
+	if ( !bp_is_current_component( 'register' ) )
+		return;
+
+	// Not a directory
+	bp_update_is_directory( false, 'register' );
+
+	// If the user is logged in, redirect away from here
+	if ( is_user_logged_in() ) {
+		if ( bp_is_component_front_page( 'register' ) )
+			bp_core_redirect( bp_get_root_domain() . '/' . bp_get_members_root_slug() );
+		else
+			bp_core_redirect( bp_get_root_domain() );
+
+		return;
+	}
+
+	$bp->signup->step = 'request-details';
+
+ 	if ( !bp_get_signup_allowed() ) {
+		$bp->signup->step = 'registration-disabled';
+	}
+
+	// If the signup page is submitted, validate and save
+	elseif ( isset( $_POST['signup_submit'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'bp_new_signup' );
+
+		// Check the base account details for problems
+		$account_details = bp_core_validate_user_signup( $_POST['signup_username'], $_POST['signup_email'] );
+
+		// If there are errors with account details, set them for display
+		if ( !empty( $account_details['errors']->errors['user_name'] ) )
+			$bp->signup->errors['signup_username'] = $account_details['errors']->errors['user_name'][0];
+
+		if ( !empty( $account_details['errors']->errors['user_email'] ) )
+			$bp->signup->errors['signup_email'] = $account_details['errors']->errors['user_email'][0];
+
+		// Check that both password fields are filled in
+		if ( empty( $_POST['signup_password'] ) || empty( $_POST['signup_password_confirm'] ) )
+			$bp->signup->errors['signup_password'] = __( 'Please make sure you enter your password twice', 'buddypress' );
+
+		// Check that the passwords match
+		if ( ( !empty( $_POST['signup_password'] ) && !empty( $_POST['signup_password_confirm'] ) ) && $_POST['signup_password'] != $_POST['signup_password_confirm'] )
+			$bp->signup->errors['signup_password'] = __( 'The passwords you entered do not match.', 'buddypress' );
+
+		$bp->signup->username = $_POST['signup_username'];
+		$bp->signup->email = $_POST['signup_email'];
+
+		// Now we've checked account details, we can check profile information
+		if ( bp_is_active( 'xprofile' ) ) {
+
+			// Make sure hidden field is passed and populated
+			if ( isset( $_POST['signup_profile_field_ids'] ) && !empty( $_POST['signup_profile_field_ids'] ) ) {
+
+				// Let's compact any profile field info into an array
+				$profile_field_ids = explode( ',', $_POST['signup_profile_field_ids'] );
+
+				// Loop through the posted fields formatting any datebox values then validate the field
+				foreach ( (array) $profile_field_ids as $field_id ) {
+					if ( !isset( $_POST['field_' . $field_id] ) ) {
+						if ( isset( $_POST['field_' . $field_id . '_day'] ) )
+							$_POST['field_' . $field_id] = strtotime( $_POST['field_' . $field_id . '_day'] . $_POST['field_' . $field_id . '_month'] . $_POST['field_' . $field_id . '_year'] );
+					}
+
+					// Create errors for required fields without values
+					if ( xprofile_check_is_required_field( $field_id ) && empty( $_POST['field_' . $field_id] ) )
+						$bp->signup->errors['field_' . $field_id] = __( 'This is a required field', 'buddypress' );
+				}
+
+			// This situation doesn't naturally occur so bounce to website root
+			} else {
+				bp_core_redirect( bp_get_root_domain() );
+			}
+		}
+
+		// Finally, let's check the blog details, if the user wants a blog and blog creation is enabled
+		if ( isset( $_POST['signup_with_blog'] ) ) {
+			$active_signup = $bp->site_options['registration'];
+
+			if ( 'blog' == $active_signup || 'all' == $active_signup ) {
+				$blog_details = bp_core_validate_blog_signup( $_POST['signup_blog_url'], $_POST['signup_blog_title'] );
+
+				// If there are errors with blog details, set them for display
+				if ( !empty( $blog_details['errors']->errors['blogname'] ) )
+					$bp->signup->errors['signup_blog_url'] = $blog_details['errors']->errors['blogname'][0];
+
+				if ( !empty( $blog_details['errors']->errors['blog_title'] ) )
+					$bp->signup->errors['signup_blog_title'] = $blog_details['errors']->errors['blog_title'][0];
+			}
+		}
+
+		do_action( 'bp_signup_validate' );
+
+		// Add any errors to the action for the field in the template for display.
+		if ( !empty( $bp->signup->errors ) ) {
+			foreach ( (array)$bp->signup->errors as $fieldname => $error_message )
+				add_action( 'bp_' . $fieldname . '_errors', create_function( '', 'echo apply_filters(\'bp_members_signup_error_message\', "<div class=\"error\">' . $error_message . '</div>" );' ) );
+		} else {
+			$bp->signup->step = 'save-details';
+
+			// No errors! Let's register those deets.
+			$active_signup = !empty( $bp->site_options['registration'] ) ? $bp->site_options['registration'] : '';
+
+			if ( 'none' != $active_signup ) {
+
+				// Let's compact any profile field info into usermeta
+				$profile_field_ids = explode( ',', $_POST['signup_profile_field_ids'] );
+
+				// Loop through the posted fields formatting any datebox values then add to usermeta
+				foreach ( (array) $profile_field_ids as $field_id ) {
+					if ( !isset( $_POST['field_' . $field_id] ) ) {
+						if ( isset( $_POST['field_' . $field_id . '_day'] ) )
+							$_POST['field_' . $field_id] = strtotime( $_POST['field_' . $field_id . '_day'] . $_POST['field_' . $field_id . '_month'] . $_POST['field_' . $field_id . '_year'] );
+					}
+
+					if ( !empty( $_POST['field_' . $field_id] ) )
+						$usermeta['field_' . $field_id] = $_POST['field_' . $field_id];
+				}
+
+				// Store the profile field ID's in usermeta
+				$usermeta['profile_field_ids'] = $_POST['signup_profile_field_ids'];
+
+				// Hash and store the password
+				$usermeta['password'] = wp_hash_password( $_POST['signup_password'] );
+
+				// If the user decided to create a blog, save those details to usermeta
+				if ( 'blog' == $active_signup || 'all' == $active_signup )
+					$usermeta['public'] = ( isset( $_POST['signup_blog_privacy'] ) && 'public' == $_POST['signup_blog_privacy'] ) ? true : false;
+
+				$usermeta = apply_filters( 'bp_signup_usermeta', $usermeta );
+
+				// Finally, sign up the user and/or blog
+				if ( isset( $_POST['signup_with_blog'] ) && is_multisite() )
+					bp_core_signup_blog( $blog_details['domain'], $blog_details['path'], $blog_details['blog_title'], $_POST['signup_username'], $_POST['signup_email'], $usermeta );
+				else
+					bp_core_signup_user( $_POST['signup_username'], $_POST['signup_password'], $_POST['signup_email'], $usermeta );
+
+				$bp->signup->step = 'completed-confirmation';
+			}
+
+			do_action( 'bp_complete_signup' );
+		}
+
+	}
+
+	do_action( 'bp_core_screen_signup' );
+	bp_core_load_template( apply_filters( 'bp_core_template_register', 'registration/register' ) );
+}
+add_action( 'bp_screens', 'bp_core_screen_signup' );
+
+function bp_core_screen_activation() {
+	global $bp, $wpdb;
+
+	if ( !bp_is_current_component( 'activate' ) )
+		return false;
+
+	// Check if an activation key has been passed
+	if ( isset( $_GET['key'] ) ) {
+
+		// Activate the signup
+		$user = apply_filters( 'bp_core_activate_account', bp_core_activate_signup( $_GET['key'] ) );
+
+		// If there were errors, add a message and redirect
+		if ( !empty( $user->errors ) ) {
+			bp_core_add_message( $user->get_error_message(), 'error' );
+			bp_core_redirect( trailingslashit( bp_get_root_domain() . '/' . $bp->pages->activate->slug ) );
+		}
+
+		// Check for an uploaded avatar and move that to the correct user folder
+		if ( is_multisite() )
+			$hashed_key = wp_hash( $_GET['key'] );
+		else
+			$hashed_key = wp_hash( $user );
+
+		// Check if the avatar folder exists. If it does, move rename it, move
+		// it and delete the signup avatar dir
+		if ( file_exists( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key ) )
+			@rename( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key, bp_core_avatar_upload_path() . '/avatars/' . $user );
+
+		bp_core_add_message( __( 'Your account is now active!', 'buddypress' ) );
+
+		$bp->activation_complete = true;
+	}
+
+	if ( '' != locate_template( array( 'registration/activate' ), false ) )
+		bp_core_load_template( apply_filters( 'bp_core_template_activate', 'activate' ) );
+	else
+		bp_core_load_template( apply_filters( 'bp_core_template_activate', 'registration/activate' ) );
+}
+add_action( 'bp_screens', 'bp_core_screen_activation' );
+
+
+/********************************************************************************
+ * Business Functions
+ *
+ * Business functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+/**
+ * Flush illegal names by getting and setting 'illegal_names' site option
+ */
+function bp_core_flush_illegal_names() {
+	$illegal_names = get_site_option( 'illegal_names' );
+	update_site_option( 'illegal_names', $illegal_names );
+}
+
+/**
+ * Filter the illegal_names site option and make sure it includes a few
+ * specific BuddyPress and Multi-site slugs
+ *
+ * @param array|string $value Illegal names from field
+ * @param array|string $oldvalue The value as it is currently
+ * @return array Merged and unique array of illegal names
+ */
+function bp_core_illegal_names( $value = '', $oldvalue = '' ) {
+
+	// Make sure $value is array
+	if ( empty( $value ) )
+		$db_illegal_names = array();
+	if ( is_array( $value ) )
+		$db_illegal_names = $value;
+	elseif ( is_string( $value ) )
+		$db_illegal_names = explode( ' ', $value );
+
+	// Add the core components' slugs to the banned list even if their components aren't active.
+	$bp_component_slugs = array(
+		'groups',
+		'members',
+		'forums',
+		'blogs',
+		'activity',
+		'profile',
+		'friends',
+		'search',
+		'settings',
+		'register',
+		'activate'
+	);
+
+	// Core constants
+	$slug_constants = array(
+		'BP_GROUPS_SLUG',
+		'BP_MEMBERS_SLUG',
+		'BP_FORUMS_SLUG',
+		'BP_BLOGS_SLUG',
+		'BP_ACTIVITY_SLUG',
+		'BP_XPROFILE_SLUG',
+		'BP_FRIENDS_SLUG',
+		'BP_SEARCH_SLUG',
+		'BP_SETTINGS_SLUG',
+		'BP_REGISTER_SLUG',
+		'BP_ACTIVATION_SLUG',
+	);
+	foreach( $slug_constants as $constant )
+		if ( defined( $constant ) )
+			$bp_component_slugs[] = constant( $constant );
+
+	// Add our slugs to the array and allow them to be filtered
+	$filtered_illegal_names = apply_filters( 'bp_core_illegal_usernames', array_merge( array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' ), $bp_component_slugs ) );
+
+	// Merge the arrays together
+	$merged_names           = array_merge( (array)$filtered_illegal_names, (array)$db_illegal_names );
+
+	// Remove duplicates
+	$illegal_names          = array_unique( (array)$merged_names );
+
+	return apply_filters( 'bp_core_illegal_names', $illegal_names );
+}
+add_filter( 'pre_update_site_option_illegal_names', 'bp_core_illegal_names', 10, 2 );
+
+/**
+ * Validate a user name and email address when creating a new user.
+ *
+ * @global object $wpdb DB Layer
+ * @param string $user_name Username to validate
+ * @param string $user_email Email address to validate
+ * @return array Results of user validation including errors, if any
+ */
+function bp_core_validate_user_signup( $user_name, $user_email ) {
+	global $wpdb;
+
+	$errors = new WP_Error();
+	$user_email = sanitize_email( $user_email );
+
+	if ( empty( $user_name ) )
+		$errors->add( 'user_name', __( 'Please enter a username', 'buddypress' ) );
+
+	$maybe = array();
+	preg_match( "/[a-z0-9]+/", $user_name, $maybe );
+
+	// Make sure illegal names include BuddyPress slugs and values
+	bp_core_flush_illegal_names();
+
+	$illegal_names = get_site_option( 'illegal_names' );
+
+	if ( !validate_username( $user_name ) || in_array( $user_name, (array)$illegal_names ) || ( !empty( $maybe[0] ) && $user_name != $maybe[0] ) )
+		$errors->add( 'user_name', __( 'Only lowercase letters and numbers allowed', 'buddypress' ) );
+
+	if( strlen( $user_name ) < 4 )
+		$errors->add( 'user_name',  __( 'Username must be at least 4 characters', 'buddypress' ) );
+
+	if ( strpos( ' ' . $user_name, '_' ) != false )
+		$errors->add( 'user_name', __( 'Sorry, usernames may not contain the character "_"!', 'buddypress' ) );
+
+	// Is the user_name all numeric?
+	$match = array();
+	preg_match( '/[0-9]*/', $user_name, $match );
+
+	if ( $match[0] == $user_name )
+		$errors->add( 'user_name', __( 'Sorry, usernames must have letters too!', 'buddypress' ) );
+
+	if ( !is_email( $user_email ) )
+		$errors->add( 'user_email', __( 'Please check your email address.', 'buddypress' ) );
+
+	if ( function_exists( 'is_email_address_unsafe' ) && is_email_address_unsafe( $user_email ) )
+		$errors->add( 'user_email',  __( 'Sorry, that email address is not allowed!', 'buddypress' ) );
+
+	$limited_email_domains = get_site_option( 'limited_email_domains', 'buddypress' );
+
+	if ( is_array( $limited_email_domains ) && empty( $limited_email_domains ) == false ) {
+		$emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
+
+		if ( in_array( $emaildomain, (array)$limited_email_domains ) == false )
+			$errors->add( 'user_email', __( 'Sorry, that email address is not allowed!', 'buddypress' ) );
+	}
+
+	// Check if the username has been used already.
+	if ( username_exists( $user_name ) )
+		$errors->add( 'user_name', __( 'Sorry, that username already exists!', 'buddypress' ) );
+
+	// Check if the email address has been used already.
+	if ( email_exists( $user_email ) )
+		$errors->add( 'user_email', __( 'Sorry, that email address is already used!', 'buddypress' ) );
+
+	$result = array( 'user_name' => $user_name, 'user_email' => $user_email, 'errors' => $errors );
+
+	// Apply WPMU legacy filter
+	$result = apply_filters( 'wpmu_validate_user_signup', $result );
+
+ 	return apply_filters( 'bp_core_validate_user_signup', $result );
+}
+
+function bp_core_validate_blog_signup( $blog_url, $blog_title ) {
+	if ( !is_multisite() || !function_exists( 'wpmu_validate_blog_signup' ) )
+		return false;
+
+	return apply_filters( 'bp_core_validate_blog_signup', wpmu_validate_blog_signup( $blog_url, $blog_title ) );
+}
+
+function bp_core_signup_user( $user_login, $user_password, $user_email, $usermeta ) {
+	global $bp, $wpdb;
+
+	// Multisite installs have their own install procedure
+	if ( is_multisite() ) {
+		wpmu_signup_user( $user_login, $user_email, $usermeta );
+
+		// On multisite, the user id is not created until the user activates the account
+		// but we need to cast $user_id to pass to the filters
+		$user_id = false;
+
+	} else {
+		$errors = new WP_Error();
+
+		$user_id = wp_insert_user( array(
+			'user_login' => $user_login,
+			'user_pass' => $user_password,
+			'display_name' => sanitize_title( $user_login ),
+			'user_email' => $user_email
+		) );
+
+		if ( empty( $user_id ) ) {
+			$errors->add( 'registerfail', sprintf( __('<strong>ERROR</strong>: Couldn&#8217;t register you... please contact the <a href="mailto:%s">webmaster</a> !', 'buddypress' ), get_option( 'admin_email' ) ) );
+			return $errors;
+		}
+
+		// Update the user status to '2' which we will use as 'not activated' (0 = active, 1 = spam, 2 = not active)
+		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->users SET user_status = 2 WHERE ID = %d", $user_id ) );
+
+		// Set any profile data
+		if ( bp_is_active( 'xprofile' ) ) {
+			if ( !empty( $usermeta['profile_field_ids'] ) ) {
+				$profile_field_ids = explode( ',', $usermeta['profile_field_ids'] );
+
+				foreach( (array)$profile_field_ids as $field_id ) {
+					if ( empty( $usermeta["field_{$field_id}"] ) )
+						continue;
+
+					$current_field = $usermeta["field_{$field_id}"];
+					xprofile_set_field_data( $field_id, $user_id, $current_field );
+				}
+			}
+		}
+	}
+	$bp->signup->username = $user_login;
+
+	/***
+	 * Now generate an activation key and send an email to the user so they can activate their account
+	 * and validate their email address. Multisite installs send their own email, so this is only for single blog installs.
+	 *
+	 * To disable sending activation emails you can user the filter 'bp_core_signup_send_activation_key' and return false.
+	 */
+	if ( apply_filters( 'bp_core_signup_send_activation_key', true ) ) {
+		if ( !is_multisite() ) {
+			$activation_key = wp_hash( $user_id );
+			update_user_meta( $user_id, 'activation_key', $activation_key );
+			bp_core_signup_send_validation_email( $user_id, $user_email, $activation_key );
+		}
+	}
+
+	do_action( 'bp_core_signup_user', $user_id, $user_login, $user_password, $user_email, $usermeta );
+
+	return $user_id;
+}
+
+function bp_core_signup_blog( $blog_domain, $blog_path, $blog_title, $user_name, $user_email, $usermeta ) {
+	if ( !is_multisite() || !function_exists( 'wpmu_signup_blog' ) )
+		return false;
+
+	return apply_filters( 'bp_core_signup_blog', wpmu_signup_blog( $blog_domain, $blog_path, $blog_title, $user_name, $user_email, $usermeta ) );
+}
+
+function bp_core_activate_signup( $key ) {
+	global $bp, $wpdb;
+
+	$user = false;
+
+	// Multisite installs have their own activation routine
+	if ( is_multisite() ) {
+		$user = wpmu_activate_signup( $key );
+
+		// If there were errors, add a message and redirect
+		if ( !empty( $user->errors ) ) {
+			return $user;
+		}
+
+		$user_id = $user['user_id'];
+
+		// Set any profile data
+		if ( bp_is_active( 'xprofile' ) ) {
+			if ( !empty( $user['meta']['profile_field_ids'] ) ) {
+				$profile_field_ids = explode( ',', $user['meta']['profile_field_ids'] );
+
+				foreach( (array)$profile_field_ids as $field_id ) {
+					$current_field = $user['meta']["field_{$field_id}"];
+
+					if ( !empty( $current_field ) )
+						xprofile_set_field_data( $field_id, $user_id, $current_field );
+				}
+			}
+		}
+
+	} else {
+		// Get the user_id based on the $key
+		$user_id = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'activation_key' AND meta_value = %s", $key ) );
+
+		if ( empty( $user_id ) )
+			return new WP_Error( 'invalid_key', __( 'Invalid activation key', 'buddypress' ) );
+
+		// Change the user's status so they become active
+		if ( !$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->users SET user_status = 0 WHERE ID = %d", $user_id ) ) )
+			return new WP_Error( 'invalid_key', __( 'Invalid activation key', 'buddypress' ) );
+
+		// Notify the site admin of a new user registration
+		wp_new_user_notification( $user_id );
+
+		// Remove the activation key meta
+		delete_user_meta( $user_id, 'activation_key' );
+	}
+
+	// Update the display_name
+	wp_update_user( array( 'ID' => $user_id, 'display_name' => bp_core_get_user_displayname( $user_id ) ) );
+
+	// Set the password on multisite installs
+	if ( is_multisite() && !empty( $user['meta']['password'] ) )
+		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->users SET user_pass = %s WHERE ID = %d", $user['meta']['password'], $user_id ) );
+
+	// Delete the total member cache
+	wp_cache_delete( 'bp_total_member_count', 'bp' );
+
+	do_action( 'bp_core_activated_user', $user_id, $key, $user );
+
+	return $user_id;
+}
+
+function bp_core_new_user_activity( $user ) {
+	if ( empty( $user ) || !bp_is_active( 'activity' ) )
+		return false;
+
+	if ( is_array( $user ) )
+		$user_id = $user['user_id'];
+	else
+		$user_id = $user;
+
+	if ( empty( $user_id ) )
+		return false;
+
+	$userlink = bp_core_get_userlink( $user_id );
+
+	bp_activity_add( array(
+		'user_id'   => $user_id,
+		'action'    => apply_filters( 'bp_core_activity_registered_member_action', sprintf( __( '%s became a registered member', 'buddypress' ), $userlink ), $user_id ),
+		'component' => 'xprofile',
+		'type'      => 'new_member'
+	) );
+}
+add_action( 'bp_core_activated_user', 'bp_core_new_user_activity' );
+
+function bp_core_map_user_registration( $user_id ) {
+	// Only map data when the site admin is adding users, not on registration.
+	if ( !is_admin() )
+		return false;
+
+	// Add the user's fullname to Xprofile
+	if ( bp_is_active( 'xprofile' ) ) {
+		$firstname = get_user_meta( $user_id, 'first_name', true );
+		$lastname = ' ' . get_user_meta( $user_id, 'last_name', true );
+		$name = $firstname . $lastname;
+
+		if ( empty( $name ) || ' ' == $name )
+			$name = get_user_meta( $user_id, 'nickname', true );
+
+		xprofile_set_field_data( 1, $user_id, $name );
+	}
+}
+add_action( 'user_register', 'bp_core_map_user_registration' );
+
+function bp_core_signup_avatar_upload_dir() {
+	global $bp;
+
+	if ( !$bp->signup->avatar_dir )
+		return false;
+
+	$path  = bp_core_avatar_upload_path() . '/avatars/signups/' . $bp->signup->avatar_dir;
+	$newbdir = $path;
+
+	if ( !file_exists( $path ) )
+		@wp_mkdir_p( $path );
+
+	$newurl = bp_core_avatar_url() . '/avatars/signups/' . $bp->signup->avatar_dir;
+	$newburl = $newurl;
+	$newsubdir = '/avatars/signups/' . $bp->signup->avatar_dir;
+
+	return apply_filters( 'bp_core_signup_avatar_upload_dir', array( 'path' => $path, 'url' => $newurl, 'subdir' => $newsubdir, 'basedir' => $newbdir, 'baseurl' => $newburl, 'error' => false ) );
+}
+
+function bp_core_signup_send_validation_email( $user_id, $user_email, $key ) {
+	$activate_url = bp_get_activation_page() ."?key=$key";
+	$activate_url = esc_url( $activate_url );
+
+	$from_name = ( '' == get_option( 'blogname' ) ) ? __( 'BuddyPress', 'buddypress' ) : esc_html( get_option( 'blogname' ) );
+
+	$message = sprintf( __( "Thanks for registering! To complete the activation of your account please click the following link:\n\n%s\n\n", 'buddypress' ), $activate_url );
+	$subject = '[' . $from_name . '] ' . __( 'Activate Your Account', 'buddypress' );
+
+	// Send the message
+	$to      = apply_filters( 'bp_core_signup_send_validation_email_to',     $user_email, $user_id                );
+	$subject = apply_filters( 'bp_core_signup_send_validation_email_subject', $subject,    $user_id                );
+	$message = apply_filters( 'bp_core_signup_send_validation_email_message', $message,    $user_id, $activate_url );
+
+	wp_mail( $to, $subject, $message );
+
+	do_action( 'bp_core_sent_user_validation_email', $subject, $message, $user_id, $user_email, $key );
+}
+
+// Stop user accounts logging in that have not been activated (user_status = 2)
+function bp_core_signup_disable_inactive( $auth_obj, $username ) {
+	global $bp, $wpdb;
+
+	if ( !$user_id = bp_core_get_userid( $username ) )
+		return $auth_obj;
+
+	$user_status = (int) $wpdb->get_var( $wpdb->prepare( "SELECT user_status FROM $wpdb->users WHERE ID = %d", $user_id ) );
+
+	if ( 2 == $user_status )
+		return new WP_Error( 'bp_account_not_activated', __( '<strong>ERROR</strong>: Your account has not been activated. Check your email for the activation link.', 'buddypress' ) );
+	else
+		return $auth_obj;
+}
+add_filter( 'authenticate', 'bp_core_signup_disable_inactive', 30, 2 );
+
+// Kill the wp-signup.php if custom registration signup templates are present
+function bp_core_wpsignup_redirect() {
+	$action = !empty( $_GET['action'] ) ? $_GET['action'] : '';
+
+	// Not at the WP core signup page and action is not register
+	if ( false === strpos( $_SERVER['SCRIPT_NAME'], 'wp-signup.php' ) && ( 'register' != $action ) )
+		return;
+
+	// Redirect to sign-up page
+	if ( locate_template( array( 'registration/register.php' ), false ) || locate_template( array( 'register.php' ), false ) )
+		bp_core_redirect( bp_get_signup_page() );
+}
+add_action( 'bp_init', 'bp_core_wpsignup_redirect' );
+?>
Index: /tags/1.5-beta-3/bp-members/bp-members-template.php
===================================================================
--- /tags/1.5-beta-3/bp-members/bp-members-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-members/bp-members-template.php	(revision 5057)
@@ -0,0 +1,1187 @@
+<?php
+/**
+ * BuddyPress Member Template Tags
+ *
+ * Functions that are safe to use inside your template files and themes
+ *
+ * @package BuddyPress
+ * @subpackage Members
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the members component slug
+ *
+ * @package BuddyPress
+ * @subpackage Members Template
+ * @since 1.5
+ *
+ * @uses bp_get_members_slug()
+ */
+function bp_members_slug() {
+	echo bp_get_members_slug();
+}
+	/**
+	 * Return the members component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Members Template
+	 * @since 1.5
+	 */
+	function bp_get_members_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_members_slug', $bp->members->slug );
+	}
+
+/**
+ * Output the members component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Members Template
+ * @since 1.5
+ *
+ * @uses bp_get_members_root_slug()
+ */
+function bp_members_root_slug() {
+	echo bp_get_members_root_slug();
+}
+	/**
+	 * Return the members component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Members Template
+	 * @since 1.5
+	 */
+	function bp_get_members_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_members_root_slug', $bp->members->root_slug );
+	}
+
+/**
+ * Output member directory permalink
+ *
+ * @package BuddyPress
+ * @subpackage Members Template
+ * @since 1.5
+ * @uses bp_get_members_directory_permalink()
+ */
+function bp_members_directory_permalink() {
+	echo bp_get_members_directory_permalink();
+}
+	/**
+	 * Return member directory permalink
+	 *
+	 * @package BuddyPress
+	 * @subpackage Members Template
+	 * @since 1.5
+	 * @uses apply_filters()
+	 * @uses traisingslashit()
+	 * @uses bp_get_root_domain()
+	 * @uses bp_get_members_root_slug()
+	 * @return string
+	 */
+	function bp_get_members_directory_permalink() {
+		return apply_filters( 'bp_get_members_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_members_root_slug() ) );
+	}
+
+/**
+ * Output the sign-up slug
+ *
+ * @package BuddyPress
+ * @subpackage Members Template
+ * @since 1.5
+ *
+ * @uses bp_get_signup_slug()
+ */
+function bp_signup_slug() {
+	echo bp_get_signup_slug();
+}
+	/**
+	 * Return the sign-up slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Members Template
+	 * @since 1.5
+	 */
+	function bp_get_signup_slug() {
+		global $bp;
+
+		if ( !empty( $bp->pages->register->slug ) )
+			$slug = $bp->pages->register->slug;
+		elseif ( defined( 'BP_REGISTER_SLUG' ) )
+			$slug = BP_REGISTER_SLUG;
+		else
+			$slug = 'register';
+
+		return apply_filters( 'bp_get_signup_slug', $slug );
+	}
+
+/**
+ * Output the activation slug
+ *
+ * @package BuddyPress
+ * @subpackage Members Template
+ * @since 1.5
+ *
+ * @uses bp_get_activate_slug()
+ */
+function bp_activate_slug() {
+	echo bp_get_activate_slug();
+}
+	/**
+	 * Return the activation slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Members Template
+	 * @since 1.5
+	 */
+	function bp_get_activate_slug() {
+		global $bp;
+
+		if ( !empty( $bp->pages->activate->slug ) )
+			$slug = $bp->pages->activate->slug;
+		elseif ( defined( 'BP_ACTIVATION_SLUG' ) )
+			$slug = BP_ACTIVATION_SLUG;
+		else
+			$slug = 'activate';
+
+		return apply_filters( 'bp_get_activate_slug', $slug );
+	}
+
+/***
+ * Members template loop that will allow you to loop all members or friends of a member
+ * if you pass a user_id.
+ */
+
+class BP_Core_Members_Template {
+	var $current_member = -1;
+	var $member_count;
+	var $members;
+	var $member;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_member_count;
+
+	function bp_core_members_template( $type, $page_number, $per_page, $max, $user_id, $search_terms, $include, $populate_extras, $exclude, $meta_key, $meta_value ) {
+		$this->__construct( $type, $page_number, $per_page, $max, $user_id, $search_terms, $include, $populate_extras, $exclude, $meta_key, $meta_value );
+	}
+
+	function __construct( $type, $page_number, $per_page, $max, $user_id, $search_terms, $include, $populate_extras, $exclude, $meta_key, $meta_value ) {
+		global $bp;
+
+		$this->pag_page  = !empty( $_REQUEST['upage'] ) ? intval( $_REQUEST['upage'] ) : (int)$page_number;
+		$this->pag_num   = !empty( $_REQUEST['num'] )   ? intval( $_REQUEST['num'] )   : (int)$per_page;
+		$this->type      = $type;
+
+		if ( isset( $_REQUEST['letter'] ) && '' != $_REQUEST['letter'] )
+			$this->members = BP_Core_User::get_users_by_letter( $_REQUEST['letter'], $this->pag_num, $this->pag_page, $populate_extras, $exclude );
+		else if ( false !== $include )
+			$this->members = BP_Core_User::get_specific_users( $include, $this->pag_num, $this->pag_page, $populate_extras );
+		else
+			$this->members = bp_core_get_users( array( 'type' => $this->type, 'per_page' => $this->pag_num, 'page' => $this->pag_page, 'user_id' => $user_id, 'include' => $include, 'search_terms' => $search_terms, 'populate_extras' => $populate_extras, 'exclude' => $exclude, 'meta_key' => $meta_key, 'meta_value' => $meta_value ) );
+
+		if ( !$max || $max >= (int)$this->members['total'] )
+			$this->total_member_count = (int)$this->members['total'];
+		else
+			$this->total_member_count = (int)$max;
+
+		$this->members = $this->members['users'];
+
+		if ( $max ) {
+			if ( $max >= count( $this->members ) ) {
+				$this->member_count = count( $this->members );
+			} else {
+				$this->member_count = (int)$max;
+			}
+		} else {
+			$this->member_count = count( $this->members );
+		}
+
+		if ( (int)$this->total_member_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( 'upage', '%#%' ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_member_count / (int)$this->pag_num ),
+				'current'   => (int) $this->pag_page,
+				'prev_text' => _x( '&larr;', 'Member pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Member pagination next text', 'buddypress' ),
+				'mid_size'   => 1
+			) );
+		}
+	}
+
+	function has_members() {
+		if ( $this->member_count )
+			return true;
+
+		return false;
+	}
+
+	function next_member() {
+		$this->current_member++;
+		$this->member = $this->members[$this->current_member];
+
+		return $this->member;
+	}
+
+	function rewind_members() {
+		$this->current_member = -1;
+		if ( $this->member_count > 0 ) {
+			$this->member = $this->members[0];
+		}
+	}
+
+	function members() {
+		if ( $this->current_member + 1 < $this->member_count ) {
+			return true;
+		} elseif ( $this->current_member + 1 == $this->member_count ) {
+			do_action('member_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_members();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_member() {
+		global $member, $bp;
+
+		$this->in_the_loop = true;
+		$this->member = $this->next_member();
+
+		if ( 0 == $this->current_member ) // loop has just started
+			do_action('member_loop_start');
+	}
+}
+
+function bp_rewind_members() {
+	global $members_template;
+
+	return $members_template->rewind_members();
+}
+
+function bp_has_members( $args = '' ) {
+	global $bp, $members_template;
+
+	/***
+	 * Set the defaults based on the current page. Any of these will be overridden
+	 * if arguments are directly passed into the loop. Custom plugins should always
+	 * pass their parameters directly to the loop.
+	 */
+	$type         = 'active';
+	$user_id      = 0;
+	$page         = 1;
+	$search_terms = null;
+
+	// User filtering
+	if ( !empty( $bp->displayed_user->id ) )
+		$user_id = $bp->displayed_user->id;
+
+	// type: active ( default ) | random | newest | popular | online | alphabetical
+	$defaults = array(
+		'type'            => $type,
+		'page'            => $page,
+		'per_page'        => 20,
+		'max'             => false,
+
+		'include'         => false,         // Pass a user_id or a list (comma-separated or array) of user_ids to only show these users
+		'exclude'         => false,         // Pass a user_id or a list (comma-separated or array) of user_ids to exclude these users
+
+		'user_id'         => $user_id,      // Pass a user_id to only show friends of this user
+		'search_terms'    => $search_terms, // Pass search_terms to filter users by their profile data
+
+		'meta_key'        => false,	        // Only return users with this usermeta
+		'meta_value'	  => false,	        // Only return users where the usermeta value matches. Requires meta_key
+
+		'populate_extras' => true           // Fetch usermeta? Friend count, last active etc.
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r );
+
+	// Pass a filter if ?s= is set.
+	if ( is_null( $search_terms ) ) {
+		if ( !empty( $_REQUEST['s'] ) )
+			$search_terms = $_REQUEST['s'];
+		else
+			$search_terms = false;
+	}
+
+	// Set per_page to max if max is larger than per_page
+	if ( !empty( $max ) && ( $per_page > $max ) )
+		$per_page = $max;
+
+	// Make sure we return no members if we looking at friendship requests and there are none.
+	if ( empty( $include ) && bp_is_friends_component() && bp_is_current_action( 'requests' ) )
+		return false;
+
+	$members_template = new BP_Core_Members_Template( $type, $page, $per_page, $max, $user_id, $search_terms, $include, (bool)$populate_extras, $exclude, $meta_key, $meta_value );
+	return apply_filters( 'bp_has_members', $members_template->has_members(), $members_template );
+}
+
+function bp_the_member() {
+	global $members_template;
+	return $members_template->the_member();
+}
+
+function bp_members() {
+	global $members_template;
+	return $members_template->members();
+}
+
+function bp_members_pagination_count() {
+	echo bp_get_members_pagination_count();
+}
+	function bp_get_members_pagination_count() {
+		global $bp, $members_template;
+
+		if ( empty( $members_template->type ) )
+			$members_template->type = '';
+
+		$start_num = intval( ( $members_template->pag_page - 1 ) * $members_template->pag_num ) + 1;
+		$from_num  = bp_core_number_format( $start_num );
+		$to_num    = bp_core_number_format( ( $start_num + ( $members_template->pag_num - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->pag_num - 1 ) );
+		$total     = bp_core_number_format( $members_template->total_member_count );
+
+		if ( 'active' == $members_template->type )
+			$pag = sprintf( __( 'Viewing member %1$s to %2$s (of %3$s active members)', 'buddypress' ), $from_num, $to_num, $total );
+		else if ( 'popular' == $members_template->type )
+			$pag = sprintf( __( 'Viewing member %1$s to %2$s (of %3$s members with friends)', 'buddypress' ), $from_num, $to_num, $total );
+		else if ( 'online' == $members_template->type )
+			$pag = sprintf( __( 'Viewing member %1$s to %2$s (of %3$s members online)', 'buddypress' ), $from_num, $to_num, $total );
+		else
+			$pag = sprintf( __( 'Viewing member %1$s to %2$s (of %3$s members)', 'buddypress' ), $from_num, $to_num, $total );
+
+		return apply_filters( 'bp_members_pagination_count', $pag );
+	}
+
+function bp_members_pagination_links() {
+	echo bp_get_members_pagination_links();
+}
+	function bp_get_members_pagination_links() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_members_pagination_links', $members_template->pag_links );
+	}
+
+/**
+ * bp_member_user_id()
+ *
+ * Echo id from bp_get_member_user_id()
+ *
+ * @uses bp_get_member_user_id()
+ */
+function bp_member_user_id() {
+	echo bp_get_member_user_id();
+}
+	/**
+	 * bp_get_member_user_id()
+	 *
+	 * Get the id of the user in a members loop
+	 *
+	 * @global object $members_template
+	 * @return string Members id
+	 */
+	function bp_get_member_user_id() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_member_user_id', $members_template->member->id );
+	}
+
+/**
+ * bp_member_user_nicename()
+ *
+ * Echo nicename from bp_get_member_user_nicename()
+ *
+ * @uses bp_get_member_user_nicename()
+ */
+function bp_member_user_nicename() {
+	echo bp_get_member_user_nicename();
+}
+	/**
+	 * bp_get_member_user_nicename()
+	 *
+	 * Get the nicename of the user in a members loop
+	 *
+	 * @global object $members_template
+	 * @return string Members nicename
+	 */
+	function bp_get_member_user_nicename() {
+		global $members_template;
+		return apply_filters( 'bp_get_member_user_nicename', $members_template->member->user_nicename );
+	}
+
+/**
+ * bp_member_user_login()
+ *
+ * Echo login from bp_get_member_user_login()
+ *
+ * @uses bp_get_member_user_login()
+ */
+function bp_member_user_login() {
+	echo bp_get_member_user_login();
+}
+	/**
+	 * bp_get_member_user_login()
+	 *
+	 * Get the login of the user in a members loop
+	 *
+	 * @global object $members_template
+	 * @return string Members login
+	 */
+	function bp_get_member_user_login() {
+		global $members_template;
+		return apply_filters( 'bp_get_member_user_login', $members_template->member->user_login );
+	}
+
+/**
+ * bp_member_user_email()
+ *
+ * Echo email address from bp_get_member_user_email()
+ *
+ * @uses bp_get_member_user_email()
+ */
+function bp_member_user_email() {
+	echo bp_get_member_user_email();
+}
+	/**
+	 * bp_get_member_user_email()
+	 *
+	 * Get the email address of the user in a members loop
+	 *
+	 * @global object $members_template
+	 * @return string Members email address
+	 */
+	function bp_get_member_user_email() {
+		global $members_template;
+		return apply_filters( 'bp_get_member_user_email', $members_template->member->user_email );
+	}
+
+function bp_member_is_loggedin_user() {
+	global $bp, $members_template;
+	return apply_filters( 'bp_member_is_loggedin_user', $bp->loggedin_user->id == $members_template->member->id ? true : false );
+}
+
+function bp_member_avatar( $args = '' ) {
+	echo apply_filters( 'bp_member_avatar', bp_get_member_avatar( $args ) );
+}
+	function bp_get_member_avatar( $args = '' ) {
+		global $bp, $members_template;
+
+		$defaults = array(
+			'type' => 'thumb',
+			'width' => false,
+			'height' => false,
+			'class' => 'avatar',
+			'id' => false,
+			'alt' => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_member_avatar', bp_core_fetch_avatar( array( 'item_id' => $members_template->member->id, 'type' => $type, 'alt' => $alt, 'css_id' => $id, 'class' => $class, 'width' => $width, 'height' => $height, 'email' => $members_template->member->user_email ) ) );
+	}
+
+function bp_member_permalink() {
+	echo bp_get_member_permalink();
+}
+	function bp_get_member_permalink() {
+		global $members_template;
+
+		return apply_filters( 'bp_get_member_permalink', bp_core_get_user_domain( $members_template->member->id, $members_template->member->user_nicename, $members_template->member->user_login ) );
+	}
+	function bp_member_link() { echo bp_get_member_permalink(); }
+	function bp_get_member_link() { return bp_get_member_permalink(); }
+
+/**
+ * Echoes bp_get_member_name()
+ *
+ * @package BuddyPress
+ */
+function bp_member_name() {
+	echo apply_filters( 'bp_member_name', bp_get_member_name() );
+}
+	/**
+	 * Used inside a bp_has_members() loop, this function returns a user's full name
+	 *
+	 * Full name is, by default, pulled from xprofile's Full Name field. When this field is
+	 * empty, we try to get an alternative name from the WP users table, in the following order
+	 * of preference: display_name, user_nicename, user_login.
+	 *
+	 * @package BuddyPress
+	 *
+	 * @uses apply_filters() Filter bp_get_the_member_name() to alter the function's output
+	 * @return str The user's fullname for display
+	 */
+	function bp_get_member_name() {
+		global $members_template;
+
+		// Generally, this only fires when xprofile is disabled
+		if ( empty( $members_template->member->fullname ) ) {
+			// Our order of preference for alternative fullnames
+			$name_stack = array(
+				'display_name',
+				'user_nicename',
+				'user_login'
+			);
+
+			foreach ( $name_stack as $source ) {
+				if ( !empty( $members_template->member->{$source} ) ) {
+					// When a value is found, set it as fullname and be done
+					// with it
+					$members_template->member->fullname = $members_template->member->{$source};
+					break;
+				}
+			}
+		}
+
+		return apply_filters( 'bp_get_member_name', $members_template->member->fullname );
+	}
+	add_filter( 'bp_get_member_name', 'wp_filter_kses' );
+	add_filter( 'bp_get_member_name', 'stripslashes' );
+	add_filter( 'bp_get_member_name', 'strip_tags' );
+
+function bp_member_last_active() {
+	echo bp_get_member_last_active();
+}
+	function bp_get_member_last_active() {
+		global $members_template;
+
+		if ( isset( $members_template->member->last_activity ) )
+			$last_activity = bp_core_get_last_activity( $members_template->member->last_activity, __( 'active %s', 'buddypress' ) );
+		else
+			$last_activity = __( 'Never active', 'buddypress' );
+
+		return apply_filters( 'bp_member_last_active', $last_activity );
+	}
+
+function bp_member_latest_update( $args = '' ) {
+	echo bp_get_member_latest_update( $args );
+}
+	function bp_get_member_latest_update( $args = '' ) {
+		global $bp, $members_template;
+
+		$defaults = array(
+			'length'    => 225,
+			'view_link' => true
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r );
+
+		if ( empty( $members_template->member->latest_update ) || !$update = maybe_unserialize( $members_template->member->latest_update ) )
+			return false;
+
+		$update_content = apply_filters( 'bp_get_activity_latest_update_excerpt', sprintf( _x( '- &quot;%s &quot;', 'member latest update in member directory', 'buddypress' ), trim( strip_tags( bp_create_excerpt( $update['content'], $length ) ) ) ) );
+
+		// If $view_link is true and the text returned by bp_create_excerpt() is different from the original text (ie it's
+		// been truncated), add the "View" link.
+		if ( $view_link && ( $update_content != $update['content'] ) ) {
+			$view = __( 'View', 'buddypress' );
+
+			$update_content .= '<span class="activity-read-more"><a href="' . bp_activity_get_permalink( $update['id'] ) . '" rel="nofollow">' . $view . '</a></span>';
+		}
+
+		return apply_filters( 'bp_get_member_latest_update', $update_content );
+	}
+
+function bp_member_profile_data( $args = '' ) {
+	echo bp_get_member_profile_data( $args );
+}
+	function bp_get_member_profile_data( $args = '' ) {
+		global $members_template;
+
+		if ( !bp_is_active( 'xprofile' ) )
+			return false;
+
+		$defaults = array(
+			'field' => false, // Field name
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Populate the user if it hasn't been already.
+		if ( empty( $members_template->member->profile_data ) && method_exists( 'BP_XProfile_ProfileData', 'get_all_for_user' ) )
+			$members_template->member->profile_data = BP_XProfile_ProfileData::get_all_for_user( $members_template->member->id );
+
+		$data = xprofile_format_profile_field( $members_template->member->profile_data[$field]['field_type'], $members_template->member->profile_data[$field]['field_data'] );
+
+		return apply_filters( 'bp_get_member_profile_data', $data );
+	}
+
+function bp_member_registered() {
+	echo bp_get_member_registered();
+}
+	function bp_get_member_registered() {
+		global $members_template;
+
+		$registered = esc_attr( bp_core_get_last_activity( $members_template->member->user_registered, __( 'registered %s', 'buddypress' ) ) );
+
+		return apply_filters( 'bp_member_last_active', $registered );
+	}
+
+function bp_member_random_profile_data() {
+	global $members_template;
+
+	if ( bp_is_active( 'xprofile' ) ) { ?>
+		<?php $random_data = xprofile_get_random_profile_data( $members_template->member->id, true ); ?>
+			<strong><?php echo wp_filter_kses( $random_data[0]->name ) ?></strong>
+			<?php echo wp_filter_kses( $random_data[0]->value ) ?>
+	<?php }
+}
+
+function bp_member_hidden_fields() {
+	if ( isset( $_REQUEST['s'] ) )
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['s'] ) . '" name="search_terms" />';
+
+	if ( isset( $_REQUEST['letter'] ) )
+		echo '<input type="hidden" id="selected_letter" value="' . esc_attr( $_REQUEST['letter'] ) . '" name="selected_letter" />';
+
+	if ( isset( $_REQUEST['members_search'] ) )
+		echo '<input type="hidden" id="search_terms" value="' . esc_attr( $_REQUEST['members_search'] ) . '" name="search_terms" />';
+}
+
+function bp_directory_members_search_form() {
+	global $bp;
+
+	$default_search_value = bp_get_search_default_text( 'members' );
+	$search_value         = !empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : $default_search_value; ?>
+
+	<form action="" method="get" id="search-members-form">
+		<label><input type="text" name="s" id="members_search" value="<?php echo esc_attr( $search_value ) ?>"  onfocus="if (this.value == '<?php echo $default_search_value ?>') {this.value = '';}" onblur="if (this.value == '') {this.value = '<?php echo $default_search_value ?>';}" /></label>
+		<input type="submit" id="members_search_submit" name="members_search_submit" value="<?php _e( 'Search', 'buddypress' ) ?>" />
+	</form>
+
+<?php
+}
+
+function bp_total_site_member_count() {
+	echo bp_get_total_site_member_count();
+}
+	function bp_get_total_site_member_count() {
+		return apply_filters( 'bp_get_total_site_member_count', bp_core_number_format( bp_core_get_total_member_count() ) );
+	}
+
+/** Navigation and other misc template tags **/
+
+/**
+ * Uses the $bp->bp_nav global to render out the navigation within a BuddyPress install.
+ * Each component adds to this navigation array within its own [component_name]setup_nav() function.
+ *
+ * This navigation array is the top level navigation, so it contains items such as:
+ *      [Blog, Profile, Messages, Groups, Friends] ...
+ *
+ * The function will also analyze the current component the user is in, to determine whether
+ * or not to highlight a particular nav item.
+ *
+ * @package BuddyPress Core
+ * @todo Move to a back-compat file?
+ * @deprecated Does not seem to be called anywhere in the core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_get_loggedin_user_nav() {
+	global $bp;
+
+	// Loop through each navigation item
+	foreach( (array) $bp->bp_nav as $nav_item ) {
+		// If the current component matches the nav item id, then add a highlight CSS class.
+		if ( !bp_is_directory() && $bp->active_components[$bp->current_component] == $nav_item['css_id'] )
+			$selected = ' class="current selected"';
+		else
+			$selected = '';
+
+		/* If we are viewing another person (current_userid does not equal loggedin_user->id)
+		   then check to see if the two users are friends. if they are, add a highlight CSS class
+		   to the friends nav item if it exists. */
+		if ( !bp_is_my_profile() && $bp->displayed_user->id ) {
+			$selected = '';
+
+			if ( bp_is_active( 'friends' ) ) {
+				if ( $nav_item['css_id'] == $bp->friends->id ) {
+					if ( friends_check_friendship( $bp->loggedin_user->id, $bp->displayed_user->id ) )
+						$selected = ' class="current selected"';
+				}
+			}
+		}
+
+		// echo out the final list item
+		echo apply_filters_ref_array( 'bp_get_loggedin_user_nav_' . $nav_item['css_id'], array( '<li id="li-nav-' . $nav_item['css_id'] . '" ' . $selected . '><a id="my-' . $nav_item['css_id'] . '" href="' . $nav_item['link'] . '">' . $nav_item['name'] . '</a></li>', &$nav_item ) );
+	}
+
+	// Always add a log out list item to the end of the navigation
+	$logout_link = '<li><a id="wp-logout" href="' .  wp_logout_url( bp_get_root_domain() ) . '">' . __( 'Log Out', 'buddypress' ) . '</a></li>';
+
+	echo apply_filters( 'bp_logout_nav_link', $logout_link );
+}
+
+/**
+ * Uses the $bp->bp_nav global to render out the user navigation when viewing another user other than
+ * yourself.
+ *
+ * @package BuddyPress Core
+ * @global object $bp Global BuddyPress settings object
+ */
+function bp_get_displayed_user_nav() {
+	global $bp;
+
+	foreach ( (array)$bp->bp_nav as $user_nav_item ) {
+		if ( !$user_nav_item['show_for_displayed_user'] && !bp_is_my_profile() )
+			continue;
+
+		if ( $bp->current_component == $user_nav_item['slug'] )
+			$selected = ' class="current selected"';
+		else
+			$selected = '';
+
+		if ( $bp->loggedin_user->domain )
+			$link = str_replace( $bp->loggedin_user->domain, $bp->displayed_user->domain, $user_nav_item['link'] );
+		else
+			$link = $bp->displayed_user->domain . $user_nav_item['link'];
+
+		echo apply_filters_ref_array( 'bp_get_displayed_user_nav_' . $user_nav_item['css_id'], array( '<li id="' . $user_nav_item['css_id'] . '-personal-li" ' . $selected . '><a id="user-' . $user_nav_item['css_id'] . '" href="' . $link . '">' . $user_nav_item['name'] . '</a></li>', &$user_nav_item ) );
+	}
+}
+
+/** Avatars *******************************************************************/
+
+function bp_loggedin_user_avatar( $args = '' ) {
+	echo bp_get_loggedin_user_avatar( $args );
+}
+	function bp_get_loggedin_user_avatar( $args = '' ) {
+		global $bp;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => false,
+			'height' => false,
+			'html'   => true,
+			'alt'    => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_loggedin_user_avatar', bp_core_fetch_avatar( array( 'item_id' => $bp->loggedin_user->id, 'type' => $type, 'width' => $width, 'height' => $height, 'html' => $html, 'alt' => $alt ) ) );
+	}
+
+function bp_displayed_user_avatar( $args = '' ) {
+	echo bp_get_displayed_user_avatar( $args );
+}
+	function bp_get_displayed_user_avatar( $args = '' ) {
+		global $bp;
+
+		$defaults = array(
+			'type'   => 'thumb',
+			'width'  => false,
+			'height' => false,
+			'html'   => true,
+			'alt'    => __( 'Profile picture of %s', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_displayed_user_avatar', bp_core_fetch_avatar( array( 'item_id' => $bp->displayed_user->id, 'type' => $type, 'width' => $width, 'height' => $height, 'html' => $html, 'alt' => $alt ) ) );
+	}
+
+function bp_displayed_user_email() {
+	echo bp_get_displayed_user_email();
+}
+	function bp_get_displayed_user_email() {
+		global $bp;
+
+		// If displayed user exists, return email address
+		if ( isset( $bp->displayed_user->userdata->user_email ) )
+			$retval = $bp->displayed_user->userdata->user_email;
+		else
+			$retval = '';
+
+		return apply_filters( 'bp_get_displayed_user_email', esc_attr( $retval ) );
+	}
+
+function bp_last_activity( $user_id = 0 ) {
+	echo apply_filters( 'bp_last_activity', bp_get_last_activity( $user_id ) );
+}
+	function bp_get_last_activity( $user_id = 0 ) {
+		global $bp;
+
+		if ( empty( $user_id ) )
+			$user_id = $bp->displayed_user->id;
+
+		$last_activity = bp_core_get_last_activity( bp_get_user_meta( $user_id, 'last_activity', true ), __('active %s', 'buddypress') );
+
+		return apply_filters( 'bp_get_last_activity', $last_activity );
+	}
+
+function bp_user_firstname() {
+	echo bp_get_user_firstname();
+}
+	function bp_get_user_firstname( $name = false ) {
+		global $bp;
+
+		// Try to get displayed user
+		if ( empty( $name ) )
+			$name = $bp->displayed_user->fullname;
+
+		// Fall back on logged in user
+		if ( empty( $name ) )
+			$name = $bp->loggedin_user->fullname;
+
+		$fullname = (array)explode( ' ', $name );
+
+		return apply_filters( 'bp_get_user_firstname', $fullname[0], $fullname );
+	}
+
+function bp_loggedin_user_link() {
+	echo bp_get_loggedin_user_link();
+}
+	function bp_get_loggedin_user_link() {
+		return apply_filters( 'bp_get_loggedin_user_link', bp_loggedin_user_domain() );
+	}
+
+function bp_displayed_user_link() {
+	echo bp_get_displayed_user_link();
+}
+	function bp_get_displayed_user_link() {
+		return apply_filters( 'bp_get_displayed_user_link', bp_displayed_user_domain() );
+	}
+	function bp_user_link() { bp_displayed_user_domain(); } // Deprecated.
+
+function bp_displayed_user_id() {
+	global $bp;
+	return apply_filters( 'bp_displayed_user_id', !empty( $bp->displayed_user->id ) ? $bp->displayed_user->id : 0 );
+}
+	function bp_current_user_id() { return bp_displayed_user_id(); }
+
+function bp_loggedin_user_id() {
+	global $bp;
+	return apply_filters( 'bp_loggedin_user_id', !empty( $bp->loggedin_user->id ) ? $bp->loggedin_user->id : 0 );
+}
+
+function bp_displayed_user_domain() {
+	global $bp;
+	return apply_filters( 'bp_displayed_user_domain', isset( $bp->displayed_user->domain ) ? $bp->displayed_user->domain : '' );
+}
+
+function bp_loggedin_user_domain() {
+	global $bp;
+	return apply_filters( 'bp_loggedin_user_domain', isset( $bp->loggedin_user->domain ) ? $bp->loggedin_user->domain : '' );
+}
+
+function bp_displayed_user_fullname() {
+	echo bp_get_displayed_user_fullname();
+}
+	function bp_get_displayed_user_fullname() {
+		global $bp;
+		return apply_filters( 'bp_displayed_user_fullname', isset( $bp->displayed_user->fullname ) ? $bp->displayed_user->fullname : '' );
+	}
+	function bp_user_fullname() { echo bp_get_displayed_user_fullname(); }
+
+
+function bp_loggedin_user_fullname() {
+	echo bp_get_loggedin_user_fullname();
+}
+	function bp_get_loggedin_user_fullname() {
+		global $bp;
+		return apply_filters( 'bp_get_loggedin_user_fullname', isset( $bp->loggedin_user->fullname ) ? $bp->loggedin_user->fullname : '' );
+	}
+
+function bp_displayed_user_username() {
+	echo bp_get_displayed_user_username();
+}
+	function bp_get_displayed_user_username() {
+		global $bp;
+
+		if ( !empty( $bp->displayed_user->id ) ) {
+			$username = bp_core_get_username( $bp->displayed_user->id, $bp->displayed_user->userdata->user_nicename, $bp->displayed_user->userdata->user_login );
+		} else {
+			$username = '';
+		}
+
+		return apply_filters( 'bp_get_displayed_user_username', $username );
+	}
+
+function bp_loggedin_user_username() {
+	echo bp_get_loggedin_user_username();
+}
+	function bp_get_loggedin_user_username() {
+		global $bp;
+
+		if ( !empty( $bp->loggedin_user->id ) ) {
+			$username = bp_core_get_username( $bp->loggedin_user->id, $bp->loggedin_user->userdata->user_nicename, $bp->loggedin_user->userdata->user_login );
+		} else {
+			$username = '';
+		}
+
+		return apply_filters( 'bp_get_loggedin_user_username', $username );
+	}
+
+/** Signup Form ***************************************************************/
+
+function bp_has_custom_signup_page() {
+	if ( locate_template( array( 'register.php' ), false ) || locate_template( array( '/registration/register.php' ), false ) )
+		return true;
+
+	return false;
+}
+
+function bp_signup_page() {
+	echo bp_get_signup_page();
+}
+	function bp_get_signup_page() {
+		global $bp;
+
+		if ( bp_has_custom_signup_page() ) {
+			$page = trailingslashit( bp_get_root_domain() . '/' . bp_get_signup_slug() );
+		} else {
+			$page = bp_get_root_domain() . '/wp-signup.php';
+		}
+
+		return apply_filters( 'bp_get_signup_page', $page );
+	}
+
+function bp_has_custom_activation_page() {
+	if ( locate_template( array( 'activate.php' ), false ) || locate_template( array( '/registration/activate.php' ), false ) )
+		return true;
+
+	return false;
+}
+
+function bp_activation_page() {
+	echo bp_get_activation_page();
+}
+	function bp_get_activation_page() {
+		global $bp;
+
+		if ( bp_has_custom_activation_page() )
+			$page = trailingslashit( bp_get_root_domain() . '/' . $bp->pages->activate->slug );
+		else
+			$page = trailingslashit( bp_get_root_domain() ) . 'wp-activate.php';
+
+		return apply_filters( 'bp_get_activation_page', $page );
+	}
+
+function bp_signup_username_value() {
+	echo bp_get_signup_username_value();
+}
+	function bp_get_signup_username_value() {
+		$value = '';
+		if ( isset( $_POST['signup_username'] ) )
+			$value = $_POST['signup_username'];
+
+		return apply_filters( 'bp_get_signup_username_value', $value );
+	}
+
+function bp_signup_email_value() {
+	echo bp_get_signup_email_value();
+}
+	function bp_get_signup_email_value() {
+		$value = '';
+		if ( isset( $_POST['signup_email'] ) )
+			$value = $_POST['signup_email'];
+
+		return apply_filters( 'bp_get_signup_email_value', $value );
+	}
+
+function bp_signup_with_blog_value() {
+	echo bp_get_signup_with_blog_value();
+}
+	function bp_get_signup_with_blog_value() {
+		$value = '';
+		if ( isset( $_POST['signup_with_blog'] ) )
+			$value = $_POST['signup_with_blog'];
+
+		return apply_filters( 'bp_get_signup_with_blog_value', $value );
+	}
+
+function bp_signup_blog_url_value() {
+	echo bp_get_signup_blog_url_value();
+}
+	function bp_get_signup_blog_url_value() {
+		$value = '';
+		if ( isset( $_POST['signup_blog_url'] ) )
+			$value = $_POST['signup_blog_url'];
+
+		return apply_filters( 'bp_get_signup_blog_url_value', $value );
+	}
+
+function bp_signup_blog_title_value() {
+	echo bp_get_signup_blog_title_value();
+}
+	function bp_get_signup_blog_title_value() {
+		$value = '';
+		if ( isset( $_POST['signup_blog_title'] ) )
+			$value = $_POST['signup_blog_title'];
+
+		return apply_filters( 'bp_get_signup_blog_title_value', $value );
+	}
+
+function bp_signup_blog_privacy_value() {
+	echo bp_get_signup_blog_privacy_value();
+}
+	function bp_get_signup_blog_privacy_value() {
+		$value = '';
+		if ( isset( $_POST['signup_blog_privacy'] ) )
+			$value = $_POST['signup_blog_privacy'];
+
+		return apply_filters( 'bp_get_signup_blog_privacy_value', $value );
+	}
+
+function bp_signup_avatar_dir_value() {
+	echo bp_get_signup_avatar_dir_value();
+}
+	function bp_get_signup_avatar_dir_value() {
+		global $bp;
+
+		// Check if signup_avatar_dir is passed
+		if ( !empty( $_POST['signup_avatar_dir'] ) )
+			$signup_avatar_dir = $_POST['signup_avatar_dir'];
+
+		// If not, check if global is set
+		elseif ( !empty( $bp->signup->avatar_dir ) )
+			$signup_avatar_dir = $bp->signup->avatar_dir;
+
+		// If not, set false
+		else
+			$signup_avatar_dir = false;
+
+		return apply_filters( 'bp_get_signup_avatar_dir_value', $bp->signup->avatar_dir );
+	}
+
+function bp_current_signup_step() {
+	echo bp_get_current_signup_step();
+}
+	function bp_get_current_signup_step() {
+		global $bp;
+
+		return $bp->signup->step;
+	}
+
+function bp_signup_avatar( $args = '' ) {
+	echo bp_get_signup_avatar( $args );
+}
+	function bp_get_signup_avatar( $args = '' ) {
+		global $bp;
+
+		$defaults = array(
+			'size' => bp_core_avatar_full_width(),
+			'class' => 'avatar',
+			'alt' => __( 'Your Avatar', 'buddypress' )
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		// Avatar DIR is found
+		if ( $signup_avatar_dir = bp_get_signup_avatar_dir_value() ) {
+			$gravatar_img = bp_core_fetch_avatar( array(
+				'item_id'    => $signup_avatar_dir,
+				'object'     => 'signup',
+				'avatar_dir' => 'avatars/signups',
+				'type'       => 'full',
+				'width'      => $size,
+				'height'     => $size,
+				'alt'        => $alt,
+				'class'      => $class
+			) );
+
+		// No avatar DIR was found
+		} else {
+
+			// Set default gravatar type
+			if ( empty( $bp->grav_default->user ) )
+				$default_grav = 'wavatar';
+			else if ( 'mystery' == $bp->grav_default->user )
+				$default_grav = BP_PLUGIN_URL . '/bp-core/images/mystery-man.jpg';
+			else
+				$default_grav = $bp->grav_default->user;
+
+			// Create
+			$gravatar_url    = apply_filters( 'bp_gravatar_url', 'http://www.gravatar.com/avatar/' );
+			$md5_lcase_email = md5( strtolower( bp_get_signup_email_value() ) );
+			$gravatar_img    = '<img src="' . $gravatar_url . $md5_lcase_email . '?d=' . $default_grav . '&amp;s=' . $size . '" width="' . $size . '" height="' . $size . '" alt="' . $alt . '" class="' . $class . '" />';
+		}
+
+		return apply_filters( 'bp_get_signup_avatar', $gravatar_img, $args );
+	}
+
+function bp_signup_allowed() {
+	echo bp_get_signup_allowed();
+}
+	function bp_get_signup_allowed() {
+		global $bp;
+
+		$signup_allowed = false;
+
+		if ( is_multisite() ) {
+			if ( in_array( $bp->site_options['registration'], array( 'all', 'user' ) ) )
+				$signup_allowed = true;
+
+		} else {
+			if ( get_option( 'users_can_register') )
+				$signup_allowed = true;
+		}
+
+		return apply_filters( 'bp_get_signup_allowed', $signup_allowed );
+	}
+
+/**
+ * Hook member activity feed to <head>
+ *
+ * @since 1.5
+ */
+function bp_members_activity_feed() {
+	if ( !bp_is_active( 'activity' ) || !bp_is_user() )
+		return; ?>
+
+	<link rel="alternate" type="application/rss+xml" title="<?php bloginfo( 'name' ) ?> | <?php bp_displayed_user_fullname() ?> | <?php _e( 'Activity RSS Feed', 'buddypress' ) ?>" href="<?php bp_member_activity_feed_link() ?>" />
+
+<?php
+}
+add_action( 'bp_head', 'bp_members_activity_feed' );
+
+
+function bp_members_component_link( $component, $action = '', $query_args = '', $nonce = false ) {
+	echo bp_get_members_component_link( $component, $action, $query_args, $nonce );
+}
+	function bp_get_members_component_link( $component, $action = '', $query_args = '', $nonce = false ) {
+		global $bp;
+
+		// Must be displayed user
+		if ( empty( $bp->displayed_user->id ) )
+			return;
+
+		// Append $action to $url if there is no $type
+		if ( !empty( $action ) )
+			$url = $bp->displayed_user->domain . $bp->{$component}->slug . '/' . $action;
+		else
+			$url = $bp->displayed_user->domain . $bp->{$component}->slug;
+
+		// Add a slash at the end of our user url
+		$url = trailingslashit( $url );
+
+		// Add possible query arg
+		if ( !empty( $query_args ) && is_array( $query_args ) )
+			$url = add_query_arg( $query_args, $url );
+
+		// To nonce, or not to nonce...
+		if ( true === $nonce )
+			$url = wp_nonce_url( $url );
+		elseif ( is_string( $nonce ) )
+			$url = wp_nonce_url( $url, $nonce );
+
+		// Return the url, if there is one
+		if ( !empty( $url ) )
+			return $url;
+	}
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-actions.php	(revision 5057)
@@ -0,0 +1,110 @@
+<?php
+/*******************************************************************************
+ * Action functions are exactly the same as screen functions, however they do not
+ * have a template screen associated with them. Usually they will send the user
+ * back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function messages_action_view_message() {
+	global $thread_id, $bp;
+
+	if ( !bp_is_messages_component() || !bp_is_current_action( 'view' ) )
+		return false;
+
+	$thread_id = (int)bp_action_variable( 0 );
+
+	if ( !$thread_id || !messages_is_valid_thread( $thread_id ) || ( !messages_check_thread_access( $thread_id ) && !is_super_admin() ) )
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() );
+
+	// Check if a new reply has been submitted
+	if ( isset( $_POST['send'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'messages_send_message', 'send_message_nonce' );
+
+		// Send the reply
+		if ( messages_new_message( array( 'thread_id' => $thread_id, 'subject' => $_POST['subject'], 'content' => $_POST['content'] ) ) )
+			bp_core_add_message( __( 'Your reply was sent successfully', 'buddypress' ) );
+		else
+			bp_core_add_message( __( 'There was a problem sending your reply, please try again', 'buddypress' ), 'error' );
+
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/view/' . $thread_id . '/' );
+	}
+
+	// Mark message read
+	messages_mark_thread_read( $thread_id );
+
+	// Decrease the unread count in the nav before it's rendered
+	$name = sprintf( __( 'Messages <span>%s</span>', 'buddypress' ), bp_get_total_unread_messages_count() );
+
+	$bp->bp_nav[$bp->messages->slug]['name'] = $name;
+
+	do_action( 'messages_action_view_message' );
+
+	bp_core_new_subnav_item( array(
+		'name'            => sprintf( __( 'From: %s', 'buddypress' ), BP_Messages_Thread::get_last_sender( $thread_id ) ),
+		'slug'            => 'view',
+		'parent_url'      => trailingslashit( bp_displayed_user_domain() . bp_get_messages_slug() ),
+		'parent_slug'     => bp_get_messages_slug(),
+		'screen_function' => true,
+		'position'        => 40,
+		'user_has_access' => bp_is_my_profile(),
+		'link'            => bp_displayed_user_domain() . bp_get_messages_slug() . '/view/' . (int) $thread_id
+	) );
+
+	bp_core_load_template( apply_filters( 'messages_template_view_message', 'members/single/home' ) );
+}
+add_action( 'bp_actions', 'messages_action_view_message' );
+
+function messages_action_delete_message() {
+	global $thread_id;
+
+	if ( !bp_is_messages_component() || bp_is_current_action( 'notices' ) || !bp_is_action_variable( 'delete', 0 ) )
+		return false;
+
+	$thread_id = bp_action_variable( 1 );
+
+	if ( !$thread_id || !is_numeric( $thread_id ) || !messages_check_thread_access( $thread_id ) ) {
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() );
+	} else {
+		if ( !check_admin_referer( 'messages_delete_thread' ) )
+			return false;
+
+		// Delete message
+		if ( !messages_delete_thread( $thread_id ) ) {
+			bp_core_add_message( __('There was an error deleting that message.', 'buddypress'), 'error' );
+		} else {
+			bp_core_add_message( __('Message deleted.', 'buddypress') );
+		}
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() );
+	}
+}
+add_action( 'bp_actions', 'messages_action_delete_message' );
+
+function messages_action_bulk_delete() {
+	global $thread_ids;
+
+	if ( !bp_is_messages_component() || !bp_is_action_variable( 'bulk-delete', 0 ) )
+		return false;
+
+	$thread_ids = $_POST['thread_ids'];
+
+	if ( !$thread_ids || !messages_check_thread_access( $thread_ids ) ) {
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() );
+	} else {
+		if ( !check_admin_referer( 'messages_delete_thread' ) )
+			return false;
+
+		if ( !messages_delete_thread( $thread_ids ) )
+			bp_core_add_message( __('There was an error deleting messages.', 'buddypress'), 'error' );
+		else
+			bp_core_add_message( __('Messages deleted.', 'buddypress') );
+
+		bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() );
+	}
+}
+add_action( 'bp_actions', 'messages_action_bulk_delete' );
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-cache.php	(revision 5057)
@@ -0,0 +1,20 @@
+<?php
+/*******************************************************************************
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'messages_delete_thread',  'bp_core_clear_cache' );
+add_action( 'messages_send_notice',    'bp_core_clear_cache' );
+add_action( 'messages_message_sent',   'bp_core_clear_cache' );
+
+// Don't cache message inbox/sentbox/compose as it's too problematic
+add_action( 'messages_screen_compose', 'bp_core_clear_cache' );
+add_action( 'messages_screen_sentbox', 'bp_core_clear_cache' );
+add_action( 'messages_screen_inbox',   'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-classes.php	(revision 5057)
@@ -0,0 +1,482 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_Messages_Thread {
+	var $thread_id;
+	var $messages;
+	var $recipients;
+	var $sender_ids;
+
+	var $unread_count;
+
+	function bp_messages_thread ( $thread_id = false, $order = 'ASC' ) {
+		$this->__construct( $thread_id, $order);
+	}
+
+	function __construct( $thread_id = false, $order = 'ASC' ) {
+		if ( $thread_id )
+			$this->populate( $thread_id, $order );
+	}
+
+	function populate( $thread_id, $order ) {
+		global $wpdb, $bp;
+
+		if( 'ASC' != $order && 'DESC' != $order )
+			$order= 'ASC';
+
+		$this->messages_order = $order;
+		$this->thread_id      = $thread_id;
+
+		if ( !$this->messages = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->messages->table_name_messages} WHERE thread_id = %d ORDER BY date_sent " . $order, $this->thread_id ) ) )
+			return false;
+
+		foreach ( (array)$this->messages as $key => $message )
+			$this->sender_ids[$message->sender_id] = $message->sender_id;
+
+		// Fetch the recipients
+		$this->recipients = $this->get_recipients();
+
+		// Get the unread count for the logged in user
+		if ( isset( $this->recipients[$bp->loggedin_user->id] ) )
+			$this->unread_count = $this->recipients[$bp->loggedin_user->id]->unread_count;
+	}
+
+	function mark_read() {
+		BP_Messages_Thread::mark_as_read( $this->thread_id );
+	}
+
+	function mark_unread() {
+		BP_Messages_Thread::mark_as_unread( $this->thread_id );
+	}
+
+	function get_recipients() {
+		global $wpdb, $bp;
+
+		$results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d", $this->thread_id ) );
+
+		foreach ( (array)$results as $recipient )
+			$recipients[$recipient->user_id] = $recipient;
+
+		return $recipients;
+	}
+
+	/** Static Functions **/
+
+	function delete( $thread_id ) {
+		global $wpdb, $bp;
+
+		$delete_for_user = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET is_deleted = 1 WHERE thread_id = %d AND user_id = %d", $thread_id, $bp->loggedin_user->id ) );
+
+		// Check to see if any more recipients remain for this message
+		// if not, then delete the message from the database.
+		$recipients = $wpdb->get_results( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d AND is_deleted = 0", $thread_id ) );
+
+		if ( empty( $recipients ) ) {
+			// Delete all the messages
+			$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->messages->table_name_messages} WHERE thread_id = %d", $thread_id ) );
+
+			// Delete all the recipients
+			$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d", $thread_id ) );
+		}
+
+		return true;
+	}
+
+	function get_current_threads_for_user( $user_id, $box = 'inbox', $type = 'all', $limit = null, $page = null ) {
+		global $wpdb, $bp;
+
+		$pag_sql = $type_sql = '';
+		if ( $limit && $page )
+			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
+
+		if ( $type == 'unread' )
+			$type_sql = $wpdb->prepare( " AND r.unread_count != 0 " );
+		elseif ( $type == 'read' )
+			$type_sql = $wpdb->prepare( " AND r.unread_count = 0 " );
+
+		if ( 'sentbox' == $box ) {
+			$thread_ids = $wpdb->get_results( $wpdb->prepare( "SELECT m.thread_id, MAX(m.date_sent) AS date_sent FROM {$bp->messages->table_name_recipients} r, {$bp->messages->table_name_messages} m WHERE m.thread_id = r.thread_id AND m.sender_id = r.user_id AND m.sender_id = %d AND r.is_deleted = 0 GROUP BY m.thread_id ORDER BY date_sent DESC {$pag_sql}", $user_id ) );
+			$total_threads = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( DISTINCT m.thread_id ) FROM {$bp->messages->table_name_recipients} r, {$bp->messages->table_name_messages} m WHERE m.thread_id = r.thread_id AND m.sender_id = r.user_id AND m.sender_id = %d AND r.is_deleted = 0 ", $user_id ) );
+		} else {
+			$thread_ids = $wpdb->get_results( $wpdb->prepare( "SELECT m.thread_id, MAX(m.date_sent) AS date_sent FROM {$bp->messages->table_name_recipients} r, {$bp->messages->table_name_messages} m WHERE m.thread_id = r.thread_id AND r.is_deleted = 0 AND r.user_id = %d AND r.sender_only = 0 {$type_sql} GROUP BY m.thread_id ORDER BY date_sent DESC {$pag_sql}", $user_id ) );
+			$total_threads = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( DISTINCT m.thread_id ) FROM {$bp->messages->table_name_recipients} r, {$bp->messages->table_name_messages} m WHERE m.thread_id = r.thread_id AND r.is_deleted = 0 AND r.user_id = %d AND r.sender_only = 0 {$type_sql}", $user_id ) );
+		}
+
+		if ( empty( $thread_ids ) )
+			return false;
+
+		// Sort threads by date_sent
+		foreach( (array)$thread_ids as $thread )
+			$sorted_threads[$thread->thread_id] = strtotime( $thread->date_sent );
+
+		arsort( $sorted_threads );
+
+		$threads = false;
+		foreach ( (array)$sorted_threads as $thread_id => $date_sent )
+			$threads[] = new BP_Messages_Thread( $thread_id );
+
+		return array( 'threads' => &$threads, 'total' => (int)$total_threads );
+	}
+
+	function mark_as_read( $thread_id ) {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET unread_count = 0 WHERE user_id = %d AND thread_id = %d", $bp->loggedin_user->id, $thread_id );
+		$wpdb->query($sql);
+	}
+
+	function mark_as_unread( $thread_id ) {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET unread_count = 1 WHERE user_id = %d AND thread_id = %d", $bp->loggedin_user->id, $thread_id );
+		$wpdb->query($sql);
+	}
+
+	function get_total_threads_for_user( $user_id, $box = 'inbox', $type = 'all' ) {
+		global $wpdb, $bp;
+
+		$exclude_sender = '';
+		if ( $box != 'sentbox' )
+			$exclude_sender = ' AND sender_only != 1';
+
+		if ( $type == 'unread' )
+			$type_sql = $wpdb->prepare( " AND unread_count != 0 " );
+		else if ( $type == 'read' )
+			$type_sql = $wpdb->prepare( " AND unread_count = 0 " );
+
+		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(thread_id) FROM {$bp->messages->table_name_recipients} WHERE user_id = %d AND is_deleted = 0$exclude_sender $type_sql", $user_id ) );
+	}
+
+	function user_is_sender( $thread_id ) {
+		global $wpdb, $bp;
+
+		$sender_ids = $wpdb->get_col( $wpdb->prepare( "SELECT sender_id FROM {$bp->messages->table_name_messages} WHERE thread_id = %d", $thread_id ) );
+
+		if ( !$sender_ids )
+			return false;
+
+		return in_array( $bp->loggedin_user->id, $sender_ids );
+	}
+
+	function get_last_sender( $thread_id ) {
+		global $wpdb, $bp;
+
+		if ( !$sender_id = $wpdb->get_var( $wpdb->prepare( "SELECT sender_id FROM {$bp->messages->table_name_messages} WHERE thread_id = %d GROUP BY sender_id ORDER BY date_sent LIMIT 1", $thread_id ) ) )
+			return false;
+
+		return bp_core_get_userlink( $sender_id, true );
+	}
+
+	function get_inbox_count( $user_id = 0 ) {
+		global $wpdb, $bp;
+
+		if ( empty( $user_id ) )
+			$user_id = $bp->loggedin_user->id;
+
+		$sql = $wpdb->prepare( "SELECT SUM(unread_count) FROM {$bp->messages->table_name_recipients} WHERE user_id = %d AND is_deleted = 0 AND sender_only = 0", $user_id );
+		$unread_count = $wpdb->get_var( $sql );
+
+		if ( empty( $unread_count ) || is_wp_error( $unread_count ) )
+			return 0;
+
+		return (int) $unread_count;
+	}
+
+	function check_access( $thread_id, $user_id = 0 ) {
+		global $wpdb, $bp;
+
+		if ( empty( $user_id ) )
+			$user_id = $bp->loggedin_user->id;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d AND user_id = %d", $thread_id, $user_id ) );
+	}
+
+	function is_valid( $thread_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT thread_id FROM {$bp->messages->table_name_messages} WHERE thread_id = %d LIMIT 1", $thread_id ) );
+	}
+
+	function get_recipient_links($recipients) {
+		if ( count($recipients) >= 5 )
+			return count( $recipients ) . __(' Recipients', 'buddypress');
+
+		foreach ( (array)$recipients as $recipient )
+			$recipient_links[] = bp_core_get_userlink( $recipient->user_id );
+
+		return implode( ', ', (array) $recipient_links );
+	}
+
+	// Update Functions
+
+	function update_tables() {
+		global $wpdb, $bp;
+
+		$bp_prefix = bp_core_get_table_prefix();
+		$errors    = false;
+		$threads   = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp_prefix}bp_messages_threads" ) );
+
+		// Nothing to update, just return true to remove the table
+		if ( empty( $threads ) )
+			return true;
+
+		foreach( (array)$threads as $thread ) {
+			$message_ids = maybe_unserialize( $thread->message_ids );
+
+			if ( !empty( $message_ids ) ) {
+				$message_ids = implode( ',', $message_ids );
+
+				// Add the thread_id to the messages table
+				if ( !$wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_messages} SET thread_id = %d WHERE id IN ({$message_ids})", $thread->id ) ) )
+					$errors = true;
+			}
+		}
+
+		if ( $errors )
+			return false;
+
+		return true;
+	}
+}
+
+Class BP_Messages_Message {
+	var $id;
+	var $thread_id;
+	var $sender_id;
+	var $subject;
+	var $message;
+	var $date_sent;
+
+	var $recipients = false;
+
+	function bp_messages_message( $id = null ) {
+		$this->__construct( $id );
+	}
+
+	function __construct( $id = null ) {
+		global $bp;
+
+		$this->date_sent = bp_core_current_time();
+		$this->sender_id = $bp->loggedin_user->id;
+
+		if ( !empty( $id ) )
+			$this->populate( $id );
+	}
+
+	function populate( $id ) {
+		global $wpdb, $bp;
+
+		if ( $message = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->messages->table_name_messages} WHERE id = %d", $id ) ) ) {
+			$this->id        = $message->id;
+			$this->thread_id = $message->thread_id;
+			$this->sender_id = $message->sender_id;
+			$this->subject   = $message->subject;
+			$this->message   = $message->message;
+			$this->date_sent = $message->date_sent;
+		}
+	}
+
+	function send() {
+		global $wpdb, $bp;
+
+		$this->sender_id = apply_filters( 'messages_message_sender_id_before_save', $this->sender_id, $this->id );
+		$this->thread_id = apply_filters( 'messages_message_thread_id_before_save', $this->thread_id, $this->id );
+		$this->subject   = apply_filters( 'messages_message_subject_before_save',   $this->subject,   $this->id );
+		$this->message   = apply_filters( 'messages_message_content_before_save',   $this->message,   $this->id );
+		$this->date_sent = apply_filters( 'messages_message_date_sent_before_save', $this->date_sent, $this->id );
+
+		do_action_ref_array( 'messages_message_before_save', array( &$this ) );
+
+		// Make sure we have at least one recipient before sending.
+		if ( empty( $this->recipients ) )
+			return false;
+
+		$new_thread = false;
+
+		// If we have no thread_id then this is the first message of a new thread.
+		if ( empty( $this->thread_id ) ) {
+			$this->thread_id = (int)$wpdb->get_var( $wpdb->prepare( "SELECT MAX(thread_id) FROM {$bp->messages->table_name_messages}" ) ) + 1;
+			$new_thread = true;
+		}
+
+		// First insert the message into the messages table
+		if ( !$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->messages->table_name_messages} ( thread_id, sender_id, subject, message, date_sent ) VALUES ( %d, %d, %s, %s, %s )", $this->thread_id, $this->sender_id, $this->subject, $this->message, $this->date_sent ) ) )
+			return false;
+
+		$recipient_ids = array();
+
+		if ( $new_thread ) {
+			// Add an recipient entry for all recipients
+			foreach ( (array)$this->recipients as $recipient ) {
+				$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->messages->table_name_recipients} ( user_id, thread_id, unread_count ) VALUES ( %d, %d, 1 )", $recipient->user_id, $this->thread_id ) );
+				$recipient_ids[] = $recipient->user_id;
+			}
+
+			// Add a sender recipient entry if the sender is not in the list of recipients
+			if ( !in_array( $this->sender_id, $recipient_ids ) )
+				$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->messages->table_name_recipients} ( user_id, thread_id, sender_only ) VALUES ( %d, %d, 1 )", $this->sender_id, $this->thread_id ) );
+		} else {
+			// Update the unread count for all recipients
+			$wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET unread_count = unread_count + 1, sender_only = 0, is_deleted = 0 WHERE thread_id = %d AND user_id != %d", $this->thread_id, $this->sender_id ) );
+		}
+
+		$this->id = $wpdb->insert_id;
+		messages_remove_callback_values();
+
+		do_action_ref_array( 'messages_message_after_save', array( &$this ) );
+
+		return $this->id;
+	}
+
+	function get_recipients() {
+		global $bp, $wpdb;
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT user_id FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d", $this->thread_id ) );
+	}
+
+	// Static Functions
+
+	function get_recipient_ids( $recipient_usernames ) {
+		if ( !$recipient_usernames )
+			return false;
+
+		if ( is_array( $recipient_usernames ) ) {
+			for ( $i = 0, $count = count( $recipient_usernames ); $i < $count; ++$i ) {
+				if ( $rid = bp_core_get_userid( trim($recipient_usernames[$i]) ) )
+					$recipient_ids[] = $rid;
+			}
+		}
+
+		return $recipient_ids;
+	}
+
+	function get_last_sent_for_user( $thread_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_messages} WHERE sender_id = %d AND thread_id = %d ORDER BY date_sent DESC LIMIT 1", $bp->loggedin_user->id, $thread_id ) );
+	}
+
+	function is_user_sender( $user_id, $message_id ) {
+		global $wpdb, $bp;
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_messages} WHERE sender_id = %d AND id = %d", $user_id, $message_id ) );
+	}
+
+	function get_message_sender( $message_id ) {
+		global $wpdb, $bp;
+		return $wpdb->get_var( $wpdb->prepare( "SELECT sender_id FROM {$bp->messages->table_name_messages} WHERE id = %d", $message_id ) );
+	}
+}
+
+Class BP_Messages_Notice {
+	var $id = null;
+	var $subject;
+	var $message;
+	var $date_sent;
+	var $is_active;
+
+	function bp_messages_notice( $id = null ) {
+		$this->__construct($id);
+	}
+
+	function __construct( $id = null ) {
+		if ( $id ) {
+			$this->id = $id;
+			$this->populate($id);
+		}
+	}
+
+	function populate() {
+		global $wpdb, $bp;
+
+		$notice = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->messages->table_name_notices} WHERE id = %d", $this->id ) );
+
+		if ( $notice ) {
+			$this->subject   = $notice->subject;
+			$this->message   = $notice->message;
+			$this->date_sent = $notice->date_sent;
+			$this->is_active = $notice->is_active;
+		}
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->subject = apply_filters( 'messages_notice_subject_before_save', $this->subject, $this->id );
+		$this->message = apply_filters( 'messages_notice_message_before_save', $this->message, $this->id );
+
+		do_action_ref_array( 'messages_notice_before_save', array( &$this ) );
+
+		if ( empty( $this->id ) )
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->messages->table_name_notices} (subject, message, date_sent, is_active) VALUES (%s, %s, %s, %d)", $this->subject, $this->message, $this->date_sent, $this->is_active );
+		else
+			$sql = $wpdb->prepare( "UPDATE {$bp->messages->table_name_notices} SET subject = %s, message = %s, is_active = %d WHERE id = %d", $this->subject, $this->message, $this->is_active, $this->id );
+
+		if ( !$wpdb->query( $sql ) )
+			return false;
+
+		if ( !$id = $this->id )
+			$id = $wpdb->insert_id;
+
+		// Now deactivate all notices apart from the new one.
+		$wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_notices} SET is_active = 0 WHERE id != %d", $id ) );
+
+		bp_update_user_meta( $bp->loggedin_user->id, 'last_activity', bp_core_current_time() );
+
+		do_action_ref_array( 'messages_notice_after_save', array( &$this ) );
+
+		return true;
+	}
+
+	function activate() {
+		$this->is_active = 1;
+		if ( !$this->save() )
+			return false;
+
+		return true;
+	}
+
+	function deactivate() {
+		$this->is_active = 0;
+		if ( !$this->save() )
+			return false;
+
+		return true;
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "DELETE FROM {$bp->messages->table_name_notices} WHERE id = %d", $this->id );
+
+		if ( !$wpdb->query( $sql ) )
+			return false;
+
+		return true;
+	}
+
+	// Static Functions
+
+	function get_notices() {
+		global $wpdb, $bp;
+
+		$notices = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->messages->table_name_notices} ORDER BY date_sent DESC" ) );
+		return $notices;
+	}
+
+	function get_total_notice_count() {
+		global $wpdb, $bp;
+
+		$notice_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM " . $bp->messages->table_name_notices ) );
+
+		return $notice_count;
+	}
+
+	function get_active() {
+		global $wpdb, $bp;
+
+		$notice_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_notices} WHERE is_active = 1") );
+		return new BP_Messages_Notice( $notice_id );
+	}
+}
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-cssjs.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-cssjs.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-cssjs.php	(revision 5057)
@@ -0,0 +1,56 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function messages_add_autocomplete_js() {
+	global $bp;
+
+	// Include the autocomplete JS for composing a message.
+	if ( bp_is_messages_component() && bp_is_current_action( 'compose' ) ) {
+		add_action( 'wp_head', 'messages_autocomplete_init_jsblock' );
+
+		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
+			wp_enqueue_script( 'bp-jquery-autocomplete', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.autocomplete.dev.js', array( 'jquery' ), '20110723' );
+			wp_enqueue_script( 'bp-jquery-autocomplete-fb', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.autocompletefb.dev.js', array(), '20110723' );
+			wp_enqueue_script( 'bp-jquery-bgiframe', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.bgiframe.dev.js', array(), '20110723' );
+			wp_enqueue_script( 'bp-jquery-dimensions', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.dimensions.dev.js', array(), '20110723' );
+
+		} else {
+			wp_enqueue_script( 'bp-jquery-autocomplete', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.autocomplete.js', array( 'jquery' ), '20110723' );
+			wp_enqueue_script( 'bp-jquery-autocomplete-fb', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.autocompletefb.js', array(), '20110723' );
+			wp_enqueue_script( 'bp-jquery-bgiframe', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.bgiframe.js', array(), '20110723' );
+			wp_enqueue_script( 'bp-jquery-dimensions', BP_PLUGIN_URL . '/bp-messages/js/autocomplete/jquery.dimensions.js', array(), '20110723' );
+		}
+	}
+}
+add_action( 'bp_actions', 'messages_add_autocomplete_js' );
+
+function messages_add_autocomplete_css() {
+	global $bp;
+
+	if ( bp_is_messages_component() && bp_is_current_action( 'compose' ) ) {
+		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+			wp_enqueue_style( 'bp-messages-autocomplete', BP_PLUGIN_URL . '/bp-messages/css/autocomplete/jquery.autocompletefb.dev.css', array(), '20110723' );
+		else
+			wp_enqueue_style( 'bp-messages-autocomplete', BP_PLUGIN_URL . '/bp-messages/css/autocomplete/jquery.autocompletefb.css', array(), '20110723' );
+
+		wp_print_styles();
+	}
+}
+add_action( 'wp_head', 'messages_add_autocomplete_css' );
+
+function messages_autocomplete_init_jsblock() {
+?>
+	<script type="text/javascript">
+		jQuery(document).ready(function() {
+			var acfb =
+			jQuery("ul.first").autoCompletefb({urlLookup:'<?php echo site_url( 'wp-load.php' ) ?>'});
+
+			jQuery('#send_message_form').submit( function() {
+				var users = document.getElementById('send-to-usernames').className;
+				document.getElementById('send-to-usernames').value = String(users);
+			});
+		});
+	</script>
+<?php
+}
Index: /tags/1.5-beta-3/bp-messages/bp-messages-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-filters.php	(revision 5057)
@@ -0,0 +1,63 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Apply WordPress defined filters */
+add_filter( 'bp_get_message_notice_subject', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_message_notice_text', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_message_thread_subject', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_message_thread_excerpt', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_messages_subject_value', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_messages_content_value', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_the_thread_message_content', 'wp_filter_kses', 1 );
+
+add_filter( 'messages_message_content_before_save', 'wp_filter_kses', 1 );
+add_filter( 'messages_message_subject_before_save', 'wp_filter_kses', 1 );
+add_filter( 'messages_notice_message_before_save', 'wp_filter_kses', 1 );
+add_filter( 'messages_notice_subject_before_save', 'wp_filter_kses', 1 );
+
+add_filter( 'bp_get_the_thread_message_content', 'wp_filter_kses', 1 );
+add_filter( 'bp_get_the_thread_subject', 'wp_filter_kses', 1 );
+
+add_filter( 'messages_message_content_before_save', 'force_balance_tags' );
+add_filter( 'messages_message_subject_before_save', 'force_balance_tags' );
+add_filter( 'messages_notice_message_before_save', 'force_balance_tags' );
+add_filter( 'messages_notice_subject_before_save', 'force_balance_tags' );
+
+add_filter( 'bp_get_message_notice_subject', 'wptexturize' );
+add_filter( 'bp_get_message_notice_text', 'wptexturize' );
+add_filter( 'bp_get_message_thread_subject', 'wptexturize' );
+add_filter( 'bp_get_message_thread_excerpt', 'wptexturize' );
+add_filter( 'bp_get_the_thread_message_content', 'wptexturize' );
+
+add_filter( 'bp_get_message_notice_subject', 'convert_smilies', 2 );
+add_filter( 'bp_get_message_notice_text', 'convert_smilies', 2 );
+add_filter( 'bp_get_message_thread_subject', 'convert_smilies', 2 );
+add_filter( 'bp_get_message_thread_excerpt', 'convert_smilies', 2 );
+add_filter( 'bp_get_the_thread_message_content', 'convert_smilies', 2 );
+
+add_filter( 'bp_get_message_notice_subject', 'convert_chars' );
+add_filter( 'bp_get_message_notice_text', 'convert_chars' );
+add_filter( 'bp_get_message_thread_subject', 'convert_chars' );
+add_filter( 'bp_get_message_thread_excerpt', 'convert_chars' );
+add_filter( 'bp_get_the_thread_message_content', 'convert_chars' );
+
+add_filter( 'bp_get_message_notice_text', 'make_clickable', 9 );
+add_filter( 'bp_get_message_thread_excerpt', 'make_clickable', 9 );
+add_filter( 'bp_get_the_thread_message_content', 'make_clickable', 9 );
+
+add_filter( 'bp_get_message_notice_text', 'wpautop' );
+add_filter( 'bp_get_the_thread_message_content', 'wpautop' );
+
+add_filter( 'bp_get_message_notice_subject', 'stripslashes_deep' );
+add_filter( 'bp_get_message_notice_text', 'stripslashes_deep' );
+add_filter( 'bp_get_message_thread_subject', 'stripslashes_deep' );
+add_filter( 'bp_get_message_thread_excerpt', 'stripslashes_deep' );
+add_filter( 'bp_get_messages_subject_value', 'stripslashes_deep' );
+add_filter( 'bp_get_messages_content_value', 'stripslashes_deep' );
+add_filter( 'bp_get_the_thread_message_content', 'stripslashes_deep' );
+
+add_filter( 'bp_get_the_thread_message_content', 'stripslashes_deep' );
+add_filter( 'bp_get_the_thread_subject', 'stripslashes_deep' );
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-functions.php	(revision 5057)
@@ -0,0 +1,261 @@
+<?php
+/*******************************************************************************
+ * Business functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function messages_new_message( $args = '' ) {
+	global $bp;
+
+	$defaults = array (
+		'sender_id'  => $bp->loggedin_user->id,
+		'thread_id'  => false, // false for a new message, thread id for a reply to a thread.
+		'recipients' => false, // Can be an array of usernames, user_ids or mixed.
+		'subject'    => false,
+		'content'    => false,
+		'date_sent'  => bp_core_current_time()
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( empty( $sender_id ) || empty( $content ) )
+		return false;
+
+	// Create a new message object
+	$message            = new BP_Messages_Message;
+	$message->thread_id = $thread_id;
+	$message->sender_id = $sender_id;
+	$message->subject   = $subject;
+	$message->message   = $content;
+	$message->date_sent = $date_sent;
+
+	// If we have a thread ID, use the existing recipients, otherwise use the recipients passed
+	if ( !empty( $thread_id ) ) {
+		$thread = new BP_Messages_Thread( $thread_id );
+		$message->recipients = $thread->get_recipients();
+
+		// Strip the sender from the recipient list if they exist
+		if ( isset( $message->recipients[$sender_id] ) )
+			unset( $message->recipients[$sender_id] );
+
+		if ( empty( $message->subject ) )
+			$message->subject = sprintf( __( 'Re: %s', 'buddypress' ), $thread->messages[0]->subject );
+
+	// No thread ID, so make some adjustments
+	} else {
+		if ( empty( $recipients ) )
+			return false;
+
+		if ( empty( $message->subject ) )
+			$message->subject = __( 'No Subject', 'buddypress' );
+
+		$recipient_ids 	    = array();
+
+		// Invalid recipients are added to an array, for future enhancements
+		$invalid_recipients = array();
+
+		// Loop the recipients and convert all usernames to user_ids where needed
+		foreach( (array) $recipients as $recipient ) {
+			$recipient = trim( $recipient );
+			if ( empty( $recipient ) )
+				continue;
+
+			if ( is_numeric( $recipient ) ) {
+				if ( bp_core_get_core_userdata( (int) $recipient ) )
+					$recipient_id = (int) $recipient;
+
+			} else {
+				if ( bp_is_username_compatibility_mode() )
+					$recipient_id = bp_core_get_userid( $recipient );
+				else
+					$recipient_id = bp_core_get_userid_from_nicename( $recipient );
+			}
+
+			if ( !$recipient_id )
+				$invalid_recipients[] = $recipient;
+			else
+				$recipient_ids[] = (int) $recipient_id;
+		}
+
+		// Strip the sender from the recipient list if they exist
+		if ( $key = array_search( $sender_id, (array)$recipient_ids ) )
+			unset( $recipient_ids[$key] );
+
+		// Remove duplicates
+		$recipient_ids = array_unique( (array)$recipient_ids );
+
+		if ( empty( $recipient_ids ) )
+			return false;
+
+		// Format this to match existing recipients
+		foreach( (array)$recipient_ids as $i => $recipient_id ) {
+			$message->recipients[$i]          = new stdClass;
+			$message->recipients[$i]->user_id = $recipient_id;
+		}
+	}
+
+	if ( $message->send() ) {
+
+		// Send screen notifications to the recipients
+		foreach ( (array)$message->recipients as $recipient )
+			bp_core_add_notification( $message->id, $recipient->user_id, 'messages', 'new_message' );
+
+		// Send email notifications to the recipients
+		messages_notification_new_message( array( 'message_id' => $message->id, 'sender_id' => $message->sender_id, 'subject' => $message->subject, 'content' => $message->message, 'recipients' => $message->recipients, 'thread_id' => $message->thread_id) );
+
+		do_action_ref_array( 'messages_message_sent', array( &$message ) );
+
+		return $message->thread_id;
+	}
+
+	return false;
+}
+
+
+function messages_send_notice( $subject, $message ) {
+	if ( !is_super_admin() || empty( $subject ) || empty( $message ) ) {
+		return false;
+
+	// Has access to send notices, lets do it.
+	} else {
+		$notice            = new BP_Messages_Notice;
+		$notice->subject   = $subject;
+		$notice->message   = $message;
+		$notice->date_sent = bp_core_current_time();
+		$notice->is_active = 1;
+		$notice->save(); // send it.
+
+		do_action_ref_array( 'messages_send_notice', array( $subject, $message ) );
+
+		return true;
+	}
+}
+
+function messages_delete_thread( $thread_ids ) {
+	do_action( 'messages_before_delete_thread', $thread_ids );
+
+	if ( is_array( $thread_ids ) ) {
+		$error = 0;
+		for ( $i = 0, $count = count( $thread_ids ); $i < $count; ++$i ) {
+			if ( !$status = BP_Messages_Thread::delete( $thread_ids[$i]) )
+				$error = 1;
+		}
+
+		if ( !empty( $error ) )
+			return false;
+
+		do_action( 'messages_delete_thread', $thread_ids );
+
+		return true;
+	} else {
+		if ( !BP_Messages_Thread::delete( $thread_ids ) )
+			return false;
+
+		do_action( 'messages_delete_thread', $thread_ids );
+
+		return true;
+	}
+}
+
+function messages_check_thread_access( $thread_id, $user_id = 0 ) {
+	global $bp;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->loggedin_user->id;
+
+	return BP_Messages_Thread::check_access( $thread_id, $user_id );
+}
+
+function messages_mark_thread_read( $thread_id ) {
+	return BP_Messages_Thread::mark_as_read( $thread_id );
+}
+
+function messages_mark_thread_unread( $thread_id ) {
+	return BP_Messages_Thread::mark_as_unread( $thread_id );
+}
+
+function messages_add_callback_values( $recipients, $subject, $content ) {
+	@setcookie( 'bp_messages_send_to', $recipients, time() + 60 * 60 * 24, COOKIEPATH );
+	@setcookie( 'bp_messages_subject', $subject,    time() + 60 * 60 * 24, COOKIEPATH );
+	@setcookie( 'bp_messages_content', $content,    time() + 60 * 60 * 24, COOKIEPATH );
+}
+
+function messages_remove_callback_values() {
+	@setcookie( 'bp_messages_send_to', false, time() - 1000, COOKIEPATH );
+	@setcookie( 'bp_messages_subject', false, time() - 1000, COOKIEPATH );
+	@setcookie( 'bp_messages_content', false, time() - 1000, COOKIEPATH );
+}
+
+function messages_get_unread_count( $user_id = 0 ) {
+	global $bp;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->loggedin_user->id;
+
+	return BP_Messages_Thread::get_inbox_count( $user_id );
+}
+
+function messages_is_user_sender( $user_id, $message_id ) {
+	return BP_Messages_Message::is_user_sender( $user_id, $message_id );
+}
+
+function messages_get_message_sender( $message_id ) {
+	return BP_Messages_Message::get_message_sender( $message_id );
+}
+
+function messages_is_valid_thread( $thread_id ) {
+	return BP_Messages_Thread::is_valid( $thread_id );
+}
+
+/*******************************************************************************
+ * These functions handle the recording, deleting and formatting of activity and
+ * notifications for the user and for this specific component.
+ */
+
+/**
+ * Format the BuddyBar/admin bar notifications for the Messages component
+ *
+ * @package BuddyPress
+ *
+ * @param str $action The kind of notification being rendered
+ * @param int $item_id The primary item id
+ * @param int $secondary_item_id The secondary item id
+ * @param int $total_items The total number of messaging-related notifications waiting for the user
+ * @param str $format 'string' for BuddyBar-compatible notifications; 'array' for WP Admin Bar
+ */
+function messages_format_notifications( $action, $item_id, $secondary_item_id, $total_items, $format = 'string' ) {
+	global $bp;
+
+	if ( 'new_message' == $action ) {
+		$link  = bp_loggedin_user_domain() . bp_get_messages_slug() . '/inbox';
+		$title = __( 'Inbox', 'buddypress' );
+
+		if ( (int)$total_items > 1 ) {
+			$text = sprintf( __('You have %d new messages', 'buddypress' ), (int)$total_items );
+			$filter = 'bp_messages_multiple_new_message_notification';
+		} else {
+			$text = sprintf( __('You have %d new message', 'buddypress' ), (int)$total_items );
+			$filter = 'bp_messages_single_new_message_notification';
+		}
+	}
+
+	if ( 'string' == $format ) {
+		$return = apply_filters( $filter, '<a href="' . $link . '" title="' . $title . '">' . $text . '</a>', (int)$total_items, $text, $link );
+	} else {
+		$return = apply_filters( $filter, array(
+			'text' => $text,
+			'link' => $link
+		), $link, (int)$total_items, $text, $link );
+	}
+
+	do_action( 'messages_format_notifications', $action, $item_id, $secondary_item_id, $total_items );
+
+	return $return;
+}
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-loader.php	(revision 5057)
@@ -0,0 +1,253 @@
+<?php
+/**
+ * BuddyPress Private Messages Loader
+ *
+ * A private messages component, for users to send messages to each other
+ *
+ * @package BuddyPress
+ * @subpackage Messages Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Messages_Component extends BP_Component {
+
+	/**
+	 * Start the messages component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'messages',
+			__( 'Private Messages', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		// Files to include
+		$includes = array(
+			'cssjs',
+			'cache',
+			'actions',
+			'screens',
+			'classes',
+			'filters',
+			'template',
+			'functions',
+			'notifications'
+		);
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_MESSAGES_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_MESSAGES_SLUG' ) )
+			define( 'BP_MESSAGES_SLUG', $this->id );
+
+		// Global tables for messaging component
+		$global_tables = array(
+			'table_name_notices'    => $bp->table_prefix . 'bp_messages_notices',
+			'table_name_messages'   => $bp->table_prefix . 'bp_messages_messages',
+			'table_name_recipients' => $bp->table_prefix . 'bp_messages_recipients'
+		);
+
+		// All globals for messaging component.
+		// Note that global_tables is included in this array.
+		$globals = array(
+			'path'                  => BP_PLUGIN_DIR,
+			'slug'                  => BP_MESSAGES_SLUG,
+			'has_directory'         => false,
+			'notification_callback' => 'messages_format_notifications',
+			'search_string'         => __( 'Search Messages...', 'buddypress' ),
+			'global_tables'         => $global_tables
+		);
+
+		$this->autocomplete_all = defined( 'BP_MESSAGES_AUTOCOMPLETE_ALL' );
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		$name = sprintf( __( 'Messages <span>%s</span>', 'buddypress' ), bp_get_total_unread_messages_count() );
+
+		// Add 'Messages' to the main navigation
+		$main_nav = array(
+			'name'                    => $name,
+			'slug'                    => $this->slug,
+			'position'                => 50,
+			'show_for_displayed_user' => false,
+			'screen_function'         => 'messages_screen_inbox',
+			'default_subnav_slug'     => 'inbox',
+			'item_css_id'             => $this->id
+		);
+
+		// Link to user messages
+		$messages_link = trailingslashit( $bp->loggedin_user->domain . $this->slug );
+
+		// Add the subnav items to the profile
+		$sub_nav[] = array(
+			'name'            => __( 'Inbox', 'buddypress' ),
+			'slug'            => 'inbox',
+			'parent_url'      => $messages_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'messages_screen_inbox',
+			'position'        => 10,
+			'user_has_access' => bp_is_my_profile()
+		);
+
+		$sub_nav[] = array(
+			'name'            => __( 'Sent', 'buddypress' ),
+			'slug'            => 'sentbox',
+			'parent_url'      => $messages_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'messages_screen_sentbox',
+			'position'        => 20,
+			'user_has_access' => bp_is_my_profile()
+		);
+
+		$sub_nav[] = array(
+			'name'            => __( 'Compose', 'buddypress' ),
+			'slug'            => 'compose',
+			'parent_url'      => $messages_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'messages_screen_compose',
+			'position'        => 30,
+			'user_has_access' => bp_is_my_profile()
+		);
+
+		if ( is_super_admin() ) {
+			$sub_nav[] = array(
+				'name'            => __( 'Notices', 'buddypress' ),
+				'slug'            => 'notices',
+				'parent_url'      => $messages_link,
+				'parent_slug'     => $this->slug,
+				'screen_function' => 'messages_screen_notices',
+				'position'        => 90,
+				'user_has_access' => is_super_admin()
+			);
+		}
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain   = $bp->loggedin_user->domain;
+			$messages_link = trailingslashit( $user_domain . $this->slug );
+
+			// Unread message count
+			if ( $count = messages_get_unread_count() ) {
+				$title = sprintf( __( 'Messages <span class="count">%s</span>', 'buddypress' ), $count );
+				$inbox = sprintf( __( 'Inbox <span class="count">%s</span>',    'buddypress' ), $count );
+			} else {
+				$title = __( 'Messages', 'buddypress' );
+				$inbox = __( 'Inbox',    'buddypress' );
+			}
+
+			// Add main Messages menu
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => $title,
+				'href'   => trailingslashit( $messages_link )
+			);
+
+			// Inbox
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => $inbox,
+				'href'   => trailingslashit( $messages_link . 'inbox' )
+			);
+
+			// Sent Messages
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Sent', 'buddypress' ),
+				'href'   => trailingslashit( $messages_link . 'sentbox' )
+			);
+
+			// Compose Message
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Compose', 'buddypress' ),
+				'href'   => trailingslashit( $messages_link . 'compose' )
+			);
+
+			// Site Wide Notices
+			if ( is_super_admin() ) {
+				$wp_admin_nav[] = array(
+					'parent' => 'my-account-' . $this->id,
+					'title'  => __( 'All Member Notices', 'buddypress' ),
+					'href'   => trailingslashit( $messages_link . 'notices' )
+				);
+			}
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		if ( bp_is_messages_component() ) {
+			if ( bp_is_my_profile() ) {
+				$bp->bp_options_title = __( 'My Messages', 'buddypress' );
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the messages component
+$bp->messages = new BP_Messages_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-notifications.php	(revision 5057)
@@ -0,0 +1,57 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function messages_notification_new_message( $args ) {
+	global $bp;
+
+	extract($args);
+
+	$email_subject = $email_content = $args = '';
+	$sender_name   = bp_core_get_user_displayname( $sender_id );
+
+	foreach( $recipients as $recipient ) {
+		if ( $sender_id == $recipient->user_id || 'no' == bp_get_user_meta( $recipient->user_id, 'notification_messages_new_message', true ) )
+			continue;
+
+		// User data and links
+		$ud            = get_userdata( $recipient->user_id );
+		$message_link  = bp_core_get_user_domain( $recipient->user_id ) . bp_get_messages_slug() .'/';
+		$settings_link = bp_core_get_user_domain( $recipient->user_id ) . bp_get_settings_slug() . '/notifications/';
+
+		// Sender info
+		$sender_name   = stripslashes( $sender_name );
+		$subject       = stripslashes( wp_filter_kses( $subject ) );
+		$content       = stripslashes( wp_filter_kses( $content ) );
+
+		// Set up and send the message
+		$email_to      = $ud->user_email;
+		$sitename      = wp_specialchars_decode( get_blog_option( bp_get_root_blog_id(), 'blogname' ), ENT_QUOTES );
+		$email_subject = '[' . $sitename . '] ' . sprintf( __( 'New message from %s', 'buddypress' ), $sender_name );
+
+		$email_content = sprintf( __(
+'%s sent you a new message:
+
+Subject: %s
+
+"%s"
+
+To view and read your messages please log in and visit: %s
+
+---------------------
+', 'buddypress' ), $sender_name, $subject, $content, $message_link );
+
+		$email_content .= sprintf( __( 'To disable these notifications please log in and go to: %s', 'buddypress' ), $settings_link );
+
+		// Send the message
+		$email_to = apply_filters( 'messages_notification_new_message_to', $email_to );
+		$email_subject = apply_filters( 'messages_notification_new_message_subject', $email_subject, $sender_name );
+		$email_content = apply_filters( 'messages_notification_new_message_message', $email_content, $sender_name, $subject, $content, $message_link, $settings_link );
+
+		wp_mail( $email_to, $email_subject, $email_content );
+	}
+
+	do_action( 'bp_messages_sent_notification_email', $recipients, $email_subject, $email_content, $args );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-screens.php	(revision 5057)
@@ -0,0 +1,177 @@
+<?php
+/*******************************************************************************
+ * Screen functions are the controllers of BuddyPress. They will execute when their
+ * specific URL is caught. They will first save or manipulate data using business
+ * functions, then pass on the user to a template file.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function messages_screen_inbox() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	do_action( 'messages_screen_inbox' );
+	bp_core_load_template( apply_filters( 'messages_template_inbox', 'members/single/home' ) );
+}
+
+function messages_screen_sentbox() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	do_action( 'messages_screen_sentbox' );
+	bp_core_load_template( apply_filters( 'messages_template_sentbox', 'members/single/home' ) );
+}
+
+function messages_screen_compose() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	// Remove any saved message data from a previous session.
+	messages_remove_callback_values();
+
+	// Check if the message form has been submitted
+	if ( isset( $_POST['send'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'messages_send_message' );
+
+		// Check we have what we need
+		if ( empty( $_POST['subject'] ) || empty( $_POST['content'] ) ) {
+			bp_core_add_message( __( 'There was an error sending that message, please try again', 'buddypress' ), 'error' );
+		} else {
+			// If this is a notice, send it
+			if ( isset( $_POST['send-notice'] ) ) {
+				if ( messages_send_notice( $_POST['subject'], $_POST['content'] ) ) {
+					bp_core_add_message( __( 'Notice sent successfully!', 'buddypress' ) );
+					bp_core_redirect( $bp->loggedin_user->domain . $bp->messages->slug . '/inbox/' );
+				} else {
+					bp_core_add_message( __( 'There was an error sending that notice, please try again', 'buddypress' ), 'error' );
+				}
+			} else {
+				// Filter recipients into the format we need - array( 'username/userid', 'username/userid' )
+				$autocomplete_recipients = explode( ',', $_POST['send-to-input'] );
+				$typed_recipients        = explode( ' ', $_POST['send_to_usernames'] );
+				$recipients              = array_merge( (array) $autocomplete_recipients, (array) $typed_recipients );
+				$recipients              = apply_filters( 'bp_messages_recipients', $recipients );
+
+				// Send the message
+				if ( $thread_id = messages_new_message( array( 'recipients' => $recipients, 'subject' => $_POST['subject'], 'content' => $_POST['content'] ) ) ) {
+					bp_core_add_message( __( 'Message sent successfully!', 'buddypress' ) );
+					bp_core_redirect( $bp->loggedin_user->domain . $bp->messages->slug . '/view/' . $thread_id . '/' );
+				} else {
+					bp_core_add_message( __( 'There was an error sending that message, please try again', 'buddypress' ), 'error' );
+				}
+			}
+		}
+	}
+
+	do_action( 'messages_screen_compose' );
+
+	bp_core_load_template( apply_filters( 'messages_template_compose', 'members/single/home' ) );
+}
+
+function messages_screen_notices() {
+	global $notice_id;
+
+	if ( !is_super_admin() )
+		return false;
+
+	$notice_id = (int)bp_action_variable( 1 );
+
+	if ( !empty( $notice_id ) && is_numeric( $notice_id ) ) {
+		$notice = new BP_Messages_Notice( $notice_id );
+
+		if ( bp_is_action_variable( 'deactivate', 0 ) ) {
+			if ( !$notice->deactivate() ) {
+				bp_core_add_message( __('There was a problem deactivating that notice.', 'buddypress'), 'error' );
+			} else {
+				bp_core_add_message( __('Notice deactivated.', 'buddypress') );
+			}
+		} else if ( bp_is_action_variable( 'activate', 0 ) ) {
+			if ( !$notice->activate() ) {
+				bp_core_add_message( __('There was a problem activating that notice.', 'buddypress'), 'error' );
+			} else {
+				bp_core_add_message( __('Notice activated.', 'buddypress') );
+			}
+		} else if ( bp_is_action_variable( 'delete' ) ) {
+			if ( !$notice->delete() ) {
+				bp_core_add_message( __('There was a problem deleting that notice.', 'buddypress'), 'buddypress' );
+			} else {
+				bp_core_add_message( __('Notice deleted.', 'buddypress') );
+			}
+		}
+		bp_core_redirect( bp_loggedin_user_domain() . bp_get_messages_slug() . '/notices' );
+	}
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	do_action( 'messages_screen_notices' );
+
+	bp_core_load_template( apply_filters( 'messages_template_notices', 'members/single/home' ) );
+}
+
+function messages_screen_notification_settings() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	if ( !$new_messages = bp_get_user_meta( $bp->displayed_user->id, 'notification_messages_new_message', true ) )
+		$new_messages = 'yes';
+
+	if ( !$new_notices = bp_get_user_meta( $bp->displayed_user->id, 'notification_messages_new_notice', true ) )
+		$new_notices  = 'yes';
+?>
+
+	<table class="notification-settings" id="messages-notification-settings">
+		<thead>
+			<tr>
+				<th class="icon"></th>
+				<th class="title"><?php _e( 'Messages', 'buddypress' ) ?></th>
+				<th class="yes"><?php _e( 'Yes', 'buddypress' ) ?></th>
+				<th class="no"><?php _e( 'No', 'buddypress' )?></th>
+			</tr>
+		</thead>
+
+		<tbody>
+			<tr id="messages-notification-settings-new-message">
+				<td></td>
+				<td><?php _e( 'A member sends you a new message', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_messages_new_message]" value="yes" <?php checked( $new_messages, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_messages_new_message]" value="no" <?php checked( $new_messages, 'no', true ) ?>/></td>
+			</tr>
+			<tr id="messages-notification-settings-new-site-notice">
+				<td></td>
+				<td><?php _e( 'A new site notice is posted', 'buddypress' ) ?></td>
+				<td class="yes"><input type="radio" name="notifications[notification_messages_new_notice]" value="yes" <?php checked( $new_notices, 'yes', true ) ?>/></td>
+				<td class="no"><input type="radio" name="notifications[notification_messages_new_notice]" value="no" <?php checked( $new_notices, 'no', true ) ?>/></td>
+			</tr>
+
+			<?php do_action( 'messages_screen_notification_settings' ) ?>
+		</tbody>
+	</table>
+
+<?php
+}
+add_action( 'bp_notification_settings', 'messages_screen_notification_settings', 2 );
+
+?>
Index: /tags/1.5-beta-3/bp-messages/bp-messages-template.php
===================================================================
--- /tags/1.5-beta-3/bp-messages/bp-messages-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/bp-messages-template.php	(revision 5057)
@@ -0,0 +1,882 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/*****************************************************************************
+ * Message Box Template Class
+ **/
+Class BP_Messages_Box_Template {
+	var $current_thread = -1;
+	var $current_thread_count;
+	var $total_thread_count;
+	var $threads;
+	var $thread;
+
+	var $in_the_loop;
+	var $user_id;
+	var $box;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+
+	function bp_messages_box_template( $user_id, $box, $per_page, $max, $type ) {
+		$this->__construct( $user_id, $box, $per_page, $max, $type );
+	}
+
+	function __construct( $user_id, $box, $per_page, $max, $type ) {
+		$this->pag_page = isset( $_GET['mpage'] ) ? intval( $_GET['mpage'] ) : 1;
+		$this->pag_num  = isset( $_GET['num'] ) ? intval( $_GET['num'] ) : $per_page;
+
+		$this->user_id  = $user_id;
+		$this->box      = $box;
+		$this->type     = $type;
+
+		if ( 'notices' == $this->box ) {
+			$this->threads = BP_Messages_Notice::get_notices();
+		} else {
+			$threads = BP_Messages_Thread::get_current_threads_for_user( $this->user_id, $this->box, $this->type, $this->pag_num, $this->pag_page );
+
+			$this->threads            = $threads['threads'];
+			$this->total_thread_count = $threads['total'];
+		}
+
+		if ( !$this->threads ) {
+			$this->thread_count       = 0;
+			$this->total_thread_count = 0;
+		} else {
+			$total_notice_count = BP_Messages_Notice::get_total_notice_count();
+
+			if ( !$max || $max >= (int)$total_notice_count ) {
+				if ( 'notices' == $this->box ) {
+					$this->total_thread_count = (int)$total_notice_count;
+				}
+			} else {
+				$this->total_thread_count = (int)$max;
+			}
+
+			if ( $max ) {
+				if ( $max >= count( $this->threads ) ) {
+					$this->thread_count = count( $this->threads );
+				} else {
+					$this->thread_count = (int)$max;
+				}
+			} else {
+				$this->thread_count = count( $this->threads );
+			}
+		}
+
+		if ( (int)$this->total_thread_count && (int)$this->pag_num ) {
+			$this->pag_links = paginate_links( array(
+				'base'      => add_query_arg( 'mpage', '%#%' ),
+				'format'    => '',
+				'total'     => ceil( (int)$this->total_thread_count / (int)$this->pag_num ),
+				'current'   => $this->pag_page,
+				'prev_text' => _x( '&larr;', 'Message pagination previous text', 'buddypress' ),
+				'next_text' => _x( '&rarr;', 'Message pagination next text', 'buddypress' ),
+				'mid_size'  => 1
+			) );
+		}
+	}
+
+	function has_threads() {
+		if ( $this->thread_count )
+			return true;
+
+		return false;
+	}
+
+	function next_thread() {
+		$this->current_thread++;
+		$this->thread = $this->threads[$this->current_thread];
+
+		return $this->thread;
+	}
+
+	function rewind_threads() {
+		$this->current_thread = -1;
+		if ( $this->thread_count > 0 ) {
+			$this->thread = $this->threads[0];
+		}
+	}
+
+	function message_threads() {
+		if ( $this->current_thread + 1 < $this->thread_count ) {
+			return true;
+		} elseif ( $this->current_thread + 1 == $this->thread_count ) {
+			do_action('messages_box_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_threads();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_message_thread() {
+		global $bp;
+
+		$this->in_the_loop = true;
+		$this->thread = $this->next_thread();
+
+		if ( 'notices' != $bp->current_action ) {
+			$last_message_index = count( $this->thread->messages ) - 1;
+			$this->thread->messages = array_reverse( (array)$this->thread->messages );
+
+			// Set up the last message data
+			if ( count($this->thread->messages) > 1 ) {
+				if ( 'inbox' == $this->box ) {
+					foreach ( (array)$this->thread->messages as $key => $message ) {
+						if ( $bp->loggedin_user->id != $message->sender_id ) {
+							$last_message_index = $key;
+							break;
+						}
+					}
+
+				} elseif ( 'sentbox' == $this->box ) {
+					foreach ( (array)$this->thread->messages as $key => $message ) {
+						if ( $bp->loggedin_user->id == $message->sender_id ) {
+							$last_message_index = $key;
+							break;
+						}
+					}
+				}
+			}
+
+			$this->thread->last_message_id = $this->thread->messages[$last_message_index]->id;
+			$this->thread->last_message_date = $this->thread->messages[$last_message_index]->date_sent;
+			$this->thread->last_sender_id = $this->thread->messages[$last_message_index]->sender_id;
+			$this->thread->last_message_subject = $this->thread->messages[$last_message_index]->subject;
+			$this->thread->last_message_content = $this->thread->messages[$last_message_index]->message;
+		}
+
+		if ( 0 == $this->current_thread ) // loop has just started
+			do_action('messages_box_loop_start');
+	}
+}
+
+function bp_has_message_threads( $args = '' ) {
+	global $bp, $messages_template;
+
+	$defaults = array(
+		'user_id' => $bp->loggedin_user->id,
+		'box' => 'inbox',
+		'per_page' => 10,
+		'max' => false,
+		'type' => 'all'
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( 'notices' == $bp->current_action && !is_super_admin() ) {
+		wp_redirect( $bp->displayed_user->id );
+	} else {
+		if ( 'inbox' == $bp->current_action )
+			bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->messages->id, 'new_message' );
+
+		if ( 'sentbox' == $bp->current_action )
+			$box = 'sentbox';
+
+		if ( 'notices' == $bp->current_action )
+			$box = 'notices';
+
+		$messages_template = new BP_Messages_Box_Template( $user_id, $box, $per_page, $max, $type );
+	}
+
+	return apply_filters( 'bp_has_message_threads', $messages_template->has_threads(), $messages_template );
+}
+
+function bp_message_threads() {
+	global $messages_template;
+	return $messages_template->message_threads();
+}
+
+function bp_message_thread() {
+	global $messages_template;
+	return $messages_template->the_message_thread();
+}
+
+function bp_message_thread_id() {
+	echo bp_get_message_thread_id();
+}
+	function bp_get_message_thread_id() {
+		global $messages_template;
+
+		return apply_filters( 'bp_get_message_thread_id', $messages_template->thread->thread_id );
+	}
+
+function bp_message_thread_subject() {
+	echo bp_get_message_thread_subject();
+}
+	function bp_get_message_thread_subject() {
+		global $messages_template, $message_template_subject;
+
+		return apply_filters( 'bp_get_message_thread_subject', stripslashes_deep( $messages_template->thread->last_message_subject ) );
+	}
+
+function bp_message_thread_excerpt() {
+	echo bp_get_message_thread_excerpt();
+}
+	function bp_get_message_thread_excerpt() {
+		global $messages_template;
+
+		return apply_filters( 'bp_get_message_thread_excerpt', strip_tags( bp_create_excerpt( $messages_template->thread->last_message_content, 75 ) ) );
+	}
+
+function bp_message_thread_from() {
+	echo bp_get_message_thread_from();
+}
+	function bp_get_message_thread_from() {
+		global $messages_template, $bp;
+
+		return apply_filters( 'bp_get_message_thread_from', bp_core_get_userlink( $messages_template->thread->last_sender_id ) );
+	}
+
+function bp_message_thread_to() {
+	echo bp_get_message_thread_to();
+}
+	function bp_get_message_thread_to() {
+		global $messages_template;
+		return apply_filters( 'bp_message_thread_to', BP_Messages_Thread::get_recipient_links($messages_template->thread->recipients) );
+	}
+
+function bp_message_thread_view_link() {
+	echo bp_get_message_thread_view_link();
+}
+	function bp_get_message_thread_view_link() {
+		global $messages_template, $bp;
+		return apply_filters( 'bp_get_message_thread_view_link', trailingslashit( $bp->loggedin_user->domain . $bp->messages->slug . '/view/' . $messages_template->thread->thread_id ) );
+	}
+
+function bp_message_thread_delete_link() {
+	echo bp_get_message_thread_delete_link();
+}
+	function bp_get_message_thread_delete_link() {
+		global $messages_template, $bp;
+		return apply_filters( 'bp_get_message_thread_delete_link', wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/' . $bp->current_action . '/delete/' . $messages_template->thread->thread_id, 'messages_delete_thread' ) );
+	}
+
+function bp_message_css_class() {
+	echo bp_get_message_css_class();
+}
+
+	function bp_get_message_css_class() {
+		global $messages_template;
+
+		$class = false;
+
+		if ( $messages_template->current_thread % 2 == 1 )
+			$class .= 'alt';
+
+		return apply_filters( 'bp_get_message_css_class', trim( $class ) );
+	}
+
+function bp_message_thread_has_unread() {
+	global $messages_template;
+
+	if ( $messages_template->thread->unread_count )
+		return true;
+
+	return false;
+}
+
+function bp_message_thread_unread_count() {
+	echo bp_get_message_thread_unread_count();
+}
+	function bp_get_message_thread_unread_count() {
+		global $messages_template;
+
+		if ( (int)$messages_template->thread->unread_count )
+			return apply_filters( 'bp_get_message_thread_unread_count', $messages_template->thread->unread_count );
+
+		return false;
+	}
+
+function bp_message_thread_last_post_date() {
+	echo bp_get_message_thread_last_post_date();
+}
+	function bp_get_message_thread_last_post_date() {
+		global $messages_template;
+
+		return apply_filters( 'bp_get_message_thread_last_post_date', bp_format_time( strtotime( $messages_template->thread->last_message_date ) ) );
+	}
+
+function bp_message_thread_avatar() {
+	echo bp_get_message_thread_avatar();
+}
+	function bp_get_message_thread_avatar() {
+		global $messages_template, $bp;
+
+		return apply_filters( 'bp_get_message_thread_avatar', bp_core_fetch_avatar( array( 'item_id' => $messages_template->thread->last_sender_id, 'type' => 'thumb' ) ) );
+	}
+
+function bp_message_thread_view() {
+	global $thread_id;
+
+	messages_view_thread($thread_id);
+}
+
+function bp_total_unread_messages_count() {
+	echo bp_get_total_unread_messages_count();
+}
+	function bp_get_total_unread_messages_count() {
+		return apply_filters( 'bp_get_total_unread_messages_count', BP_Messages_Thread::get_inbox_count() );
+	}
+
+function bp_messages_pagination() {
+	echo bp_get_messages_pagination();
+}
+	function bp_get_messages_pagination() {
+		global $messages_template;
+		return apply_filters( 'bp_get_messages_pagination', $messages_template->pag_links );
+	}
+
+function bp_messages_pagination_count() {
+	global $messages_template;
+
+	$start_num = intval( ( $messages_template->pag_page - 1 ) * $messages_template->pag_num ) + 1;
+	$from_num = bp_core_number_format( $start_num );
+	$to_num = bp_core_number_format( ( $start_num + ( $messages_template->pag_num - 1 ) > $messages_template->total_thread_count ) ? $messages_template->total_thread_count : $start_num + ( $messages_template->pag_num - 1 ) );
+	$total = bp_core_number_format( $messages_template->total_thread_count );
+
+	echo sprintf( __( 'Viewing message %1$s to %2$s (of %3$s messages)', 'buddypress' ), $from_num, $to_num, $total ); ?><?php
+}
+
+/**
+ * Echoes the form action for Messages HTML forms
+ *
+ * @package BuddyPress
+ */
+function bp_messages_form_action() {
+	echo bp_get_messages_form_action();
+}
+	/**
+	 * Returns the form action for Messages HTML forms
+	 *
+	 * @package BuddyPress
+	 *
+	 * @return str The form action
+	 */
+	function bp_get_messages_form_action() {
+		return apply_filters( 'bp_get_messages_form_action', trailingslashit( bp_loggedin_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() . '/' . bp_action_variable( 0 ) ) );
+	}
+
+function bp_messages_username_value() {
+	echo bp_get_messages_username_value();
+}
+	function bp_get_messages_username_value() {
+		if ( isset( $_COOKIE['bp_messages_send_to'] ) ) {
+			return apply_filters( 'bp_get_messages_username_value', $_COOKIE['bp_messages_send_to'] );
+		} else if ( isset( $_GET['r'] ) && !isset( $_COOKIE['bp_messages_send_to'] ) ) {
+			return apply_filters( 'bp_get_messages_username_value', $_GET['r'] );
+		}
+	}
+
+function bp_messages_subject_value() {
+	echo bp_get_messages_subject_value();
+}
+	function bp_get_messages_subject_value() {
+		$subject = '';
+		if ( !empty( $_POST['subject'] ) )
+			$subject = $_POST['subject'];
+
+		return apply_filters( 'bp_get_messages_subject_value', $subject );
+	}
+
+function bp_messages_content_value() {
+	echo bp_get_messages_content_value();
+}
+	function bp_get_messages_content_value() {
+		$content = '';
+		if ( !empty( $_POST['content'] ) )
+			$content = $_POST['content'];
+
+		return apply_filters( 'bp_get_messages_content_value', $content );
+	}
+
+function bp_messages_options() {
+	global $bp;
+?>
+	<?php _e( 'Select:', 'buddypress' ) ?>
+	<select name="message-type-select" id="message-type-select">
+		<option value=""></option>
+		<option value="read"><?php _e('Read', 'buddypress') ?></option>
+		<option value="unread"><?php _e('Unread', 'buddypress') ?></option>
+		<option value="all"><?php _e('All', 'buddypress') ?></option>
+	</select> &nbsp;
+	<?php if ( $bp->current_action != 'sentbox' && $bp->current_action != 'notices' ) : ?>
+		<a href="#" id="mark_as_read"><?php _e('Mark as Read', 'buddypress') ?></a> &nbsp;
+		<a href="#" id="mark_as_unread"><?php _e('Mark as Unread', 'buddypress') ?></a> &nbsp;
+	<?php endif; ?>
+	<a href="#" id="delete_<?php echo $bp->current_action ?>_messages"><?php _e('Delete Selected', 'buddypress') ?></a> &nbsp;
+<?php
+}
+
+function bp_message_is_active_notice() {
+	global $messages_template;
+
+	if ( $messages_template->thread->is_active ) {
+		echo "<strong>";
+		_e( 'Currently Active', 'buddypress' );
+		echo "</strong>";
+	}
+}
+	function bp_get_message_is_active_notice() {
+		global $messages_template;
+
+		if ( $messages_template->thread->is_active )
+			return true;
+
+		return false;
+	}
+
+function bp_message_notice_id() {
+	echo bp_get_message_notice_id();
+}
+	function bp_get_message_notice_id() {
+		global $messages_template;
+		return apply_filters( 'bp_get_message_notice_id', $messages_template->thread->id );
+	}
+
+function bp_message_notice_post_date() {
+	echo bp_get_message_notice_post_date();
+}
+	function bp_get_message_notice_post_date() {
+		global $messages_template;
+		return apply_filters( 'bp_get_message_notice_post_date', bp_format_time( strtotime($messages_template->thread->date_sent) ) );
+	}
+
+function bp_message_notice_subject() {
+	echo bp_get_message_notice_subject();
+}
+	function bp_get_message_notice_subject() {
+		global $messages_template;
+		return apply_filters( 'bp_get_message_notice_subject', $messages_template->thread->subject );
+	}
+
+function bp_message_notice_text() {
+	echo bp_get_message_notice_text();
+}
+	function bp_get_message_notice_text() {
+		global $messages_template;
+		return apply_filters( 'bp_get_message_notice_text', $messages_template->thread->message );
+	}
+
+function bp_message_notice_delete_link() {
+	echo bp_get_message_notice_delete_link();
+}
+	function bp_get_message_notice_delete_link() {
+		global $messages_template, $bp;
+
+		return apply_filters( 'bp_get_message_notice_delete_link', wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/notices/delete/' . $messages_template->thread->id, 'messages_delete_thread' ) );
+	}
+
+function bp_message_activate_deactivate_link() {
+	echo bp_get_message_activate_deactivate_link();
+}
+	function bp_get_message_activate_deactivate_link() {
+		global $messages_template, $bp;
+
+		if ( 1 == (int)$messages_template->thread->is_active ) {
+			$link = wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/notices/deactivate/' . $messages_template->thread->id, 'messages_deactivate_notice' );
+		} else {
+			$link = wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/notices/activate/' . $messages_template->thread->id, 'messages_activate_notice' );
+		}
+		return apply_filters( 'bp_get_message_activate_deactivate_link', $link );
+	}
+
+function bp_message_activate_deactivate_text() {
+	echo bp_get_message_activate_deactivate_text();
+}
+	function bp_get_message_activate_deactivate_text() {
+		global $messages_template;
+
+		if ( 1 == (int)$messages_template->thread->is_active  ) {
+			$text = __('Deactivate', 'buddypress');
+		} else {
+			$text = __('Activate', 'buddypress');
+		}
+		return apply_filters( 'bp_message_activate_deactivate_text', $text );
+	}
+
+/**
+ * Output the messages component slug
+ *
+ * @package BuddyPress
+ * @subpackage Messages Template
+ * @since 1.5
+ *
+ * @uses bp_get_messages_slug()
+ */
+function bp_messages_slug() {
+	echo bp_get_messages_slug();
+}
+	/**
+	 * Return the messages component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Messages Template
+	 * @since 1.5
+	 */
+	function bp_get_messages_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_messages_slug', $bp->messages->slug );
+	}
+
+function bp_message_get_notices() {
+	global $userdata;
+
+	$notice = BP_Messages_Notice::get_active();
+
+	if ( empty( $notice ) )
+		return false;
+
+	$closed_notices = bp_get_user_meta( $userdata->ID, 'closed_notices', true );
+
+	if ( !$closed_notices )
+		$closed_notices = array();
+
+	if ( is_array($closed_notices) ) {
+		if ( !in_array( $notice->id, $closed_notices ) && $notice->id ) {
+			?>
+			<div id="message" class="info notice" rel="n-<?php echo $notice->id ?>">
+				<p>
+					<strong><?php echo stripslashes( wp_filter_kses( $notice->subject ) ) ?></strong><br />
+					<?php echo stripslashes( wp_filter_kses( $notice->message) ) ?>
+					<a href="#" id="close-notice"><?php _e( 'Close', 'buddypress' ) ?></a>
+				</p>
+			</div>
+			<?php
+		}
+	}
+}
+
+function bp_send_private_message_link() {
+	echo bp_get_send_private_message_link();
+}
+	function bp_get_send_private_message_link() {
+		global $bp;
+
+		if ( bp_is_my_profile() || !is_user_logged_in() )
+			return false;
+
+		return apply_filters( 'bp_get_send_private_message_link', wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/compose/?r=' . bp_core_get_username( $bp->displayed_user->id, $bp->displayed_user->userdata->user_nicename, $bp->displayed_user->userdata->user_login ) ) );
+	}
+
+/**
+ * bp_send_private_message_button()
+ *
+ * Explicitly named function to avoid confusion with public messages.
+ *
+ * @uses bp_get_send_message_button()
+ * @since 1.2.6
+ */
+function bp_send_private_message_button() {
+	echo bp_get_send_message_button();
+}
+
+function bp_send_message_button() {
+	echo bp_get_send_message_button();
+}
+	function bp_get_send_message_button() {
+		return apply_filters( 'bp_get_send_message_button',
+			bp_get_button( array(
+				'id'                => 'private_message',
+				'component'         => 'messages',
+				'must_be_logged_in' => true,
+				'block_self'        => true,
+				'wrapper_id'        => 'send-private-message',
+				'link_href'         => bp_get_send_private_message_link(),
+				'link_title'        => __( 'Send a private message to this user.', 'buddypress' ),
+				'link_text'         => __( 'Private Message', 'buddypress' ),
+				'link_class'        => 'send-message',
+			) )
+		);
+	}
+
+function bp_message_loading_image_src() {
+	echo bp_get_message_loading_image_src();
+}
+	function bp_get_message_loading_image_src() {
+		global $bp;
+		return apply_filters( 'bp_get_message_loading_image_src', $bp->messages->image_base . '/ajax-loader.gif' );
+	}
+
+function bp_message_get_recipient_tabs() {
+	$recipients = explode( ' ', bp_get_message_get_recipient_usernames() );
+
+	foreach ( $recipients as $recipient ) {
+		if ( $user_id = bp_core_get_userid( $recipient ) ) : ?>
+
+			<li id="un-<?php echo esc_attr( $recipient ); ?>" class="friend-tab">
+				<span><?php
+					echo bp_core_fetch_avatar( array( 'item_id' => $user_id, 'type' => 'thumb', 'width' => 15, 'height' => 15 ) );
+					echo bp_core_get_userlink( $user_id );
+				?></span>
+			</li>
+
+		<?php endif;
+	}
+}
+
+function bp_message_get_recipient_usernames() {
+	echo bp_get_message_get_recipient_usernames();
+}
+	function bp_get_message_get_recipient_usernames() {
+		$recipients = isset( $_GET['r'] ) ? stripslashes( $_GET['r'] ) : '';
+
+		return apply_filters( 'bp_get_message_get_recipient_usernames', $recipients );
+	}
+
+
+/*****************************************************************************
+ * Message Thread Template Class
+ **/
+
+class BP_Messages_Thread_Template {
+	var $current_message = -1;
+	var $message_count;
+	var $message;
+
+	var $thread;
+
+	var $in_the_loop;
+
+	var $pag_page;
+	var $pag_num;
+	var $pag_links;
+	var $total_message_count;
+
+	function bp_messages_thread_template( $thread_id, $order ) {
+		$this->__construct( $thread_id, $order );
+	}
+
+	function __construct( $thread_id, $order ) {
+		global $bp;
+
+		$this->thread = new BP_Messages_Thread( $thread_id, $order );
+		$this->message_count = count( $this->thread->messages );
+
+		$last_message_index = $this->message_count - 1;
+		$this->thread->last_message_id = $this->thread->messages[$last_message_index]->id;
+		$this->thread->last_message_date = $this->thread->messages[$last_message_index]->date_sent;
+		$this->thread->last_sender_id = $this->thread->messages[$last_message_index]->sender_id;
+		$this->thread->last_message_subject = $this->thread->messages[$last_message_index]->subject;
+		$this->thread->last_message_content = $this->thread->messages[$last_message_index]->message;
+	}
+
+	function has_messages() {
+		if ( $this->message_count )
+			return true;
+
+		return false;
+	}
+
+	function next_message() {
+		$this->current_message++;
+		$this->message = $this->thread->messages[$this->current_message];
+
+		return $this->message;
+	}
+
+	function rewind_messages() {
+		$this->current_message = -1;
+		if ( $this->message_count > 0 ) {
+			$this->message = $this->thread->messages[0];
+		}
+	}
+
+	function messages() {
+		if ( $this->current_message + 1 < $this->message_count ) {
+			return true;
+		} elseif ( $this->current_message + 1 == $this->message_count ) {
+			do_action('thread_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_messages();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_message() {
+		global $message;
+
+		$this->in_the_loop = true;
+		$this->message = $this->next_message();
+
+		if ( 0 == $this->current_message ) // loop has just started
+			do_action('thread_loop_start');
+	}
+}
+
+function bp_thread_has_messages( $args = '' ) {
+	global $bp, $thread_template, $group_id;
+
+	$defaults = array(
+		'thread_id' => false,
+		'order' => 'ASC'
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( !$thread_id && bp_is_current_component( 'messages' ) && bp_is_current_action( 'view' ) )
+		$thread_id = (int)bp_action_variable( 0 );
+
+	$thread_template = new BP_Messages_Thread_Template( $thread_id, $order );
+	return $thread_template->has_messages();
+}
+
+function bp_thread_messages_order() {
+	echo bp_get_thread_messages_order();
+}
+
+	function bp_get_thread_messages_order() {
+		global $thread_template;
+		return $thread_template->thread->messages_order;
+	}
+
+function bp_thread_messages() {
+	global $thread_template;
+
+	return $thread_template->messages();
+}
+
+function bp_thread_the_message() {
+	global $thread_template;
+
+	return $thread_template->the_message();
+}
+
+function bp_the_thread_id() {
+	echo bp_get_the_thread_id();
+}
+	function bp_get_the_thread_id() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_id', $thread_template->thread->thread_id );
+	}
+
+function bp_the_thread_subject() {
+	echo bp_get_the_thread_subject();
+}
+	function bp_get_the_thread_subject() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_subject', $thread_template->thread->last_message_subject );
+	}
+
+function bp_the_thread_recipients() {
+	echo bp_get_the_thread_recipients();
+}
+	function bp_get_the_thread_recipients() {
+		global $thread_template, $bp;
+
+		$recipient_links = array();
+
+		if ( count( $thread_template->thread->recipients ) >= 5 )
+			return apply_filters( 'bp_get_the_thread_recipients', sprintf( __( '%d Recipients', 'buddypress' ), count($thread_template->thread->recipients) ) );
+
+		foreach( (array)$thread_template->thread->recipients as $recipient ) {
+			if ( $recipient->user_id !== $bp->loggedin_user->id )
+				$recipient_links[] = bp_core_get_userlink( $recipient->user_id );
+		}
+
+		return apply_filters( 'bp_get_the_thread_recipients', implode( ', ', $recipient_links ) );
+	}
+
+function bp_the_thread_message_alt_class() {
+	echo bp_get_the_thread_message_alt_class();
+}
+	function bp_get_the_thread_message_alt_class() {
+		global $thread_template;
+
+		if ( $thread_template->current_message % 2 == 1 )
+			$class = ' alt';
+		else
+			$class = '';
+
+		return apply_filters( 'bp_get_the_thread_message_alt_class', $class );
+	}
+
+function bp_the_thread_message_sender_avatar( $args = '' ) {
+	echo bp_get_the_thread_message_sender_avatar_thumb( $args );
+}
+	function bp_get_the_thread_message_sender_avatar_thumb( $args = '' ) {
+		global $thread_template;
+
+		$defaults = array(
+			'type' => 'thumb',
+			'width' => false,
+			'height' => false,
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_the_thread_message_sender_avatar_thumb', bp_core_fetch_avatar( array( 'item_id' => $thread_template->message->sender_id, 'type' => $type, 'width' => $width, 'height' => $height ) ) );
+	}
+
+function bp_the_thread_message_sender_link() {
+	echo bp_get_the_thread_message_sender_link();
+}
+	function bp_get_the_thread_message_sender_link() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_message_sender_link', bp_core_get_userlink( $thread_template->message->sender_id, false, true ) );
+	}
+
+function bp_the_thread_message_sender_name() {
+	echo bp_get_the_thread_message_sender_name();
+}
+	function bp_get_the_thread_message_sender_name() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_message_sender_name', bp_core_get_user_displayname( $thread_template->message->sender_id ) );
+	}
+
+function bp_the_thread_delete_link() {
+	echo bp_get_the_thread_delete_link();
+}
+	function bp_get_the_thread_delete_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_message_thread_delete_link', wp_nonce_url( $bp->loggedin_user->domain . $bp->messages->slug . '/inbox/delete/' . bp_get_the_thread_id(), 'messages_delete_thread' ) );
+	}
+
+function bp_the_thread_message_time_since() {
+	echo bp_get_the_thread_message_time_since();
+}
+	function bp_get_the_thread_message_time_since() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_message_time_since', sprintf( __( 'Sent %s', 'buddypress' ), bp_core_time_since( strtotime( $thread_template->message->date_sent ) ) ) );
+	}
+
+function bp_the_thread_message_content() {
+	echo bp_get_the_thread_message_content();
+}
+	function bp_get_the_thread_message_content() {
+		global $thread_template;
+
+		return apply_filters( 'bp_get_the_thread_message_content', $thread_template->message->message );
+	}
+
+/** Embeds *******************************************************************/
+
+/**
+ * Enable oembed support for Messages.
+ *
+ * There's no caching as BP 1.5 does not have a Messages meta API.
+ *
+ * @see BP_Embed
+ * @since 1.5
+ * @todo Add Messages meta?
+ */
+function bp_messages_embed() {
+	add_filter( 'embed_post_id', 'bp_get_message_thread_id' );
+}
+add_action( 'messages_box_loop_start', 'bp_messages_embed' );
+?>
Index: /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.css
===================================================================
--- /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.css	(revision 5057)
@@ -0,0 +1,1 @@
+.ac_results{padding:0;overflow:hidden;z-index:99999;background:#fff;border:1px solid #ccc;-moz-border-radius-bottomleft:3px;-khtml-border-bottom-left-radius:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-moz-border-radius-bottomright:3px;-khtml-border-bottom-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;}.ac_results ul{width:100%;list-style-position:outside;list-style:none;padding:0;margin:0;}.ac_results li{margin:0;padding:5px 10px;cursor:pointer;display:block;font-size:1em;line-height:16px;overflow:hidden;}.ac_results li img{margin-right:5px;}.ac_loading{background:url('../../../bp-themes/bp-default/_inc/images/ajax-loader.gif') right center no-repeat;}.ac_odd{background-color:#f0f0f0;}.ac_over{background-color:#888;color:#fff;}ul.acfb-holder{margin:0;height:auto!important;height:1%;overflow:hidden;padding:0;list-style:none;}ul.acfb-holder li{float:left;margin:0 5px 4px 0;list-style-type:none;}ul.acfb-holder li.friend-tab{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;border:1px solid #ffe7c7;padding:2px 7px 2px;background:#FFF9DF;font-size:1em;}li.friend-tab img.avatar{border-width:2px!important;vertical-align:middle;}li.friend-tab span.p{padding-left:5px;font-size:.8em;cursor:pointer;}input#send-to-input{width:275px;}
Index: /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/css/autocomplete/jquery.autocompletefb.dev.css	(revision 5057)
@@ -0,0 +1,85 @@
+.ac_results {
+	padding: 0px;
+	overflow: hidden;
+	z-index: 99999;
+	background: #fff;
+	border: 1px solid #ccc;
+	-moz-border-radius-bottomleft: 3px;
+	-khtml-border-bottom-left-radius: 3px;
+	-webkit-border-bottom-left-radius: 3px;
+	border-bottom-left-radius: 3px;
+	-moz-border-radius-bottomright: 3px;
+	-khtml-border-bottom-right-radius: 3px;
+	-webkit-border-bottom-right-radius: 3px;
+	border-bottom-right-radius: 3px;
+}
+	.ac_results ul {
+		width: 100%;
+		list-style-position: outside;
+		list-style: none;
+		padding: 0;
+		margin: 0;
+	}
+
+	.ac_results li {
+		margin: 0px;
+		padding: 5px 10px;
+		cursor: pointer;
+		display: block;
+		font-size: 1em;
+		line-height: 16px;
+		overflow: hidden;
+	}
+		.ac_results li img {
+			margin-right: 5px;
+		}
+
+.ac_loading {
+	background : url('../../../bp-themes/bp-default/_inc/images/ajax-loader.gif') right center no-repeat;
+}
+
+.ac_odd {
+	background-color: #f0f0f0;
+}
+
+.ac_over {
+	background-color: #888;
+	color: #fff;
+}
+
+ul.acfb-holder {
+	margin  : 0;
+	height  : auto !important;
+	height  : 1%;
+	overflow: hidden;
+	padding: 0;
+	list-style: none;
+}
+	ul.acfb-holder li {
+		float   : left;
+		margin  : 0 5px 4px 0;
+		list-style-type: none;
+	}
+
+	ul.acfb-holder li.friend-tab {
+		border-radius         : 3px;
+		-moz-border-radius    : 3px;
+		-webkit-border-radius : 3px;
+		border     : 1px solid #ffe7c7;
+		padding    : 2px 7px 2px;
+		background : #FFF9DF;
+		font-size: 1em;
+	}
+		li.friend-tab img.avatar {
+			border-width: 2px !important;
+			vertical-align: middle;
+		}
+
+		li.friend-tab span.p {
+			padding-left: 5px;
+			font-size: 0.8em;
+			cursor: pointer;
+		}
+
+input#send-to-input { width: 275px; }
+
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.dev.js	(revision 5057)
@@ -0,0 +1,730 @@
+/*!
+ * Autocomplete - jQuery plugin 1.0 Beta
+ *
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 4485 2008-01-20 13:52:47Z joern.zaefferer $
+ *
+ */
+
+;(function($) {
+
+$.fn.extend({
+	autocomplete: function(urlOrData, options) {
+		var isUrl = typeof urlOrData == "string";
+		options = $.extend({}, $.Autocompleter.defaults, {
+			url: isUrl ? urlOrData : null,
+			data: isUrl ? null : urlOrData,
+			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+			max: options && !options.scroll ? 10 : 150
+		}, options);
+
+		// if highlight is set to false, replace it with a do-nothing function
+		options.highlight = options.highlight || function(value) { return value; };
+
+		return this.each(function() {
+			new $.Autocompleter(this, options);
+		});
+	},
+	result: function(handler) {
+		return this.bind("result", handler);
+	},
+	search: function(handler) {
+		return this.trigger("search", [handler]);
+	},
+	flushCache: function() {
+		return this.trigger("flushCache");
+	},
+	setOptions: function(options){
+		return this.trigger("setOptions", [options]);
+	},
+	unautocomplete: function() {
+		return this.trigger("unautocomplete");
+	}
+});
+
+$.Autocompleter = function(input, options) {
+
+	var KEY = {
+		UP: 38,
+		DOWN: 40,
+		DEL: 46,
+		TAB: 9,
+		RETURN: 13,
+		ESC: 27,
+		COMMA: 188,
+		PAGEUP: 33,
+		PAGEDOWN: 34
+	};
+
+	// Create $ object for input element
+	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+	var timeout;
+	var previousValue = "";
+	var cache = $.Autocompleter.Cache(options);
+	var hasFocus = 0;
+	var lastKeyPressCode;
+	var config = {
+		mouseDownOnSelect: false
+	};
+	var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+
+	$input.keydown(function(event) {
+		// track last key pressed
+		lastKeyPressCode = event.keyCode;
+		switch(event.keyCode) {
+
+			case KEY.UP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.prev();
+				} else {
+					onChange(0, true);
+				}
+				break;
+
+			case KEY.DOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.next();
+				} else {
+					onChange(0, true);
+				}
+				break;
+
+			case KEY.PAGEUP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageUp();
+				} else {
+					onChange(0, true);
+				}
+				break;
+
+			case KEY.PAGEDOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageDown();
+				} else {
+					onChange(0, true);
+				}
+				break;
+
+			// matches also semicolon
+			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+			case KEY.TAB:
+			case KEY.RETURN:
+				if( selectCurrent() ){
+					// make sure to blur off the current field
+					if( !options.multiple )
+						$input.blur();
+					event.preventDefault();
+						$input.focus();
+				}
+				break;
+
+			case KEY.ESC:
+				select.hide();
+				break;
+
+			default:
+				clearTimeout(timeout);
+				timeout = setTimeout(onChange, options.delay);
+				break;
+		}
+	}).keypress(function() {
+		// having fun with opera - remove this binding and Opera submits the form when we select an entry via return
+	}).focus(function(){
+		// track whether the field has focus, we shouldn't process any
+		// results if the field no longer has focus
+		hasFocus++;
+	}).blur(function() {
+		hasFocus = 0;
+		if (!config.mouseDownOnSelect) {
+			hideResults();
+		}
+	}).click(function() {
+		// show select when clicking in a focused field
+		if ( hasFocus++ > 1 && !select.visible() ) {
+			onChange(0, true);
+		}
+	}).bind("search", function() {
+		// TODO why not just specifying both arguments?
+		var fn = (arguments.length > 1) ? arguments[1] : null;
+		function findValueCallback(q, data) {
+			var result;
+			if( data && data.length ) {
+				for (var i=0; i < data.length; i++) {
+					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+						result = data[i];
+						break;
+					}
+				}
+			}
+			if( typeof fn == "function" ) fn(result);
+			else $input.trigger("result", result && [result.data, result.value]);
+		}
+		$.each(trimWords($input.val()), function(i, value) {
+			request(value, findValueCallback, findValueCallback);
+		});
+	}).bind("flushCache", function() {
+		cache.flush();
+	}).bind("setOptions", function() {
+		$.extend(options, arguments[1]);
+		// if we've updated the data, repopulate
+		if ( "data" in arguments[1] )
+			cache.populate();
+	}).bind("unautocomplete", function() {
+		select.unbind();
+		$input.unbind();
+	});
+
+
+	function selectCurrent() {
+		var selected = select.selected();
+		if( !selected )
+			return false;
+
+		var v = selected.result;
+		previousValue = v;
+
+		if ( options.multiple ) {
+			var words = trimWords($input.val());
+			if ( words.length > 1 ) {
+				v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
+			}
+			v += options.multipleSeparator;
+		}
+
+		$input.val(v);
+		hideResultsNow();
+		$input.trigger("result", [selected.data, selected.value]);
+		return true;
+	}
+
+	function onChange(crap, skipPrevCheck) {
+		if( lastKeyPressCode == KEY.DEL ) {
+			select.hide();
+			return;
+		}
+
+		var currentValue = $input.val();
+
+		if ( !skipPrevCheck && currentValue == previousValue )
+			return;
+
+		previousValue = currentValue;
+
+		currentValue = lastWord(currentValue);
+		if ( currentValue.length >= options.minChars) {
+			$input.addClass(options.loadingClass);
+			jQuery('#send-to-input').addClass('loading');
+			if (!options.matchCase)
+				currentValue = currentValue.toLowerCase();
+			request(currentValue, receiveData, hideResultsNow);
+		} else {
+			stopLoading();
+			select.hide();
+		}
+	};
+
+	function trimWords(value) {
+		if ( !value ) {
+			return [""];
+		}
+		var words = value.split( $.trim( options.multipleSeparator ) );
+		var result = [];
+		$.each(words, function(i, value) {
+			if ( $.trim(value) )
+				result[i] = $.trim(value);
+		});
+		return result;
+	}
+
+	function lastWord(value) {
+		if ( !options.multiple )
+			return value;
+		var words = trimWords(value);
+		return words[words.length - 1];
+	}
+
+	// fills in the input box w/the first match (assumed to be the best match)
+	function autoFill(q, sValue){
+		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
+		// if the last user key pressed was backspace, don't autofill
+		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != 8 ) {
+			// fill in the value (keep the case the user has typed)
+			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+			// select the portion of the value not typed by the user (so the next character will erase)
+			$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
+		}
+	};
+
+	function hideResults() {
+		clearTimeout(timeout);
+		timeout = setTimeout(hideResultsNow, 200);
+	};
+
+	function hideResultsNow() {
+		select.hide();
+		clearTimeout(timeout);
+		stopLoading();
+		if (options.mustMatch) {
+			// call search and run callback
+			$input.search(
+				function (result){
+					// if no value found, clear the input box
+					if( !result ) $input.val("");
+				}
+			);
+		}
+	};
+
+	function receiveData(q, data) {
+		if ( data && data.length && hasFocus ) {
+			stopLoading();
+			select.display(data, q);
+
+			var newData = data[0].value.split(';');
+			data.value = newData[0];
+
+			autoFill(q, data.value);
+			select.show();
+		} else {
+			hideResultsNow();
+		}
+	};
+
+	function request(term, success, failure) {
+		if (!options.matchCase)
+			term = term.toLowerCase();
+		var data = cache.load(term);
+		// recieve the cached data
+		if (data && data.length) {
+			success(term, data);
+		// if an AJAX url has been supplied, try loading the data now
+		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+
+			var extraParams = {};
+			$.each(options.extraParams, function(key, param) {
+				extraParams[key] = typeof param == "function" ? param() : param;
+			});
+
+			$.ajax({
+				// try to leverage ajaxQueue plugin to abort previous requests
+				mode: "abort",
+				// limit abortion to this input
+				port: "autocomplete" + input.name,
+				dataType: options.dataType,
+				url: options.url,
+				data: $.extend({
+					q: lastWord(term),
+					limit: options.max,
+					action: 'messages_autocomplete_results',
+					'cookie': encodeURIComponent(document.cookie)
+				}, extraParams),
+				success: function(data) {
+					var parsed = options.parse && options.parse(data) || parse(data);
+					cache.add(term, parsed);
+					success(term, parsed);
+				}
+			});
+		} else {
+			failure(term);
+		}
+	};
+
+	function parse(data) {
+		var parsed = [];
+		var rows = data.split("\n");
+		for (var i=0; i < rows.length; i++) {
+			var row = $.trim(rows[i]);
+			if (row) {
+				row = row.split("|");
+				parsed[parsed.length] = {
+					data: row,
+					value: row[0],
+					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+				};
+			}
+		}
+		return parsed;
+	};
+
+	function stopLoading() {
+		$input.removeClass(options.loadingClass);
+		jQuery('#send-to-input').removeClass('loading');
+	};
+
+};
+
+$.Autocompleter.defaults = {
+	inputClass: "ac_input",
+	resultsClass: "ac_results",
+	loadingClass: "ac_loading",
+	minChars: 1,
+	delay: 400,
+	matchCase: false,
+	matchSubset: true,
+	matchContains: false,
+	cacheLength: 10,
+	max: 100,
+	mustMatch: false,
+	extraParams: {},
+	selectFirst: true,
+	formatItem: function(row) { return row[0]; },
+	autoFill: false,
+	width: 0,
+	multiple: false,
+	multipleSeparator: ", ",
+	highlight: function(value, term) {
+		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+	},
+    scroll: true,
+    scrollHeight: 250,
+	attachTo: 'body'
+};
+
+$.Autocompleter.Cache = function(options) {
+
+	var data = {};
+	var length = 0;
+
+	function matchSubset(s, sub) {
+		if (!options.matchCase)
+			s = s.toLowerCase();
+		var i = s.indexOf(sub);
+		if (i == -1) return false;
+		return i == 0 || options.matchContains;
+	};
+
+	function add(q, value) {
+		if (length > options.cacheLength){
+			flush();
+		}
+		if (!data[q]){
+			length++;
+		}
+		data[q] = value;
+	}
+
+	function populate(){
+		if( !options.data ) return false;
+		// track the matches
+		var stMatchSets = {},
+			nullData = 0;
+
+		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
+		if( !options.url ) options.cacheLength = 1;
+
+		// track all options for minChars = 0
+		stMatchSets[""] = [];
+
+		// loop through the array and create a lookup structure
+		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+			var rawValue = options.data[i];
+			// if rawValue is a string, make an array otherwise just reference the array
+			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+
+			var value = options.formatItem(rawValue, i+1, options.data.length);
+			if ( value === false )
+				continue;
+
+			var firstChar = value.charAt(0).toLowerCase();
+			// if no lookup array for this character exists, look it up now
+			if( !stMatchSets[firstChar] )
+				stMatchSets[firstChar] = [];
+
+			// if the match is a string
+			var row = {
+				value: value,
+				data: rawValue,
+				result: options.formatResult && options.formatResult(rawValue) || value
+			};
+
+			// push the current match into the set list
+			stMatchSets[firstChar].push(row);
+
+			// keep track of minChars zero items
+			if ( nullData++ < options.max ) {
+				stMatchSets[""].push(row);
+			}
+		};
+
+		// add the data items to the cache
+		$.each(stMatchSets, function(i, value) {
+			// increase the cache size
+			options.cacheLength++;
+			// add to the cache
+			add(i, value);
+		});
+	}
+
+	// populate any existing data
+	setTimeout(populate, 25);
+
+	function flush(){
+		data = {};
+		length = 0;
+	}
+
+	return {
+		flush: flush,
+		add: add,
+		populate: populate,
+		load: function(q) {
+			if (!options.cacheLength || !length)
+				return null;
+			/*
+			 * if dealing w/local data and matchContains than we must make sure
+			 * to loop through all the data collections looking for matches
+			 */
+			if( !options.url && options.matchContains ){
+				// track all matches
+				var csub = [];
+				// loop through all the data grids for matches
+				for( var k in data ){
+					// don't search through the stMatchSets[""] (minChars: 0) cache
+					// this prevents duplicates
+					if( k.length > 0 ){
+						var c = data[k];
+						$.each(c, function(i, x) {
+							// if we've got a match, add it to the array
+							if (matchSubset(x.value, q)) {
+								csub.push(x);
+							}
+						});
+					}
+				}
+				return csub;
+			} else
+			// if the exact item exists, use it
+			if (data[q]){
+				return data[q];
+			} else
+			if (options.matchSubset) {
+				for (var i = q.length - 1; i >= options.minChars; i--) {
+					var c = data[q.substr(0, i)];
+					if (c) {
+						var csub = [];
+						$.each(c, function(i, x) {
+							if (matchSubset(x.value, q)) {
+								csub[csub.length] = x;
+							}
+						});
+						return csub;
+					}
+				}
+			}
+			return null;
+		}
+	};
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+	var CLASSES = {
+		ACTIVE: "ac_over"
+	};
+
+	var listItems,
+		active = -1,
+		data,
+		term = "",
+		needsInit = true,
+		element,
+		list;
+
+	// Create results
+	function init() {
+		if (!needsInit)
+			return;
+		element = $("<div/>")
+		.hide()
+		.addClass(options.resultsClass)
+		.css("position", "absolute")
+		.appendTo(options.attachTo);
+
+		list = $("<ul>").appendTo(element).mouseover( function(event) {
+			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+			    $(target(event)).addClass(CLASSES.ACTIVE);
+	        }
+		}).click(function(event) {
+			$(target(event)).addClass(CLASSES.ACTIVE);
+			select();
+			input.focus();
+			return false;
+		}).mousedown(function() {
+			config.mouseDownOnSelect = true;
+		}).mouseup(function() {
+			config.mouseDownOnSelect = false;
+		});
+
+		if( options.width > 0 )
+			element.css("width", options.width);
+
+		needsInit = false;
+	}
+
+	function target(event) {
+		var element = event.target;
+		while(element && element.tagName != "LI")
+			element = element.parentNode;
+		// more fun with IE, sometimes event.target is empty, just ignore it then
+		if(!element)
+			return [];
+		return element;
+	}
+
+	function moveSelect(step) {
+		listItems.slice(active, active + 1).removeClass();
+		movePosition(step);
+        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+        if(options.scroll) {
+            var offset = 0;
+            listItems.slice(0, active).each(function() {
+				offset += this.offsetHeight;
+			});
+            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+            } else if(offset < list.scrollTop()) {
+                list.scrollTop(offset);
+            }
+        }
+	};
+
+	function movePosition(step) {
+		active += step;
+		if (active < 0) {
+			active = listItems.size() - 1;
+		} else if (active >= listItems.size()) {
+			active = 0;
+		}
+	}
+
+	function limitNumberOfItems(available) {
+		return options.max && options.max < available
+			? options.max
+			: available;
+	}
+
+	function fillList() {
+		list.empty();
+		var max = limitNumberOfItems(data.length);
+		for (var i=0; i < max; i++) {
+			if (!data[i])
+				continue;
+			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+			if ( formatted === false )
+				continue;
+			var li = $("<li>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_event" : "ac_odd").appendTo(list)[0];
+			$.data(li, "ac_data", data[i]);
+		}
+		listItems = list.find("li");
+		if ( options.selectFirst ) {
+			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+			active = 0;
+		}
+		list.bgiframe();
+	}
+
+	return {
+		display: function(d, q) {
+			init();
+			data = d;
+			term = q;
+			fillList();
+		},
+		next: function() {
+			moveSelect(1);
+		},
+		prev: function() {
+			moveSelect(-1);
+		},
+		pageUp: function() {
+			if (active != 0 && active - 8 < 0) {
+				moveSelect( -active );
+			} else {
+				moveSelect(-8);
+			}
+		},
+		pageDown: function() {
+			if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+				moveSelect( listItems.size() - 1 - active );
+			} else {
+				moveSelect(8);
+			}
+		},
+		hide: function() {
+			element && element.hide();
+			active = -1;
+		},
+		visible : function() {
+			return element && element.is(":visible");
+		},
+		current: function() {
+			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+		},
+		show: function() {
+			var offset = $(input).offset();
+			element.css({
+				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+				top: offset.top + input.offsetHeight,
+				left: offset.left
+			}).show();
+            if(options.scroll) {
+                list.scrollTop(0);
+                list.css({
+					maxHeight: options.scrollHeight,
+					overflow: 'auto'
+				});
+
+                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+					var listHeight = 0;
+					listItems.each(function() {
+						listHeight += this.offsetHeight;
+					});
+					var scrollbarsVisible = listHeight > options.scrollHeight;
+                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+					if (!scrollbarsVisible) {
+						// IE doesn't recalculate width when scrollbar disappears
+						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+					}
+                }
+
+            }
+		},
+		selected: function() {
+			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+			return selected && selected.length && $.data(selected[0], "ac_data");
+		},
+		unbind: function() {
+			element && element.remove();
+		}
+	};
+};
+
+$.Autocompleter.Selection = function(field, start, end) {
+	if( field.createTextRange ){
+		var selRange = field.createTextRange();
+		selRange.collapse(true);
+		selRange.moveStart("character", start);
+		selRange.moveEnd("character", end);
+		selRange.select();
+	} else if( field.setSelectionRange ){
+		field.setSelectionRange(start, end);
+	} else {
+		if( field.selectionStart ){
+			field.selectionStart = start;
+			field.selectionEnd = end;
+		}
+	}
+	field.focus();
+};
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocomplete.js	(revision 5057)
@@ -0,0 +1,13 @@
+/*
+ * Autocomplete - jQuery plugin 1.0 Beta
+ *
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 4485 2008-01-20 13:52:47Z joern.zaefferer $
+ *
+ */
+(function(a){a.fn.extend({autocomplete:function(b,c){var d=typeof b=="string";c=a.extend({},a.Autocompleter.defaults,{url:d?b:null,data:d?null:b,delay:d?a.Autocompleter.defaults.delay:10,max:c&&!c.scroll?10:150},c);c.highlight=c.highlight||function(e){return e};return this.each(function(){new a.Autocompleter(this,c)})},result:function(b){return this.bind("result",b)},search:function(b){return this.trigger("search",[b])},flushCache:function(){return this.trigger("flushCache")},setOptions:function(b){return this.trigger("setOptions",[b])},unautocomplete:function(){return this.trigger("unautocomplete")}});a.Autocompleter=function(l,g){var c={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34};var b=a(l).attr("autocomplete","off").addClass(g.inputClass);var j;var p="";var m=a.Autocompleter.Cache(g);var e=0;var u;var w={mouseDownOnSelect:false};var r=a.Autocompleter.Select(g,l,d,w);b.keydown(function(x){u=x.keyCode;switch(x.keyCode){case c.UP:x.preventDefault();if(r.visible()){r.prev()}else{t(0,true)}break;case c.DOWN:x.preventDefault();if(r.visible()){r.next()}else{t(0,true)}break;case c.PAGEUP:x.preventDefault();if(r.visible()){r.pageUp()}else{t(0,true)}break;case c.PAGEDOWN:x.preventDefault();if(r.visible()){r.pageDown()}else{t(0,true)}break;case g.multiple&&a.trim(g.multipleSeparator)==","&&c.COMMA:case c.TAB:case c.RETURN:if(d()){if(!g.multiple){b.blur()}x.preventDefault();b.focus()}break;case c.ESC:r.hide();break;default:clearTimeout(j);j=setTimeout(t,g.delay);break}}).keypress(function(){}).focus(function(){e++}).blur(function(){e=0;if(!w.mouseDownOnSelect){s()}}).click(function(){if(e++>1&&!r.visible()){t(0,true)}}).bind("search",function(){var x=(arguments.length>1)?arguments[1]:null;function y(C,B){var z;if(B&&B.length){for(var A=0;A<B.length;A++){if(B[A].result.toLowerCase()==C.toLowerCase()){z=B[A];break}}}if(typeof x=="function"){x(z)}else{b.trigger("result",z&&[z.data,z.value])}}a.each(h(b.val()),function(z,A){f(A,y,y)})}).bind("flushCache",function(){m.flush()}).bind("setOptions",function(){a.extend(g,arguments[1]);if("data" in arguments[1]){m.populate()}}).bind("unautocomplete",function(){r.unbind();b.unbind()});function d(){var y=r.selected();if(!y){return false}var x=y.result;p=x;if(g.multiple){var z=h(b.val());if(z.length>1){x=z.slice(0,z.length-1).join(g.multipleSeparator)+g.multipleSeparator+x}x+=g.multipleSeparator}b.val(x);v();b.trigger("result",[y.data,y.value]);return true}function t(z,y){if(u==c.DEL){r.hide();return}var x=b.val();if(!y&&x==p){return}p=x;x=i(x);if(x.length>=g.minChars){b.addClass(g.loadingClass);jQuery("#send-to-input").addClass("loading");if(!g.matchCase){x=x.toLowerCase()}f(x,k,v)}else{n();r.hide()}}function h(y){if(!y){return[""]}var z=y.split(a.trim(g.multipleSeparator));var x=[];a.each(z,function(A,B){if(a.trim(B)){x[A]=a.trim(B)}});return x}function i(x){if(!g.multiple){return x}var y=h(x);return y[y.length-1]}function q(x,y){if(g.autoFill&&(i(b.val()).toLowerCase()==x.toLowerCase())&&u!=8){b.val(b.val()+y.substring(i(p).length));a.Autocompleter.Selection(l,p.length,p.length+y.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(x){if(!x){b.val("")}})}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);var x=y[0].value.split(";");y.value=x[0];q(z,y.value);r.show()}else{v()}}function f(y,A,x){if(!g.matchCase){y=y.toLowerCase()}var z=m.load(y);if(z&&z.length){A(y,z)}else{if((typeof g.url=="string")&&(g.url.length>0)){var B={};a.each(g.extraParams,function(C,D){B[C]=typeof D=="function"?D():D});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(y),limit:g.max,action:"messages_autocomplete_results",cookie:encodeURIComponent(document.cookie)},B),success:function(D){var C=g.parse&&g.parse(D)||o(D);m.add(y,C);A(y,C)}})}else{x(y)}}}function o(A){var x=[];var z=A.split("\n");for(var y=0;y<z.length;y++){var B=a.trim(z[y]);if(B){B=B.split("|");x[x.length]={data:B,value:B[0],result:g.formatResult&&g.formatResult(B,B[0])||B[0]}}}return x}function n(){b.removeClass(g.loadingClass);jQuery("#send-to-input").removeClass("loading")}};a.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(b){return b[0]},autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(c,b){return c.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>")},scroll:true,scrollHeight:250,attachTo:"body"};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m<l;m++){var p=c.data[m];p=(typeof p=="string")?[p]:p;var o=c.formatItem(p,m+1,c.data.length);if(o===false){continue}var n=o.charAt(0).toLowerCase();if(!k[n]){k[n]=[]}var q={value:o,data:p,result:c.formatResult&&c.formatResult(p)||o};k[n].push(q);if(j++<c.max){k[""].push(q)}}a.each(k,function(r,s){c.cacheLength++;g(r,s)})}setTimeout(e,25);function b(){f={};d=0}return{flush:b,add:g,populate:e,load:function(n){if(!c.cacheLength||!d){return null}if(!c.url&&c.matchContains){var m=[];for(var j in f){if(j.length>0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("<div/>").hide().addClass(e.resultsClass).css("position","absolute").appendTo(e.attachTo);o=a("<ul>").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass();g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u<o.scrollTop()){o.scrollTop(u)}}}}function g(t){f+=t;if(f<0){f=k.size()-1}else{if(f>=k.size()){f=0}}}function b(t){return e.max&&e.max<t?e.max:t}function d(){o.empty();var u=b(r.length);for(var v=0;v<u;v++){if(!r[v]){continue}var w=e.formatItem(r[v].data,v+1,u,r[v].value,m);if(w===false){continue}var t=a("<li>").html(e.highlight(w,m)).addClass(v%2==0?"ac_event":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}o.bgiframe()}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},unbind:function(){c&&c.remove()}}};a.Autocompleter.Selection=function(d,e,c){if(d.createTextRange){var b=d.createTextRange();b.collapse(true);b.moveStart("character",e);b.moveEnd("character",c);b.select()}else{if(d.setSelectionRange){d.setSelectionRange(e,c)}else{if(d.selectionStart){d.selectionStart=e;d.selectionEnd=c}}}d.focus()}})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.dev.js	(revision 5057)
@@ -0,0 +1,76 @@
+/*!
+ * jQuery plugin: autoCompletefb(AutoComplete Facebook)
+ * @requires jQuery v1.2.2 or later
+ * using plugin:jquery.autocomplete.js
+ *
+ * Credits:
+ * - Idea: Facebook
+ * - Guillermo Rauch: Original MooTools script
+ * - InteRiders <http://interiders.com/>
+ *
+ * Copyright (c) 2008 Widi Harsojo <wharsojo@gmail.com>, http://wharsojo.wordpress.com/
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ */
+
+jQuery.fn.autoCompletefb = function(options)
+{
+	var tmp = this;
+	var settings =
+	{
+		ul         : tmp,
+		urlLookup  : [""],
+		acOptions  : {},
+		foundClass : ".friend-tab",
+		inputClass : ".send-to-input"
+	}
+
+	if(options) jQuery.extend(settings, options);
+
+	var acfb =
+	{
+		params  : settings,
+		removeFind : function(o){
+			acfb.removeUsername(o);
+			jQuery(o).unbind('click').parent().remove();
+			jQuery(settings.inputClass,tmp).focus();
+			return tmp.acfb;
+		},
+		removeUsername: function(o){
+			var newID = o.parentNode.id.split('-');
+			jQuery('#send-to-usernames').removeClass(newID[1]);
+		}
+	}
+
+	jQuery(settings.foundClass+" img.p").click(function(){
+		acfb.removeFind(this);
+	});
+
+	jQuery(settings.inputClass,tmp).autocomplete(settings.urlLookup,settings.acOptions);
+	jQuery(settings.inputClass,tmp).result(function(e,d,f){
+		var f = settings.foundClass.replace(/\./,'');
+		var d = String(d).split(' (');
+		var un = d[1].substr(0, d[1].length-1);
+		
+		/* Don't add the same user multiple times */
+		if( 0 === jQuery(settings.inputClass).siblings('#un-' + un).length ) {		
+			var ln = '#link-' + un;
+			var l = jQuery(ln).attr('href');
+			var v = '<li class="'+f+'" id="un-'+un+'"><span><a href="'+l+'">'+d[0]+'</a></span> <span class="p">X</span></li>';
+			
+			var x = jQuery(settings.inputClass,tmp).before(v);
+			jQuery('#send-to-usernames').addClass(un);
+				
+			jQuery('.p',x[0].previousSibling).click(function(){
+				acfb.removeFind(this);
+			});
+		} 
+			
+		jQuery(settings.inputClass,tmp).val('');
+
+	});
+
+	jQuery(settings.inputClass,tmp).focus();
+	return acfb;
+}
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.autocompletefb.js	(revision 5057)
@@ -0,0 +1,16 @@
+/*
+ * jQuery plugin: autoCompletefb(AutoComplete Facebook)
+ * @requires jQuery v1.2.2 or later
+ * using plugin:jquery.autocomplete.js
+ *
+ * Credits:
+ * - Idea: Facebook
+ * - Guillermo Rauch: Original MooTools script
+ * - InteRiders <http://interiders.com/>
+ *
+ * Copyright (c) 2008 Widi Harsojo <wharsojo@gmail.com>, http://wharsojo.wordpress.com/
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ */
+jQuery.fn.autoCompletefb=function(b){var c=this;var d={ul:c,urlLookup:[""],acOptions:{},foundClass:".friend-tab",inputClass:".send-to-input"};if(b){jQuery.extend(d,b)}var a={params:d,removeFind:function(e){a.removeUsername(e);jQuery(e).unbind("click").parent().remove();jQuery(d.inputClass,c).focus();return c.acfb},removeUsername:function(f){var e=f.parentNode.id.split("-");jQuery("#send-to-usernames").removeClass(e[1])}};jQuery(d.foundClass+" img.p").click(function(){a.removeFind(this)});jQuery(d.inputClass,c).autocomplete(d.urlLookup,d.acOptions);jQuery(d.inputClass,c).result(function(n,o,m){var m=d.foundClass.replace(/\./,"");var o=String(o).split(" (");var j=o[1].substr(0,o[1].length-1);if(0===jQuery(d.inputClass).siblings("#un-"+j).length){var k="#link-"+j;var h=jQuery(k).attr("href");var i='<li class="'+m+'" id="un-'+j+'"><span><a href="'+h+'">'+o[0]+'</a></span> <span class="p">X</span></li>';var g=jQuery(d.inputClass,c).before(i);jQuery("#send-to-usernames").addClass(j);jQuery(".p",g[0].previousSibling).click(function(){a.removeFind(this)})}jQuery(d.inputClass,c).val("")});jQuery(d.inputClass,c).focus();return a};
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.dev.js	(revision 5057)
@@ -0,0 +1,39 @@
+/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (license.bgiframe.txt).
+ *
+ * Version 2.1.2
+ */
+
+(function($){
+
+$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) {
+    s = $.extend({
+        top     : 'auto', // auto == .currentStyle.borderTopWidth
+        left    : 'auto', // auto == .currentStyle.borderLeftWidth
+        width   : 'auto', // auto == offsetWidth
+        height  : 'auto', // auto == offsetHeight
+        opacity : true,
+        src     : 'javascript:false;'
+    }, s);
+    var html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
+                   'style="display:block;position:absolute;z-index:-1;'+
+                       (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
+                       'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
+                       'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
+                       'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
+                       'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
+                '"/>';
+    return this.each(function() {
+        if ( $(this).children('iframe.bgiframe').length === 0 )
+            this.insertBefore( document.createElement(html), this.firstChild );
+    });
+} : function() { return this; });
+
+// old alias
+$.fn.bgIframe = $.fn.bgiframe;
+
+function prop(n) {
+    return n && n.constructor === Number ? n + 'px' : n;
+}
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.bgiframe.js	(revision 5057)
@@ -0,0 +1,6 @@
+/* Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (license.bgiframe.txt).
+ *
+ * Version 2.1.2
+ */
+(function(a){a.fn.bgiframe=(a.browser.msie&&/msie 6\.0/i.test(navigator.userAgent)?function(d){d=a.extend({top:"auto",left:"auto",width:"auto",height:"auto",opacity:true,src:"javascript:false;"},d);var c='<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+d.src+'"style="display:block;position:absolute;z-index:-1;'+(d.opacity!==false?"filter:Alpha(Opacity='0');":"")+"top:"+(d.top=="auto"?"expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+'px')":b(d.top))+";left:"+(d.left=="auto"?"expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+'px')":b(d.left))+";width:"+(d.width=="auto"?"expression(this.parentNode.offsetWidth+'px')":b(d.width))+";height:"+(d.height=="auto"?"expression(this.parentNode.offsetHeight+'px')":b(d.height))+';"/>';return this.each(function(){if(a(this).children("iframe.bgiframe").length===0){this.insertBefore(document.createElement(c),this.firstChild)}})}:function(){return this});a.fn.bgIframe=a.fn.bgiframe;function b(c){return c&&c.constructor===Number?c+"px":c}})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.dev.js	(revision 5057)
@@ -0,0 +1,117 @@
+/*!
+ * Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * $LastChangedDate: 2007-09-11 05:38:31 +0300 (Вт, 11 сен 2007) $
+ * $Rev: 3238 $
+ *
+ * Version: @VERSION
+ *
+ * Requires: jQuery 1.2+
+ */
+
+(function($){
+
+$.dimensions = {
+	version: '@VERSION'
+};
+
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+$.each( [ 'Height', 'Width' ], function(i, name){
+
+	// innerHeight and innerWidth
+	$.fn[ 'inner' + name ] = function() {
+		if (!this[0]) return;
+
+		var torl = name == 'Height' ? 'Top'    : 'Left',  // top or left
+		    borr = name == 'Height' ? 'Bottom' : 'Right'; // bottom or right
+
+		return this[ name.toLowerCase() ]() + num(this, 'padding' + torl) + num(this, 'padding' + borr);
+	};
+
+	// outerHeight and outerWidth
+	$.fn[ 'outer' + name ] = function(options) {
+		if (!this[0]) return;
+
+		var torl = name == 'Height' ? 'Top'    : 'Left',  // top or left
+		    borr = name == 'Height' ? 'Bottom' : 'Right'; // bottom or right
+
+		options = $.extend({ margin: false }, options || {});
+
+		return this[ name.toLowerCase() ]()
+				+ num(this, 'border' + torl + 'Width') + num(this, 'border' + borr + 'Width')
+				+ num(this, 'padding' + torl) + num(this, 'padding' + borr)
+				+ (options.margin ? (num(this, 'margin' + torl) + num(this, 'margin' + borr)) : 0);
+	};
+});
+
+// Create scrollLeft and scrollTop methods
+$.each( ['Left', 'Top'], function(i, name) {
+	$.fn[ 'scroll' + name ] = function(val) {
+		if (!this[0]) return;
+
+		return val != undefined ?
+
+			// Set the scroll offset
+			this.each(function() {
+				this == window || this == document ?
+					window.scrollTo(
+						name == 'Left' ? val : $(window)[ 'scrollLeft' ](),
+						name == 'Top'  ? val : $(window)[ 'scrollTop'  ]()
+					) :
+					this[ 'scroll' + name ] = val;
+			}) :
+
+			// Return the scroll offset
+			this[0] == window || this[0] == document ?
+				self[ (name == 'Left' ? 'pageXOffset' : 'pageYOffset') ] ||
+					$.boxModel && document.documentElement[ 'scroll' + name ] ||
+					document.body[ 'scroll' + name ] :
+				this[0][ 'scroll' + name ];
+	};
+});
+
+$.fn.extend({
+	position: function() {
+		var left = 0, top = 0, elem = this[0], offset, parentOffset, offsetParent, results;
+
+		if (elem) {
+			// Get *real* offsetParent
+			offsetParent = this.offsetParent();
+
+			// Get correct offsets
+			offset       = this.offset();
+			parentOffset = offsetParent.offset();
+
+			// Subtract element margins
+			offset.top  -= num(elem, 'marginTop');
+			offset.left -= num(elem, 'marginLeft');
+
+			// Add offsetParent borders
+			parentOffset.top  += num(offsetParent, 'borderTopWidth');
+			parentOffset.left += num(offsetParent, 'borderLeftWidth');
+
+			// Subtract the two offsets
+			results = {
+				top:  offset.top  - parentOffset.top,
+				left: offset.left - parentOffset.left
+			};
+		}
+
+		return results;
+	},
+
+	offsetParent: function() {
+		var offsetParent = this[0].offsetParent;
+		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && $.css(offsetParent, 'position') == 'static') )
+			offsetParent = offsetParent.offsetParent;
+		return $(offsetParent);
+	}
+});
+
+var num = function(el, prop) {
+	return parseInt($.css(el.jquery?el[0]:el,prop))||0;
+};
+
+})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.js
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/jquery.dimensions.js	(revision 5057)
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * $LastChangedDate: 2007-09-11 05:38:31 +0300 (Вт, 11 сен 2007) $
+ * $Rev: 3238 $
+ *
+ * Version: @VERSION
+ *
+ * Requires: jQuery 1.2+
+ */
+(function(b){b.dimensions={version:"@VERSION"};b.each(["Height","Width"],function(d,c){b.fn["inner"+c]=function(){if(!this[0]){return}var f=c=="Height"?"Top":"Left",e=c=="Height"?"Bottom":"Right";return this[c.toLowerCase()]()+a(this,"padding"+f)+a(this,"padding"+e)};b.fn["outer"+c]=function(f){if(!this[0]){return}var g=c=="Height"?"Top":"Left",e=c=="Height"?"Bottom":"Right";f=b.extend({margin:false},f||{});return this[c.toLowerCase()]()+a(this,"border"+g+"Width")+a(this,"border"+e+"Width")+a(this,"padding"+g)+a(this,"padding"+e)+(f.margin?(a(this,"margin"+g)+a(this,"margin"+e)):0)}});b.each(["Left","Top"],function(d,c){b.fn["scroll"+c]=function(e){if(!this[0]){return}return e!=undefined?this.each(function(){this==window||this==document?window.scrollTo(c=="Left"?e:b(window)["scrollLeft"](),c=="Top"?e:b(window)["scrollTop"]()):this["scroll"+c]=e}):this[0]==window||this[0]==document?self[(c=="Left"?"pageXOffset":"pageYOffset")]||b.boxModel&&document.documentElement["scroll"+c]||document.body["scroll"+c]:this[0]["scroll"+c]}});b.fn.extend({position:function(){var h=0,g=0,f=this[0],i,c,e,d;if(f){e=this.offsetParent();i=this.offset();c=e.offset();i.top-=a(f,"marginTop");i.left-=a(f,"marginLeft");c.top+=a(e,"borderTopWidth");c.left+=a(e,"borderLeftWidth");d={top:i.top-c.top,left:i.left-c.left}}return d},offsetParent:function(){var c=this[0].offsetParent;while(c&&(!/^body|html$/i.test(c.tagName)&&b.css(c,"position")=="static")){c=c.offsetParent}return b(c)}});var a=function(c,d){return parseInt(b.css(c.jquery?c[0]:c,d))||0}})(jQuery);
Index: /tags/1.5-beta-3/bp-messages/js/autocomplete/license.bgiframe.txt
===================================================================
--- /tags/1.5-beta-3/bp-messages/js/autocomplete/license.bgiframe.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-messages/js/autocomplete/license.bgiframe.txt	(revision 5057)
@@ -0,0 +1,20 @@
+Copyright 2010, Brandon Aaron (http://brandonaaron.net/)
+ 
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+ 
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Index: /tags/1.5-beta-3/bp-settings/bp-settings-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-settings/bp-settings-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-settings/bp-settings-actions.php	(revision 5057)
@@ -0,0 +1,167 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/** General *******************************************************************/
+
+function bp_core_screen_general_settings() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	// Setup private variables
+	$bp_settings_updated = $pass_error = $email_error = $pwd_error = false;
+
+	if ( isset( $_POST['submit'] ) ) {
+
+		// Nonce check
+		check_admin_referer('bp_settings_general');
+
+		// Validate the user again for the current password when making a big change
+		if ( is_super_admin() || ( !empty( $_POST['pwd'] ) && $_POST['pwd'] != '' && wp_check_password( $_POST['pwd'], $bp->displayed_user->userdata->user_pass, $bp->displayed_user->id ) ) ) {
+
+			$update_user = get_userdata( $bp->displayed_user->id );
+
+			// Make sure changing an email address does not already exist
+			if ( $_POST['email'] != '' ) {
+
+				// What is missing from the profile page vs signup - lets double check the goodies
+				$user_email = sanitize_email( esc_html( trim( $_POST['email'] ) ) );
+
+				// Is email valid
+				if ( !is_email( $user_email ) )
+					$email_error = true;
+
+				// Get blocked email domains
+				$limited_email_domains = get_site_option( 'limited_email_domains', 'buddypress' );
+
+				// If blocked email domains exist, see if this is one of them
+				if ( is_array( $limited_email_domains ) && empty( $limited_email_domains ) == false ) {
+					$emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
+
+					if ( in_array( $emaildomain, (array)$limited_email_domains ) == false ) {
+						$email_error = true;
+					}
+				}
+
+				// No errors, and email address doesn't match
+				if ( ( false === $email_error ) && ( $bp->displayed_user->userdata->user_email != $user_email ) ) {
+
+					// We don't want email dupes in the system
+					if ( email_exists( $user_email ) )
+						$email_error = true;
+
+					// Set updated user email to this email address
+					$update_user->user_email = $user_email;
+				}
+			}
+
+			// Password change
+			if ( !empty( $_POST['pass1'] ) && !empty( $_POST['pass2'] ) ) {
+
+				// Password change attempt is successful
+				if ( $_POST['pass1'] == $_POST['pass2'] && !strpos( " " . $_POST['pass1'], "\\" ) ) {
+					$update_user->user_pass = $_POST['pass1'];
+
+				// Password change attempt was unsuccessful
+				} else {
+					$pass_error = true;
+				}
+
+			// One of the password boxes was left empty
+			} else if ( ( empty( $_POST['pass1'] ) && !empty( $_POST['pass2'] ) ) || ( !empty( $_POST['pass1'] ) && empty( $_POST['pass2'] ) ) ) {
+				$pass_error = true;
+
+			// Not a password change attempt so empty the user_pass
+			} else {
+				unset( $update_user->user_pass );
+			}
+
+			// Make sure these changes are in $bp for the current page load
+			if ( ( false === $email_error ) && ( false === $pass_error ) && ( wp_update_user( get_object_vars( $update_user ) ) ) ) {
+				$bp->displayed_user->userdata = bp_core_get_core_userdata( $bp->displayed_user->id );
+				$bp_settings_updated = true;
+			}
+
+		// Password Error
+		} else {
+			$pwd_error = true;
+		}
+
+		// Add user feedback messages
+		if ( empty( $pass_error ) && empty( $pwd_error ) )
+			bp_core_add_message( __( 'Changes saved.', 'buddypress' ), 'success' );
+
+		elseif ( !empty( $pass_error ) )
+			bp_core_add_message( __( 'Your new passwords did not match.', 'buddypress' ), 'error' );
+
+		elseif ( !empty( $pwd_error ) )
+			bp_core_add_message( __( 'Your existing password is incorrect.', 'buddypress' ), 'error' );
+
+		elseif ( !empty( $email_error ) )
+			bp_core_add_message( __( 'Sorry, that email address is already used or is invalid.', 'buddypress' ), 'error' );
+
+		// Execute additional code
+		do_action( 'bp_core_general_settings_after_save' );
+	}
+
+	// Load the template
+	bp_core_load_template( apply_filters( 'bp_core_screen_general_settings', 'members/single/settings/general' ) );
+}
+
+/** Notifications *************************************************************/
+
+function bp_core_screen_notification_settings() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	if ( isset( $_POST['submit'] ) ) {
+		check_admin_referer('bp_settings_notifications');
+
+		if ( isset( $_POST['notifications'] ) ) {
+			foreach ( (array)$_POST['notifications'] as $key => $value ) {
+				if ( $meta_key = bp_get_user_meta_key( $key ) )
+					bp_update_user_meta( (int)$bp->displayed_user->id, $meta_key, $value );
+			}
+		}
+
+		bp_core_add_message( __( 'Changes saved.', 'buddypress' ), 'success' );
+
+		do_action( 'bp_core_notification_settings_after_save' );
+	}
+
+	bp_core_load_template( apply_filters( 'bp_core_screen_notification_settings', 'members/single/settings/notifications' ) );
+}
+
+/** Delete Account ************************************************************/
+
+function bp_core_screen_delete_account() {
+	global $bp;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	if ( isset( $_POST['delete-account-understand'] ) ) {
+		// Nonce check
+		check_admin_referer( 'delete-account' );
+
+		// delete the users account
+		if ( bp_core_delete_account( $bp->displayed_user->id ) ) {
+			bp_core_redirect( home_url() );
+		}
+	}
+
+	// Load the template
+	bp_core_load_template( apply_filters( 'bp_core_screen_delete_account', 'members/single/settings/delete-account' ) );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-settings/bp-settings-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-settings/bp-settings-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-settings/bp-settings-functions.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+?>
Index: /tags/1.5-beta-3/bp-settings/bp-settings-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-settings/bp-settings-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-settings/bp-settings-loader.php	(revision 5057)
@@ -0,0 +1,176 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_Settings_Component extends BP_Component {
+
+	/**
+	 * Start the settings component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'settings',
+			__( 'Settings', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 *
+	 * @global obj $bp
+	 */
+	function includes() {
+		// Files to include
+		$includes = array(
+			'actions',
+			'screens',
+			'template',
+			'functions',
+		);
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_SETTINGS_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_SETTINGS_SLUG' ) )
+			define( 'BP_SETTINGS_SLUG', $this->id );
+
+		// All globals for settings component.
+		$globals = array(
+			'slug'          => BP_SETTINGS_SLUG,
+			'has_directory' => false,
+		);
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add the settings navigation item
+		$main_nav = array(
+			'name'                    => __( 'Settings', 'buddypress' ),
+			'slug'                    => $this->slug,
+			'position'                => 100,
+			'show_for_displayed_user' => bp_core_can_edit_settings(),
+			'screen_function'         => 'bp_core_screen_general_settings',
+			'default_subnav_slug'     => 'general'
+		);
+
+		$settings_link = trailingslashit( $bp->displayed_user->domain . $this->slug );
+
+		// Add General Settings nav item
+		$sub_nav[] = array(
+			'name'            => __( 'General', 'buddypress' ),
+			'slug'            => 'general',
+			'parent_url'      => $settings_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_core_screen_general_settings',
+			'position'        => 10,
+			'user_has_access' => bp_core_can_edit_settings()
+		);
+
+		// Add Notifications nav item
+		$sub_nav[] = array(
+			'name'            => __( 'Notifications', 'buddypress' ),
+			'slug'            => 'notifications',
+			'parent_url'      => $settings_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'bp_core_screen_notification_settings',
+			'position'        => 20,
+			'user_has_access' => bp_core_can_edit_settings()
+		);
+
+		// Add Delete Account nav item
+		if ( !is_super_admin() && empty( $bp->site_options['bp-disable-account-deletion'] ) ) {
+			$sub_nav[] = array(
+				'name'            => __( 'Delete Account', 'buddypress' ),
+				'slug'            => 'delete-account',
+				'parent_url'      => $settings_link,
+				'parent_slug'     => $this->slug,
+				'screen_function' => 'bp_core_screen_delete_account',
+				'position'        => 90,
+				'user_has_access' => bp_is_my_profile()
+			);
+		}
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Setup the logged in user variables
+			$user_domain   = $bp->loggedin_user->domain;
+			$settings_link = trailingslashit( $user_domain . $this->slug );
+
+			// Add main Settings menu
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => __( 'Settings', 'buddypress' ),
+				'href'   => trailingslashit( $settings_link )
+			);
+
+			// General Account
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'General', 'buddypress' ),
+				'href'   => trailingslashit( $settings_link . 'general' )
+			);
+
+			// Notifications
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Notifications', 'buddypress' ),
+				'href'   => trailingslashit( $settings_link . 'notifications' )
+			);
+
+			// Delete Account
+			if ( !is_super_admin() && empty( $bp->site_options['bp-disable-account-deletion'] ) ) {
+				$wp_admin_nav[] = array(
+					'parent' => 'my-account-' . $this->id,
+					'title'  => __( 'Delete Account', 'buddypress' ),
+					'href'   => trailingslashit( $settings_link . 'delete-account' )
+				);
+			}
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+}
+// Create the settingss component
+$bp->settings = new BP_Settings_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-settings/bp-settings-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-settings/bp-settings-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-settings/bp-settings-screens.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+?>
Index: /tags/1.5-beta-3/bp-settings/bp-settings-template.php
===================================================================
--- /tags/1.5-beta-3/bp-settings/bp-settings-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-settings/bp-settings-template.php	(revision 5057)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * BuddyPress Settings Template Functions
+ *
+ * @package BuddyPress
+ * @subpackage Settings Template
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Output the settings component slug
+ *
+ * @package BuddyPress
+ * @subpackage Settings Template
+ * @since 1.5
+ *
+ * @uses bp_get_settings_slug()
+ */
+function bp_settings_slug() {
+	echo bp_get_settings_slug();
+}
+	/**
+	 * Return the settings component slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Settings Template
+	 * @since 1.5
+	 */
+	function bp_get_settings_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_settings_slug', $bp->settings->slug );
+	}
+
+/**
+ * Output the settings component root slug
+ *
+ * @package BuddyPress
+ * @subpackage Settings Template
+ * @since 1.5
+ *
+ * @uses bp_get_settings_root_slug()
+ */
+function bp_settings_root_slug() {
+	echo bp_get_settings_root_slug();
+}
+	/**
+	 * Return the settings component root slug
+	 *
+	 * @package BuddyPress
+	 * @subpackage Settings Template
+	 * @since 1.5
+	 */
+	function bp_get_settings_root_slug() {
+		global $bp;
+		return apply_filters( 'bp_get_settings_root_slug', $bp->settings->root_slug );
+	}
+
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/404.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/404.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/404.php	(revision 5057)
@@ -0,0 +1,19 @@
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder one-column">
+			<?php do_action( 'bp_before_404' ); ?>
+			<div id="post-0" class="post page-404 error404 not-found" role="main">
+				<h2 class="posttitle"><?php _e( "Page not found", 'buddypress' ); ?></h2>
+
+				<p><?php _e( "We're sorry, but we can't find the page that you're looking for. Perhaps searching will help.", 'buddypress' ); ?></p>
+				<?php get_search_form(); ?>
+
+				<?php do_action( 'bp_404' ); ?>
+			</div>
+
+			<?php do_action( 'bp_after_404' ) ?>
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_footer() ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/ajax.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/ajax.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/ajax.php	(revision 5057)
@@ -0,0 +1,665 @@
+<?php
+/***
+ * AJAX Functions
+ *
+ * All of these functions enhance the responsiveness of the user interface in the default
+ * theme by adding AJAX functionality.
+ */
+
+/***
+ * This function looks scarier than it actually is. :)
+ * Each object loop (activity/members/groups/blogs/forums) contains default parameters to
+ * show specific information based on the page we are currently looking at.
+ * The following function will take into account any cookies set in the JS and allow us
+ * to override the parameters sent. That way we can change the results returned without reloading the page.
+ * By using cookies we can also make sure that user settings are retained across page loads.
+ */
+function bp_dtheme_ajax_querystring( $query_string, $object ) {
+	global $bp;
+
+	if ( empty( $object ) )
+		return false;
+
+	/* Set up the cookies passed on this AJAX request. Store a local var to avoid conflicts */
+	if ( !empty( $_POST['cookie'] ) )
+		$_BP_COOKIE = wp_parse_args( str_replace( '; ', '&', urldecode( $_POST['cookie'] ) ) );
+	else
+		$_BP_COOKIE = &$_COOKIE;
+
+	$qs = false;
+
+	/***
+	 * Check if any cookie values are set. If there are then override the default params passed to the
+	 * template loop
+	 */
+	if ( !empty( $_BP_COOKIE['bp-' . $object . '-filter'] ) && '-1' != $_BP_COOKIE['bp-' . $object . '-filter'] ) {
+		$qs[] = 'type=' . $_BP_COOKIE['bp-' . $object . '-filter'];
+		$qs[] = 'action=' . $_BP_COOKIE['bp-' . $object . '-filter']; // Activity stream filtering on action
+	}
+
+	if ( !empty( $_BP_COOKIE['bp-' . $object . '-scope'] ) ) {
+		if ( 'personal' == $_BP_COOKIE['bp-' . $object . '-scope'] ) {
+			$user_id = ( $bp->displayed_user->id ) ? $bp->displayed_user->id : $bp->loggedin_user->id;
+			$qs[] = 'user_id=' . $user_id;
+		}
+		if ( 'all' != $_BP_COOKIE['bp-' . $object . '-scope'] && empty( $bp->displayed_user->id ) && !$bp->is_single_item )
+			$qs[] = 'scope=' . $_BP_COOKIE['bp-' . $object . '-scope']; // Activity stream scope only on activity directory.
+	}
+
+	/* If page and search_terms have been passed via the AJAX post request, use those */
+	if ( !empty( $_POST['page'] ) && '-1' != $_POST['page'] )
+		$qs[] = 'page=' . $_POST['page'];
+
+	$object_search_text = bp_get_search_default_text( $object );
+ 	if ( !empty( $_POST['search_terms'] ) && $object_search_text != $_POST['search_terms'] && 'false' != $_POST['search_terms'] && 'undefined' != $_POST['search_terms'] )
+		$qs[] = 'search_terms=' . $_POST['search_terms'];
+
+	/* Now pass the querystring to override default values. */
+	$query_string = empty( $qs ) ? '' : join( '&', (array)$qs );
+
+	$object_filter = '';
+	if ( isset( $_BP_COOKIE['bp-' . $object . '-filter'] ) )
+		$object_filter = $_BP_COOKIE['bp-' . $object . '-filter'];
+
+	$object_scope = '';
+	if ( isset( $_BP_COOKIE['bp-' . $object . '-scope'] ) )
+		$object_scope = $_BP_COOKIE['bp-' . $object . '-scope'];
+
+	$object_page = '';
+	if ( isset( $_BP_COOKIE['bp-' . $object . '-page'] ) )
+		$object_page = $_BP_COOKIE['bp-' . $object . '-page'];
+
+	$object_search_terms = '';
+	if ( isset( $_BP_COOKIE['bp-' . $object . '-search-terms'] ) )
+		$object_search_terms = $_BP_COOKIE['bp-' . $object . '-search-terms'];
+
+	$object_extras = '';
+	if ( isset( $_BP_COOKIE['bp-' . $object . '-extras'] ) )
+		$object_extras = $_BP_COOKIE['bp-' . $object . '-extras'];
+
+	return apply_filters( 'bp_dtheme_ajax_querystring', $query_string, $object, $object_filter, $object_scope, $object_page, $object_search_terms, $object_extras );
+}
+add_filter( 'bp_ajax_querystring', 'bp_dtheme_ajax_querystring', 10, 2 );
+
+/* This function will simply load the template loop for the current object. On an AJAX request */
+function bp_dtheme_object_template_loader() {
+	$object = esc_attr( $_POST['object'] );
+	locate_template( array( "$object/$object-loop.php" ), true );
+}
+add_action( 'wp_ajax_members_filter', 'bp_dtheme_object_template_loader' );
+add_action( 'wp_ajax_groups_filter', 'bp_dtheme_object_template_loader' );
+add_action( 'wp_ajax_blogs_filter', 'bp_dtheme_object_template_loader' );
+add_action( 'wp_ajax_forums_filter', 'bp_dtheme_object_template_loader' );
+
+// This function will load the activity loop template when activity is requested via AJAX
+function bp_dtheme_activity_template_loader() {
+	global $bp;
+
+	$scope = '';
+	if ( !empty( $_POST['scope'] ) )
+		$scope = $_POST['scope'];
+
+	// We need to calculate and return the feed URL for each scope
+	switch ( $scope ) {
+		case 'friends':
+			$feed_url = $bp->loggedin_user->domain . bp_get_activity_slug() . '/friends/feed/';
+			break;
+		case 'groups':
+			$feed_url = $bp->loggedin_user->domain . bp_get_activity_slug() . '/groups/feed/';
+			break;
+		case 'favorites':
+			$feed_url = $bp->loggedin_user->domain . bp_get_activity_slug() . '/favorites/feed/';
+			break;
+		case 'mentions':
+			$feed_url = $bp->loggedin_user->domain . bp_get_activity_slug() . '/mentions/feed/';
+			bp_activity_clear_new_mentions( $bp->loggedin_user->id );
+			break;
+		default:
+			$feed_url = home_url( bp_get_activity_root_slug() . '/feed/' );
+			break;
+	}
+
+	/* Buffer the loop in the template to a var for JS to spit out. */
+	ob_start();
+	locate_template( array( 'activity/activity-loop.php' ), true );
+	$result['contents'] = ob_get_contents();
+	$result['feed_url'] = apply_filters( 'bp_dtheme_activity_feed_url', $feed_url, $scope );
+	ob_end_clean();
+
+	echo json_encode( $result );
+}
+add_action( 'wp_ajax_activity_widget_filter', 'bp_dtheme_activity_template_loader' );
+add_action( 'wp_ajax_activity_get_older_updates', 'bp_dtheme_activity_template_loader' );
+
+/* AJAX update posting */
+function bp_dtheme_post_update() {
+	global $bp;
+
+	// Check the nonce
+	check_admin_referer( 'post_update', '_wpnonce_post_update' );
+
+	if ( !is_user_logged_in() ) {
+		echo '-1';
+		return false;
+	}
+
+	if ( empty( $_POST['content'] ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'Please enter some content to post.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	$activity_id = 0;
+	if ( empty( $_POST['object'] ) && bp_is_active( 'activity' ) ) {
+		$activity_id = bp_activity_post_update( array( 'content' => $_POST['content'] ) );
+
+	} elseif ( $_POST['object'] == 'groups' ) {
+		if ( !empty( $_POST['item_id'] ) && bp_is_active( 'groups' ) )
+			$activity_id = groups_post_update( array( 'content' => $_POST['content'], 'group_id' => $_POST['item_id'] ) );
+
+	} else {
+		$activity_id = apply_filters( 'bp_activity_custom_update', $_POST['object'], $_POST['item_id'], $_POST['content'] );
+	}
+
+	if ( empty( $activity_id ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'There was a problem posting your update, please try again.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	if ( bp_has_activities ( 'include=' . $activity_id ) ) : ?>
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+			<?php locate_template( array( 'activity/entry.php' ), true ) ?>
+		<?php endwhile; ?>
+	 <?php endif;
+}
+add_action( 'wp_ajax_post_update', 'bp_dtheme_post_update' );
+
+/* AJAX activity comment posting */
+function bp_dtheme_new_activity_comment() {
+	global $bp;
+
+	// Check the nonce
+	check_admin_referer( 'new_activity_comment', '_wpnonce_new_activity_comment' );
+
+	if ( !is_user_logged_in() ) {
+		echo '-1';
+		return false;
+	}
+
+	if ( empty( $_POST['content'] ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'Please do not leave the comment area blank.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	if ( empty( $_POST['form_id'] ) || empty( $_POST['comment_id'] ) || !is_numeric( $_POST['form_id'] ) || !is_numeric( $_POST['comment_id'] ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'There was an error posting that reply, please try again.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	$comment_id = bp_activity_new_comment( array(
+		'activity_id' => $_POST['form_id'],
+		'content'     => $_POST['content'],
+		'parent_id'   => $_POST['comment_id']
+	) );
+
+	if ( !$comment_id ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'There was an error posting that reply, please try again.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	global $activities_template;
+
+	// Load the new activity item into the $activities_template global
+	bp_has_activities( 'display_comments=stream&include=' . $comment_id );
+
+	// Swap the current comment with the activity item we just loaded
+	$activities_template->activity->id              = $activities_template->activities[0]->item_id;
+	$activities_template->activity->current_comment = $activities_template->activities[0];
+
+	$template = locate_template( 'activity/comment.php', false, false );
+
+	// Backward compatibility. In older versions of BP, the markup was
+	// generated in the PHP instead of a template. This ensures that
+	// older themes (which are not children of bp-default and won't
+	// have the new template) will still work.
+	if ( empty( $template ) )
+		$template = BP_PLUGIN_DIR . '/bp-themes/bp-default/activity/comment.php';
+
+	load_template( $template, false );
+
+	unset( $activities_template );
+}
+add_action( 'wp_ajax_new_activity_comment', 'bp_dtheme_new_activity_comment' );
+
+/* AJAX delete an activity */
+function bp_dtheme_delete_activity() {
+	global $bp;
+
+	// Check the nonce
+	check_admin_referer( 'bp_activity_delete_link' );
+
+	if ( !is_user_logged_in() ) {
+		echo '-1';
+		return false;
+	}
+
+	if ( empty( $_POST['id'] ) || !is_numeric( $_POST['id'] ) ) {
+		echo '-1';
+		return false;
+	}
+
+	$activity = new BP_Activity_Activity( (int) $_POST['id'] );
+
+	// Check access
+	if ( empty( $activity->user_id ) || !bp_activity_user_can_delete( $activity ) ) {
+		echo '-1';
+		return false;
+	}
+
+	// Call the action before the delete so plugins can still fetch information about it
+	do_action( 'bp_activity_before_action_delete_activity', $activity->id, $activity->user_id );
+
+	if ( !bp_activity_delete( array( 'id' => $activity->id, 'user_id' => $activity->user_id ) ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'There was a problem when deleting. Please try again.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	do_action( 'bp_activity_action_delete_activity', $activity->id, $activity->user_id );
+
+	return true;
+}
+add_action( 'wp_ajax_delete_activity', 'bp_dtheme_delete_activity' );
+
+/* AJAX delete an activity comment */
+function bp_dtheme_delete_activity_comment() {
+	global $bp;
+
+	/* Check the nonce */
+	check_admin_referer( 'bp_activity_delete_link' );
+
+	if ( !is_user_logged_in() ) {
+		echo '-1';
+		return false;
+	}
+
+	$comment = new BP_Activity_Activity( $_POST['id'] );
+
+	/* Check access */
+	if ( !is_super_admin() && $comment->user_id != $bp->loggedin_user->id )
+		return false;
+
+	if ( empty( $_POST['id'] ) || !is_numeric( $_POST['id'] ) )
+		return false;
+
+	/* Call the action before the delete so plugins can still fetch information about it */
+	do_action( 'bp_activity_before_action_delete_activity', $_POST['id'], $comment->user_id );
+
+	if ( !bp_activity_delete_comment( $comment->item_id, $comment->id ) ) {
+		echo '-1<div id="message" class="error"><p>' . __( 'There was a problem when deleting. Please try again.', 'buddypress' ) . '</p></div>';
+		return false;
+	}
+
+	do_action( 'bp_activity_action_delete_activity', $_POST['id'], $comment->user_id );
+
+	return true;
+}
+add_action( 'wp_ajax_delete_activity_comment', 'bp_dtheme_delete_activity_comment' );
+
+/* AJAX mark an activity as a favorite */
+function bp_dtheme_mark_activity_favorite() {
+	global $bp;
+
+	bp_activity_add_user_favorite( $_POST['id'] );
+	_e( 'Remove Favorite', 'buddypress' );
+}
+add_action( 'wp_ajax_activity_mark_fav', 'bp_dtheme_mark_activity_favorite' );
+
+/* AJAX mark an activity as not a favorite */
+function bp_dtheme_unmark_activity_favorite() {
+	global $bp;
+
+	bp_activity_remove_user_favorite( $_POST['id'] );
+	_e( 'Favorite', 'buddypress' );
+}
+add_action( 'wp_ajax_activity_mark_unfav', 'bp_dtheme_unmark_activity_favorite' );
+
+/**
+ * AJAX handler for Read More link on long activity items
+ *
+ * @package BuddyPress
+ * @since 1.5
+ */
+function bp_dtheme_get_single_activity_content() {
+	$activity_array = bp_activity_get_specific( array(
+		'activity_ids'     => $_POST['activity_id'],
+		'display_comments' => 'stream'
+	) );
+
+	$activity = !empty( $activity_array['activities'][0] ) ? $activity_array['activities'][0] : false;
+
+	if ( !$activity )
+		exit(); // todo: error?
+
+	do_action_ref_array( 'bp_dtheme_get_single_activity_content', array( &$activity ) );
+
+	// Activity content retrieved through AJAX should run through normal filters, but not be truncated
+	remove_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 );
+	$content = apply_filters( 'bp_get_activity_content_body', $activity->content );
+
+	echo $content;
+	exit();
+}
+add_action( 'wp_ajax_get_single_activity_content', 'bp_dtheme_get_single_activity_content' );
+
+/* AJAX invite a friend to a group functionality */
+function bp_dtheme_ajax_invite_user() {
+	global $bp;
+
+	check_ajax_referer( 'groups_invite_uninvite_user' );
+
+	if ( !$_POST['friend_id'] || !$_POST['friend_action'] || !$_POST['group_id'] )
+		return false;
+
+	if ( !bp_groups_user_can_send_invites( $_POST['group_id'] ) )
+		return false;
+
+	if ( !friends_check_friendship( $bp->loggedin_user->id, $_POST['friend_id'] ) )
+		return false;
+
+	if ( 'invite' == $_POST['friend_action'] ) {
+
+		if ( !groups_invite_user( array( 'user_id' => $_POST['friend_id'], 'group_id' => $_POST['group_id'] ) ) )
+			return false;
+
+		$user = new BP_Core_User( $_POST['friend_id'] );
+
+		echo '<li id="uid-' . $user->id . '">';
+		echo $user->avatar_thumb;
+		echo '<h4>' . $user->user_link . '</h4>';
+		echo '<span class="activity">' . esc_attr( $user->last_active ) . '</span>';
+		echo '<div class="action">
+				<a class="button remove" href="' . wp_nonce_url( $bp->loggedin_user->domain . bp_get_groups_slug() . '/' . $_POST['group_id'] . '/invites/remove/' . $user->id, 'groups_invite_uninvite_user' ) . '" id="uid-' . esc_attr( $user->id ) . '">' . __( 'Remove Invite', 'buddypress' ) . '</a>
+			  </div>';
+		echo '</li>';
+
+	} else if ( 'uninvite' == $_POST['friend_action'] ) {
+
+		if ( !groups_uninvite_user( $_POST['friend_id'], $_POST['group_id'] ) )
+			return false;
+
+		return true;
+
+	} else {
+		return false;
+	}
+}
+add_action( 'wp_ajax_groups_invite_user', 'bp_dtheme_ajax_invite_user' );
+
+/* AJAX add/remove a user as a friend when clicking the button */
+function bp_dtheme_ajax_addremove_friend() {
+	global $bp;
+
+	if ( 'is_friend' == BP_Friends_Friendship::check_is_friend( $bp->loggedin_user->id, $_POST['fid'] ) ) {
+
+		check_ajax_referer('friends_remove_friend');
+
+		if ( !friends_remove_friend( $bp->loggedin_user->id, $_POST['fid'] ) ) {
+			echo __("Friendship could not be canceled.", 'buddypress');
+		} else {
+			echo '<a id="friend-' . $_POST['fid'] . '" class="add" rel="add" title="' . __( 'Add Friend', 'buddypress' ) . '" href="' . wp_nonce_url( $bp->loggedin_user->domain . bp_get_friends_slug() . '/add-friend/' . $_POST['fid'], 'friends_add_friend' ) . '">' . __( 'Add Friend', 'buddypress' ) . '</a>';
+		}
+
+	} else if ( 'not_friends' == BP_Friends_Friendship::check_is_friend( $bp->loggedin_user->id, $_POST['fid'] ) ) {
+
+		check_ajax_referer('friends_add_friend');
+
+		if ( !friends_add_friend( $bp->loggedin_user->id, $_POST['fid'] ) ) {
+			echo __("Friendship could not be requested.", 'buddypress');
+		} else {
+			echo '<a href="' . $bp->loggedin_user->domain . bp_get_friends_slug() . '/requests" class="requested">' . __( 'Friendship Requested', 'buddypress' ) . '</a>';
+		}
+	} else {
+		echo __( 'Request Pending', 'buddypress' );
+	}
+
+	return false;
+}
+add_action( 'wp_ajax_addremove_friend', 'bp_dtheme_ajax_addremove_friend' );
+
+/* AJAX accept a user as a friend when clicking the "accept" button */
+function bp_dtheme_ajax_accept_friendship() {
+	check_admin_referer( 'friends_accept_friendship' );
+
+	if ( !friends_accept_friendship( $_POST['id'] ) )
+		echo "-1<div id='message' class='error'><p>" . __( 'There was a problem accepting that request. Please try again.', 'buddypress' ) . '</p></div>';
+
+	return true;
+}
+add_action( 'wp_ajax_accept_friendship', 'bp_dtheme_ajax_accept_friendship' );
+
+/* AJAX reject a user as a friend when clicking the "reject" button */
+function bp_dtheme_ajax_reject_friendship() {
+	check_admin_referer( 'friends_reject_friendship' );
+
+	if ( !friends_reject_friendship( $_POST['id'] ) )
+		echo "-1<div id='message' class='error'><p>" . __( 'There was a problem rejecting that request. Please try again.', 'buddypress' ) . '</p></div>';
+
+	return true;
+}
+add_action( 'wp_ajax_reject_friendship', 'bp_dtheme_ajax_reject_friendship' );
+
+/* AJAX join or leave a group when clicking the "join/leave" button */
+function bp_dtheme_ajax_joinleave_group() {
+	global $bp;
+
+	if ( groups_is_user_banned( $bp->loggedin_user->id, $_POST['gid'] ) )
+		return false;
+
+	if ( !$group = new BP_Groups_Group( $_POST['gid'], false, false ) )
+		return false;
+
+	if ( !groups_is_user_member( $bp->loggedin_user->id, $group->id ) ) {
+
+		if ( 'public' == $group->status ) {
+
+			check_ajax_referer( 'groups_join_group' );
+
+			if ( !groups_join_group( $group->id ) ) {
+				_e( 'Error joining group', 'buddypress' );
+			} else {
+				echo '<a id="group-' . esc_attr( $group->id ) . '" class="leave-group" rel="leave" title="' . __( 'Leave Group', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'leave-group', 'groups_leave_group' ) . '">' . __( 'Leave Group', 'buddypress' ) . '</a>';
+			}
+
+		} else if ( 'private' == $group->status ) {
+
+			check_ajax_referer( 'groups_request_membership' );
+
+			if ( !groups_send_membership_request( $bp->loggedin_user->id, $group->id ) ) {
+				_e( 'Error requesting membership', 'buddypress' );
+			} else {
+				echo '<a id="group-' . esc_attr( $group->id ) . '" class="membership-requested" rel="membership-requested" title="' . __( 'Membership Requested', 'buddypress' ) . '" href="' . bp_get_group_permalink( $group ) . '">' . __( 'Membership Requested', 'buddypress' ) . '</a>';
+			}
+		}
+
+	} else {
+
+		check_ajax_referer( 'groups_leave_group' );
+
+		if ( !groups_leave_group( $group->id ) ) {
+			_e( 'Error leaving group', 'buddypress' );
+		} else {
+			if ( 'public' == $group->status ) {
+				echo '<a id="group-' . esc_attr( $group->id ) . '" class="join-group" rel="join" title="' . __( 'Join Group', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'join', 'groups_join_group' ) . '">' . __( 'Join Group', 'buddypress' ) . '</a>';
+			} else if ( 'private' == $group->status ) {
+				echo '<a id="group-' . esc_attr( $group->id ) . '" class="request-membership" rel="join" title="' . __( 'Request Membership', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'request-membership', 'groups_send_membership_request' ) . '">' . __( 'Request Membership', 'buddypress' ) . '</a>';
+			}
+		}
+	}
+}
+add_action( 'wp_ajax_joinleave_group', 'bp_dtheme_ajax_joinleave_group' );
+
+/* AJAX close and keep closed site wide notices from an admin in the sidebar */
+function bp_dtheme_ajax_close_notice() {
+	global $userdata;
+
+	if ( !isset( $_POST['notice_id'] ) ) {
+		echo "-1<div id='message' class='error'><p>" . __('There was a problem closing the notice.', 'buddypress') . '</p></div>';
+	} else {
+		$notice_ids = bp_get_user_meta( $userdata->ID, 'closed_notices', true );
+
+		$notice_ids[] = (int) $_POST['notice_id'];
+
+		bp_update_user_meta( $userdata->ID, 'closed_notices', $notice_ids );
+	}
+}
+add_action( 'wp_ajax_messages_close_notice', 'bp_dtheme_ajax_close_notice' );
+
+/* AJAX send a private message reply to a thread */
+function bp_dtheme_ajax_messages_send_reply() {
+	global $bp;
+
+	check_ajax_referer( 'messages_send_message' );
+
+	$result = messages_new_message( array( 'thread_id' => $_REQUEST['thread_id'], 'content' => $_REQUEST['content'] ) );
+
+	if ( $result ) { ?>
+		<div class="message-box new-message">
+			<div class="message-metadata">
+				<?php do_action( 'bp_before_message_meta' ) ?>
+				<?php echo bp_loggedin_user_avatar( 'type=thumb&width=30&height=30' ); ?>
+
+				<strong><a href="<?php echo $bp->loggedin_user->domain ?>"><?php echo $bp->loggedin_user->fullname ?></a> <span class="activity"><?php printf( __( 'Sent %s', 'buddypress' ), bp_core_time_since( bp_core_current_time() ) ) ?></span></strong>
+
+				<?php do_action( 'bp_after_message_meta' ) ?>
+			</div>
+
+			<?php do_action( 'bp_before_message_content' ) ?>
+
+			<div class="message-content">
+				<?php echo stripslashes( apply_filters( 'bp_get_the_thread_message_content', $_REQUEST['content'] ) ) ?>
+			</div>
+
+			<?php do_action( 'bp_after_message_content' ) ?>
+
+			<div class="clear"></div>
+		</div>
+	<?php
+	} else {
+		echo "-1<div id='message' class='error'><p>" . __( 'There was a problem sending that reply. Please try again.', 'buddypress' ) . '</p></div>';
+	}
+}
+add_action( 'wp_ajax_messages_send_reply', 'bp_dtheme_ajax_messages_send_reply' );
+
+/* AJAX mark a private message as unread in your inbox */
+function bp_dtheme_ajax_message_markunread() {
+	global $bp;
+
+	if ( !isset($_POST['thread_ids']) ) {
+		echo "-1<div id='message' class='error'><p>" . __('There was a problem marking messages as unread.', 'buddypress' ) . '</p></div>';
+	} else {
+		$thread_ids = explode( ',', $_POST['thread_ids'] );
+
+		for ( $i = 0, $count = count( $thread_ids ); $i < $count; ++$i ) {
+			BP_Messages_Thread::mark_as_unread($thread_ids[$i]);
+		}
+	}
+}
+add_action( 'wp_ajax_messages_markunread', 'bp_dtheme_ajax_message_markunread' );
+
+/* AJAX mark a private message as read in your inbox */
+function bp_dtheme_ajax_message_markread() {
+	global $bp;
+
+	if ( !isset($_POST['thread_ids']) ) {
+		echo "-1<div id='message' class='error'><p>" . __('There was a problem marking messages as read.', 'buddypress' ) . '</p></div>';
+	} else {
+		$thread_ids = explode( ',', $_POST['thread_ids'] );
+
+		for ( $i = 0, $count = count( $thread_ids ); $i < $count; ++$i ) {
+			BP_Messages_Thread::mark_as_read($thread_ids[$i]);
+		}
+	}
+}
+add_action( 'wp_ajax_messages_markread', 'bp_dtheme_ajax_message_markread' );
+
+/* AJAX delete a private message or array of messages in your inbox */
+function bp_dtheme_ajax_messages_delete() {
+	global $bp;
+
+	if ( !isset($_POST['thread_ids']) ) {
+		echo "-1<div id='message' class='error'><p>" . __( 'There was a problem deleting messages.', 'buddypress' ) . '</p></div>';
+	} else {
+		$thread_ids = explode( ',', $_POST['thread_ids'] );
+
+		for ( $i = 0, $count = count( $thread_ids ); $i < $count; ++$i )
+			BP_Messages_Thread::delete($thread_ids[$i]);
+
+		_e( 'Messages deleted.', 'buddypress' );
+	}
+}
+add_action( 'wp_ajax_messages_delete', 'bp_dtheme_ajax_messages_delete' );
+
+/**
+ * bp_dtheme_ajax_messages_autocomplete_results()
+ *
+ * AJAX handler for autocomplete. Displays friends only, unless BP_MESSAGES_AUTOCOMPLETE_ALL is defined
+ *
+ * @global object object $bp Global BuddyPress settings object
+ * @return none
+ */
+function bp_dtheme_ajax_messages_autocomplete_results() {
+	global $bp;
+
+	// Include everyone in the autocomplete, or just friends?
+	if ( $bp->messages->slug == $bp->current_component )
+		$autocomplete_all = $bp->messages->autocomplete_all;
+
+	$friends  = false;
+	$pag_page = 1;
+
+	$limit = $_GET['limit'] ? $_GET['limit'] : apply_filters( 'bp_autocomplete_max_results', 10 );
+
+	// Get the user ids based on the search terms
+	if ( !empty( $autocomplete_all ) ) {
+		$users = BP_Core_User::search_users( $_GET['q'], $limit, $pag_page );
+
+		if ( !empty( $users['users'] ) ) {
+			// Build an array with the correct format
+			$user_ids = array();
+			foreach( $users['users'] as $user ) {
+				if ( $user->id != $bp->loggedin_user->id )
+					$user_ids[] = $user->id;
+			}
+
+			$user_ids = apply_filters( 'bp_core_autocomplete_ids', $user_ids, $_GET['q'], $limit );
+		}
+	} else {
+		if ( bp_is_active( 'friends' ) ) {
+			$users = friends_search_friends( $_GET['q'], $bp->loggedin_user->id, $limit, 1 );
+
+			// Keeping the bp_friends_autocomplete_list filter for backward compatibility
+			$users = apply_filters( 'bp_friends_autocomplete_list', $users, $_GET['q'], $limit );
+
+			if ( !empty( $users['friends'] ) )
+				$user_ids = apply_filters( 'bp_friends_autocomplete_ids', $users['friends'], $_GET['q'], $limit );
+		}
+	}
+
+	if ( !empty( $user_ids ) ) {
+		foreach ( $user_ids as $user_id ) {
+			$ud = get_userdata( $user_id );
+			if ( !$ud )
+				continue;
+
+			if ( bp_is_username_compatibility_mode() )
+				$username = $ud->user_login;
+			else
+				$username = $ud->user_nicename;
+
+			echo '<span id="link-' . $username . '" href="' . bp_core_get_user_domain( $user_id ) . '"></span>' . bp_core_fetch_avatar( array( 'item_id' => $user_id, 'type' => 'thumb', 'width' => 15, 'height' => 15 ) ) . ' &nbsp;' . bp_core_get_user_displayname( $user_id ) . ' (' . $username . ')
+			';
+		}
+	}
+}
+add_action( 'wp_ajax_messages_autocomplete_results', 'bp_dtheme_ajax_messages_autocomplete_results' );
+
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default-rtl.css	(revision 5057)
@@ -0,0 +1,769 @@
+/*--------------------------------------------------------------
+Hello, this is the BuddyPress Default rtl stylesheet.
+Right to left styles. This will transform the theme to read from
+right to left for languages that support this method.
+
+----------------------------------------------------------------
+>>> TABLE OF CONTENTS:
+----------------------------------------------------------------
+1.0 - Structural
+	1.1 - Content
+	1.2 - Header
+	1.3 - Footer
+	1.4 - Sidebar
+2.0 - Non-structural
+	2.1 - Text
+	2.2 - Headers
+	2.3 - Lists
+3.0 - Navigation
+	3.1 - Pagination
+4.0 - WordPress
+	4.1 - Alignments
+	4.2 - Comments
+	4.3 - Gallery
+	4.4 - Images
+	4.5 - Posts
+5.0 - BuddyPress
+	5.1 - Activity
+		5.1.1 - Activity Listing
+		5.1.2 - Activity Comments
+	5.2 - Admin Bar
+	5.3 - Ajax Loading
+	5.4 - Directories - Members, Groups, Blogs, Forums
+	5.5 - Error / Success Messages
+	5.6 - Forms
+	5.7 - Topics and Tables - Forums and General
+	5.8 - Headers, Lists and Tabs - Activity, Groups, Blogs, Forums
+	5.9 - Private Messaging Threads
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+1.0 - Structure
+--------------------------------------------------------------*/
+body {
+	direction: rtl;
+	unicode-bidi: embed;
+}
+body {
+	background: #eaeaea url( ../images/background.gif ) top right repeat-x;
+}
+
+
+/*--------------------------------------------------------------
+1.1 - Content
+--------------------------------------------------------------*/
+div#container {
+	border-left: 1px solid #e0e0e0;
+	border-right: 0;
+}
+div#content {
+	float: right;
+	-moz-border-radius-topleft: 0;
+	-moz-border-radius-topright: 6px;
+	-webkit-border-top-right-radius: 6px;
+	-webkit-border-top-left-radius: 0;
+	-moz-border-radius-bottomleft: 0;
+	-moz-border-radius-bottomright: 6px;
+	-webkit-border-bottom-right-radius: 6px;
+	-webkit-border-bottom-left-radius: 0;
+}
+div#content .padder {
+	margin-left: 225px;
+	margin-right: 0;
+	border-left: 1px solid #e4e4e4;
+	border-right: 0;
+	-moz-border-radius-topright: 6px;
+	-moz-border-radius-topleft: 0;
+	-webkit-border-top-right-radius: 6px;
+	-webkit-border-top-left-radius: 0;
+	-moz-border-radius-bottomright: 6px;
+	-moz-border-radius-bottomleft: 0;
+	-webkit-border-bottom-right-radius: 6px;
+	-webkit-border-bottom-left-radius: 0;
+}
+div#content .left-menu {
+	float: right;
+}
+div#content .main-column {
+	margin-right: 190px;
+	margin-left: 0;
+}
+
+
+/*--------------------------------------------------------------
+1.2 - Header
+--------------------------------------------------------------*/
+#header #search-bar {
+	left: 0;
+	right: auto;
+	text-align: left;
+}
+#header #search-bar input[type=text] {
+	margin-left: 4px;
+	margin-right: 0;
+}
+#header #search-bar input[type=submit] {
+	margin-right: 4px;
+	margin-left: 0;
+}
+#header h1 {
+	float: right;
+	left: auto;
+	right: 20px;
+}
+
+
+/*--------------------------------------------------------------
+1.3 - Footer
+--------------------------------------------------------------*/
+#footer-widget-area div.widget-area > ul {
+	float: right;
+	margin-left: 19px;
+	margin-right: 0;
+}
+#footer-widget-area ul.item-list li img.avatar {
+	margin-right: 15px; /* border is 4px */
+	margin-left: 0;
+}
+
+
+/*--------------------------------------------------------------
+1.4 - Sidebar
+--------------------------------------------------------------*/
+div#sidebar {
+	float: right;
+	margin-right: -226px;
+	margin-left: 0;
+	border-right: 1px solid #e4e4e4;
+	border-left: 0;
+	-moz-border-radius-topleft: 3px;
+	-webkit-border-top-left-radius: 3px;
+	-moz-border-radius-topright: 0;
+	-webkit-border-top-right-radius: 0;
+}
+div#sidebar div#sidebar-me img.avatar {
+	float: right;
+	margin: 0 0 15px 10px;
+}
+div#sidebar ul#bp-nav {
+	clear: right;
+}
+#footer-widget-area h3.widgettitle,
+div#sidebar h3.widgettitle {
+	margin: 25px -19px 10px -20px;
+	clear: right;
+}
+#footer-widget-area ul.item-list img.avatar,
+div#sidebar ul.item-list img.avatar {
+	margin-left: 10px;
+	margin-right: 0;
+}
+#footer-widget-area div.item-options,
+div#sidebar div.item-options {
+	margin: -10px -19px 0 -20px;
+}
+#footer-widget-area div.item-meta,
+#footer-widget-area div.item-content,
+div#sidebar div.item-meta,
+div#sidebar div.item-content {
+	margin-right: 38px;
+	margin-left: 0;
+}
+
+
+/*--------------------------------------------------------------
+2.0 - Non-structural
+--------------------------------------------------------------*/
+p.postmetadata {
+	clear: right;
+}
+
+
+/*--------------------------------------------------------------
+2.1 - Text
+--------------------------------------------------------------*/
+code {
+	font-family: "Monaco", courier, sans-serif;
+}
+blockquote {
+	font-family: sans-serif;
+}
+table th {
+	text-align: right;
+}
+div.author-box,
+div.comment-avatar-box {
+	float: right;
+	margin: 0 0 15px 15px;
+	font-family: sans-serif;
+}
+div.post-content {
+	margin-right: 105px;
+	margin-left: 0;
+}
+p.date,
+p.postmetadata,
+div.comment-meta,
+div.comment-options {
+	font-family: sans-serif;
+}
+
+
+/*--------------------------------------------------------------
+2.2 - Headers
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+2.3 - Lists
+--------------------------------------------------------------*/
+div.page ul,
+div.page ol,
+div.page dl,
+div.post ul,
+div.post ol,
+div.post dl {
+	margin: 0 1.5em 18px 0;
+}
+dl {
+	margin-right: 0;
+}
+
+
+/*--------------------------------------------------------------
+3.0 - Navigation
+--------------------------------------------------------------*/
+#navigation {
+	text-align: left;
+}
+#nav {
+	margin: 40px 0 0 20px;
+}
+#nav li {
+	margin-left: auto;
+	margin-right: 5px;
+}
+#nav li a {
+	text-align: right;
+}
+#nav li li {
+	margin-left: auto;
+	margin-right: 0;
+}
+#nav ul,
+#nav .sfhover ul ul,
+#nav :hover ul ul {
+	left: auto;
+	right: -9999px;
+}
+#nav .sfhover ul,
+#nav :hover ul {
+	left: auto;
+	right: 0;
+}
+#nav .sfhover ul .sfhover > ul,
+#nav :hover ul :hover > ul {
+	left: auto;
+	right: 125px;
+}
+.navigation,
+.paged-navigation,
+.comment-navigation {
+	font-family: sans-serif;
+}
+
+
+/*--------------------------------------------------------------
+3.1 - Pagination
+--------------------------------------------------------------*/
+div.pagination .pag-count {
+	float: right;
+}
+div.pagination .pagination-links {
+	float: left;
+}
+
+
+/*--------------------------------------------------------------
+4.0 - WordPress
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+4.1 - Alignments
+--------------------------------------------------------------*/
+.alignright {
+	float: left;
+}
+.alignleft {
+	float: right;
+}
+.clear {
+	clear: right;
+}
+img.avatar {
+	float: right;
+}
+.accessibly-hidden {
+	left: auto;
+	right: -999em;
+}
+
+
+/*--------------------------------------------------------------
+4.2 - Comments
+--------------------------------------------------------------*/
+.comments {
+	float: left;
+}
+ol.commentlist li.comment {
+	clear: right;
+}
+.commentlist ul.children {
+	margin: 15px 70px 15px 0;
+}
+.commentlist ul.children ul {
+	margin: 0 25px 0 0;
+	padding-left: 0;
+}
+ol.commentlist #respond {
+	clear: right;
+	margin-left: auto;
+	margin-right: 70px;
+}
+.commentlist ul.children #respond {
+	margin-left: 20px;
+	margin-right: 35px;
+}
+ol.commentlist div.comment-avatar-box {
+	float: right;
+	margin: 15px 0 0 15px;
+}
+.commentlist ul.children div.comment-avatar-box {
+	float: right;
+	margin: 0 0 0 10px;
+}
+#comments ol.commentlist ol {
+	margin: 0 2.5em 18px 0;
+}
+#reply-title small {
+	float: left;
+}
+#reply-title small a {
+	margin-right: auto;
+	margin-left: 0;
+}
+#respond .comment-avatar-box {
+	float: right;
+	margin: 0 0 0 15px;
+}
+.acomment-options {
+	float: right;
+	margin-right: 40px;
+	margin-left: auto;
+}
+.comment-options .comment-reply-link,
+.comment-options .comment-edit-link {
+	margin-left: 5px;
+	margin-right: auto;
+}
+
+
+/*--------------------------------------------------------------
+4.3 - Gallery
+--------------------------------------------------------------*/
+dd.wp-caption p.wp-caption-text,
+.wp-caption p.wp-caption-text {
+	padding: 0 0 5px 4px;
+}
+
+
+/*--------------------------------------------------------------
+4.4 - Images
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+4.5 - Posts
+--------------------------------------------------------------*/
+.page .tags,
+.post .tags {
+	float: right;
+}
+
+
+/*--------------------------------------------------------------
+5.0 - BuddyPress
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+5.1 - Activity
+--------------------------------------------------------------*/
+form#whats-new-form h5 {
+	margin-right: 76px;
+	margin-left: 0;
+}
+form#whats-new-form #whats-new-avatar {
+	float: right;
+}
+form#whats-new-form #whats-new-content {
+	margin-right: 54px;
+	padding-right: 22px;
+}
+form#whats-new-form #whats-new-submit {
+	float: left;
+}
+
+
+/*--------------------------------------------------------------
+5.1.1 - Activity Listing
+--------------------------------------------------------------*/
+.activity-header {
+	margin-left: 20px;
+	margin-right: auto;
+}
+.activity-list .activity-avatar {
+	float: right;
+}
+.activity-list li.mini .activity-avatar img.avatar,
+.activity-list li.mini .activity-avatar img.FB_profile_pic {
+	margin-right: 30px;
+	margin-left: 0;
+}
+body.activity-permalink .activity-list li .activity-header > p {
+	margin-left: auto;
+	margin-right: -35px;
+	padding: 5px 35px 0 0;
+}
+.activity-list li.mini .activity-content p {
+	float: right;
+}
+.activity-list li.mini .activity-meta {
+	left: 0;
+	right: auto;
+}
+body.activity-permalink .activity-list li.mini .activity-meta {
+	left: 15px;
+	right: auto;
+}
+.activity-list li.mini .activity-comments {
+	clear: right;
+}
+.activity-list li .activity-inreplyto {
+	margin-right: 5px;
+	margin-left: 0;
+	padding-right: 25px;
+	padding-left: 0;
+	background-position: 100% 0;
+}
+.activity-list .activity-content {
+	margin-right: 70px;
+	margin-left: 0;
+}
+.activity-list li.new_forum_post .activity-content .activity-inner,
+.activity-list li.new_forum_topic .activity-content .activity-inner {
+	border-left: none;
+	margin-left: 0;
+	padding-left: 0;
+	border-right: 2px solid #EAEAEA;
+	margin-right: 5px;
+	padding-right: 10px;
+}
+body.activity-permalink .activity-list li .activity-content {
+	margin-right: 185px;
+	margin-left: 0;
+}
+body.activity-permalink .activity-list li .activity-content > p {
+	background-position: top right;
+	margin-right: -35px;
+	margin-left: 0;
+	padding: 5px 38px 0 0;
+}
+.activity-list .activity-content > p > a:first-child,
+span.highlight,
+.activity-list .activity-content > .comment-header > a:first-child {
+	margin-left: 3px;
+	margin-right: 0;
+}
+.activity-list .activity-content img.thumbnail {
+	float: right;
+	margin: 0 0 5px 10px;
+}
+.activity-list li.load-more {
+	border-left: 1px solid #ddd;
+	border-right: 0;
+}
+a.bp-secondary-action,
+span.highlight {
+	margin-left: 5px;
+	margin-right: auto;
+}
+.activity-list .activity-content .activity-inner,
+.activity-list .activity-content blockquote {
+	margin-left: 10px;
+	margin-right: 0;
+}
+
+
+/*--------------------------------------------------------------
+5.1.2 - Activity Comments
+--------------------------------------------------------------*/
+div.activity-comments {
+	margin: 0 70px 0 0;
+}
+body.activity-permalink div.activity-comments {
+	margin-right: 185px;
+	margin-left: 0;
+}
+div.activity-comments ul {
+	clear: right;
+}
+div.activity-comments > ul {
+	padding: 0 10px 0 0;
+}
+div.activity-comments ul li > ul {
+	margin-left: 0;
+	margin-right: 30px;
+	padding-left: 0;
+	padding-right: 10px;
+}
+div.activity-comments div.acomment-avatar img {
+	float: right;
+	margin-left: 10px;
+	margin-right: 0;
+}
+div.activity-comments div.acomment-content {
+	margin-left: 0;
+	margin-right: 40px;
+}
+div.activity-comments form.ac-form {
+	margin: 10px 33px 10px 0;
+	border-left: 1px solid #ddd;
+	border-right: 0;
+}
+div.activity-comments li form.ac-form {
+	margin-left: 15px;
+	margin-right: 0;
+}
+div.activity-comments form.root {
+	margin-right: 0;
+}
+div.activity-comments form.loading {
+	background-position: 88% 95%;
+}
+div.activity-comments form div.ac-reply-avatar {
+	float: right;
+}
+div.activity-comments form div.ac-reply-content {
+	margin-right: 50px;
+	margin-left: 0;
+	padding-right: 15px;
+	padding-left: 0;
+}
+body.activity-permalink div.activity-comments > ul {
+	padding: 0 15px 0 10px;
+}
+
+
+/*--------------------------------------------------------------
+5.2 - Admin Bar
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+5.3 - Ajax Loading
+--------------------------------------------------------------*/
+a.loading {
+	background-position: 5% 50% !important;
+	padding-left: 25px !important;
+	padding-right: 0 !important;
+}
+
+
+/*--------------------------------------------------------------
+5.4 - Directories - Members, Groups, Blogs, Forums
+--------------------------------------------------------------*/
+div.dir-search {
+	float: left;
+}
+
+
+/*--------------------------------------------------------------
+5.5 - Error / Success Messages
+--------------------------------------------------------------*/
+span.activity,
+div#message p {
+	border-left: 1px solid #FFE8C4;
+	border-right: 0;
+}
+
+
+/*--------------------------------------------------------------
+5.6 - Forms
+--------------------------------------------------------------*/
+.standard-form#signup_form div.submit {
+	float: left;
+}
+.standard-form#signup_form div.signup-avatar {
+	margin-left: 15px;
+	margin-right: 0;
+}
+.standard-form div.submit input {
+	margin-left: 15px;
+	margin-right: 0;
+}
+.standard-form div.radio ul {
+	margin: 10px 38px 15px 0;
+}
+.standard-form #basic-details-section,
+.standard-form #blog-details-section,
+.standard-form #profile-details-section {
+	float: right;
+}
+.standard-form #profile-details-section {
+	float: left;
+}
+.standard-form #blog-details-section {
+	clear: right;
+}
+a.button,
+input[type=submit],
+input[type=button],
+ul.button-nav li a,
+div.generic-button a {
+	background-position: right top;
+}
+div.accept, div.reject {
+	float: right;
+	margin-left: 0;
+	margin-right: 10px;
+}
+ul.button-nav li {
+	float: right;
+	margin: 0 0 10px 10px;
+}
+
+
+/*--------------------------------------------------------------
+5.7 - Topics and Tables - Forums and General
+--------------------------------------------------------------*/
+table tr td.label {
+	border-left: 1px solid #eaeaea;
+	border-right: 0;
+}
+table.forum tr.closed td.td-title {
+	padding-right: 35px;
+	padding-left: 0;
+	background-position: 5% 50%;
+}
+table.forum tr > td:first-child,
+table.forum tr > th:first-child {
+	padding-right: 15px;
+	padding-left: 0;
+}
+table.forum tr > td:last-child,
+table.forum tr > th:last-child {
+	padding-left: 15px;
+	padding-right: 0;
+}
+table.forum tr th#th-title,
+table.forum tr th#th-poster,
+table.forum tr th#th-group,
+table.forum td.td-poster,
+table.forum td.td-group,
+table.forum td.td-title {
+	text-align: right;
+}
+table.forum td img.avatar {
+	margin-left: 5px;
+	margin-right: 0;
+}
+ul#topic-post-list li div.post-content {
+	margin-right: 54px;
+	margin-left: 0;
+}
+div.admin-links {
+	left: 25px;
+	right: auto
+}
+div#topic-meta div.admin-links {
+	left: 0;
+	right: auto;
+}
+
+
+/*--------------------------------------------------------------
+5.8 - Headers, Lists and Tabs - Activity, Groups, Blogs, Forums
+--------------------------------------------------------------*/
+div#item-header img.avatar {
+	float: right;
+	margin: 0 0 25px 15px;
+}
+div#item-header span.activity {
+	margin: 0 4px 5px 0;
+}
+div#item-header div#item-actions {
+	float: left;
+	margin: 0 15px 15px 0;
+	text-align: left;
+}
+div#item-header ul li {
+	float: left;
+}
+div#item-header div.generic-button,
+div#item-header a.button {
+	float: right;
+	margin: 10px 0 0 10px;
+}
+ul.item-list li img.avatar {
+	float: right;
+	margin: 0 0 0 10px;
+}
+ul.item-list li div.item-desc {
+	margin: 10px 64px 0 0;
+}
+ul.item-list li div.action {
+	left: 0;
+	right: auto;
+	text-align: left;
+}
+div.item-list-tabs {
+	clear: right;
+}
+div.item-list-tabs ul li {
+	float: right;
+	margin: 5px 5px 0 0;
+}
+div.item-list-tabs ul li:first-child {
+	margin-left: 0;
+	margin-right: 20px;
+}
+div.item-list-tabs ul li.last {
+	float: left;
+	margin: 7px 0 0 20px;
+}
+ul li.loading a {
+	background-position: 8% 50%;
+	padding-left: 30px !important;
+	padding-right: 0 !important;
+}
+div#item-nav ul li.loading a {
+	background-position: 12% 50%;
+}
+div.item-list-tabs ul li.feed a {
+	background-position: right center;
+	padding-right: 20px;
+	padding-left: 0;
+}
+
+
+/*--------------------------------------------------------------
+5.9 - Private Messaging Threads
+--------------------------------------------------------------*/
+div.messages-options-nav {
+	text-align: left;
+}
+div#message-thread img.avatar {
+	float: right;
+	margin: 0 0 0 10px;
+}
+div#message-thread strong span.activity {
+	margin: 4px 10px 0 0;
+}
+div#message-thread div.message-content {
+	margin-right: 45px;
+	margin-left: 0;
+}
+div#message-thread div.message-options {
+	text-align: left;
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/default.css	(revision 5057)
@@ -0,0 +1,2351 @@
+/*--------------------------------------------------------------
+Hello, this is the BuddyPress Default theme stylesheet.
+
+----------------------------------------------------------------
+>>> TABLE OF CONTENTS:
+----------------------------------------------------------------
+1.0 - Reset - Based on work by Eric Meyer
+2.0 - Structural
+	2.1 - Content
+	2.2 - Header
+	2.3 - Footer
+	2.4 - Sidebar
+3.0 - Non-structural
+	3.1 - Text
+	3.2 - Headers
+	3.3 - Lists
+4.0 - Navigation
+	4.1 - Pagination
+5.0 - WordPress
+	5.1 - Alignments
+	5.2 - Comments
+	5.3 - Gallery
+	5.4 - Images
+	5.5 - Posts
+6.0 - BuddyPress
+	6.1 - Activity
+		6.1.1 - Activity Listing
+		6.1.2 - Activity Comments
+	6.2 - Admin Bar
+	6.3 - Directories - Members, Groups, Blogs, Forums
+	6.4 - Error / Success Messages
+	6.5 - Forms
+	6.6 - Ajax Loading
+	6.7 - Topics and Tables - Forums and General
+	6.8 - Headers, Lists and Tabs - Activity, Groups, Blogs, Forums
+	6.9 - Private Messaging Threads
+--------------------------------------------------------------*/
+
+/*--------------------------------------------------------------
+1.0 - Reset - Reset default browser CSS. Based on work by Eric
+Meyer: v1.0  http://meyerweb.com/eric/tools/css/reset/index.html
+--------------------------------------------------------------*/
+html,body,div,span,applet,object,iframe,
+h1,h2,h3,h4,h5,h6,p,blockquote,pre,
+a,abbr,acronym,address,big,cite,code,
+del,dfn,em,font,img,ins,kbd,q,s,samp,
+small,strike,strong,sub,sup,tt,var,
+b,u,i,center,
+dl,dt,dd,ol,ul,li,
+fieldset,form,label,legend,
+table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;}
+body{background:#fff;line-height:1;}
+ol,ul{list-style:none;}
+blockquote,q{quotes:none;}
+blockquote:before,blockquote:after,
+q:before,q:after{content:'';content:none;}
+:focus{outline:0;}
+ins{text-decoration:none;}
+del{text-decoration:line-through;}
+table{border-collapse:collapse;border-spacing:0;}
+a img{border:none;}
+
+
+/*--------------------------------------------------------------
+2.0 - Structural
+--------------------------------------------------------------*/
+body {
+	background-color: #eaeaea;
+	background-image: url( ../images/background.gif );
+	background-repeat: repeat-x;
+	background-position: top left;
+	color: #555;
+	font-size: 12px;
+	font-family: Arial, Tahoma, Verdana, sans-serif;
+	line-height: 170%;
+	max-width: 1250px;
+	margin: 0 auto;
+	width: 95%;
+	-webkit-text-size-adjust: none;
+}
+.padder {
+	padding: 19px;
+}
+div#container {
+	background: #fff;
+	border-right: 1px solid #e0e0e0;
+	border-bottom: 1px solid #e0e0e0;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	overflow: hidden;
+	position: relative;
+	width: 100%;
+}
+body.activity-permalink div#container {
+	background: none;
+	border: none;
+}
+
+
+/*--------------------------------------------------------------
+2.1 - Content
+--------------------------------------------------------------*/
+div#content {
+	-moz-border-radius-topleft: 6px;
+	-webkit-border-top-left-radius: 6px;
+	-moz-border-radius-bottomleft: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	border-top-left-radius: 6px;
+	border-top-right-radius: 6px;
+	float: left;
+	width: 100%;
+}
+div#content .padder {
+	border-right: 1px solid #ddd;
+	-moz-border-radius-topleft: 6px;
+	-webkit-border-top-left-radius: 6px;
+	-moz-border-radius-bottomleft: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	border-top-left-radius: 6px;
+	border-bottom-left-radius: 6px;
+	margin-right: 225px;
+}
+div#content .one-column {
+	border-right: 0 none;
+	margin-right: 0;
+}
+div#content .left-menu {
+	float: left;
+	width: 170px;
+}
+div#content .main-column {
+	margin-left: 190px;
+}
+
+
+/*--------------------------------------------------------------
+2.2 - Header
+--------------------------------------------------------------*/
+#header {
+	background: #1fb3dd; /* Old browsers */
+	background: -moz-linear-gradient(top, #1fb3dd 0%, #1fb3dd 15%, #394c79 100%); /* FF3.6+ */
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#1fb3dd), color-stop(15%,#1fb3dd), color-stop(100%,#394c79)); /* Chrome,Safari4+ */
+	background: -webkit-linear-gradient(top, #1fb3dd 0%,#1fb3dd 15%,#394c79 100%); /* Chrome10+,Safari5.1+ */
+	background: -o-linear-gradient(top, #1fb3dd 0%,#1fb3dd 15%,#394c79 100%); /* Opera11.10+ */
+	background: -ms-linear-gradient(top, #1fb3dd 0%,#1fb3dd 15%,#394c79 100%); /* IE10+ */
+	background: linear-gradient(top, #1fb3dd 0%,#1fb3dd 15%,#394c79 100%); /* W3C */
+	-moz-border-radius-bottomleft: 6px;
+	-webkit-border-bottom-left-radius: 6px;
+	border-bottom-left-radius: 6px;
+	-moz-border-radius-bottomright: 6px;
+	-webkit-border-bottom-right-radius: 6px;
+	border-bottom-right-radius: 6px;
+	color: #fff;
+	height: auto;
+	margin-bottom: 20px;
+	padding-top: 25px;
+	position: relative;
+	z-index: 1000;
+}
+#header #search-bar {
+	margin-top: 5px;
+	text-align: right;
+	width: 100%;
+}
+#header #search-bar .padder {
+	padding: 10px 15px 0 20px;
+	height: auto;
+}
+#header #search-bar input[type=text] {
+	border: 1px inset #888;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	margin-right: 4px;
+	padding: 2px;
+}
+#header #search-bar input[type=submit] {
+	font-size: 11px;
+	padding: 1px 4px;
+	margin-left: 4px;
+}
+#header h1 {
+	float: left;
+	margin-bottom: 0;
+	margin-top: 0;
+}
+#header h1 a {
+	color: #fff;
+	font-size: 26px;
+	text-decoration: none;
+}
+
+
+/*--------------------------------------------------------------
+2.3 - Footer
+--------------------------------------------------------------*/
+#footer {
+	margin: 20px 0;
+}
+#footer-widgets {
+	background: #fff;
+	border-bottom: 1px solid #e0e0e0;
+	border-right: 1px solid #e0e0e0;
+	-moz-border-radius: 6px;
+	-webkit-border-radius: 6px;
+	border-radius: 6px;
+	padding: 19px;
+}
+#site-generator {
+	color: #4D4D4D;
+	margin: 20px 20px;
+	text-align: center;
+	text-shadow: #fafafa 1px 1px 0;
+}
+#footer-widget-area {
+	margin-top: -19px;
+	overflow: hidden;
+}
+#footer-widget-area div.widget-area > ul {
+	float: left;
+	margin-right: 2.5%;
+	width: 23%;
+}
+#footer-widget-area div.widget-area > ul > li {
+	margin-top: 19px;
+}
+#footer-widget-area h3.widgettitle {
+	background: #eaeaea;
+	clear: left;
+	padding: 5px 15px;
+	font-size: 12px;
+	margin: 0 0 10px 0;
+}
+#footer-widget-area div div.item-options {
+	margin-left: 0;
+	margin-right: 0;
+}
+#footer-widget-area ul.item-list li img.avatar {
+	margin-left: 15px; /* border is 4px */
+}
+div#fourth ul.xoxo { margin-right: 0; }
+
+
+/*--------------------------------------------------------------
+2.4 - Sidebar
+--------------------------------------------------------------*/
+div#sidebar {
+	border-left: 1px solid #ddd;
+	-moz-border-radius-topright: 3px;
+	-webkit-border-top-right-radius: 3px;
+	border-top-right-radius: 3px;
+	float: left;
+	margin-left: -226px;
+	margin-top: 1px;
+	width: 224px;
+}
+div#sidebar div#sidebar-me img.avatar {
+	float: left;
+	margin: 0 10px 15px 0;
+}
+div#sidebar div#sidebar-me h4 {
+	font-size: 16px;
+	margin: 0 0 8px 0;
+	font-weight: normal;
+}
+div#sidebar ul#bp-nav {
+	clear: left;
+	margin: 15px -16px;
+}
+div#sidebar ul#bp-nav li {
+	padding: 10px 15px;
+}
+div#sidebar h3.widgettitle {
+	background: #eaeaea;
+	clear: left;
+	font-size: 12px;
+	margin: 25px -20px 10px -19px;
+	padding: 5px 15px;
+}
+#footer-widget-area .widget_search,
+div#sidebar .widget_search {
+	margin-top: 20px;
+}
+#footer-widget-area .widget_search input[type=text],
+div#sidebar .widget_search input[type=text] {
+	padding: 2px;
+	width: 110px;
+}
+#footer-widget-area ul.item-list img.avatar,
+div#sidebar ul.item-list img.avatar {
+	height: 20px;
+	margin-right: 10px;
+	width: 20px;
+}
+#footer-widget-area div.item-avatar img,
+div#sidebar div.item-avatar img {
+	height: 40px;
+	margin: 1px;
+	width: 40px;
+}
+#footer-widget-area .avatar-block,
+div#sidebar .avatar-block {
+	overflow: hidden;
+}
+#footer-widget-area ul.item-list div.item-title,
+div#sidebar ul.item-list div.item-title {
+	font-size: 12px;
+}
+#footer-widget-area div.item-options,
+div#sidebar div.item-options {
+	background: #f5f5f5;
+	font-size: 11px;
+	margin: -10px -20px 0 -19px;
+	padding: 5px 15px;
+}
+#footer-widget-area div.item-content {
+	margin-left: 38px;
+}
+#footer-widget-area div.item-meta,
+div#sidebar div.item-meta,
+div#sidebar div.item-content {
+	font-size: 11px;
+	margin-left: 38px;
+}
+#footer-widget-area div.tags div#tag-text,
+div#sidebar div.tags div#tag-text {
+	font-size: 1.4em;
+	line-height: 140%;
+	padding: 10px 0 15px;
+}
+
+
+/*--------------------------------------------------------------
+3.0 - Non-structural
+--------------------------------------------------------------*/
+p {
+	margin-bottom: 15px;
+}
+p:last-child {
+	margin-bottom: 0;
+}
+.accessibly-hidden {
+	left: -999em;
+	position: absolute;
+}
+hr {
+	background-color: #e7e7e7;
+	border: 0 none;
+	clear: both;
+	height: 1px;
+	margin: 20px 0;
+}
+textarea {
+	resize: vertical;
+}
+
+
+/*--------------------------------------------------------------
+3.1 - Text
+--------------------------------------------------------------*/
+big {
+	font-size: 18px;
+}
+del {
+	text-decoration: line-through;
+}
+ins {
+	background: #fff9db;
+	text-decoration: none;
+}
+sub {
+	top: .5ex;
+}
+sup {
+	bottom: 1ex;
+}
+sub,
+sup {
+	height: 0;
+	line-height: 1;
+	position: relative;
+	vertical-align: baseline;
+}
+pre, blockquote {
+	margin-bottom: 20px;
+}
+pre,
+code p {
+	background: #f4f4f4;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	padding: 15px;
+	overflow: auto;
+}
+code {
+	font-family: "Monaco", courier, sans-serif;
+}
+blockquote {
+	font-family: georgia, times, serif;
+	font-size: 16px;
+	font-style: italic;
+	line-height: 150%;
+	padding: 0 3em;
+	quotes: none;
+}
+blockquote p {
+	margin-bottom: 15px !important;
+}
+
+
+/*--------------------------------------------------------------
+3.2 - Headers
+--------------------------------------------------------------*/
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+	margin: 5px 0 15px 0;
+}
+h1 {
+	font-size: 28px;
+	margin-bottom: 25px;
+}
+h2 {
+	font-size: 24px;
+	margin-bottom: 20px;
+}
+h3 {
+	font-size: 20px;
+}
+h4 {
+	font-size: 16px;
+	margin-bottom: 15px;
+}
+h5 {
+	font-size: 14px;
+	margin-bottom: 0;
+}
+h6 {
+	font-size: 12px;
+	margin-bottom: 0;
+}
+
+
+/*--------------------------------------------------------------
+3.3 - Lists
+--------------------------------------------------------------*/
+div.page ul,
+div.page ol,
+div.page dl,
+div.post ul,
+div.post ol,
+div.post dl {
+	margin: 0 0 18px 1.5em;
+}
+div.page ul,
+div.post ul {
+	list-style: square;
+}
+div.page ol,
+div.post ol {
+	list-style: decimal;
+}
+div.page ol ol,
+div.post ol ol {
+	list-style: lower-alpha;
+}
+div.page ol ol ol,
+div.post ol ol ol {
+	list-style: lower-roman;
+}
+dl {
+	margin-left: 0;
+}
+dt {
+	font-size: 14px;
+	font-weight: bold;
+}
+dd {
+	margin: 0 0 15px 0;
+}
+div.post ul ul,
+div.post ol ol,
+div.post ol ul,
+div.post ul ol,
+div.page ul ul,
+div.page ol ol,
+div.page ol ul,
+div.page ul ol {
+	margin-bottom: 0px;
+}
+
+
+/*--------------------------------------------------------------
+4.0 - Navigation
+--------------------------------------------------------------*/
+a {
+	color: #1fb3dd;
+}
+a:hover,
+a:active {
+	color: #888;
+}
+a:focus {
+	outline: 1px dotted #ccc;
+}
+#navigation {
+	position: relative;
+	text-align: right;
+	width: 100%;
+}
+#nav {
+	margin: 40px 20px 0 0;
+	list-style: none;
+	padding: 0;
+	position: relative;
+	right: auto;
+	bottom: 0;
+}
+#nav li a {
+	background: url( ../images/60pc_black.png );
+	color: #fff;
+	display: block;
+	padding: 5px 15px;
+	text-decoration: none;
+	text-align: left;
+}
+#nav li {
+	margin-left: 5px;
+	display: inline-block;
+}
+#nav li li {
+	margin-left: 0;
+}
+#nav > li > a {
+	-moz-border-radius-topleft: 3px;
+	-webkit-border-top-left-radius: 3px;
+	-moz-border-radius-topright: 3px;
+	-webkit-border-top-right-radius: 3px;
+	border-top-left-radius: 3px;
+	border-top-right-radius: 3px;
+}
+#nav li.selected a,
+#nav > li.current-menu-item a,
+#nav > li.current_page_item a,
+#nav > li.current_page_ancestor a {
+	background: #f5f5f5;
+	color: #4D4D4D;
+}
+#nav > li > ul {
+	border-top-width: 0;
+}
+#nav > ul li.sfhover a,
+#nav > ul li:hover a {
+	background: #333;
+	color: #fff;
+}
+#nav li.sfhover,
+#nav li:hover {
+	position: relative;
+	z-index: 1000;
+}
+#nav ul {
+	border: 1px solid #222;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	-moz-border-radius-topleft: 0;
+	-moz-border-radius-topright: 0;
+	-webkit-border-top-left-radius: 0;
+	-webkit-border-top-right-radius: 0;
+	border-top-left-radius: 0;
+	border-top-right-radius: 0;
+	-moz-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+	-webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+	box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+#nav ul li ul {
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+#nav ul,
+#nav .sfhover ul ul,
+#nav :hover ul ul {
+	left: -9999px;
+	position: absolute;
+}
+#nav .sfhover ul,
+#nav :hover ul {
+	left: 0;
+	top: 100%;
+	width: 128px;
+}
+#nav .sfhover ul li > a,
+#nav :hover ul li > a {
+	background: #333;
+	color: #fff;
+}
+#nav .sfhover ul .sfhover > ul,
+#nav :hover ul :hover > ul {
+	left: 125px;
+	top: -1px;
+	width: 128px;
+}
+#nav ul li.sfhover > a,
+#nav ul li:hover > a {
+	background-color: #222;
+	color: #fff;
+}
+#nav .sfhover ul li,
+#nav :hover ul li {
+	height: 30px;
+	line-height: 30px;
+	text-indent: 10px;
+	width: 128px;
+}
+#nav .sfhover ul li a,
+#nav :hover ul li a {
+	padding: 0 0;
+}
+
+
+/*--------------------------------------------------------------
+4.1 - Pagination
+--------------------------------------------------------------*/
+div.pagination {
+	background: #f4f4f4;
+	border: none;
+	color: #888;
+	font-size: 11px;
+	height: 19px;
+	margin: -20px -19px 0px -19px;
+	padding: 8px 20px;
+	position: relative;
+}
+div.pagination .pag-count {
+	float: left;
+}
+div.pagination .pagination-links {
+	float: right;
+}
+div.pagination .pagination-links span,
+div.pagination .pagination-links a {
+	font-size: 12px;
+	padding: 0 5px;
+}
+div.pagination .pagination-links a:hover {
+	font-weight: bold;
+}
+div#pag-bottom {
+	margin-top: -1px;
+}
+#nav-above {
+	display: none;
+}
+.paged #nav-above {
+	display: block;
+}
+
+
+/*--------------------------------------------------------------
+5.0 - WordPress
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+5.1 - Alignments
+--------------------------------------------------------------*/
+.alignright {
+	float: right;
+	margin-left: 15px;
+}
+.alignleft {
+	float: left;
+	margin-right: 15px;
+}
+.aligncenter {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+}
+.clear {
+	clear: left;
+}
+
+
+/*--------------------------------------------------------------
+5.2 - Comments
+--------------------------------------------------------------*/
+.navigation,
+.paged-navigation,
+.comment-navigation {
+	overflow: hidden;
+	font-family: georgia, times, serif;
+	font-size: 14px;
+	font-style: italic;
+	margin: 5px 0 25px 0;
+	padding: 5px 0;
+}
+.comments {
+	float: right;
+}
+#trackbacks {
+	margin-top: 30px;
+}
+.commentlist .bypostauthor {
+}
+#comments {
+	margin-top: 30px;
+}
+#comments ol.commentlist {
+	border-bottom: 1px solid #e4e4e4;
+	margin: 0 0 30px 0;
+}
+#comments ol.commentlist ol {
+	list-style-type: decimal;
+	margin: 0 0 18px 2.5em;
+}
+#comments ol.commentlist ol ol {
+	list-style: lower-alpha;
+	margin-bottom: 0px;
+}
+#comments ol.commentlist ol ol ol {
+	list-style: lower-roman;
+}
+ol.commentlist li.comment {
+	border-top: 1px solid #e4e4e4;
+	clear: left;
+	list-style: none;
+	margin-bottom: 15px;
+}
+ol.commentlist ul.children li {
+	margin-bottom: 0;
+}
+ol.commentlist div.comment-avatar-box {
+	float: left;
+	margin: 15px 15px 0 0;
+}
+.commentlist ul.children div.comment-avatar-box {
+	float: left;
+	margin: 0 10px 0 0;
+}
+div.comment-avatar-box img {
+	border: 2px solid #eee;
+}
+div.comment-content {
+	overflow: hidden;
+}
+ul.children .comment-entry {
+	margin-bottom: 10px;
+}
+div.comment-meta {
+	color: #888;
+	font-size: 11px;
+	margin: 15px 0;
+	width: 100%;
+}
+div.comment-meta em {
+	font-style: normal;
+}
+#reply-title {
+	margin-top: 0;
+}
+#reply-title small {
+	float: right;
+	font-size: 11px;
+	font-weight: normal;
+}
+#reply-title small a {
+	margin-right: 0;
+}
+.commentlist ul.children {
+	background: #f5f5f5;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	margin: 15px 0 15px 70px;
+	padding: 0 10px;
+}
+.commentlist ul.children ul {
+	margin: 0 0 0 25px;
+	padding-right: 0;
+}
+.commentlist ul.children img.avatar {
+	border-style: none;
+	height: 25px;
+	margin: 0;
+	width: 25px;
+}
+.commentlist ul.children div.comment-meta {
+	font-size: 11px;
+	margin: 0 0 10px 0;
+}
+.commentlist ul.children li {
+	border-top: 2px solid #fffeff;
+	padding-top: 10px;
+}
+.commentlist ul.children li:first-child {
+	border-top: none;
+}
+.commentlist ul.children ul li:first-child {
+	border-top: 2px solid #fffeff;
+}
+#respond {
+	background-color: #fafafa;
+	border: 1px solid #e5e5e5;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 15px;
+}
+#respond .avb img {
+	float: inherit;
+}
+#respond .comment-avatar-box {
+	float: left;
+	margin: 0 15px 0 0;
+}
+#respond .form-submit {
+	margin-bottom: 0;
+}
+ol.commentlist #respond {
+	clear: left;
+	margin-left: 70px;
+}
+.commentlist ul.children #respond {
+	margin-bottom: 10px;
+	margin-left: 35px;
+	margin-right: 20px;
+}
+h3#reply .comments-header {
+	font-size: 14px;
+}
+#respond div.comment-content {
+	border-style: none;
+}
+#nav-below {
+	margin: 0 0;
+	padding: 0 0;
+}
+#nav-above {
+	margin: 0 0 15px;
+	padding: 0;
+}
+.comment-options {
+	margin: 10px 0 2px;
+}
+.comment-options .comment-reply-link,
+.comment-options .comment-edit-link {
+	font: normal 11px/20px Arial, Tahoma, Verdana, sans-serif;
+	margin-right: 5px;
+	padding: 4px 8px;
+}
+.commentlist ul.children div.comment-options {
+	margin-bottom: 10px;
+}
+
+
+/*--------------------------------------------------------------
+5.3 - Gallery
+--------------------------------------------------------------*/
+.wp-caption {
+	background-color: #f3f3f3;
+	border: 1px solid #ddd;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	margin-bottom: 15px;
+	padding-top: 4px;
+	text-align: center;
+
+}
+.wp-caption img {
+	border: 0 none;
+	margin: 0;
+	padding: 0;
+
+}
+dd.wp-caption p.wp-caption-text,
+.wp-caption p.wp-caption-text {
+	font-size: 0.9em;
+	line-height: 17px;
+	margin: 0;
+	padding: 5px 4px 5px 0;
+}
+#content .gallery {
+	margin: 0 auto 15px;
+}
+#content .gallery .gallery-item {
+	margin-bottom: 0;
+	margin-left: 0;
+}
+.gallery-item img {
+	margin-bottom: 15px;
+}
+.gallery .gallery-caption {
+	color: #555;
+}
+
+
+/*--------------------------------------------------------------
+5.4 - Images
+--------------------------------------------------------------*/
+img.avatar {
+	border: 2px solid #eee;
+	float: left;
+}
+img.wp-smiley {
+	border: none !important;
+	clear: none !important;
+	float: none !important;
+	margin: 0 !important;
+	padding: 0 !important;
+}
+img.centered,
+img.aligncenter {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+}
+img.alignright {
+	display: inline;
+	margin: 0 0 2px 7px;
+	padding: 4px;
+}
+img.alignleft {
+	display: inline;
+	margin: 0 7px 2px 0;
+	padding: 4px;
+}
+
+
+/*--------------------------------------------------------------
+5.5 - Posts
+--------------------------------------------------------------*/
+div.page,
+div.post,
+div.attachment {
+	margin: 0 0 15px 0;
+	overflow: hidden;
+}
+div.page:last-child,
+div.post:last-child,
+#item-body:last-child,
+#trackbacklist {
+	margin-bottom: 0;
+}
+h2.posttitle {
+	line-height: 120%;
+	margin: 0;
+}
+h2.pagetitle {
+	line-height: 120%;
+}
+h2.pagetitle a,
+h2.posttitle a {
+	color: #666;
+	text-decoration: none;
+}
+.edit-link,
+.page-link {
+	clear: both;
+	font-weight: bold;
+}
+div.post table,
+div.page table {
+	border: 1px solid #eee;
+	border-collapse: collapse;
+	border-spacing: 0;
+	margin-bottom: 15px;
+}
+div.post table th,
+div.page table th {
+	border-top: 1px solid #eee;
+	text-align: left;
+}
+div.post table td,
+div.page table td {
+	border-top: 1px solid #eee;
+}
+div.author-box {
+	background: #f0f0f0;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	float: left;
+	font-family: georgia, times, serif;
+	font-style: italic;
+	margin: 0 15px 15px 0;
+	padding: 10px;
+	text-align: center;
+	width: 70px;
+}
+div.author-box p {
+	word-wrap: break-word;
+}
+div.author-box p,
+div.comment-avatar-box p {
+	margin: 5px 0 0;
+}
+div.author-box a,
+div.comment-avatar-box a {
+	text-decoration: none;
+}
+div.author-box img {
+	float: none;
+	border: 4px solid #fff;
+	margin: 0;
+}
+div.post-content {
+	margin-left: 105px;
+}
+p.date span:first-child {
+	font-style: italic;
+}
+div.post .entry {
+	margin-bottom: 15px;
+}
+p.date,
+p.postmetadata {
+	border-bottom: 1px solid #e4e4e4;
+	border-top: 1px solid #e4e4e4;
+	color: #888;
+	font-family: Georgia, times, serif;
+	font-size: 12px;
+	margin: 10px 0;
+	padding: 3px 0;
+}
+p.postmetadata {
+	clear: left;
+	overflow: hidden;
+}
+.page .tags,
+.post .tags {
+	float: left;
+}
+span.sticky-post {
+	font-style: normal;
+}
+
+
+/*--------------------------------------------------------------
+6.0 - BuddyPress
+--------------------------------------------------------------*/
+/*--------------------------------------------------------------
+6.1 - Activity
+--------------------------------------------------------------*/
+#activity-stream {
+	margin-top: -5px;
+}
+#item-body form#whats-new-form {
+	border-bottom: 1px solid #ddd;
+	margin: 20px 0 10px;
+	padding-bottom: 20px;
+}
+.home-page form#whats-new-form {
+	border-bottom: none;
+	padding-bottom: 0;
+}
+form#whats-new-form h5 {
+	font-weight: normal;
+	font-size: 12px;
+	color: #888;
+	margin: 0;
+	margin-left: 76px;
+	padding: 0 0 3px 0;
+}
+form#whats-new-form #whats-new-avatar {
+	float: left;
+}
+form#whats-new-form #whats-new-content {
+	margin-left: 54px;
+	padding-left: 22px;
+}
+form#whats-new-form textarea {
+	background: #fff;
+	border: 1px inset #ccc;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #555;
+	font-family: inherit;
+	font-size: 14px;
+	height: 20px;
+	padding: 6px;
+	width: 98%;
+}
+form#whats-new-form #whats-new-options select {
+	max-width: 200px;
+	margin-top: 12px;
+}
+form#whats-new-form #whats-new-submit {
+	float: right;
+	margin-top: 12px;
+}
+#whats-new-options {
+	overflow: auto;
+	height: 0;
+}
+#whats-new:focus {
+	border-color: rgba(31, 179, 221, 0.9) !important;
+	outline-color: rgba(31, 179, 221, 0.9);
+	box-shadow: 0 0 7px         rgba(31, 179, 221, 0.7);
+	-moz-box-shadow: 0 0 7px    rgba(31, 179, 221, 0.7);
+	-webkit-box-shadow: 0 0 7px rgba(31, 179, 221, 0.7);
+}
+
+/*--------------------------------------------------------------
+6.1.1 - Activity Listing
+--------------------------------------------------------------*/
+ul.activity-list li {
+	overflow: hidden;
+	padding: 15px 0 0;
+}
+.activity-list .activity-avatar {
+	float: left;
+}
+ul.activity-list > li:first-child {
+	padding-top: 5px;
+}
+ul.item-list.activity-list li.has-comments {
+	padding-bottom: 15px;
+}
+body.activity-permalink ul.activity-list li.has-comments {
+	padding-bottom: 0;
+}
+.activity-list li.mini {
+	font-size: 11px;
+	min-height: 35px;
+	padding: 15px 0 0 0;
+	position: relative;
+}
+.activity-list li.mini .activity-avatar img.avatar,
+.activity-list li.mini .activity-avatar img.FB_profile_pic {
+	height: 20px;
+	margin-left: 30px;
+	width: 20px;
+}
+.activity-permalink .activity-list li.mini .activity-avatar img.avatar,
+.activity-permalink .activity-list li.mini .activity-avatar img.FB_profile_pic {
+	height: auto;
+	margin-left: 0;
+	width: auto;
+}
+body.activity-permalink .activity-list > li:first-child {
+	padding-top: 0;
+}
+.activity-list li .activity-content {
+	position: relative;
+}
+.activity-list li.mini .activity-content p {
+	margin: 0;
+}
+.activity-list li.mini .activity-comments {
+	clear: both;
+	font-size: 12px;
+}
+body.activity-permalink li.mini .activity-meta {
+	margin-top: 4px;
+}
+.activity-list li .activity-inreplyto {
+	background: url( ../images/replyto_arrow.gif ) 7px 0 no-repeat;
+	color: #888;
+	font-size: 11px;
+	margin-left: 5px;
+	margin-top: 5px;
+	padding-left: 25px;
+}
+.activity-list li .activity-inreplyto > p {
+	margin: 0;
+	display: inline;
+}
+.activity-list li .activity-inreplyto blockquote,
+.activity-list li .activity-inreplyto div.activity-inner {
+	background: none;
+	border: none;
+	display: inline;
+	margin: 0;
+	overflow: hidden;
+	padding: 0;
+}
+.activity-list .activity-content {
+	margin-left: 70px;
+	margin-bottom: 15px;
+}
+body.activity-permalink .activity-list li .activity-content {
+	background: #fff;
+	border-bottom: 1px solid #ddd;
+	border-right: 1px solid #ddd;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	font-size: 16px;
+	line-height: 150%;
+	min-height: 35px;
+	margin-left: 185px;
+	margin-right: 0;
+	padding: 15px;
+}
+body.activity-permalink .activity-list li .activity-header > p {
+	background: url( ../images/activity_arrow.gif ) top left no-repeat;
+	height: 35px;
+	margin-bottom: 0;
+	margin-left: -35px;
+	padding: 5px 0 0 35px;
+}
+.activity-list .activity-content .activity-header,
+.activity-list .activity-content .comment-header {
+	color: #888;
+	font-size: 11px;
+	line-height: 220%;
+}
+.activity-header {
+	margin-right: 20px;
+}
+.activity-header a,
+.comment-meta a,
+.acomment-meta a {
+	text-decoration: none;
+}
+.activity-list .activity-content .activity-header img.avatar {
+	float: none !important;
+	margin: 0 5px -8px 0 !important;
+}
+a.bp-secondary-action,
+span.highlight {
+	font-size: 11px;
+	padding: 0;
+	margin-right: 5px;
+	text-decoration: none;
+}
+.activity-list .activity-content .activity-inner,
+.activity-list .activity-content blockquote {
+	margin: 10px 10px 5px 0;
+	overflow: hidden;
+}
+.activity-list li.new_forum_post .activity-content .activity-inner,
+.activity-list li.new_forum_topic .activity-content .activity-inner {
+	border-left: 2px solid #EAEAEA;
+	margin-left: 5px;
+	padding-left: 10px;
+}
+body.activity-permalink .activity-content .activity-inner,
+body.activity-permalink .activity-content blockquote {
+	margin-left: 0;
+	margin-top: 5px;
+}
+.activity-inner > p {
+	word-wrap: break-word;
+}
+.activity-inner > .activity-inner {
+	margin: 0 !important;
+}
+.activity-inner > blockquote {
+	margin: 0 !important;
+}
+.activity-list .activity-content img.thumbnail {
+	border: 2px solid #eee;
+	float: left;
+	margin: 0 10px 5px 0;
+}
+.activity-read-more {
+	margin-left: 1em;
+	white-space: nowrap;
+}
+.activity-list li.load-more {
+	background: #f0f0f0 !important;
+	border-right: 1px solid #ddd;
+	border-bottom: 1px solid #ddd;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	font-size: 1.2em;
+	margin: 15px 0 !important;
+	padding: 10px 15px !important;
+	text-align: center;
+}
+.activity-list li.load-more a {
+	color: #4D4D4D;
+}
+
+
+/*--------------------------------------------------------------
+6.1.2 - Activity Comments
+--------------------------------------------------------------*/
+div.activity-meta {
+	margin: 18px 0;
+}
+body.activity-permalink div.activity-meta {
+	margin-bottom: 6px;
+}
+div.activity-meta a {
+	font: normal 11px/20px Arial, Tahoma, Verdana, sans-serif;
+	padding: 4px 8px;
+}
+a.activity-time-since {
+	color: #aaa;
+	text-decoration: none;
+}
+a.activity-time-since:hover {
+	color: #888;
+	text-decoration: underline;
+}
+a.bp-primary-action,
+#reply-title small a {
+	font-size: 11px;
+	margin-right: 5px;
+	text-decoration: none;
+}
+a.bp-primary-action span,
+#reply-title small a span {
+	background: #999;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #fff;
+	font-size: 90%;
+	margin-left: 2px;
+	padding: 0 5px;
+}
+a.bp-primary-action:hover span,
+#reply-title small a:hover span {
+	background: #555;
+	color: #fff;
+}
+div.activity-comments {
+	margin: 0 0 0 70px;
+	overflow: hidden; /* IE fix */
+	position: relative;
+	width: auto;
+	clear: both;
+}
+body.activity-permalink div.activity-comments {
+	background: none;
+	margin-left: 185px;
+	width: auto;
+}
+div.activity-comments > ul {
+	background: #f5f5f5;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	padding: 0 0 0 10px;
+}
+div.activity-comments ul,
+div.activity-comments ul li {
+	border: none;
+	list-style: none;
+}
+div.activity-comments ul {
+	clear: both;
+}
+div.activity-comments ul li {
+	border-top: 2px solid #fff;
+	padding: 10px 0 0;
+}
+body.activity-permalink .activity-list li.mini .activity-comments {
+	clear: none;
+	margin-top: 0;
+}
+body.activity-permalink div.activity-comments ul li {
+	border-width: 1px;
+	padding: 10px 0 0 0;
+}
+div.activity-comments > ul > li:first-child {
+	border-top: none;
+}
+div.activity-comments ul li:last-child {
+	margin-bottom: 0;
+}
+div.activity-comments ul li > ul {
+	margin-left: 30px;
+	margin-top: 0;
+	padding-left: 10px;
+}
+body.activity-permalink div.activity-comments ul li > ul {
+	margin-top: 10px;
+}
+body.activity-permalink div.activity-comments > ul {
+	padding: 0 10px 0 15px;
+}
+div.activity-comments div.acomment-avatar img {
+	border-width: 2px !important;
+	float: left;
+	height: 25px;
+	margin-right: 10px;
+	width: 25px;
+}
+div.activity-comments div.acomment-content {
+	font-size: 11px;
+	margin: 5px 0 0 40px;
+}
+div.acomment-content .time-since,
+div.acomment-content .activity-delete-link,
+div.acomment-content .comment-header {
+	display: none;
+}
+body.activity-permalink div.activity-comments div.acomment-content {
+	font-size: 14px;
+}
+div.activity-comments div.acomment-meta {
+	color: #888;
+	font-size: 11px;
+}
+div.activity-comments form.ac-form {
+	background: #fafafa;
+	border: 1px solid #ddd;
+	-moz-border-radius: 4px;
+	-webkit-border-radius: 4px;
+	border-radius: 4px;
+	display: none;
+	margin: 0 0 15px 33px;
+	padding: 8px;
+}
+div.activity-comments li form.ac-form {
+	margin-right: 15px;
+	clear: both;
+}
+div.activity-comments form.root {
+	margin-left: 0;
+}
+div.activity-comments div#message {
+	margin-top: 15px;
+	margin-bottom: 0;
+}
+div.activity-comments form.loading {
+	background-image: url( ../images/ajax-loader.gif );
+	background-position: 2% 95%;
+	background-repeat: no-repeat;
+}
+div.activity-comments form .ac-textarea {
+	background: #fff;
+	border: 1px inset #ccc;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	margin-bottom: 10px;
+	padding: 8px;
+}
+div.activity-comments form textarea {
+	border: none;
+	color: #555;
+	font-family: inherit;
+	font-size: 11px;
+	height: 60px;
+	padding: 0;
+	width: 100%;
+}
+div.activity-comments form input {
+	margin-top: 5px;
+}
+div.activity-comments form div.ac-reply-avatar {
+	float: left;
+}
+div.ac-reply-avatar img {
+	border: 2px solid #fff !important;
+}
+div.activity-comments form div.ac-reply-content {
+	color: #888;
+	font-size: 11px;
+	margin-left: 50px;
+	padding-left: 15px;
+}
+.acomment-options {
+	float: left;
+	margin: 5px 0 5px 40px;
+}
+.acomment-options a {
+	color: #999;
+}
+.acomment-options a:hover {
+	color: inherit;
+}
+
+
+/*--------------------------------------------------------------
+6.2 - Admin Bar
+--------------------------------------------------------------*/
+#wp-admin-bar .padder {
+	width: 95% !important; /* Line up the admin bar with the content body in this theme */
+}
+
+
+/*--------------------------------------------------------------
+6.3 - Directories - Members, Groups, Blogs, Forums
+--------------------------------------------------------------*/
+div.dir-search {
+	float: right;
+	margin: -39px 0 0 0;
+}
+div.dir-search input[type=text] {
+	font-size: 12px;
+	padding: 1px 3px;
+}
+
+
+/*--------------------------------------------------------------
+6.4 - Errors / Success Messages
+--------------------------------------------------------------*/
+div#message {
+	margin: 0 0 15px;
+}
+#message.info {
+	margin-bottom: 0;
+}
+div#message.updated {
+	clear: both;
+}
+div#message p {
+	font-size: 12px;
+	display: block;
+	padding: 10px 15px;
+}
+div#message.error p {
+	background-color: #db1717;
+	border-color: #a71a1a;
+	clear: left;
+	color: #fff;
+}
+div#message.updated p {
+	background-color: #8ff57a;
+	border-color: #80cf70;
+	color: #1a6a00;
+}
+.standard-form#signup_form div div.error {
+	background: #e41717;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #fff;
+	margin: 0 0 10px 0;
+	padding: 6px;
+	width: 90%;
+}
+div.accept,
+div.reject {
+	float: left;
+	margin-left: 10px;
+}
+ul.button-nav li {
+	float: left;
+	margin: 0 10px 10px 0;
+}
+ul.button-nav li.current a {
+	font-weight: bold;
+}
+
+
+/*--------------------------------------------------------------
+6.5 - Forms
+--------------------------------------------------------------*/
+.standard-form textarea,
+.standard-form input[type=text],
+.standard-form select,
+.standard-form input[type=password],
+.dir-search input[type=text] {
+	border: 1px inset #ccc;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #888;
+	font: inherit;
+	font-size: 14px;
+	padding: 6px;
+}
+.standard-form select {
+	padding: 3px;
+}
+.standard-form input[type=password] {
+	margin-bottom: 5px;
+}
+.standard-form label,
+.standard-form span.label {
+	display: block;
+	font-weight: bold;
+	margin: 15px 0 5px 0;
+}
+.standard-form div.checkbox label,
+.standard-form div.radio label {
+	color: #888;
+	font-size: 14px;
+	font-weight: normal;
+	margin: 5px 0 0 0;
+}
+.standard-form#sidebar-login-form label {
+	margin-top: 5px;
+}
+.standard-form input[type=text] {
+	width: 75%;
+}
+.standard-form#sidebar-login-form input[type=text],
+.standard-form#sidebar-login-form input[type=password] {
+	padding: 4px;
+	width: 95%;
+}
+.standard-form #basic-details-section input[type=password],
+.standard-form #blog-details-section input#signup_blog_url {
+	width: 35%;
+}
+.standard-form#signup_form input[type=text],
+.standard-form#signup_form textarea,
+.form-allowed-tags,
+#commentform input[type=text],
+#commentform textarea {
+	width: 90%;
+}
+.standard-form#signup_form div.submit {
+	float: right;
+}
+div#signup-avatar img {
+	margin: 0 15px 10px 0;
+}
+.standard-form textarea {
+	width: 75%;
+	height: 120px;
+}
+.standard-form textarea#message_content {
+	height: 200px;
+}
+.standard-form#send-reply textarea {
+	width: 97.5%;
+}
+.standard-form p.description {
+	color: #888;
+	font-size: 11px;
+	margin: 5px 0;
+}
+.standard-form div.submit {
+	clear: both;
+	padding: 15px 0 0 0;
+}
+.standard-form p.submit {
+	margin-bottom: 0;
+	padding: 15px 0 0 0;
+}
+.standard-form div.submit input {
+	margin-right: 15px;
+}
+.standard-form div.radio ul {
+	margin: 10px 0 15px 38px;
+	list-style: disc;
+}
+.standard-form div.radio ul li {
+	margin-bottom: 5px;
+}
+.standard-form a.clear-value {
+	display: block;
+	margin-top: 5px;
+	outline: none;
+}
+.standard-form #basic-details-section,
+.standard-form #blog-details-section,
+.standard-form #profile-details-section {
+	float: left;
+	width: 48%;
+}
+.standard-form #profile-details-section {
+	float: right;
+}
+.standard-form #blog-details-section {
+	clear: left;
+}
+.standard-form input:focus,
+.standard-form textarea:focus,
+.standard-form select:focus {
+	background: #fafafa;
+	color: #555;
+}
+form#send-invite-form {
+	margin-top: 20px;
+}
+div#invite-list {
+	background: #f5f5f5;
+	border: 1px solid #e4e4e4;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	height: 400px;
+	margin: 0 0 10px;
+	overflow: auto;
+	padding: 5px;
+	width: 160px;
+}
+button,
+a.button,
+input[type=submit],
+input[type=button],
+input[type=reset],
+ul.button-nav li a,
+div.generic-button a,
+.comment-reply-link {
+	background: #fff; /* Old browsers */
+	background: -moz-linear-gradient(top, #ffffff 0%, #ededed 100%); /* FF3.6+ */
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#ededed)); /* Chrome,Safari4+ */
+	background: -webkit-linear-gradient(top, #ffffff 0%,#ededed 100%); /* Chrome10+,Safari5.1+ */
+	background: -o-linear-gradient(top, #ffffff 0%,#ededed 100%); /* Opera11.10+ */
+	background: -ms-linear-gradient(top, #ffffff 0%,#ededed 100%); /* IE10+ */
+	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed',GradientType=0 ); /* IE6-9 */
+	background: linear-gradient(top, #ffffff 0%,#ededed 100%); /* W3C */
+	border: 1px solid #ccc;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #777;
+	cursor: pointer;
+	font: normal 12px/20px Arial, Tahoma, Verdana, sans-serif;
+	outline: none;
+	padding: 4px 10px;
+	text-align: center;
+	text-decoration: none;
+	line-height: 14px;
+}
+button:hover,
+a.button:hover,
+a.button:focus,
+input[type=submit]:hover,
+input[type=button]:hover,
+input[type=reset]:hover,
+ul.button-nav li a:hover,
+ul.button-nav li.current a,
+div.generic-button a:hover,
+.comment-reply-link:hover {
+	background: #ededed;
+	background: -moz-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); /* FF3.6+ */
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#e0e0e0)); /* Chrome,Safari4+ */
+	background: -webkit-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Chrome10+,Safari5.1+ */
+	background: -o-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Opera11.10+ */
+	background: -ms-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* IE10+ */
+	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e0e0e0',GradientType=0 ); /* IE6-9 */
+	background: linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* W3C */
+	border: 1px solid #bbb;
+	color: #555;
+	outline: none;
+	text-decoration: none;
+}
+
+/*--------------------------------------------------------------
+6.6 - Ajax Loading
+--------------------------------------------------------------*/
+a.loading,
+input.loading {
+    background-image: url( ../images/ajax-loader.gif );
+    background-position: 95% 50%;
+    background-repeat: no-repeat;
+    padding-right: 25px;
+}
+a.loading:hover,
+input.loading:hover {
+    background-image: url( ../images/ajax-loader.gif );
+    background-position: 95% 50%;
+    background-repeat: no-repeat;
+    padding-right: 25px;
+    color: #777;
+}
+a.button.loading,
+input[type="submit"].loading,
+input[type="button"].loading,
+input[type="reset"].loading {
+	background-image: url( ../images/ajax-loader.gif ), -moz-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); /* FF3.6+ */
+	background-image: url( ../images/ajax-loader.gif ), -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#e0e0e0)); /* Chrome,Safari4+ */
+	background-image: url( ../images/ajax-loader.gif ), -webkit-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Chrome10+,Safari5.1+ */
+	background-image: url( ../images/ajax-loader.gif ), -o-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Opera11.10+ */
+	background-image: url( ../images/ajax-loader.gif ), -ms-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* IE10+ */
+	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e0e0e0',GradientType=0 ); /* IE6-9 */
+	background-image: url( ../images/ajax-loader.gif ), linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* W3C */
+	background-position: 95% 50%;
+	background-repeat: no-repeat;
+	padding-right: 25px;
+}
+a.button.loading:hover,
+input[type="submit"].loading:hover,
+input[type="button"].loading:hover,
+input[type="reset"].loading:hover {
+  	background-image: url( ../images/ajax-loader.gif ), -moz-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); /* FF3.6+ */
+	background-image: url( ../images/ajax-loader.gif ), -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#e0e0e0)); /* Chrome,Safari4+ */
+	background-image: url( ../images/ajax-loader.gif ), -webkit-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Chrome10+,Safari5.1+ */
+	background-image: url( ../images/ajax-loader.gif ), -o-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* Opera11.10+ */
+	background-image: url( ../images/ajax-loader.gif ), -ms-linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* IE10+ */
+	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e0e0e0',GradientType=0 ); /* IE6-9 */
+	background-image: url( ../images/ajax-loader.gif ), linear-gradient(top, #ffffff 0%,#e0e0e0 100%); /* W3C */
+	border: 1px solid #ccc;
+	padding-right: 25px;
+}
+input[type="submit"].pending,
+input[type="button"].pending,
+input[type="reset"].pending,
+input[type="submit"].disabled,
+input[type="button"].disabled,
+input[type="reset"].disabled,
+button.pending,
+button.disabled,
+div.pending a,
+a.disabled {
+    border-color: #eee;
+    color: #bbb;
+    cursor: default;
+}
+input[type="submit"]:hover.pending,
+input[type="button"]:hover.pending,
+input[type="reset"]:hover.pending,
+input[type="submit"]:hover.disabled,
+input[type="button"]:hover.disabled,
+input[type="reset"]:hover.disabled,
+button.pending:hover,
+button.disabled:hover,
+div.pending a:hover,
+a.disabled:hover {
+	background: -moz-linear-gradient(top, #ffffff 0%, #ededed 100%); /* FF3.6+ */
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#ededed)); /* Chrome,Safari4+ */
+	background: -webkit-linear-gradient(top, #ffffff 0%,#ededed 100%); /* Chrome10+,Safari5.1+ */
+	background: -o-linear-gradient(top, #ffffff 0%,#ededed 100%); /* Opera11.10+ */
+	background: -ms-linear-gradient(top, #ffffff 0%,#ededed 100%); /* IE10+ */
+	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed',GradientType=0 ); /* IE6-9 */
+	background: linear-gradient(top, #ffffff 0%,#ededed 100%); /* W3C */
+    border-color: #eee;
+    color: #bbb;
+}
+
+
+/*--------------------------------------------------------------
+6.7 - Forums, Tables and Topics
+--------------------------------------------------------------*/
+ul#topic-post-list {
+	margin: 0px -19px 15px;
+	width: auto;
+}
+ul#topic-post-list li {
+	padding: 15px;
+	position: relative;
+}
+ul#topic-post-list li.alt {
+	background: #f5f5f5;
+}
+ul#topic-post-list li div.poster-meta {
+	color: #888;
+	margin-bottom: 10px;
+}
+ul#topic-post-list li div.post-content {
+	margin-left: 54px;
+}
+div.topic-tags {
+	font-size: 11px;
+	float: right;
+	margin-top: -25px;
+}
+div.admin-links {
+	color: #888;
+	font-size: 11px;
+	position: absolute;
+	top: 15px;
+	right: 25px;
+}
+div#topic-meta {
+	margin: -10px -19px;
+	padding: 5px 19px 30px;
+	position: relative;
+}
+div#topic-meta div.admin-links {
+	right: 19px;
+	top: -36px;
+}
+div#topic-meta h3 {
+	font-size: 20px;
+	margin: 5px 0;
+}
+div#new-topic-post {
+	display: none;
+	margin: 20px 0 0 0;
+	padding: 1px 0 0 0;
+}
+table {
+	width: 100%;
+}
+table thead tr {
+	background: #eaeaea;
+}
+table#message-threads {
+	margin: 0 -19px;
+	width: auto;
+}
+table.profile-fields {
+	margin-bottom: 20px;
+}
+table.profile-fields:last-child {
+	margin-bottom: 0;
+}
+table.profile-fields p {
+	margin-top: 15px;
+}
+table.profile-fields p:last-child {
+	margin-top: 0;
+}
+div#sidebar table {
+	margin: 0 -16px;
+	width: 117%;
+}
+table tr td,
+table tr th {
+	padding: 8px;
+	vertical-align: middle;
+}
+table tr td.label {
+	border-right: 1px solid #eaeaea;
+	font-weight: bold;
+	width: 25%;
+}
+table tr td.thread-info p {
+	margin: 0;
+}
+table tr td.thread-info p.thread-excerpt {
+	color: #888;
+	font-size: 11px;
+	margin-top: 3px;
+}
+div#sidebar table td,
+table.forum td {
+	text-align: center;
+}
+table tr.alt td {
+	background: #f5f5f5;
+}
+table.notification-settings {
+	margin-bottom: 20px;
+	text-align: left;
+}
+#groups-notification-settings {
+	margin-bottom: 0;
+}
+table.notification-settings th.icon,
+table.notification-settings td:first-child {
+	display: none;
+}
+table.notification-settings th.title {
+	width: 80%;
+}
+table.notification-settings .yes,
+table.notification-settings .no {
+	text-align: center;
+	width: 40px;
+}
+table.forum {
+	margin: 0 -19px;
+	width: auto;
+}
+table.forum tr.sticky td {
+	font-size: 1.2em;
+	background: #fff9db;
+	border-top: 1px solid #ffe8c4;
+	border-bottom: 1px solid #ffe8c4;
+}
+table.forum tr.closed td.td-title {
+	background-image: url( ../images/closed.png );
+	background-position: 15px 50%;
+	background-repeat: no-repeat;
+	padding-left: 35px;
+}
+table.forum td p.topic-text {
+	color: #888;
+	font-size: 13px;
+}
+table.forum tr > td:first-child,
+table.forum tr > th:first-child {
+	padding-left: 15px;
+}
+table.forum tr > td:last-child,
+table.forum tr > th:last-child {
+	padding-right: 15px;
+}
+table.forum tr th#th-title,
+table.forum tr th#th-poster,
+table.forum tr th#th-group,
+table.forum td.td-poster,
+table.forum td.td-group,
+table.forum td.td-title {
+	text-align: left;
+}
+table.forum tr td.td-title a.topic-title {
+	font-size: 1.2em;
+}
+table.forum td.td-freshness {
+	white-space: nowrap;
+}
+table.forum td.td-freshness span.time-since {
+	font-size: 0.9em;
+	color: #888;
+}
+table.forum td img.avatar {
+	float: none;
+	margin: 0 5px -8px 0;
+}
+table.forum td.td-poster,
+table.forum td.td-group {
+	min-width: 140px;
+}
+table.forum th#th-title {
+	width: 80%;
+}
+table.forum th#th-freshness {
+	width: 25%;
+}
+table.forum th#th-postcount {
+	width: 15%;
+}
+table.forum p.topic-meta {
+	font-size: 0.9em;
+	margin: 5px 0 0 0;
+}
+
+/*--------------------------------------------------------------
+6.8 - Headers, Lists and Tabs - Activity, Groups, Blogs, Forums
+--------------------------------------------------------------*/
+.item-body {
+	margin: 20px 0;
+}
+span.activity {
+	display: inline-block;
+	font-size: 11px;
+	opacity: 0.8;
+	padding: 1px 8px;
+}
+span.user-nicename {
+	color: #777;
+	display: inline-block;
+	font-size: 16px;
+	font-weight: bold;
+}
+span.activity,
+div#message p {
+	border: 1px solid #e1ca82;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	font-weight: normal;
+	margin-top: 3px;
+	text-decoration: none;
+	background: #ffeaa6;
+	background-image: -webkit-linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
+	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255, 255, 255, .5)), color-stop(100%,rgba(255, 255, 255, 0))); /* Chrome,Safari4+ */
+	background-image: -moz-linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
+	background-image: -ms-linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
+	background-image: -o-linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
+	background-image: linear-gradient(rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
+}
+div#item-header {
+	overflow: hidden;
+}
+div#item-header div#item-header-content {
+	margin-left: 170px;
+}
+div#item-header h2 {
+	font-size: 28px;
+	line-height: 120%;
+	margin: 0 0 15px 0;
+}
+div#item-header h2 a {
+	color: #777;
+	text-decoration: none;
+}
+div#item-header img.avatar {
+	float: left;
+	margin: 0 15px 19px 0;
+}
+div#item-header h2 {
+	margin-bottom: 5px;
+}
+div#item-header span.activity,
+div#item-header h2 span.highlight {
+	font-size: 11px;
+	font-weight: normal;
+	line-height: 170%;
+	margin-bottom: 7px;
+	vertical-align: middle;
+}
+div#item-header h2 span.highlight {
+	font-size: 16px;
+}
+div#item-header h2 span.highlight span {
+	background: #a1dcfa;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #fff;
+	cursor: pointer;
+	font-weight: bold;
+	font-size: 11px;
+	margin-bottom: 2px;
+	padding: 1px 4px;
+	position: relative;
+	right: -2px;
+	top: -2px;
+	vertical-align: middle;
+}
+div#item-header div#item-meta {
+	font-size: 14px;
+	color: #aaa;
+	overflow: hidden;
+	margin: 15px 0 5px 0;
+	padding-bottom: 10px;
+}
+div#item-header div#item-actions {
+	float: right;
+	margin: 0 0 15px 15px;
+	text-align: right;
+	width: 20%;
+}
+div#item-header div#item-actions h3 {
+	font-size: 12px;
+	margin: 0 0 5px 0;
+}
+div#item-header ul {
+	margin-bottom: 15px;
+	overflow: hidden;
+}
+div#item-header ul h5,
+div#item-header ul span,
+div#item-header ul hr {
+	display: none;
+}
+div#item-header ul li {
+	float: right;
+}
+div#item-header ul img.avatar,
+div#item-header ul.avatars img.avatar {
+	height: 30px;
+	margin: 2px;
+	width: 30px;
+}
+div#item-header div.generic-button,
+div#item-header a.button {
+	float: left;
+	margin: 10px 10px 0 0;
+}
+div#item-header div#message.info {
+	line-height: 80%;
+}
+ul.item-list {
+	width: 100%;
+}
+ul.item-list li {
+	border-bottom: 1px solid #eaeaea;
+	padding: 15px 0;
+	position: relative;
+}
+ul.item-list.activity-list li {
+	padding-bottom: 0;
+}
+ul.single-line li {
+	border: none;
+}
+ul.item-list li img.avatar {
+	float: left;
+	margin: 0 10px 0 0;
+}
+ul.item-list li div.item-title,
+ul.item-list li h4 {
+	font-weight: normal;
+	font-size: 14px;
+	margin: 0;
+	width: 75%;
+}
+ul.item-list li div.item-title span {
+	color: #999;
+	font-size: 12px;
+}
+ul.item-list li div.item-desc {
+	color: #888;
+	font-size: 11px;
+	margin: 10px 0 0 64px;
+	width: 50%;
+}
+ul.item-list li div.action {
+	position: absolute;
+	top: 15px;
+	right: 0;
+	text-align: right;
+}
+ul.item-list li div.meta {
+	color: #888;
+	font-size: 11px;
+	margin-top: 10px;
+}
+ul.item-list li h5 span.small {
+	float: right;
+	font-size: 11px;
+	font-weight: normal;
+}
+div.item-list-tabs {
+	background: #eaeaea;
+	clear: left;
+	margin: 25px -19px 20px -19px;
+	overflow: hidden;
+}
+div.item-list-tabs ul li a {
+	text-decoration: none;
+	height: 20px;
+}
+div.item-list-tabs ul {
+	width: 100%;
+}
+div.item-list-tabs ul li {
+	float: left;
+	margin: 5px 0 0 5px;
+}
+div.item-list-tabs#subnav ul li {
+	margin-top: 0;
+}
+div.item-list-tabs ul li:first-child {
+	margin-left: 20px;
+}
+div.item-list-tabs ul li.last {
+	float: right;
+	margin: 7px 20px 0 0;
+}
+div.item-list-tabs#subnav ul li.last {
+	margin-top: 4px;
+}
+div.item-list-tabs ul li.last select {
+	max-width: 175px;
+}
+div.item-list-tabs ul li a,
+div.item-list-tabs ul li span {
+	display: block;
+	padding: 5px 10px;
+	text-decoration: none;
+}
+div.item-list-tabs ul li a span {
+	background: #1fb3dd;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #fff;
+	display: inline;
+	font-size: 90%;
+	margin-left: 2px;
+	padding: 1px 6px;
+}
+div.item-list-tabs ul li.selected a,
+div.item-list-tabs ul li.current a {
+	background-color: #fff;
+	-moz-border-radius-topleft: 3px;
+	-webkit-border-top-left-radius: 3px;
+	-moz-border-radius-topright: 3px;
+	-webkit-border-top-right-radius: 3px;
+	border-top-left-radius: 3px;
+	border-top-right-radius: 3px;
+	color: #555;
+	font-weight: bold;
+}
+div.item-list-tabs ul li.selected a span,
+div.item-list-tabs ul li.current a span,
+div.item-list-tabs ul li a:hover span {
+	background-color: #999;
+}
+div.item-list-tabs ul li.selected a span,
+div.item-list-tabs ul li.current a span {
+	background-color: #555;
+}
+ul li.loading a {
+	background-image: url( ../images/ajax-loader.gif );
+	background-position: 92% 50%;
+	background-repeat: no-repeat;
+	padding-right: 30px !important;
+}
+div#item-nav ul li.loading a {
+	background-position: 88% 50%;
+}
+div.item-list-tabs#object-nav {
+	margin-top: 0;
+}
+div.item-list-tabs#subnav {
+	background: #fff;
+	border-bottom: 1px solid #eaeaea;
+	margin: -15px -19px 15px -19px;
+	min-height: 35px;
+	overflow: hidden;
+}
+div.item-list-tabs ul li.feed a {
+	background: url( ../images/rss.png ) center left no-repeat;
+	padding-left: 20px;
+}
+#admins-list li {
+	overflow: auto;
+}
+
+
+/*--------------------------------------------------------------
+6.9 - Private Messaging Threads
+--------------------------------------------------------------*/
+table#message-threads tr.unread td {
+	background: #fff9db;
+	border-top: 1px solid #ffe8c4;
+	border-bottom: 1px solid #ffe8c4;
+	font-weight: bold;
+}
+li span.unread-count,
+tr.unread span.unread-count {
+	background: #dd0000;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	color: #fff;
+	font-weight: bold;
+	padding: 2px 8px;
+}
+div.item-list-tabs ul li a span.unread-count {
+	padding: 1px 6px;
+	color: #fff;
+}
+div.messages-options-nav {
+	background: #eee;
+	font-size: 11px;
+	margin: 0 -19px;
+	padding: 5px 15px;
+	text-align: right;
+}
+div#message-thread div.message-box {
+	margin: 0 -19px;
+	padding: 15px;
+}
+div#message-thread div.alt {
+	background: #f4f4f4;
+}
+div#message-thread p#message-recipients {
+	margin: 10px 0 20px 0;
+}
+div#message-thread img.avatar {
+	float: left;
+	margin: 0 10px 0 0;
+	vertical-align: middle;
+}
+div#message-thread strong {
+	font-size: 16px;
+	margin: 0;
+}
+div#message-thread strong a {
+	text-decoration: none;
+}
+div#message-thread strong span.activity {
+	margin: 4px 0 0 10px;
+}
+div#message-thread div.message-metadata {
+	overflow: hidden;
+}
+div#message-thread div.message-content {
+	margin-left: 45px;
+}
+div#message-thread div.message-options {
+	text-align: right;
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive-rtl.css	(revision 5057)
@@ -0,0 +1,54 @@
+/*--------------------------------------------------------------
+Hello, this is the BuddyPress Reponsive theme rtl stylesheet.
+Right to left styles. This will transform the theme to read from
+right to left for languages that support this method.
+
+----------------------------------------------------------------
+>>> TABLE OF CONTENTS:
+----------------------------------------------------------------
+1.0 - iPad
+	1.1 - Landscape
+	1.2 - Portrait
+2.0 - iPhone
+--------------------------------------------------------------*/
+
+/*--------------------------------------------------------------
+1.1 - iPad Landscape
+--------------------------------------------------------------*/
+@media only screen and (max-width: 768px) {
+	div#content .padder {
+		margin-left: 0;
+		margin-right: auto;
+	}
+	ul#nav li {
+		margin-left: 0;
+		margin-right: auto;
+	}
+	ul#nav li:nth-child(even) {
+		margin-left: 0;
+		margin-right: 3%;
+	}
+}
+
+
+/*--------------------------------------------------------------
+1.2 - iPad Portrait
+--------------------------------------------------------------*/
+@media only screen and (max-width: 640px) {
+	ul.item-list li div.action {
+		padding-left: auto;
+		padding-right: 64px;
+	}
+}
+
+
+/*--------------------------------------------------------------
+2.0 - iPhone
+--------------------------------------------------------------*/
+@media only screen and (max-width: 320px) {
+	div.post div.post-content,
+	div.comment-content {
+		margin-left: auto;
+		margin-right: 0px;
+	}
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/css/responsive.css	(revision 5057)
@@ -0,0 +1,196 @@
+/*--------------------------------------------------------------
+Hello, this is the BuddyPress Responsive theme stylesheet
+
+----------------------------------------------------------------
+>>> TABLE OF CONTENTS:
+----------------------------------------------------------------
+1.0 - iPad
+	1.1 - Landscape
+	1.2 - Portrait
+2.0 - Smartphone
+3.0 - iPhone
+--------------------------------------------------------------*/
+
+/*--------------------------------------------------------------
+1.1 - iPad Landscape
+--------------------------------------------------------------*/
+@media only screen and (max-width: 768px) {
+	html {
+		background-color: #eaeaea;
+	}
+	body {
+		margin-top: -28px !important;
+		background: #eaeaea;
+	}
+	#wpadminbar {
+		display: none;
+	}
+	div#content .padder {
+		margin-right: 0;
+		width: auto;
+		float: none;
+		border: none;
+	}
+	div#sidebar {
+		width: auto;
+		float: none;
+		margin: 2em 0 0 0;
+		border: none;
+	}
+	ul#nav {
+		padding-bottom: 1em;
+	}
+	ul#nav li {
+		width: 46%;
+		margin-bottom: 1em;
+		margin-right: 0;
+	}
+	ul#nav li:nth-child(even) {
+		margin-left: 3%;
+		margin-right: 0;
+	}
+	ul#nav li a {
+		-moz-border-radius: 0.25em;
+		-webkit-border-radius: 0.25em;
+		border-radius: 0.25em;
+		margin-bottom: 0;
+	}
+}
+
+
+/*--------------------------------------------------------------
+1.2 - iPad Portrait
+--------------------------------------------------------------*/
+@media only screen and (max-width: 640px) {
+	div.dir-search {
+		float: none;
+		margin: 1.25em 0 0;
+	}
+	ul.item-list li div.action {
+		padding-left: 64px;
+		position: relative;
+		right: auto;
+		text-align: left;
+		top: 1em;
+		margin-bottom: 1.25em;
+	}
+}
+
+
+/*--------------------------------------------------------------
+2.0 - Smartphone (any)
+--------------------------------------------------------------*/
+@media only screen and (max-width: 480px) {
+	body {
+		width: 100%;
+		margin: 0;
+	}
+	#container {
+		border: none;
+	}
+	div.item-list-tabs ul li {
+		float: none;
+		margin: 0.3em 0.6em;
+		background:none;
+	}
+	div.item-list-tabs ul li.last {
+		float: none;
+		margin: 0.3em 0.6em;
+	}
+	div#subnav.item-list-tabs ul li.last {
+		margin-top: 1em;
+	}
+	div.item-list-tabs ul li.selected,
+	div.item-list-tabs ul li.current {
+		margin: 0.3em 0.6em;
+	}
+	div.item-list-tabs ul li.selected a,
+	div.item-list-tabs ul li.current a {
+		background: #eee;
+		font-weight: bold;
+	}
+	div.item-list-tabs {
+		background: none;
+	}
+	div.item-list-tabs ul li:first-child {
+		margin: 0.6em 0.6em 0.5em 0.6em;
+	}
+	div.item-list-tabs ul li a,
+	div.item-list-tabs ul li span {
+		padding: 0.3em;
+	}
+	div#subnav ul {
+		padding-top: 0.6em;
+	}
+	div.pagination {
+		padding: 0.6em;
+	}
+	div.item-list-tabs ul {
+		border-top: 1px solid #EAEAEA;
+		padding-top: 0;
+	}
+	div.item-list-tabs ul li {
+		margin-top: 0;
+	}
+	form.standard-form #basic-details-section,
+	form.standard-form #blog-details-section,
+	form.standard-form #profile-details-section {
+		float: none;
+		width: 100%;
+	}
+}
+
+
+/*--------------------------------------------------------------
+3.0 - iPhone
+--------------------------------------------------------------*/
+@media only screen and (max-width: 320px) {
+	ul#nav li {
+		float: none;
+		width: 100%;
+		margin-bottom: 0.6em;
+		margin-right: 0;
+		margin-left: 0;
+	}
+	ul#nav li:nth-child(even) {
+		margin-left: 0;
+		margin-right: 0;
+	}
+	ul#nav li a {
+		-moz-border-radius: 0.25em;
+		-webkit-border-radius: 0.25em;
+		border-radius: 0.25em;
+		margin-bottom: 0;
+	}
+	form#groups-directory-form h3 a {
+		display: block;
+		margin-top: 0.6em;
+	}
+	div.post div.author-box,
+	div.comment-avatar-box {
+		display: none;
+	}
+	div.post div.post-content,
+	div.comment-content {
+		margin-left: 0px;
+	}
+	form#whats-new-form div#whats-new-avatar {
+		display:none;
+	}
+	form#whats-new-form h5 {
+		margin: 0 0 0 0px;
+	}
+	form#whats-new-form #whats-new-content {
+		margin-left: 0px;
+		padding-left: 0px;
+	}
+	ul#activity-stream.item-list li div.activity-avatar {
+		display: none;
+	}
+	body.activity-permalink .activity-list li .activity-content {
+		margin-left: 0;
+	}
+	body.activity-permalink .activity-list li.mini .activity-meta {
+		left: 0;
+	}
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/_inc/global.js
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/_inc/global.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/_inc/global.js	(revision 5057)
@@ -0,0 +1,1269 @@
+// AJAX Functions
+var jq = jQuery;
+
+// Global variable to prevent multiple AJAX requests
+var bp_ajax_request = null;
+
+jq(document).ready( function() {
+	/**** Page Load Actions *******************************************************/
+
+	/* Hide Forums Post Form */
+	if ( '-1' == window.location.search.indexOf('new') && jq('div.forums').length )
+		jq('div#new-topic-post').hide();
+	else
+		jq('div#new-topic-post').show();
+
+	/* Activity filter and scope set */
+	bp_init_activity();
+
+	/* Object filter and scope set. */
+	var objects = [ 'members', 'groups', 'blogs', 'forums' ];
+	bp_init_objects( objects );
+
+	/* @mention Compose Scrolling */
+	if ( jq.query.get('r') && jq('textarea#whats-new').length ) {
+		jq('#whats-new-options').animate({height:'40px'});
+		jq("form#whats-new-form textarea").animate({height:'50px'});
+		jq.scrollTo( jq('textarea#whats-new'), 500, { offset:-125, easing:'easeOutQuad' } );
+		jq('textarea#whats-new').focus();
+	}
+
+	/**** Activity Posting ********************************************************/
+
+	/* Textarea focus */
+	jq('#whats-new').focus( function(){
+		jq("#whats-new-options").animate({height:'40px'});
+		jq("form#whats-new-form textarea").animate({height:'50px'});
+		jq("#aw-whats-new-submit").prop("disabled", false);
+	});
+
+	/* New posts */
+	jq("input#aw-whats-new-submit").click( function() {
+		var button = jq(this);
+		var form = button.parent().parent().parent().parent();
+
+		form.children().each( function() {
+			if ( jq.nodeName(this, "textarea") || jq.nodeName(this, "input") )
+				jq(this).prop( 'disabled', true );
+		});
+
+		/* Remove any errors */
+		jq('div.error').remove();
+		button.addClass('loading');
+		button.prop('disabled', true);
+
+		/* Default POST values */
+		var object = '';
+		var item_id = jq("#whats-new-post-in").val();
+		var content = jq("textarea#whats-new").val();
+
+		/* Set object for non-profile posts */
+		if ( item_id > 0 ) {
+			object = jq("#whats-new-post-object").val();
+		}
+
+		jq.post( ajaxurl, {
+			action: 'post_update',
+			'cookie': encodeURIComponent(document.cookie),
+			'_wpnonce_post_update': jq("input#_wpnonce_post_update").val(),
+			'content': content,
+			'object': object,
+			'item_id': item_id
+		},
+		function(response) {
+
+			form.children().each( function() {
+				if ( jq.nodeName(this, "textarea") || jq.nodeName(this, "input") ) {
+					jq(this).prop( 'disabled', false );
+				}
+			});
+
+			/* Check for errors and append if found. */
+			if ( response[0] + response[1] == '-1' ) {
+				form.prepend( response.substr( 2, response.length ) );
+				jq( 'form#' + form.attr('id') + ' div.error').hide().fadeIn( 200 );
+			} else {
+				if ( 0 == jq("ul.activity-list").length ) {
+					jq("div.error").slideUp(100).remove();
+					jq("div#message").slideUp(100).remove();
+					jq("div.activity").append( '<ul id="activity-stream" class="activity-list item-list">' );
+				}
+
+				jq("ul#activity-stream").prepend(response);
+				jq("ul#activity-stream li:first").addClass('new-update');
+
+				if ( 0 != jq("div#latest-update").length ) {
+					var l = jq("ul#activity-stream li.new-update .activity-content .activity-inner p").html();
+					var v = jq("ul#activity-stream li.new-update .activity-content .activity-header p a.view").attr('href');
+
+					var ltext = jq("ul#activity-stream li.new-update .activity-content .activity-inner p").text();
+
+					var u = '';
+					if ( ltext != '' )
+						u = '&quot;' + l + '&quot; ';
+
+					u += '<a href="' + v + '" rel="nofollow">' + BP_DTheme.view + '</a>';
+
+					jq("div#latest-update").slideUp(300,function(){
+						jq("div#latest-update").html( u );
+						jq("div#latest-update").slideDown(300);
+					});
+				}
+
+				jq("li.new-update").hide().slideDown( 300 );
+				jq("li.new-update").removeClass( 'new-update' );
+				jq("textarea#whats-new").val('');
+			}
+
+			jq("#whats-new-options").animate({height:'0px'});
+			jq("form#whats-new-form textarea").animate({height:'20px'});
+			jq("#aw-whats-new-submit").prop("disabled", true).removeClass('loading');
+		});
+
+		return false;
+	});
+
+	/* List tabs event delegation */
+	jq('div.activity-type-tabs').click( function(event) {
+		var target = jq(event.target).parent();
+
+		if ( event.target.nodeName == 'STRONG' || event.target.nodeName == 'SPAN' )
+			target = target.parent();
+		else if ( event.target.nodeName != 'A' )
+			return false;
+
+		/* Reset the page */
+		jq.cookie( 'bp-activity-oldestpage', 1, {path: '/'} );
+
+		/* Activity Stream Tabs */
+		var scope = target.attr('id').substr( 9, target.attr('id').length );
+		var filter = jq("#activity-filter-select select").val();
+
+		if ( scope == 'mentions' )
+			jq( 'li#' + target.attr('id') + ' a strong' ).remove();
+
+		bp_activity_request(scope, filter);
+
+		return false;
+	});
+
+	/* Activity filter select */
+	jq('#activity-filter-select select').change( function() {
+		var selected_tab = jq( 'div.activity-type-tabs li.selected' );
+
+		if ( !selected_tab.length )
+			var scope = null;
+		else
+			var scope = selected_tab.attr('id').substr( 9, selected_tab.attr('id').length );
+
+		var filter = jq(this).val();
+
+		bp_activity_request(scope, filter);
+
+		return false;
+	});
+
+	/* Stream event delegation */
+	jq('div.activity').click( function(event) {
+		var target = jq(event.target);
+
+		/* Favoriting activity stream items */
+		if ( target.hasClass('fav') || target.hasClass('unfav') ) {
+			var type = target.hasClass('fav') ? 'fav' : 'unfav';
+			var parent = target.parent().parent().parent();
+			var parent_id = parent.attr('id').substr( 9, parent.attr('id').length );
+
+			target.addClass('loading');
+
+			jq.post( ajaxurl, {
+				action: 'activity_mark_' + type,
+				'cookie': encodeURIComponent(document.cookie),
+				'id': parent_id
+			},
+			function(response) {
+				target.removeClass('loading');
+
+				target.fadeOut( 100, function() {
+					jq(this).html(response);
+					jq(this).fadeIn(100);
+				});
+
+				if ( 'fav' == type ) {
+					if ( !jq('div.item-list-tabs li#activity-favorites').length )
+						jq('div.item-list-tabs ul li#activity-mentions').before( '<li id="activity-favorites"><a href="#">' + BP_DTheme.my_favs + ' <span>0</span></a></li>');
+
+					target.removeClass('fav');
+					target.addClass('unfav');
+
+					jq('div.item-list-tabs ul li#activity-favorites span').html( Number( jq('div.item-list-tabs ul li#activity-favorites span').html() ) + 1 );
+				} else {
+					target.removeClass('unfav');
+					target.addClass('fav');
+
+					jq('div.item-list-tabs ul li#activity-favorites span').html( Number( jq('div.item-list-tabs ul li#activity-favorites span').html() ) - 1 );
+
+					if ( !Number( jq('div.item-list-tabs ul li#activity-favorites span').html() ) ) {
+						if ( jq('div.item-list-tabs ul li#activity-favorites').hasClass('selected') )
+							bp_activity_request( null, null );
+
+						jq('div.item-list-tabs ul li#activity-favorites').remove();
+					}
+				}
+
+				if ( 'activity-favorites' == jq( 'div.item-list-tabs li.selected').attr('id') )
+					target.parent().parent().parent().slideUp(100);
+			});
+
+			return false;
+		}
+
+		/* Delete activity stream items */
+		if ( target.hasClass('delete-activity') ) {
+			var li        = target.parents('div.activity ul li');
+			var id        = li.attr('id').substr( 9, li.attr('id').length );
+			var link_href = target.attr('href');
+			var nonce     = link_href.split('_wpnonce=');
+
+			nonce = nonce[1];
+
+			target.addClass('loading');
+
+			jq.post( ajaxurl, {
+				action: 'delete_activity',
+				'cookie': encodeURIComponent(document.cookie),
+				'id': id,
+				'_wpnonce': nonce
+			},
+			function(response) {
+
+				if ( response[0] + response[1] == '-1' ) {
+					li.prepend( response.substr( 2, response.length ) );
+					li.children('div#message').hide().fadeIn(300);
+				} else {
+					li.slideUp(300);
+				}
+			});
+
+			return false;
+		}
+
+		/* Load more updates at the end of the page */
+		if ( target.parent().hasClass('load-more') ) {
+			jq("#content li.load-more").addClass('loading');
+
+			if ( null == jq.cookie('bp-activity-oldestpage') )
+				jq.cookie('bp-activity-oldestpage', 1, {path: '/'} );
+
+			var oldest_page = ( jq.cookie('bp-activity-oldestpage') * 1 ) + 1;
+
+			jq.post( ajaxurl, {
+				action: 'activity_get_older_updates',
+				'cookie': encodeURIComponent(document.cookie),
+				'page': oldest_page
+			},
+			function(response)
+			{
+				jq("#content li.load-more").removeClass('loading');
+				jq.cookie( 'bp-activity-oldestpage', oldest_page, {path: '/'} );
+				jq("#content ul.activity-list").append(response.contents);
+
+				target.parent().hide();
+			}, 'json' );
+
+			return false;
+		}
+	});
+
+	// Activity "Read More" links
+	jq('.activity-read-more a').live('click', function(event) {
+		var target = jq(event.target);
+		var link_id = target.parent().attr('id').split('-');
+		var a_id = link_id[3];
+		var type = link_id[0]; /* activity or acomment */
+
+		var inner_class = type == 'acomment' ? 'acomment-content' : 'activity-inner';
+		var a_inner = jq('li#' + type + '-' + a_id + ' .' + inner_class + ':first' );
+		jq(target).addClass('loading');
+
+		jq.post( ajaxurl, {
+			action: 'get_single_activity_content',
+			'activity_id': a_id
+		},
+		function(response) {
+			jq(a_inner).slideUp(300).html(response).slideDown(300);
+		});
+
+		return false;
+	});
+
+	/**** Activity Comments *******************************************************/
+
+	/* Hide all activity comment forms */
+	jq('form.ac-form').hide();
+
+	/* Hide excess comments */
+	if ( jq('div.activity-comments').length )
+		bp_dtheme_hide_comments();
+
+	/* Activity list event delegation */
+	jq('div.activity').click( function(event) {
+		var target = jq(event.target);
+
+		/* Comment / comment reply links */
+		if ( target.hasClass('acomment-reply') || target.parent().hasClass('acomment-reply') ) {
+			if ( target.parent().hasClass('acomment-reply') )
+				target = target.parent();
+
+			var id = target.attr('id');
+			ids = id.split('-');
+
+			var a_id = ids[2]
+			var c_id = target.attr('href').substr( 10, target.attr('href').length );
+			var form = jq( '#ac-form-' + a_id );
+
+			form.css( 'display', 'none' );
+			form.removeClass('root');
+			jq('.ac-form').hide();
+
+			/* Hide any error messages */
+			form.children('div').each( function() {
+				if ( jq(this).hasClass( 'error' ) )
+					jq(this).hide();
+			});
+
+			if ( ids[1] != 'comment' ) {
+				jq('div.activity-comments li#acomment-' + c_id).append( form );
+			} else {
+				jq('li#activity-' + a_id + ' div.activity-comments').append( form );
+			}
+
+	 		if ( form.parent().hasClass( 'activity-comments' ) )
+				form.addClass('root');
+
+			form.slideDown( 200 );
+			jq.scrollTo( form, 500, { offset:-100, easing:'easeOutQuad' } );
+			jq('#ac-form-' + ids[2] + ' textarea').focus();
+
+			return false;
+		}
+
+		/* Activity comment posting */
+		if ( target.attr('name') == 'ac_form_submit' ) {
+			var form = target.parent().parent();
+			var form_parent = form.parent();
+			var form_id = form.attr('id').split('-');
+
+			if ( !form_parent.hasClass('activity-comments') ) {
+				var tmp_id = form_parent.attr('id').split('-');
+				var comment_id = tmp_id[1];
+			} else {
+				var comment_id = form_id[2];
+			}
+
+			/* Hide any error messages */
+			jq( 'form#' + form + ' div.error').hide();
+			target.addClass('loading').prop('disabled', true);
+
+			jq.post( ajaxurl, {
+				action: 'new_activity_comment',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce_new_activity_comment': jq("input#_wpnonce_new_activity_comment").val(),
+				'comment_id': comment_id,
+				'form_id': form_id[2],
+				'content': jq('form#' + form.attr('id') + ' textarea').val()
+			},
+			function(response)
+			{
+				target.removeClass('loading');
+
+				/* Check for errors and append if found. */
+				if ( response[0] + response[1] == '-1' ) {
+					form.append( response.substr( 2, response.length ) ).hide().fadeIn( 200 );
+				} else {
+					form.fadeOut( 200,
+						function() {
+							if ( 0 == form.parent().children('ul').length ) {
+								if ( form.parent().hasClass('activity-comments') )
+									form.parent().prepend('<ul></ul>');
+								else
+									form.parent().append('<ul></ul>');
+							}
+
+							form.parent().children('ul').append(response).hide().fadeIn( 200 );
+							form.children('textarea').val('');
+							form.parent().parent().addClass('has-comments');
+						}
+					);
+					jq( 'form#' + form + ' textarea').val('');
+
+					/* Increase the "Reply (X)" button count */
+					jq('li#activity-' + form_id[2] + ' a.acomment-reply span').html( Number( jq('li#activity-' + form_id[2] + ' a.acomment-reply span').html() ) + 1 );
+				}
+
+				jq(target).prop("disabled", false);
+			});
+
+			return false;
+		}
+
+		/* Deleting an activity comment */
+		if ( target.hasClass('acomment-delete') ) {
+			var link_href = target.attr('href');
+			var comment_li = target.parent().parent();
+			var form = comment_li.parents('div.activity-comments').children('form');
+
+			var nonce = link_href.split('_wpnonce=');
+				nonce = nonce[1];
+
+			var comment_id = link_href.split('cid=');
+				comment_id = comment_id[1].split('&');
+				comment_id = comment_id[0];
+
+			target.addClass('loading');
+
+			/* Remove any error messages */
+			jq('div.activity-comments ul div.error').remove();
+
+			/* Reset the form position */
+			comment_li.parents('div.activity-comments').append(form);
+
+			jq.post( ajaxurl, {
+				action: 'delete_activity_comment',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce': nonce,
+				'id': comment_id
+			},
+			function(response)
+			{
+				/* Check for errors and append if found. */
+				if ( response[0] + response[1] == '-1' ) {
+					comment_li.prepend( response.substr( 2, response.length ) ).hide().fadeIn( 200 );
+				} else {
+					var children = jq( 'li#' + comment_li.attr('id') + ' ul' ).children('li');
+					var child_count = 0;
+					jq(children).each( function() {
+						if ( !jq(this).is(':hidden') )
+							child_count++;
+					});
+					comment_li.fadeOut(200);
+
+					/* Decrease the "Reply (X)" button count */
+					var parent_li = comment_li.parents('ul#activity-stream > li');
+					jq('li#' + parent_li.attr('id') + ' a.acomment-reply span').html( jq('li#' + parent_li.attr('id') + ' a.acomment-reply span').html() - ( 1 + child_count ) );
+				}
+			});
+
+			return false;
+		}
+
+		/* Showing hidden comments - pause for half a second */
+		if ( target.parent().hasClass('show-all') ) {
+			target.parent().addClass('loading');
+
+			setTimeout( function() {
+				target.parent().parent().children('li').fadeIn(200, function() {
+					target.parent().remove();
+				});
+			}, 600 );
+
+			return false;
+		}
+	});
+
+	/* Escape Key Press for cancelling comment forms */
+	jq(document).keydown( function(e) {
+		e = e || window.event;
+		if (e.target)
+			element = e.target;
+		else if (e.srcElement)
+			element = e.srcElement;
+
+		if( element.nodeType == 3)
+			element = element.parentNode;
+
+		if( e.ctrlKey == true || e.altKey == true || e.metaKey == true )
+			return;
+
+		var keyCode = (e.keyCode) ? e.keyCode : e.which;
+
+		if ( keyCode == 27 ) {
+			if (element.tagName == 'TEXTAREA') {
+				if ( jq(element).hasClass('ac-input') )
+					jq(element).parent().parent().parent().slideUp( 200 );
+			}
+		}
+	});
+
+	/**** Directory Search ****************************************************/
+
+	/* The search form on all directory pages */
+	jq('div.dir-search').click( function(event) {
+		if ( jq(this).hasClass('no-ajax') )
+			return;
+
+		var target = jq(event.target);
+
+		if ( target.attr('type') == 'submit' ) {
+			var css_id = jq('div.item-list-tabs li.selected').attr('id').split( '-' );
+			var object = css_id[0];
+
+			bp_filter_request( object, jq.cookie('bp-' + object + '-filter'), jq.cookie('bp-' + object + '-scope') , 'div.' + object, target.parent().children('label').children('input').val(), 1, jq.cookie('bp-' + object + '-extras') );
+
+			return false;
+		}
+	});
+
+	/**** Tabs and Filters ****************************************************/
+
+	/* When a navigation tab is clicked - e.g. | All Groups | My Groups | */
+	jq('div.item-list-tabs').click( function(event) {
+		if ( jq(this).hasClass('no-ajax') )
+			return;
+
+		var target = jq(event.target).parent();
+
+		if ( 'LI' == event.target.parentNode.nodeName && !target.hasClass('last') ) {
+			var css_id = target.attr('id').split( '-' );
+			var object = css_id[0];
+
+			if ( 'activity' == object )
+				return false;
+
+			var scope = css_id[1];
+			var filter = jq("#" + object + "-order-select select").val();
+			var search_terms = jq("#" + object + "_search").val();
+
+			bp_filter_request( object, filter, scope, 'div.' + object, search_terms, 1, jq.cookie('bp-' + object + '-extras') );
+
+			return false;
+		}
+	});
+
+	/* When the filter select box is changed re-query */
+	jq('li.filter select').change( function() {
+		if ( jq('div.item-list-tabs li.selected').length )
+			var el = jq('div.item-list-tabs li.selected');
+		else
+			var el = jq(this);
+
+		var css_id = el.attr('id').split('-');
+		var object = css_id[0];
+		var scope = css_id[1];
+		var filter = jq(this).val();
+		var search_terms = false;
+
+		if ( jq('div.dir-search input').length )
+			search_terms = jq('div.dir-search input').val();
+
+		if ( 'friends' == object )
+			object = 'members';
+
+		bp_filter_request( object, filter, scope, 'div.' + object, search_terms, 1, jq.cookie('bp-' + object + '-extras') );
+
+		return false;
+	});
+
+	/* All pagination links run through this function */
+	jq('div#content').click( function(event) {
+		var target = jq(event.target);
+
+		if ( target.hasClass('button') )
+			return true;
+
+		if ( target.parent().parent().hasClass('pagination') && !target.parent().parent().hasClass('no-ajax') ) {
+			if ( target.hasClass('dots') || target.hasClass('current') )
+				return false;
+
+			if ( jq('div.item-list-tabs li.selected').length )
+				var el = jq('div.item-list-tabs li.selected');
+			else
+				var el = jq('li.filter select');
+
+			var page_number = 1;
+			var css_id = el.attr('id').split( '-' );
+			var object = css_id[0];
+			var search_terms = false;
+
+			if ( jq('div.dir-search input').length )
+				search_terms = jq('div.dir-search input').val();
+
+			if ( jq(target).hasClass('next') )
+				var page_number = Number( jq('div.pagination span.current').html() ) + 1;
+			else if ( jq(target).hasClass('prev') )
+				var page_number = Number( jq('div.pagination span.current').html() ) - 1;
+			else
+				var page_number = Number( jq(target).html() );
+
+			bp_filter_request( object, jq.cookie('bp-' + object + '-filter'), jq.cookie('bp-' + object + '-scope'), 'div.' + object, search_terms, page_number, jq.cookie('bp-' + object + '-extras') );
+
+			return false;
+		}
+
+	});
+
+	/**** New Forum Directory Post **************************************/
+
+	/* Hit the "New Topic" button on the forums directory page */
+	jq('a.show-hide-new').click( function() {
+		if ( !jq('div#new-topic-post').length )
+			return false;
+
+		if ( jq('div#new-topic-post').is(":visible") )
+			jq('div#new-topic-post').slideUp(200);
+		else
+			jq('div#new-topic-post').slideDown(200, function() { jq('#topic_title').focus(); } );
+
+		return false;
+	});
+
+	/* Cancel the posting of a new forum topic */
+	jq('input#submit_topic_cancel').click( function() {
+		if ( !jq('div#new-topic-post').length )
+			return false;
+
+		jq('div#new-topic-post').slideUp(200);
+		return false;
+	});
+
+	/* Clicking a forum tag */
+	jq('div#forum-directory-tags a').click( function() {
+		bp_filter_request( 'forums', 'tags', jq.cookie('bp-forums-scope'), 'div.forums', jq(this).html().replace( /&nbsp;/g, '-' ), 1, jq.cookie('bp-forums-extras') );
+		return false;
+	});
+
+	/** Invite Friends Interface ****************************************/
+
+	/* Select a user from the list of friends and add them to the invite list */
+	jq("div#invite-list input").click( function() {
+		jq('.ajax-loader').toggle();
+
+		var friend_id = jq(this).val();
+
+		if ( jq(this).prop('checked') == true )
+			var friend_action = 'invite';
+		else
+			var friend_action = 'uninvite';
+
+		jq('div.item-list-tabs li.selected').addClass('loading');
+
+		jq.post( ajaxurl, {
+			action: 'groups_invite_user',
+			'friend_action': friend_action,
+			'cookie': encodeURIComponent(document.cookie),
+			'_wpnonce': jq("input#_wpnonce_invite_uninvite_user").val(),
+			'friend_id': friend_id,
+			'group_id': jq("input#group_id").val()
+		},
+		function(response)
+		{
+			if ( jq("#message") )
+				jq("#message").hide();
+
+			jq('.ajax-loader').toggle();
+
+			if ( friend_action == 'invite' ) {
+				jq('#friend-list').append(response);
+			} else if ( friend_action == 'uninvite' ) {
+				jq('#friend-list li#uid-' + friend_id).remove();
+			}
+
+			jq('div.item-list-tabs li.selected').removeClass('loading');
+		});
+	});
+
+	/* Remove a user from the list of users to invite to a group */
+	jq("#friend-list li a.remove").live('click', function() {
+		jq('.ajax-loader').toggle();
+
+		var friend_id = jq(this).attr('id');
+		friend_id = friend_id.split('-');
+		friend_id = friend_id[1];
+
+		jq.post( ajaxurl, {
+			action: 'groups_invite_user',
+			'friend_action': 'uninvite',
+			'cookie': encodeURIComponent(document.cookie),
+			'_wpnonce': jq("input#_wpnonce_invite_uninvite_user").val(),
+			'friend_id': friend_id,
+			'group_id': jq("input#group_id").val()
+		},
+		function(response)
+		{
+			jq('.ajax-loader').toggle();
+			jq('#friend-list li#uid-' + friend_id).remove();
+			jq('#invite-list input#f-' + friend_id).prop('checked', false);
+		});
+
+		return false;
+	});
+
+	/** Friendship Requests **************************************/
+
+	/* Accept and Reject friendship request buttons */
+	jq("ul#friend-list a.accept, ul#friend-list a.reject").click( function() {
+		var button = jq(this);
+		var li = jq(this).parents('ul#friend-list li');
+		var action_div = jq(this).parents('li div.action');
+
+		var id = li.attr('id').substr( 11, li.attr('id').length );
+		var link_href = button.attr('href');
+
+		var nonce = link_href.split('_wpnonce=');
+			nonce = nonce[1];
+
+		if ( jq(this).hasClass('accepted') || jq(this).hasClass('rejected') )
+			return false;
+
+		if ( jq(this).hasClass('accept') ) {
+			var action = 'accept_friendship';
+			action_div.children('a.reject').css( 'visibility', 'hidden' );
+		} else {
+			var action = 'reject_friendship';
+			action_div.children('a.accept').css( 'visibility', 'hidden' );
+		}
+
+		button.addClass('loading');
+
+		jq.post( ajaxurl, {
+			action: action,
+			'cookie': encodeURIComponent(document.cookie),
+			'id': id,
+			'_wpnonce': nonce
+		},
+		function(response) {
+			button.removeClass('loading');
+
+			if ( response[0] + response[1] == '-1' ) {
+				li.prepend( response.substr( 2, response.length ) );
+				li.children('div#message').hide().fadeIn(200);
+			} else {
+				button.fadeOut( 100, function() {
+					if ( jq(this).hasClass('accept') ) {
+						action_div.children('a.reject').hide();
+						jq(this).html( BP_DTheme.accepted ).fadeIn(50);
+						jq(this).addClass('accepted');
+					} else {
+						action_div.children('a.accept').hide();
+						jq(this).html( BP_DTheme.rejected ).fadeIn(50);
+						jq(this).addClass('rejected');
+					}
+				});
+			}
+		});
+
+		return false;
+	});
+
+	/* Add / Remove friendship buttons */
+	jq("div.friendship-button a").live('click', function() {
+		jq(this).parent().addClass('loading');
+		var fid = jq(this).attr('id');
+		fid = fid.split('-');
+		fid = fid[1];
+
+		var nonce = jq(this).attr('href');
+		nonce = nonce.split('?_wpnonce=');
+		nonce = nonce[1].split('&');
+		nonce = nonce[0];
+
+		var thelink = jq(this);
+
+		jq.post( ajaxurl, {
+			action: 'addremove_friend',
+			'cookie': encodeURIComponent(document.cookie),
+			'fid': fid,
+			'_wpnonce': nonce
+		},
+		function(response)
+		{
+			var action = thelink.attr('rel');
+			var parentdiv = thelink.parent();
+
+			if ( action == 'add' ) {
+				jq(parentdiv).fadeOut(200,
+					function() {
+						parentdiv.removeClass('add_friend');
+						parentdiv.removeClass('loading');
+						parentdiv.addClass('pending');
+						parentdiv.fadeIn(200).html(response);
+					}
+				);
+
+			} else if ( action == 'remove' ) {
+				jq(parentdiv).fadeOut(200,
+					function() {
+						parentdiv.removeClass('remove_friend');
+						parentdiv.removeClass('loading');
+						parentdiv.addClass('add');
+						parentdiv.fadeIn(200).html(response);
+					}
+				);
+			}
+		});
+		return false;
+	} );
+
+	/** Group Join / Leave Buttons **************************************/
+
+	jq("div.group-button a").live('click', function() {
+		var gid = jq(this).parent().attr('id');
+		gid = gid.split('-');
+		gid = gid[1];
+
+		var nonce = jq(this).attr('href');
+		nonce = nonce.split('?_wpnonce=');
+		nonce = nonce[1].split('&');
+		nonce = nonce[0];
+
+		var thelink = jq(this);
+
+		jq.post( ajaxurl, {
+			action: 'joinleave_group',
+			'cookie': encodeURIComponent(document.cookie),
+			'gid': gid,
+			'_wpnonce': nonce
+		},
+		function(response)
+		{
+			var parentdiv = thelink.parent();
+
+			if ( !jq('body.directory').length )
+				location.href = location.href;
+			else {
+				jq(parentdiv).fadeOut(200,
+					function() {
+						parentdiv.fadeIn(200).html(response);
+					}
+				);
+			}
+		});
+		return false;
+	} );
+
+	/** Button disabling ************************************************/
+
+	jq('div.pending').click(function() {
+		return false;
+	});
+
+	/** Private Messaging ******************************************/
+
+	/* AJAX send reply functionality */
+	jq("input#send_reply_button").click(
+		function() {
+			var order = jq('#messages_order').val() || 'ASC',
+				offset = jq('#message-recipients').offset();
+
+			var button = jq("input#send_reply_button");
+			jq(button).addClass('loading');
+
+			jq.post( ajaxurl, {
+				action: 'messages_send_reply',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce': jq("input#send_message_nonce").val(),
+
+				'content': jq("#message_content").val(),
+				'send_to': jq("input#send_to").val(),
+				'subject': jq("input#subject").val(),
+				'thread_id': jq("input#thread_id").val()
+			},
+			function(response)
+			{
+				if ( response[0] + response[1] == "-1" ) {
+					jq('form#send-reply').prepend( response.substr( 2, response.length ) );
+				} else {
+					jq('form#send-reply div#message').remove();
+					jq("#message_content").val('');
+
+					if ( 'ASC' == order ) {
+						jq('form#send-reply').before( response );
+					} else {
+						jq('#message-recipients').after( response );
+						jq(window).scrollTop(offset.top);
+					}
+
+					jq("div.new-message").hide().slideDown( 200, function() {
+						jq('div.new-message').removeClass('new-message');
+					});
+				}
+				jq(button).removeClass('loading');
+			});
+
+			return false;
+		}
+	);
+
+	/* Marking private messages as read and unread */
+	jq("a#mark_as_read, a#mark_as_unread").click(function() {
+		var checkboxes_tosend = '';
+		var checkboxes = jq("#message-threads tr td input[type='checkbox']");
+
+		if ( 'mark_as_unread' == jq(this).attr('id') ) {
+			var currentClass = 'read'
+			var newClass = 'unread'
+			var unreadCount = 1;
+			var inboxCount = 0;
+			var unreadCountDisplay = 'inline';
+			var action = 'messages_markunread';
+		} else {
+			var currentClass = 'unread'
+			var newClass = 'read'
+			var unreadCount = 0;
+			var inboxCount = 1;
+			var unreadCountDisplay = 'none';
+			var action = 'messages_markread';
+		}
+
+		checkboxes.each( function(i) {
+			if(jq(this).is(':checked')) {
+				if ( jq('tr#m-' + jq(this).attr('value')).hasClass(currentClass) ) {
+					checkboxes_tosend += jq(this).attr('value');
+					jq('tr#m-' + jq(this).attr('value')).removeClass(currentClass);
+					jq('tr#m-' + jq(this).attr('value')).addClass(newClass);
+					var thread_count = jq('tr#m-' + jq(this).attr('value') + ' td span.unread-count').html();
+
+					jq('tr#m-' + jq(this).attr('value') + ' td span.unread-count').html(unreadCount);
+					jq('tr#m-' + jq(this).attr('value') + ' td span.unread-count').css('display', unreadCountDisplay);
+
+					var inboxcount = jq('tr.unread').length;
+
+					jq('a#user-messages span').html( inboxcount );
+
+					if ( i != checkboxes.length - 1 ) {
+						checkboxes_tosend += ','
+					}
+				}
+			}
+		});
+		jq.post( ajaxurl, {
+			action: action,
+			'thread_ids': checkboxes_tosend
+		});
+		return false;
+	});
+
+	/* Selecting unread and read messages in inbox */
+	jq("select#message-type-select").change(
+		function() {
+			var selection = jq("select#message-type-select").val();
+			var checkboxes = jq("td input[type='checkbox']");
+			checkboxes.each( function(i) {
+				checkboxes[i].checked = "";
+			});
+
+			switch(selection) {
+				case 'unread':
+					var checkboxes = jq("tr.unread td input[type='checkbox']");
+				break;
+				case 'read':
+					var checkboxes = jq("tr.read td input[type='checkbox']");
+				break;
+			}
+			if ( selection != '' ) {
+				checkboxes.each( function(i) {
+					checkboxes[i].checked = "checked";
+				});
+			} else {
+				checkboxes.each( function(i) {
+					checkboxes[i].checked = "";
+				});
+			}
+		}
+	);
+
+	/* Bulk delete messages */
+	jq("a#delete_inbox_messages, a#delete_sentbox_messages").click( function() {
+		checkboxes_tosend = '';
+		checkboxes = jq("#message-threads tr td input[type='checkbox']");
+
+		jq('div#message').remove();
+		jq(this).addClass('loading');
+
+		jq(checkboxes).each( function(i) {
+			if( jq(this).is(':checked') )
+				checkboxes_tosend += jq(this).attr('value') + ',';
+		});
+
+		if ( '' == checkboxes_tosend ) {
+			jq(this).removeClass('loading');
+			return false;
+		}
+
+		jq.post( ajaxurl, {
+			action: 'messages_delete',
+			'thread_ids': checkboxes_tosend
+		}, function(response) {
+			if ( response[0] + response[1] == "-1" ) {
+				jq('#message-threads').prepend( response.substr( 2, response.length ) );
+			} else {
+				jq('#message-threads').before( '<div id="message" class="updated"><p>' + response + '</p></div>' );
+
+				jq(checkboxes).each( function(i) {
+					if( jq(this).is(':checked') )
+						jq(this).parent().parent().fadeOut(150);
+				});
+			}
+
+			jq('div#message').hide().slideDown(150);
+			jq("a#delete_inbox_messages, a#delete_sentbox_messages").removeClass('loading');
+		});
+		return false;
+	});
+
+	/* Close site wide notices in the sidebar */
+	jq("a#close-notice").click( function() {
+		jq(this).addClass('loading');
+		jq('div#sidebar div.error').remove();
+
+		jq.post( ajaxurl, {
+			action: 'messages_close_notice',
+			'notice_id': jq('.notice').attr('rel').substr( 2, jq('.notice').attr('rel').length )
+		},
+		function(response) {
+			jq("a#close-notice").removeClass('loading');
+
+			if ( response[0] + response[1] == '-1' ) {
+				jq('.notice').prepend( response.substr( 2, response.length ) );
+				jq( 'div#sidebar div.error').hide().fadeIn( 200 );
+			} else {
+				jq('.notice').slideUp( 100 );
+			}
+		});
+		return false;
+	});
+
+	/* Admin Bar & wp_list_pages Javascript IE6 hover class */
+	jq("#wp-admin-bar ul.main-nav li, #nav li").mouseover( function() {
+		jq(this).addClass('sfhover');
+	});
+
+	jq("#wp-admin-bar ul.main-nav li, #nav li").mouseout( function() {
+		jq(this).removeClass('sfhover');
+	});
+
+	/* Clear BP cookies on logout */
+	jq('a.logout').click( function() {
+		jq.cookie('bp-activity-scope', null, {path: '/'});
+		jq.cookie('bp-activity-filter', null, {path: '/'});
+		jq.cookie('bp-activity-oldestpage', null, {path: '/'});
+
+		var objects = [ 'members', 'groups', 'blogs', 'forums' ];
+		jq(objects).each( function(i) {
+			jq.cookie('bp-' + objects[i] + '-scope', null, {path: '/'} );
+			jq.cookie('bp-' + objects[i] + '-filter', null, {path: '/'} );
+			jq.cookie('bp-' + objects[i] + '-extras', null, {path: '/'} );
+		});
+	});
+});
+
+/* Setup activity scope and filter based on the current cookie settings. */
+function bp_init_activity() {
+	/* Reset the page */
+	jq.cookie( 'bp-activity-oldestpage', 1, {path: '/'} );
+
+	if ( null != jq.cookie('bp-activity-filter') && jq('#activity-filter-select').length )
+		jq('#activity-filter-select select option[value="' + jq.cookie('bp-activity-filter') + '"]').prop( 'selected', true );
+
+	/* Activity Tab Set */
+	if ( null != jq.cookie('bp-activity-scope') && jq('div.activity-type-tabs').length ) {
+		jq('div.activity-type-tabs li').each( function() {
+			jq(this).removeClass('selected');
+		});
+		jq('li#activity-' + jq.cookie('bp-activity-scope') + ', div.item-list-tabs li.current').addClass('selected');
+	}
+}
+
+/* Setup object scope and filter based on the current cookie settings for the object. */
+function bp_init_objects(objects) {
+	jq(objects).each( function(i) {
+		if ( null != jq.cookie('bp-' + objects[i] + '-filter') && jq('li#' + objects[i] + '-order-select select').length )
+			jq('li#' + objects[i] + '-order-select select option[value="' + jq.cookie('bp-' + objects[i] + '-filter') + '"]').prop( 'selected', true );
+
+		if ( null != jq.cookie('bp-' + objects[i] + '-scope') && jq('div.' + objects[i]).length ) {
+			jq('div.item-list-tabs li').each( function() {
+				jq(this).removeClass('selected');
+			});
+			jq('div.item-list-tabs li#' + objects[i] + '-' + jq.cookie('bp-' + objects[i] + '-scope') + ', div.item-list-tabs#object-nav li.current').addClass('selected');
+		}
+	});
+}
+
+/* Filter the current content list (groups/members/blogs/topics) */
+function bp_filter_request( object, filter, scope, target, search_terms, page, extras ) {
+	if ( 'activity' == object )
+		return false;
+
+	if ( jq.query.get('s') && !search_terms )
+		search_terms = jq.query.get('s');
+
+	if ( null == scope )
+		scope = 'all';
+
+	/* Save the settings we want to remain persistent to a cookie */
+	jq.cookie( 'bp-' + object + '-scope', scope, {path: '/'} );
+	jq.cookie( 'bp-' + object + '-filter', filter, {path: '/'} );
+	jq.cookie( 'bp-' + object + '-extras', extras, {path: '/'} );
+
+	/* Set the correct selected nav and filter */
+	jq('div.item-list-tabs li').each( function() {
+		jq(this).removeClass('selected');
+	});
+	jq('div.item-list-tabs li#' + object + '-' + scope + ', div.item-list-tabs#object-nav li.current').addClass('selected');
+	jq('div.item-list-tabs li.selected').addClass('loading');
+	jq('div.item-list-tabs select option[value="' + filter + '"]').prop( 'selected', true );
+
+	if ( 'friends' == object )
+		object = 'members';
+
+	if ( bp_ajax_request )
+		bp_ajax_request.abort();
+
+	bp_ajax_request = jq.post( ajaxurl, {
+		action: object + '_filter',
+		'cookie': encodeURIComponent(document.cookie),
+		'object': object,
+		'filter': filter,
+		'search_terms': search_terms,
+		'scope': scope,
+		'page': page,
+		'extras': extras
+	},
+	function(response)
+	{
+		jq(target).fadeOut( 100, function() {
+			jq(this).html(response);
+			jq(this).fadeIn(100);
+	 	});
+		jq('div.item-list-tabs li.selected').removeClass('loading');
+	});
+}
+
+/* Activity Loop Requesting */
+function bp_activity_request(scope, filter) {
+	/* Save the type and filter to a session cookie */
+	jq.cookie( 'bp-activity-scope', scope, {path: '/'} );
+	jq.cookie( 'bp-activity-filter', filter, {path: '/'} );
+	jq.cookie( 'bp-activity-oldestpage', 1, {path: '/'} );
+
+	/* Remove selected and loading classes from tabs */
+	jq('div.item-list-tabs li').each( function() {
+		jq(this).removeClass('selected loading');
+	});
+	/* Set the correct selected nav and filter */
+	jq('li#activity-' + scope + ', div.item-list-tabs li.current').addClass('selected');
+	jq('div#object-nav.item-list-tabs li.selected, div.activity-type-tabs li.selected').addClass('loading');
+	jq('#activity-filter-select select option[value="' + filter + '"]').prop( 'selected', true );
+
+	/* Reload the activity stream based on the selection */
+	jq('.widget_bp_activity_widget h2 span.ajax-loader').show();
+
+	if ( bp_ajax_request )
+		bp_ajax_request.abort();
+
+	bp_ajax_request = jq.post( ajaxurl, {
+		action: 'activity_widget_filter',
+		'cookie': encodeURIComponent(document.cookie),
+		'_wpnonce_activity_filter': jq("input#_wpnonce_activity_filter").val(),
+		'scope': scope,
+		'filter': filter
+	},
+	function(response)
+	{
+		jq('.widget_bp_activity_widget h2 span.ajax-loader').hide();
+
+		jq('div.activity').fadeOut( 100, function() {
+			jq(this).html(response.contents);
+			jq(this).fadeIn(100);
+
+			/* Selectively hide comments */
+			bp_dtheme_hide_comments();
+		});
+
+		/* Update the feed link */
+		if ( null != response.feed_url )
+			jq('.directory div#subnav li.feed a, .home-page div#subnav li.feed a').attr('href', response.feed_url);
+
+		jq('div.item-list-tabs li.selected').removeClass('loading');
+
+	}, 'json' );
+}
+
+/* Hide long lists of activity comments, only show the latest five root comments. */
+function bp_dtheme_hide_comments() {
+	var comments_divs = jq('div.activity-comments');
+
+	if ( !comments_divs.length )
+		return false;
+
+	comments_divs.each( function() {
+		if ( jq(this).children('ul').children('li').length < 5 ) return;
+
+		var comments_div = jq(this);
+		var parent_li = comments_div.parents('ul#activity-stream > li');
+		var comment_lis = jq(this).children('ul').children('li');
+		var comment_count = ' ';
+
+		if ( jq('li#' + parent_li.attr('id') + ' a.acomment-reply span').length )
+			var comment_count = jq('li#' + parent_li.attr('id') + ' a.acomment-reply span').html();
+
+		comment_lis.each( function(i) {
+			/* Show the latest 5 root comments */
+			if ( i < comment_lis.length - 5 ) {
+				jq(this).addClass('hidden');
+				jq(this).toggle();
+
+				if ( !i )
+					jq(this).before( '<li class="show-all"><a href="#' + parent_li.attr('id') + '/show-all/" title="' + BP_DTheme.show_all_comments + '">' + BP_DTheme.show_all + ' ' + comment_count + ' ' + BP_DTheme.comments + '</a></li>' );
+			}
+		});
+
+	});
+}
+
+/* Helper Functions */
+
+function checkAll() {
+	var checkboxes = document.getElementsByTagName("input");
+	for(var i=0; i<checkboxes.length; i++) {
+		if(checkboxes[i].type == "checkbox") {
+			if($("check_all").checked == "") {
+				checkboxes[i].checked = "";
+			}
+			else {
+				checkboxes[i].checked = "checked";
+			}
+		}
+	}
+}
+
+function clear(container) {
+	if( !document.getElementById(container) ) return;
+
+	var container = document.getElementById(container);
+
+	if ( radioButtons = container.getElementsByTagName('INPUT') ) {
+		for(var i=0; i<radioButtons.length; i++) {
+			radioButtons[i].checked = '';
+		}
+	}
+
+	if ( options = container.getElementsByTagName('OPTION') ) {
+		for(var i=0; i<options.length; i++) {
+			options[i].selected = false;
+		}
+	}
+
+	return;
+}
+
+/* ScrollTo plugin - just inline and minified */
+;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);
+
+/* jQuery Easing Plugin, v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ */
+jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}});
+
+/* jQuery Cookie plugin */
+jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};if(value===null){value='';options.expires=-1;}var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toUTCString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;}expires='; expires='+date.toUTCString();}var path=options.path?'; path='+(options.path):'';var domain=options.domain?'; domain='+(options.domain):'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i]);if(cookie.substring(0,name.length+1)==(name+'=')){cookieValue=decodeURIComponent(cookie.substring(name.length+1));break;}}}return cookieValue;}};
+
+/* jQuery querystring plugin */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('M 6(A){4 $11=A.11||\'&\';4 $V=A.V===r?r:j;4 $1p=A.1p===r?\'\':\'[]\';4 $13=A.13===r?r:j;4 $D=$13?A.D===j?"#":"?":"";4 $15=A.15===r?r:j;v.1o=M 6(){4 f=6(o,t){8 o!=1v&&o!==x&&(!!t?o.1t==t:j)};4 14=6(1m){4 m,1l=/\\[([^[]*)\\]/g,T=/^([^[]+)(\\[.*\\])?$/.1r(1m),k=T[1],e=[];19(m=1l.1r(T[2]))e.u(m[1]);8[k,e]};4 w=6(3,e,7){4 o,y=e.1b();b(I 3!=\'X\')3=x;b(y===""){b(!3)3=[];b(f(3,L)){3.u(e.h==0?7:w(x,e.z(0),7))}n b(f(3,1a)){4 i=0;19(3[i++]!=x);3[--i]=e.h==0?7:w(3[i],e.z(0),7)}n{3=[];3.u(e.h==0?7:w(x,e.z(0),7))}}n b(y&&y.T(/^\\s*[0-9]+\\s*$/)){4 H=1c(y,10);b(!3)3=[];3[H]=e.h==0?7:w(3[H],e.z(0),7)}n b(y){4 H=y.B(/^\\s*|\\s*$/g,"");b(!3)3={};b(f(3,L)){4 18={};1w(4 i=0;i<3.h;++i){18[i]=3[i]}3=18}3[H]=e.h==0?7:w(3[H],e.z(0),7)}n{8 7}8 3};4 C=6(a){4 p=d;p.l={};b(a.C){v.J(a.Z(),6(5,c){p.O(5,c)})}n{v.J(1u,6(){4 q=""+d;q=q.B(/^[?#]/,\'\');q=q.B(/[;&]$/,\'\');b($V)q=q.B(/[+]/g,\' \');v.J(q.Y(/[&;]/),6(){4 5=1e(d.Y(\'=\')[0]||"");4 c=1e(d.Y(\'=\')[1]||"");b(!5)8;b($15){b(/^[+-]?[0-9]+\\.[0-9]*$/.1d(c))c=1A(c);n b(/^[+-]?[0-9]+$/.1d(c))c=1c(c,10)}c=(!c&&c!==0)?j:c;b(c!==r&&c!==j&&I c!=\'1g\')c=c;p.O(5,c)})})}8 p};C.1H={C:j,1G:6(5,1f){4 7=d.Z(5);8 f(7,1f)},1h:6(5){b(!f(5))8 d.l;4 K=14(5),k=K[0],e=K[1];4 3=d.l[k];19(3!=x&&e.h!=0){3=3[e.1b()]}8 I 3==\'1g\'?3:3||""},Z:6(5){4 3=d.1h(5);b(f(3,1a))8 v.1E(j,{},3);n b(f(3,L))8 3.z(0);8 3},O:6(5,c){4 7=!f(c)?x:c;4 K=14(5),k=K[0],e=K[1];4 3=d.l[k];d.l[k]=w(3,e.z(0),7);8 d},w:6(5,c){8 d.N().O(5,c)},1s:6(5){8 d.O(5,x).17()},1z:6(5){8 d.N().1s(5)},1j:6(){4 p=d;v.J(p.l,6(5,7){1y p.l[5]});8 p},1F:6(Q){4 D=Q.B(/^.*?[#](.+?)(?:\\?.+)?$/,"$1");4 S=Q.B(/^.*?[?](.+?)(?:#.+)?$/,"$1");8 M C(Q.h==S.h?\'\':S,Q.h==D.h?\'\':D)},1x:6(){8 d.N().1j()},N:6(){8 M C(d)},17:6(){6 F(G){4 R=I G=="X"?f(G,L)?[]:{}:G;b(I G==\'X\'){6 1k(o,5,7){b(f(o,L))o.u(7);n o[5]=7}v.J(G,6(5,7){b(!f(7))8 j;1k(R,5,F(7))})}8 R}d.l=F(d.l);8 d},1B:6(){8 d.N().17()},1D:6(){4 i=0,U=[],W=[],p=d;4 16=6(E){E=E+"";b($V)E=E.B(/ /g,"+");8 1C(E)};4 1n=6(1i,5,7){b(!f(7)||7===r)8;4 o=[16(5)];b(7!==j){o.u("=");o.u(16(7))}1i.u(o.P(""))};4 F=6(R,k){4 12=6(5){8!k||k==""?[5].P(""):[k,"[",5,"]"].P("")};v.J(R,6(5,7){b(I 7==\'X\')F(7,12(5));n 1n(W,12(5),7)})};F(d.l);b(W.h>0)U.u($D);U.u(W.P($11));8 U.P("")}};8 M C(1q.S,1q.D)}}(v.1o||{});',62,106,'|||target|var|key|function|value|return|||if|val|this|tokens|is||length||true|base|keys||else||self||false|||push|jQuery|set|null|token|slice|settings|replace|queryObject|hash|str|build|orig|index|typeof|each|parsed|Array|new|copy|SET|join|url|obj|search|match|queryString|spaces|chunks|object|split|get||separator|newKey|prefix|parse|numbers|encode|COMPACT|temp|while|Object|shift|parseInt|test|decodeURIComponent|type|number|GET|arr|EMPTY|add|rx|path|addFields|query|suffix|location|exec|REMOVE|constructor|arguments|undefined|for|empty|delete|remove|parseFloat|compact|encodeURIComponent|toString|extend|load|has|prototype'.split('|'),0,{}))
Index: /tags/1.5-beta-3/bp-themes/bp-default/activity/activity-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/activity/activity-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/activity/activity-loop.php	(revision 5057)
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * BuddyPress - Activity Loop
+ *
+ * Querystring is set via AJAX in _inc/ajax.php - bp_dtheme_object_filter()
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_activity_loop' ); ?>
+
+<?php if ( bp_has_activities( bp_ajax_querystring( 'activity' ) ) ) : ?>
+
+	<?php /* Show pagination if JS is not enabled, since the "Load More" link will do nothing */ ?>
+	<noscript>
+		<div class="pagination">
+			<div class="pag-count"><?php bp_activity_pagination_count(); ?></div>
+			<div class="pagination-links"><?php bp_activity_pagination_links(); ?></div>
+		</div>
+	</noscript>
+
+	<?php if ( empty( $_POST['page'] ) ) : ?>
+
+		<ul id="activity-stream" class="activity-list item-list">
+
+	<?php endif; ?>
+
+	<?php while ( bp_activities() ) : bp_the_activity(); ?>
+
+		<?php locate_template( array( 'activity/entry.php' ), true, false ); ?>
+
+	<?php endwhile; ?>
+
+	<?php if ( bp_activity_has_more_items() ) : ?>
+
+		<li class="load-more">
+			<a href="#more"><?php _e( 'Load More', 'buddypress' ); ?></a>
+		</li>
+
+	<?php endif; ?>
+
+	<?php if ( empty( $_POST['page'] ) ) : ?>
+
+		</ul>
+
+	<?php endif; ?>
+
+<?php else : ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'Sorry, there was no activity found. Please try a different filter.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_activity_loop' ); ?>
+
+<form action="" name="activity-loop-form" id="activity-loop-form" method="post">
+
+	<?php wp_nonce_field( 'activity_filter', '_wpnonce_activity_filter' ); ?>
+
+</form>
Index: /tags/1.5-beta-3/bp-themes/bp-default/activity/comment.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/activity/comment.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/activity/comment.php	(revision 5057)
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * BuddyPress - Activity Stream Comment
+ *
+ * This template is used by bp_activity_comments() functions to show
+ * each activity.
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_activity_comment' ); ?>
+
+<li id="acomment-<?php bp_activity_comment_id(); ?>">
+	<div class="acomment-avatar">
+		<a href="<?php bp_activity_comment_user_link(); ?>">
+			<?php bp_activity_avatar( 'type=thumb&user_id=' . bp_get_activity_comment_user_id() ); ?>
+		</a>
+	</div>
+
+	<div class="acomment-meta">
+		<?php
+		/* translators: 1: user profile link, 2: user name, 3: activity permalink, 3: activity timestamp */
+		printf( __( '<a href="%1$s">%2$s</a> replied <a href="%3$s" class="activity-time-since"><span class="time-since">%4$s</span></a>', 'buddypress' ), bp_get_activity_comment_user_link(), bp_get_activity_comment_name(), bp_get_activity_thread_permalink(), bp_get_activity_comment_date_recorded() );
+		?>
+	</div>
+
+	<div class="acomment-content"><?php bp_activity_comment_content(); ?></div>
+
+	<div class="acomment-options">
+
+		<?php if ( is_user_logged_in() && bp_activity_can_comment_reply( bp_activity_current_comment() ) ) : ?>
+
+			<a href="#acomment-<?php bp_activity_comment_id(); ?>" class="acomment-reply bp-primary-action" id="acomment-reply-<?php bp_activity_id() ?>-from-<?php bp_activity_comment_id() ?>"><?php _e( 'Reply', 'buddypress' ); ?></a>
+
+		<?php endif; ?>
+
+		<?php if ( bp_activity_user_can_delete() ) : ?>
+
+			<a href="<?php bp_activity_comment_delete_link(); ?>" class="delete acomment-delete confirm bp-secondary-action" rel="nofollow"><?php _e( 'Delete', 'buddypress' ); ?></a>
+
+		<?php endif; ?>
+
+	</div>
+
+	<?php bp_activity_recurse_comments( bp_activity_current_comment() ); ?>
+</li>
+
+<?php do_action( 'bp_after_activity_comment' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/activity/entry.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/activity/entry.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/activity/entry.php	(revision 5057)
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * BuddyPress - Activity Stream (Single Item)
+ *
+ * This template is used by activity-loop.php and AJAX functions to show
+ * each activity.
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_activity_entry' ); ?>
+
+<li class="<?php bp_activity_css_class(); ?>" id="activity-<?php bp_activity_id(); ?>">
+	<div class="activity-avatar">
+		<a href="<?php bp_activity_user_link(); ?>">
+
+			<?php bp_activity_avatar(); ?>
+
+		</a>
+	</div>
+
+	<div class="activity-content">
+
+		<div class="activity-header">
+
+			<?php bp_activity_action(); ?>
+
+		</div>
+
+		<?php if ( 'activity_comment' == bp_get_activity_type() ) : ?>
+
+			<div class="activity-inreplyto">
+				<strong><?php _e( 'In reply to: ', 'buddypress' ); ?></strong><?php bp_activity_parent_content(); ?> <a href="<?php bp_activity_thread_permalink(); ?>" class="view" title="<?php _e( 'View Thread / Permalink', 'buddypress' ); ?>"><?php _e( 'View', 'buddypress' ); ?></a>
+			</div>
+
+		<?php endif; ?>
+
+		<?php if ( bp_activity_has_content() ) : ?>
+
+			<div class="activity-inner">
+
+				<?php bp_activity_content_body(); ?>
+
+			</div>
+
+		<?php endif; ?>
+
+		<?php do_action( 'bp_activity_entry_content' ); ?>
+
+		<?php if ( is_user_logged_in() ) : ?>
+
+			<div class="activity-meta">
+
+				<?php if ( bp_activity_can_comment() ) : ?>
+
+					<a href="<?php bp_get_activity_comment_link(); ?>" class="button acomment-reply bp-primary-action" id="acomment-comment-<?php bp_activity_id(); ?>"><?php printf( __( 'Comment <span>%s</span>', 'buddypress' ), bp_activity_get_comment_count() ); ?></a>
+
+				<?php endif; ?>
+
+				<?php if ( bp_activity_can_favorite() ) : ?>
+
+					<?php if ( !bp_get_activity_is_favorite() ) : ?>
+
+						<a href="<?php bp_activity_favorite_link(); ?>" class="button fav bp-secondary-action" title="<?php esc_attr_e( 'Mark as Favorite', 'buddypress' ); ?>"><?php _e( 'Favorite', 'buddypress' ) ?></a>
+
+					<?php else : ?>
+
+						<a href="<?php bp_activity_unfavorite_link(); ?>" class="button unfav bp-secondary-action" title="<?php esc_attr_e( 'Remove Favorite', 'buddypress' ); ?>"><?php _e( 'Remove Favorite', 'buddypress' ) ?></a>
+
+					<?php endif; ?>
+
+				<?php endif; ?>
+
+				<?php if ( bp_activity_user_can_delete() ) bp_activity_delete_link(); ?>
+
+				<?php do_action( 'bp_activity_entry_meta' ); ?>
+
+			</div>
+
+		<?php endif; ?>
+
+	</div>
+
+	<?php do_action( 'bp_before_activity_entry_comments' ); ?>
+
+	<?php if ( ( is_user_logged_in() && bp_activity_can_comment() ) || bp_activity_get_comment_count() ) : ?>
+
+		<div class="activity-comments">
+
+			<?php bp_activity_comments(); ?>
+
+			<?php if ( is_user_logged_in() ) : ?>
+
+				<form action="<?php bp_activity_comment_form_action(); ?>" method="post" id="ac-form-<?php bp_activity_id(); ?>" class="ac-form"<?php bp_activity_comment_form_nojs_display(); ?>>
+					<div class="ac-reply-avatar"><?php bp_loggedin_user_avatar( 'width=' . BP_AVATAR_THUMB_WIDTH . '&height=' . BP_AVATAR_THUMB_HEIGHT ); ?></div>
+					<div class="ac-reply-content">
+						<div class="ac-textarea">
+							<textarea id="ac-input-<?php bp_activity_id(); ?>" class="ac-input" name="ac_input_<?php bp_activity_id(); ?>"></textarea>
+						</div>
+						<input type="submit" name="ac_form_submit" value="<?php _e( 'Post', 'buddypress' ); ?>" /> &nbsp; <?php _e( 'or press esc to cancel.', 'buddypress' ); ?>
+						<input type="hidden" name="comment_form_id" value="<?php bp_activity_id(); ?>" />
+					</div>
+
+					<?php do_action( 'bp_activity_entry_comments' ); ?>
+
+					<?php wp_nonce_field( 'new_activity_comment', '_wpnonce_new_activity_comment' ); ?>
+
+				</form>
+
+			<?php endif; ?>
+
+		</div>
+
+	<?php endif; ?>
+
+	<?php do_action( 'bp_after_activity_entry_comments' ); ?>
+
+</li>
+
+<?php do_action( 'bp_after_activity_entry' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/activity/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/activity/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/activity/index.php	(revision 5057)
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * Template Name: BuddyPress - Activity Directory
+ *
+ * @package BuddyPress
+ * @subpackage Theme
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_activity_page' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_directory_activity' ); ?>
+
+			<?php if ( !is_user_logged_in() ) : ?>
+
+				<h3><?php _e( 'Site Activity', 'buddypress' ); ?></h3>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_before_directory_activity_content' ); ?>
+
+			<?php if ( is_user_logged_in() ) : ?>
+
+				<?php locate_template( array( 'activity/post-form.php'), true ); ?>
+
+			<?php endif; ?>
+
+			<?php do_action( 'template_notices' ); ?>
+
+			<div class="item-list-tabs activity-type-tabs" role="navigation">
+				<ul>
+					<?php do_action( 'bp_before_activity_type_tab_all' ); ?>
+
+					<li class="selected" id="activity-all"><a href="<?php echo bp_loggedin_user_domain() . bp_get_activity_slug() . '/'; ?>" title="<?php _e( 'The public activity for everyone on this site.', 'buddypress' ); ?>"><?php printf( __( 'All Members <span>%s</span>', 'buddypress' ), bp_get_total_site_member_count() ); ?></a></li>
+
+					<?php if ( is_user_logged_in() ) : ?>
+
+						<?php do_action( 'bp_before_activity_type_tab_friends' ) ?>
+
+						<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+							<?php if ( bp_get_total_friend_count( bp_loggedin_user_id() ) ) : ?>
+
+								<li id="activity-friends"><a href="<?php echo bp_loggedin_user_domain() . bp_get_activity_slug() . '/' . bp_get_friends_slug() . '/'; ?>" title="<?php _e( 'The activity of my friends only.', 'buddypress' ); ?>"><?php printf( __( 'My Friends <span>%s</span>', 'buddypress' ), bp_get_total_friend_count( bp_loggedin_user_id() ) ); ?></a></li>
+
+							<?php endif; ?>
+
+						<?php endif; ?>
+
+						<?php do_action( 'bp_before_activity_type_tab_groups' ) ?>
+
+						<?php if ( bp_is_active( 'groups' ) ) : ?>
+
+							<?php if ( bp_get_total_group_count_for_user( bp_loggedin_user_id() ) ) : ?>
+
+								<li id="activity-groups"><a href="<?php echo bp_loggedin_user_domain() . bp_get_activity_slug() . '/' . bp_get_groups_slug() . '/'; ?>" title="<?php _e( 'The activity of groups I am a member of.', 'buddypress' ); ?>"><?php printf( __( 'My Groups <span>%s</span>', 'buddypress' ), bp_get_total_group_count_for_user( bp_loggedin_user_id() ) ); ?></a></li>
+
+							<?php endif; ?>
+
+						<?php endif; ?>
+
+						<?php do_action( 'bp_before_activity_type_tab_favorites' ); ?>
+
+						<?php if ( bp_get_total_favorite_count_for_user( bp_loggedin_user_id() ) ) : ?>
+
+							<li id="activity-favorites"><a href="<?php echo bp_loggedin_user_domain() . bp_get_activity_slug() . '/favorites/'; ?>" title="<?php _e( "The activity I've marked as a favorite.", 'buddypress' ); ?>"><?php printf( __( 'My Favorites <span>%s</span>', 'buddypress' ), bp_get_total_favorite_count_for_user( bp_loggedin_user_id() ) ); ?></a></li>
+
+						<?php endif; ?>
+
+						<?php do_action( 'bp_before_activity_type_tab_mentions' ); ?>
+
+						<li id="activity-mentions"><a href="<?php echo bp_loggedin_user_domain() . bp_get_activity_slug() . '/mentions/'; ?>" title="<?php _e( 'Activity that I have been mentioned in.', 'buddypress' ); ?>"><?php _e( 'Public Mentions', 'buddypress' ); ?><?php if ( bp_get_total_mention_count_for_user( bp_loggedin_user_id() ) ) : ?> <strong><?php printf( __( '<span>%s new</span>', 'buddypress' ), bp_get_total_mention_count_for_user( bp_loggedin_user_id() ) ); ?></strong><?php endif; ?></a></li>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_activity_type_tabs' ); ?>
+				</ul>
+			</div><!-- .item-list-tabs -->
+
+			<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+				<ul>
+					<li class="feed"><a href="<?php bp_sitewide_activity_feed_link() ?>" title="<?php _e( 'RSS Feed', 'buddypress' ); ?>"><?php _e( 'RSS', 'buddypress' ); ?></a></li>
+
+					<?php do_action( 'bp_activity_syndication_options' ); ?>
+
+					<li id="activity-filter-select" class="last">
+						<label for="activity-filter-by"><?php _e( 'Show:', 'buddypress' ); ?></label> 
+						<select id="activity-filter-by">
+							<option value="-1"><?php _e( 'Everything', 'buddypress' ); ?></option>
+							<option value="activity_update"><?php _e( 'Updates', 'buddypress' ); ?></option>
+
+							<?php if ( bp_is_active( 'blogs' ) ) : ?>
+
+								<option value="new_blog_post"><?php _e( 'Posts', 'buddypress' ); ?></option>
+								<option value="new_blog_comment"><?php _e( 'Comments', 'buddypress' ); ?></option>
+
+							<?php endif; ?>
+
+							<?php if ( bp_is_active( 'forums' ) ) : ?>
+
+								<option value="new_forum_topic"><?php _e( 'Forum Topics', 'buddypress' ); ?></option>
+								<option value="new_forum_post"><?php _e( 'Forum Replies', 'buddypress' ); ?></option>
+
+							<?php endif; ?>
+
+							<?php if ( bp_is_active( 'groups' ) ) : ?>
+
+								<option value="created_group"><?php _e( 'New Groups', 'buddypress' ); ?></option>
+								<option value="joined_group"><?php _e( 'Group Memberships', 'buddypress' ); ?></option>
+
+							<?php endif; ?>
+
+							<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+								<option value="friendship_accepted,friendship_created"><?php _e( 'Friendships', 'buddypress' ); ?></option>
+
+							<?php endif; ?>
+
+							<option value="new_member"><?php _e( 'New Members', 'buddypress' ); ?></option>
+
+							<?php do_action( 'bp_activity_filter_options' ); ?>
+
+						</select>
+					</li>
+				</ul>
+			</div><!-- .item-list-tabs -->
+
+			<?php do_action( 'bp_before_directory_activity_list' ); ?>
+
+			<div class="activity" role="main">
+
+				<?php locate_template( array( 'activity/activity-loop.php' ), true ); ?>
+
+			</div><!-- .activity -->
+
+			<?php do_action( 'bp_after_directory_activity_list' ); ?>
+
+			<?php do_action( 'bp_directory_activity_content' ); ?>
+
+			<?php do_action( 'bp_after_directory_activity_content' ); ?>
+
+			<?php do_action( 'bp_after_directory_activity' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_activity_page' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/activity/post-form.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/activity/post-form.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/activity/post-form.php	(revision 5057)
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * BuddyPress - Activity Post Form
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<form action="<?php bp_activity_post_form_action(); ?>" method="post" id="whats-new-form" name="whats-new-form" role="complementary">
+
+	<?php do_action( 'bp_before_activity_post_form' ); ?>
+
+	<div id="whats-new-avatar">
+		<a href="<?php echo bp_loggedin_user_domain(); ?>">
+			<?php bp_loggedin_user_avatar( 'width=' . bp_core_avatar_thumb_width() . '&height=' . bp_core_avatar_thumb_height() ); ?>
+		</a>
+	</div>
+
+	<h5><?php if ( bp_is_group() )
+			printf( __( "What's new in %s, %s?", 'buddypress' ), bp_get_group_name(), bp_get_user_firstname() );
+		else
+			printf( __( "What's new, %s?", 'buddypress' ), bp_get_user_firstname() );
+	?></h5>
+
+	<div id="whats-new-content">
+		<div id="whats-new-textarea">
+			<textarea name="whats-new" id="whats-new" cols="50" rows="10"><?php if ( isset( $_GET['r'] ) ) : ?>@<?php echo esc_attr( $_GET['r'] ); ?> <?php endif; ?></textarea>
+		</div>
+
+		<div id="whats-new-options">
+			<div id="whats-new-submit">
+				<input type="submit" name="aw-whats-new-submit" id="aw-whats-new-submit" value="<?php _e( 'Post Update', 'buddypress' ); ?>" />
+			</div>
+
+			<?php if ( bp_is_active( 'groups' ) && !bp_is_my_profile() && !bp_is_group() ) : ?>
+
+				<div id="whats-new-post-in-box">
+
+					<?php _e( 'Post in', 'buddypress' ) ?>:
+
+					<select id="whats-new-post-in" name="whats-new-post-in">
+						<option selected="selected" value="0"><?php _e( 'My Profile', 'buddypress' ); ?></option>
+
+						<?php if ( bp_has_groups( 'user_id=' . bp_loggedin_user_id() . '&type=alphabetical&max=100&per_page=100&populate_extras=0' ) ) :
+							while ( bp_groups() ) : bp_the_group(); ?>
+
+								<option value="<?php bp_group_id(); ?>"><?php bp_group_name(); ?></option>
+
+							<?php endwhile;
+						endif; ?>
+
+					</select>
+				</div>
+				<input type="hidden" id="whats-new-post-object" name="whats-new-post-object" value="groups" />
+
+			<?php elseif ( bp_is_group_home() ) : ?>
+
+				<input type="hidden" id="whats-new-post-object" name="whats-new-post-object" value="groups" />
+				<input type="hidden" id="whats-new-post-in" name="whats-new-post-in" value="<?php bp_group_id(); ?>" />
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_activity_post_form_options' ); ?>
+
+		</div><!-- #whats-new-options -->
+	</div><!-- #whats-new-content -->
+
+	<?php wp_nonce_field( 'post_update', '_wpnonce_post_update' ); ?>
+	<?php do_action( 'bp_after_activity_post_form' ); ?>
+
+</form><!-- #whats-new-form -->
Index: /tags/1.5-beta-3/bp-themes/bp-default/archive.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/archive.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/archive.php	(revision 5057)
@@ -0,0 +1,64 @@
+<?php get_header(); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_archive' ) ?>
+
+		<div class="page" id="blog-archives" role="main">
+
+			<h3 class="pagetitle"><?php printf( __( 'You are browsing the archive for %1$s.', 'buddypress' ), wp_title( false, false ) ); ?></h3>
+
+			<?php if ( have_posts() ) : ?>
+
+				<?php bp_dtheme_content_nav( 'nav-above' ); ?>
+
+				<?php while (have_posts()) : the_post(); ?>
+
+					<?php do_action( 'bp_before_blog_post' ) ?>
+
+					<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+						<div class="author-box">
+							<?php echo get_avatar( get_the_author_meta( 'user_email' ), '50' ); ?>
+							<p><?php printf( _x( 'by %s', 'Post written by...', 'buddypress' ), bp_core_get_userlink( $post->post_author ) ) ?></p>
+						</div>
+
+						<div class="post-content">
+							<h2 class="posttitle"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php _e( 'Permanent Link to', 'buddypress' ) ?> <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+
+							<p class="date"><?php printf( __( '%1$s <span>in %2$s</span>', 'buddypress' ), get_the_date(), get_the_category_list( ', ' ) ); ?></p>
+
+							<div class="entry">
+								<?php the_content( __( 'Read the rest of this entry &rarr;', 'buddypress' ) ); ?>
+								<?php wp_link_pages( array( 'before' => '<div class="page-link"><p>' . __( 'Pages: ', 'buddypress' ), 'after' => '</p></div>', 'next_or_number' => 'number' ) ); ?>
+							</div>
+
+							<p class="postmetadata"><?php the_tags( '<span class="tags">' . __( 'Tags: ', 'buddypress' ), ', ', '</span>' ); ?> <span class="comments"><?php comments_popup_link( __( 'No Comments &#187;', 'buddypress' ), __( '1 Comment &#187;', 'buddypress' ), __( '% Comments &#187;', 'buddypress' ) ); ?></span></p>
+						</div>
+
+					</div>
+
+					<?php do_action( 'bp_after_blog_post' ) ?>
+
+				<?php endwhile; ?>
+
+				<?php bp_dtheme_content_nav( 'nav-below' ); ?>
+
+			<?php else : ?>
+
+				<h2 class="center"><?php _e( 'Not Found', 'buddypress' ) ?></h2>
+				<?php get_search_form() ?>
+
+			<?php endif; ?>
+
+		</div>
+
+		<?php do_action( 'bp_after_archive' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar() ?>
+
+<?php get_footer(); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/attachment.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/attachment.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/attachment.php	(revision 5057)
@@ -0,0 +1,75 @@
+<?php get_header(); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_attachment' ); ?>
+
+			<div class="page" id="attachments-page" role="main">
+
+				<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
+
+					<?php do_action( 'bp_before_blog_post' ) ?>
+
+					<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+						<div class="author-box">
+							<?php echo get_avatar( get_the_author_meta( 'user_email' ), '50' ); ?>
+							<p><?php printf( _x( 'by %s', 'Post written by...', 'buddypress' ), str_replace( '<a href=', '<a rel="author" href=', bp_core_get_userlink( $post->post_author ) ) ); ?></p>
+						</div>
+
+						<div class="post-content">
+							<h2 class="posttitle"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php _e( 'Permanent Link to', 'buddypress' ) ?> <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+
+							<p class="date">
+								<?php the_date(); ?>
+								<span class="post-utility alignright"><?php edit_post_link( __( 'Edit this entry', 'buddypress' ) ); ?></span>
+							</p>
+
+							<div class="entry">
+								<?php echo wp_get_attachment_image( $post->ID, 'large', false, array( 'class' => 'size-large aligncenter' ) ); ?>
+
+								<div class="entry-caption"><?php if ( !empty( $post->post_excerpt ) ) the_excerpt(); ?></div>
+								<?php the_content(); ?>
+							</div>
+
+							<p class="postmetadata">
+								<?php
+									if ( wp_attachment_is_image() ) :
+										$metadata = wp_get_attachment_metadata();
+										printf( __( 'Full size is %s pixels', 'buddypress' ),
+											sprintf( '<a href="%1$s" title="%2$s">%3$s &times; %4$s</a>',
+												wp_get_attachment_url(),
+												esc_attr( __( 'Link to full size image', 'buddypress' ) ),
+												$metadata['width'],
+												$metadata['height']
+											)
+										);
+									endif;
+								?>
+								&nbsp;
+							</p>
+						</div>
+
+					</div>
+
+					<?php do_action( 'bp_after_blog_post' ) ?>
+
+					<?php comments_template(); ?>
+
+				<?php endwhile; else: ?>
+
+					<p><?php _e( 'Sorry, no attachments matched your criteria.', 'buddypress' ) ?></p>
+
+				<?php endif; ?>
+
+			</div>
+
+		<?php do_action( 'bp_after_attachment' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar(); ?>
+
+<?php get_footer(); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/blogs/blogs-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/blogs/blogs-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/blogs/blogs-loop.php	(revision 5057)
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * BuddyPress - Blogs Loop
+ *
+ * Querystring is set via AJAX in _inc/ajax.php - bp_dtheme_object_filter()
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_blogs_loop' ); ?>
+
+<?php if ( bp_has_blogs( bp_ajax_querystring( 'blogs' ) ) ) : ?>
+
+	<div id="pag-top" class="pagination">
+
+		<div class="pag-count" id="blog-dir-count-top">
+			<?php bp_blogs_pagination_count(); ?>
+		</div>
+
+		<div class="pagination-links" id="blog-dir-pag-top">
+			<?php bp_blogs_pagination_links(); ?>
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_before_directory_blogs_list' ); ?>
+
+	<ul id="blogs-list" class="item-list" role="main">
+
+	<?php while ( bp_blogs() ) : bp_the_blog(); ?>
+
+		<li>
+			<div class="item-avatar">
+				<a href="<?php bp_blog_permalink(); ?>"><?php bp_blog_avatar( 'type=thumb' ); ?></a>
+			</div>
+
+			<div class="item">
+				<div class="item-title"><a href="<?php bp_blog_permalink(); ?>"><?php bp_blog_name(); ?></a></div>
+				<div class="item-meta"><span class="activity"><?php bp_blog_last_active(); ?></span></div>
+
+				<?php do_action( 'bp_directory_blogs_item' ); ?>
+			</div>
+
+			<div class="action">
+
+				<?php do_action( 'bp_directory_blogs_actions' ); ?>
+
+				<div class="meta">
+
+					<?php bp_blog_latest_post(); ?>
+
+				</div>
+
+			</div>
+
+			<div class="clear"></div>
+		</li>
+
+	<?php endwhile; ?>
+
+	</ul>
+
+	<?php do_action( 'bp_after_directory_blogs_list' ); ?>
+
+	<?php bp_blog_hidden_fields(); ?>
+
+	<div id="pag-bottom" class="pagination">
+
+		<div class="pag-count" id="blog-dir-count-bottom">
+
+			<?php bp_blogs_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="blog-dir-pag-bottom">
+
+			<?php bp_blogs_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'Sorry, there were no sites found.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_blogs_loop' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/blogs/create.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/blogs/create.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/blogs/create.php	(revision 5057)
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * BuddyPress - Create Blog
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_blogs_content' ); ?>
+
+	<div id="content">
+		<div class="padder" role="main">
+
+		<?php do_action( 'template_notices' ); ?>
+
+			<h3><?php _e( 'Create a Site', 'buddypress' ); ?> &nbsp;<a class="button" href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_blogs_root_slug() ) ?>"><?php _e( 'Site Directory', 'buddypress' ); ?></a></h3>
+
+		<?php do_action( 'bp_before_create_blog_content' ); ?>
+
+		<?php if ( bp_blog_signup_enabled() ) : ?>
+
+			<?php bp_show_blog_signup_form(); ?>
+
+		<?php else: ?>
+
+			<div id="message" class="info">
+				<p><?php _e( 'Site registration is currently disabled', 'buddypress' ); ?></p>
+			</div>
+
+		<?php endif; ?>
+
+		<?php do_action( 'bp_after_create_blog_content' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_blogs_content' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/blogs/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/blogs/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/blogs/index.php	(revision 5057)
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * BuddyPress - Blogs Directory
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_blogs_page' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_directory_blogs' ); ?>
+
+		<form action="" method="post" id="blogs-directory-form" class="dir-form">
+
+			<h3><?php _e( 'Site Directory', 'buddypress' ); ?><?php if ( is_user_logged_in() && bp_blog_signup_enabled() ) : ?> &nbsp;<a class="button" href="<?php echo bp_get_root_domain() . '/' . bp_get_blogs_slug() . '/create/' ?>"><?php _e( 'Create a Site', 'buddypress' ); ?></a><?php endif; ?></h3>
+
+			<?php do_action( 'bp_before_directory_blogs_content' ); ?>
+
+			<div id="blog-dir-search" class="dir-search" role="search">
+
+				<?php bp_directory_blogs_search_form(); ?>
+
+			</div><!-- #blog-dir-search -->
+
+			<div class="item-list-tabs" role="navigation">
+				<ul>
+					<li class="selected" id="blogs-all"><a href="<?php bp_root_domain(); ?>"><?php printf( __( 'All Sites <span>%s</span>', 'buddypress' ), bp_get_total_blog_count() ); ?></a></li>
+
+					<?php if ( is_user_logged_in() && bp_get_total_blog_count_for_user( bp_loggedin_user_id() ) ) : ?>
+
+						<li id="blogs-personal"><a href="<?php echo bp_loggedin_user_domain() . bp_get_blogs_slug() . '/my-blogs/' ?>"><?php printf( __( 'My Sites <span>%s</span>', 'buddypress' ), bp_get_total_blog_count_for_user( bp_loggedin_user_id() ) ); ?></a></li>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_blogs_directory_blog_types' ); ?>
+
+				</ul>
+			</div><!-- .item-list-tabs -->
+
+			<div class="item-list-tabs" id="subnav" role="navigation">
+				<ul>
+
+					<?php do_action( 'bp_blogs_directory_blog_sub_types' ); ?>
+
+					<li id="blogs-order-select" class="last filter">
+
+						<label for="blogs-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+						<select id="blogs-order-by">
+							<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+							<option value="newest"><?php _e( 'Newest', 'buddypress' ); ?></option>
+							<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+							<?php do_action( 'bp_blogs_directory_order_options' ); ?>
+
+						</select>
+					</li>
+				</ul>
+			</div>
+
+			<div id="blogs-dir-list" class="blogs dir-list">
+
+				<?php locate_template( array( 'blogs/blogs-loop.php' ), true ); ?>
+
+			</div><!-- #blogs-dir-list -->
+
+			<?php do_action( 'bp_directory_blogs_content' ); ?>
+
+			<?php wp_nonce_field( 'directory_blogs', '_wpnonce-blogs-filter' ); ?>
+			
+			<?php do_action( 'bp_after_directory_blogs_content' ); ?>
+
+		</form><!-- #blogs-directory-form -->
+
+		<?php do_action( 'bp_after_directory_blogs' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_blogs_page' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/comments.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/comments.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/comments.php	(revision 5057)
@@ -0,0 +1,78 @@
+<?php
+	if ( post_password_required() ) {
+		echo '<h3 class="comments-header">' . __( 'Password Protected', 'buddypress' ) . '</h3>';
+		echo '<p class="alert password-protected">' . __( 'Enter the password to view comments.', 'buddypress' ) . '</p>';
+		return;
+	}
+
+	if ( is_page() && !have_comments() && !comments_open() && !pings_open() )
+		return;
+
+	if ( have_comments() ) :
+		$num_comments = 0;
+		$num_trackbacks = 0;
+		foreach ( (array)$comments as $comment ) {
+			if ( 'comment' != get_comment_type() )
+				$num_trackbacks++;
+			else
+				$num_comments++;
+		}
+?>
+	<div id="comments">
+
+		<h3>
+			<?php printf( _n( '1 response to %2$s', '%1$s responses to %2$s', $num_comments, 'buddypress' ), number_format_i18n( $num_comments ), '<em>' . get_the_title() . '</em>' ) ?>
+		</h3>
+
+		<?php do_action( 'bp_before_blog_comment_list' ) ?>
+
+		<ol class="commentlist">
+			<?php wp_list_comments( array( 'callback' => 'bp_dtheme_blog_comments', 'type' => 'comment' ) ) ?>
+		</ol><!-- .comment-list -->
+
+		<?php do_action( 'bp_after_blog_comment_list' ) ?>
+
+		<?php if ( get_option( 'page_comments' ) ) : ?>
+			<div class="comment-navigation paged-navigation">
+				<?php paginate_comments_links() ?>
+			</div>
+		<?php endif; ?>
+
+	</div><!-- #comments -->
+<?php else : ?>
+
+	<?php if ( pings_open() && !comments_open() && ( is_single() || is_page() ) ) : ?>
+		<p class="comments-closed pings-open">
+			<?php printf( __( 'Comments are closed, but <a href="%1$s" title="Trackback URL for this post">trackbacks</a> and pingbacks are open.', 'buddypress' ), trackback_url( '0' ) ) ?>
+		</p>
+	<?php elseif ( !comments_open() && ( is_single() || is_page() ) ) : ?>
+		<p class="comments-closed">
+			<?php _e( 'Comments are closed.', 'buddypress' ) ?>
+		</p>
+	<?php endif; ?>
+
+<?php endif; ?>
+
+<?php if ( comments_open() ) : ?>
+	<?php comment_form() ?>
+<?php endif; ?>
+
+<?php if ( !empty( $num_trackbacks ) ) : ?>
+	<div id="trackbacks">
+		<h3><?php printf( _n( '1 trackback', '%d trackbacks', $num_trackbacks, 'buddypress' ), number_format_i18n( $num_trackbacks ) ) ?></h3>
+
+		<ul id="trackbacklist">
+			<?php foreach ( (array)$comments as $comment ) : ?>
+
+				<?php if ( 'comment' != get_comment_type() ) : ?>
+					<li>
+						<h5><?php comment_author_link() ?></h5>
+						<em>on <?php comment_date() ?></em>
+					</li>
+ 				<?php endif; ?>
+
+			<?php endforeach; ?>
+		</ul>
+
+	</div>
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/editor-style-rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/editor-style-rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/editor-style-rtl.css	(revision 5057)
@@ -0,0 +1,43 @@
+/*
+Theme Name: BuddyPress Default
+Description: Used to style the TinyMCE editor (RTL version).
+*/
+
+html .mceContentBody .alignright {
+	float: left;
+	margin-left: auto;
+	margin-right: 15px;
+}
+html .mceContentBody .alignleft {
+	float: right;
+	margin-left: 15px;
+	margin-right: auto;
+}
+dl {
+	margin-left: auto;
+	margin-right: 0;
+}
+html .mceContentBody img.alignright {
+	margin: 0 7px 2px 0;
+}
+html .mceContentBody img.alignleft {
+	margin: 0 0 2px 7px;
+}
+dd.wp-caption p.wp-caption-text,
+.wp-caption p.wp-caption-text {
+	padding: 0 0 5px 4px;
+}
+html .mceContentBody ul,
+html .mceContentBody ol,
+html .mceContentBody dl {
+	margin: 0 1.5em 18px 0;
+}
+
+html .mceContentBody table th:first {
+	border-left: 1px solid #eaeaea;
+	border-right: 0;
+}
+html .mceContentBody table tr td.label {
+	border-left: 1px solid #eaeaea;
+	border-right: 0;
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/editor-style.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/editor-style.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/editor-style.css	(revision 5057)
@@ -0,0 +1,272 @@
+/*
+Theme Name: BuddyPress Default
+Description: Used to style the TinyMCE editor.
+*/
+
+html .mceContentBody {
+	font-family: Arial, Tahoma, Verdana, sans-serif;
+	font-size: 12px;
+	color: #555;
+}
+ul,
+ol {
+	margin: 0;
+	padding: 0;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+	margin: 5px 0 15px 0;
+}
+h1 {
+	font-size: 28px;
+	margin-bottom: 25px;
+}
+h2 {
+	font-size: 24px;
+	margin-bottom: 20px;
+}
+h3 {
+	font-size: 20px;
+}
+h4 {
+	font-size: 16px;
+	margin-bottom: 15px;
+}
+h5 {
+	font-size: 14px;
+	margin-bottom: 0;
+}
+h6 {
+	font-size: 12px;
+	margin-bottom: 0;
+}
+a {
+	color: #1fb3dd;
+}
+a:hover,
+a:active {
+	color: #888;
+}
+a:focus {
+	outline: 1px dotted #ccc;
+}
+p {
+	margin-bottom: 15px;
+}
+p:last-child {
+	margin-bottom: 0;
+}
+hr {
+	background-color: #e7e7e7;
+	border: 0 none;
+	clear: both;
+	height: 1px;
+	margin: 20px 0;
+}
+big {
+	font-size: 18px;
+}
+acronym {
+	border-bottom: 0;
+	cursor: normal;
+}
+acronym:hover {
+	cursor: normal;
+}
+del {
+	text-decoration: line-through;
+	color: #555;
+}
+ins {
+	background: #fff9db;
+	border: 0;
+	color: #555;
+	text-decoration: none;
+}
+sub {
+	top: .5ex;
+}
+sup {
+	bottom: 1ex;
+}
+sub,
+sup {
+	height: 0;
+	line-height: 1;
+	vertical-align: baseline;
+	position: relative;
+}
+html .mceContentBody .alignright {
+	float: right;
+	margin-left: 15px;
+}
+html .mceContentBody .alignleft {
+	float: left;
+	margin-right: 15px;
+}
+p {
+	margin: 0 0 20px 0;
+}
+dl {
+	margin-left: 0;
+}
+dt {
+	font-size: 14px;
+	font-weight: bold;
+}
+dd {
+	margin: 0 0 15px 0;
+}
+pre,
+code p {
+	padding: 15px;
+	background: #f4f4f4;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+code {
+	font-family: "Monaco", courier, sans-serif;
+}
+cite {
+	border: 0;
+}
+blockquote {
+	quotes: none;
+	font-style: italic;
+	margin: 0 0;
+	padding: 0 3em;
+	font-family: georgia, times, serif;
+	font-size: 16px;
+	line-height: 150%;
+}
+blockquote p {
+	margin-bottom: 15px !important;
+}
+p.date {
+	color: #888;
+	font-size: 12px;
+	font-family: Georgia, times, serif;
+	padding: 3px 0;
+	margin: 10px 0;
+	border-bottom: 1px solid #e4e4e4;
+	border-top: 1px solid #e4e4e4;
+}
+img.wp-smiley {
+	padding: 0 !important;
+	margin: 0 !important;
+	border: none !important;
+	float: none !important;
+	clear: none !important;
+}
+html .mceContentBody img.centered,
+html .mceContentBody img.aligncenter {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+}
+html .mceContentBody img.alignright {
+	padding: 4px;
+	margin: 0 0 2px 7px;
+	display: inline;
+}
+html .mceContentBody img.alignleft {
+	padding: 4px;
+	margin: 0 7px 2px 0;
+	display: inline;
+}
+html .mceContentBody .aligncenter {
+	display: block;
+	margin-left: auto;
+	margin-right: auto;
+}
+.wp-caption {
+	border: 1px solid #ddd;
+	text-align: center;
+	background-color: #f3f3f3;
+	padding-top: 4px;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+.wp-caption img {
+	margin: 0;
+	padding: 0;
+	border: 0 none;
+}
+dd.wp-caption p.wp-caption-text,
+.wp-caption p.wp-caption-text {
+	font-size: 0.9em;
+	line-height: 17px;
+	padding: 0 4px 5px 0;
+	margin: 0;
+}
+.gallery .gallery-caption {
+	color: #555;
+}
+table {
+	width: 100%;
+}
+table thead tr {
+	background: #eaeaea;
+}
+td {
+	color: #555;
+	font-size: 12px;
+}
+table tr td,
+table tr th {
+	padding: 8px;
+	vertical-align: middle;
+}
+table tr.alt td {
+	background: #f5f5f5;
+}
+html .mceContentBody ul,
+html .mceContentBody ol,
+html .mceContentBody dl {
+	margin: 0 0 18px 1.5em;
+}
+html .mceContentBody ul {
+	list-style: square;
+}
+html .mceContentBody ol {
+	list-style: decimal;
+}
+html .mceContentBody ol ol {
+	list-style: upper-alpha;
+}
+html .mceContentBody table {
+	border-collapse: collapse;
+	border-spacing: 0;
+	border: 1px solid #eee;
+}
+html .mceContentBody table th {
+	border-top: 1px solid #eee;
+	border-left: 0 none;
+	border-right: 0 none;
+	font-size: 12px;
+	text-align: left;
+}
+html .mceContentBody table th:first {
+	border-right: 1px solid #eaeaea;
+}
+html .mceContentBody table td {
+	border-top: 1px solid #eee;
+}
+html .mceContentBody img {
+	margin-bottom: 15px;
+}
+html .mceContentBody table tr td {
+	border-left: 0 none;
+	border-right: 0 none;
+}
+html .mceContentBody table tr td.label {
+	border-right: 1px solid #eaeaea;
+	font-weight: bold;
+	width: 25%;
+}
Index: /tags/1.5-beta-3/bp-themes/bp-default/footer.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/footer.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/footer.php	(revision 5057)
@@ -0,0 +1,27 @@
+		</div> <!-- #container -->
+
+		<?php do_action( 'bp_after_container' ) ?>
+		<?php do_action( 'bp_before_footer' ) ?>
+
+		<div id="footer">
+			<?php if ( is_active_sidebar( 'first-footer-widget-area' ) || is_active_sidebar( 'second-footer-widget-area' ) || is_active_sidebar( 'third-footer-widget-area' ) || is_active_sidebar( 'fourth-footer-widget-area' ) ) : ?>
+				<div id="footer-widgets">
+					<?php get_sidebar( 'footer' ) ?>
+				</div>
+			<?php endif; ?>
+
+			<div id="site-generator" role="contentinfo">
+				<?php do_action( 'bp_dtheme_credits' ) ?>
+				<p><?php printf( __( 'Proudly powered by <a href="%1$s">WordPress</a> and <a href="%2$s">BuddyPress</a>.', 'buddypress' ), 'http://wordpress.org', 'http://buddypress.org' ) ?></p>
+			</div>
+
+			<?php do_action( 'bp_footer' ) ?>
+		</div><!-- #footer -->
+
+		<?php do_action( 'bp_after_footer' ) ?>
+
+		<?php wp_footer(); ?>
+
+	</body>
+
+</html>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/forums-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/forums-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/forums-loop.php	(revision 5057)
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * BuddyPress - Forums Loop
+ *
+ * Querystring is set via AJAX in _inc/ajax.php - bp_dtheme_object_filter()
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_forums_loop' ); ?>
+
+<?php if ( bp_has_forum_topics( bp_ajax_querystring( 'forums' ) ) ) : ?>
+
+	<div id="pag-top" class="pagination">
+
+		<div class="pag-count" id="topic-count-top">
+
+			<?php bp_forum_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="topic-pag-top">
+
+			<?php bp_forum_pagination(); ?>
+
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_before_directory_forums_list' ); ?>
+
+	<table class="forum">
+		<thead>
+			<tr>
+				<th id="th-title"><?php _e( 'Topic', 'buddypress' ); ?></th>
+				<th id="th-postcount"><?php _e( 'Posts', 'buddypress' ); ?></th>
+				<th id="th-freshness"><?php _e( 'Freshness', 'buddypress' ); ?></th>
+
+				<?php do_action( 'bp_directory_forums_extra_cell_head' ); ?>
+
+			</tr>
+		</thead>
+
+		<tbody>
+
+			<?php while ( bp_forum_topics() ) : bp_the_forum_topic(); ?>
+
+			<tr class="<?php bp_the_topic_css_class(); ?>">
+				<td class="td-title">
+					<a class="topic-title" href="<?php bp_the_topic_permalink(); ?>" title="<?php bp_the_topic_title(); ?> - <?php _e( 'Permalink', 'buddypress' ); ?>">
+
+						<?php bp_the_topic_title(); ?>
+
+					</a>
+
+					<p class="topic-meta">
+						<span class="topic-by"><?php /* translators: "started by [poster] in [forum]" */ printf( __( 'Started by %1$s', 'buddypress' ), bp_get_the_topic_poster_avatar( 'height=20&width=20') . bp_get_the_topic_poster_name() ); ?></span>
+
+						<?php if ( bp_is_directory() || bp_is_user_forums() ) : ?>
+
+							<span class="topic-in">
+
+								<?php
+									$topic_in = '<a href="' . bp_get_the_topic_object_permalink() . '">' . bp_get_the_topic_object_avatar( 'type=thumb&width=20&height=20' ) . '</a>' .
+													'<a href="' . bp_get_the_topic_object_permalink() . '" title="' . bp_get_the_topic_object_name() . '">' . bp_get_the_topic_object_name() .'</a>';
+
+									/* translators: "started by [poster] in [forum]" */
+									printf( __( 'in %1$s', 'buddypress' ), $topic_in );
+								?>
+
+							</span>
+
+						<?php endif; ?>
+
+					</p>
+				</td>
+				<td class="td-postcount">
+					<?php bp_the_topic_total_posts(); ?>
+				</td>
+				<td class="td-freshness">
+					<span class="time-since"><?php bp_the_topic_time_since_last_post(); ?></span>
+					<p class="topic-meta">
+						<span class="freshness-author">
+							<a href="<?php bp_the_topic_permalink(); ?>"><?php bp_the_topic_last_poster_avatar( 'type=thumb&width=20&height=20' ); ?></a>
+							<?php bp_the_topic_last_poster_name(); ?>
+						</span>
+					</p>
+				</td>
+
+				<?php do_action( 'bp_directory_forums_extra_cell' ); ?>
+
+			</tr>
+
+			<?php do_action( 'bp_directory_forums_extra_row' ); ?>
+
+			<?php endwhile; ?>
+
+		</tbody>
+	</table>
+
+	<?php do_action( 'bp_after_directory_forums_list' ); ?>
+
+	<div id="pag-bottom" class="pagination">
+
+		<div class="pag-count" id="topic-count-bottom">
+			<?php bp_forum_pagination_count(); ?>
+		</div>
+
+		<div class="pagination-links" id="topic-pag-bottom">
+			<?php bp_forum_pagination(); ?>
+		</div>
+
+	</div>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'Sorry, there were no forum topics found.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_forums_loop' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/index.php	(revision 5057)
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * BuddyPress - Forums Directory
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_forums_page' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_directory_forums' ); ?>
+
+			<form action="" method="post" id="forums-search-form" class="dir-form">
+
+				<h3><?php _e( 'Forums Directory', 'buddypress' ); ?><?php if ( is_user_logged_in() ) : ?> &nbsp;<a class="button show-hide-new" href="#new-topic" id="new-topic-button"><?php _e( 'New Topic', 'buddypress' ); ?></a><?php endif; ?></h3>
+
+				<?php do_action( 'bp_before_directory_forums_content' ); ?>
+
+				<div id="forums-dir-search" class="dir-search" role="search">
+
+					<?php bp_directory_forums_search_form(); ?>
+
+				</div>
+			</form>
+
+			<?php do_action( 'bp_before_topics' ); ?>
+
+			<form action="" method="post" id="forums-directory-form" class="dir-form">
+
+				<div class="item-list-tabs" role="navigation">
+					<ul>
+						<li class="selected" id="forums-all"><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_forums_root_slug() ); ?>"><?php printf( __( 'All Topics <span>%s</span>', 'buddypress' ), bp_get_forum_topic_count() ); ?></a></li>
+
+						<?php if ( is_user_logged_in() && bp_get_forum_topic_count_for_user( bp_loggedin_user_id() ) ) : ?>
+
+							<li id="forums-personal"><a href="<?php echo trailingslashit( bp_loggedin_user_domain() . bp_get_forums_slug() . '/topics' ); ?>"><?php printf( __( 'My Topics <span>%s</span>', 'buddypress' ), bp_get_forum_topic_count_for_user( bp_loggedin_user_id() ) ); ?></a></li>
+
+						<?php endif; ?>
+
+						<?php do_action( 'bp_forums_directory_group_types' ); ?>
+
+					</ul>
+				</div>
+
+				<div class="item-list-tabs" id="subnav" role="navigation">
+					<ul>
+
+						<?php do_action( 'bp_forums_directory_group_sub_types' ); ?>
+
+						<li id="forums-order-select" class="last filter">
+
+							<label for="forums-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+							<select id="forums-order-by">
+								<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+								<option value="popular"><?php _e( 'Most Posts', 'buddypress' ); ?></option>
+								<option value="unreplied"><?php _e( 'Unreplied', 'buddypress' ); ?></option>
+
+								<?php do_action( 'bp_forums_directory_order_options' ); ?>
+
+							</select>
+						</li>
+					</ul>
+				</div>
+
+				<div id="forums-dir-list" class="forums dir-list" role="main">
+
+					<?php locate_template( array( 'forums/forums-loop.php' ), true ); ?>
+
+				</div>
+
+				<?php do_action( 'bp_directory_forums_content' ); ?>
+
+				<?php wp_nonce_field( 'directory_forums', '_wpnonce-forums-filter' ); ?>
+
+			</form>
+
+			<?php do_action( 'bp_after_directory_forums' ); ?>
+
+			<?php do_action( 'bp_before_new_topic_form' ); ?>
+
+			<div id="new-topic-post">
+
+				<?php if ( is_user_logged_in() ) : ?>
+
+					<?php if ( bp_is_active( 'groups' ) && bp_has_groups( 'user_id=' . bp_loggedin_user_id() . '&type=alphabetical&max=100&per_page=100' ) ) : ?>
+
+						<form action="" method="post" id="forum-topic-form" class="standard-form">
+
+							<?php do_action( 'groups_forum_new_topic_before' ) ?>
+
+							<a name="post-new"></a>
+							<h5><?php _e( 'Create New Topic:', 'buddypress' ); ?></h5>
+
+							<?php do_action( 'template_notices' ); ?>
+
+							<label><?php _e( 'Title:', 'buddypress' ); ?></label>
+							<input type="text" name="topic_title" id="topic_title" value="" />
+
+							<label><?php _e( 'Content:', 'buddypress' ); ?></label>
+							<textarea name="topic_text" id="topic_text"></textarea>
+
+							<label><?php _e( 'Tags (comma separated):', 'buddypress' ); ?></label>
+							<input type="text" name="topic_tags" id="topic_tags" value="" />
+
+							<label><?php _e( 'Post In Group Forum:', 'buddypress' ); ?></label>
+							<select id="topic_group_id" name="topic_group_id">
+
+								<option value=""><?php /* translators: no option picked in select box */ _e( '----', 'buddypress' ); ?></option>
+
+								<?php while ( bp_groups() ) : bp_the_group(); ?>
+
+									<?php if ( bp_group_is_forum_enabled() && ( is_super_admin() || 'public' == bp_get_group_status() || bp_group_is_member() ) ) : ?>
+
+										<option value="<?php bp_group_id(); ?>"><?php bp_group_name(); ?></option>
+
+									<?php endif; ?>
+
+								<?php endwhile; ?>
+
+							</select><!-- #topic_group_id -->
+
+							<?php do_action( 'groups_forum_new_topic_after' ); ?>
+
+							<div class="submit">
+								<input type="submit" name="submit_topic" id="submit" value="<?php _e( 'Post Topic', 'buddypress' ); ?>" />
+								<input type="button" name="submit_topic_cancel" id="submit_topic_cancel" value="<?php _e( 'Cancel', 'buddypress' ); ?>" />
+							</div>
+
+							<?php wp_nonce_field( 'bp_forums_new_topic' ); ?>
+
+						</form><!-- #forum-topic-form -->
+
+					<?php elseif ( bp_is_active( 'groups' ) ) : ?>
+
+						<div id="message" class="info">
+
+							<p><?php printf( __( "You are not a member of any groups so you don't have any group forums you can post in. To start posting, first find a group that matches the topic subject you'd like to start. If this group does not exist, why not <a href='%s'>create a new group</a>? Once you have joined or created the group you can post your topic in that group's forum.", 'buddypress' ), site_url( bp_get_groups_root_slug() . '/create/' ) ) ?></p>
+
+						</div>
+
+					<?php endif; ?>
+
+				<?php endif; ?>
+			</div><!-- #new-topic-post -->
+
+			<?php do_action( 'bp_after_new_topic_form' ); ?>
+
+			<?php do_action( 'bp_after_directory_forums_content' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_forums_page' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/single/edit.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/single/edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/single/edit.php	(revision 5057)
@@ -0,0 +1,93 @@
+<?php do_action( 'bp_before_group_forum_edit_form' ); ?>
+
+<?php if ( bp_has_forum_topic_posts() ) : ?>
+
+	<form action="<?php bp_forum_topic_action(); ?>" method="post" id="forum-topic-form" class="standard-form">
+
+		<div class="item-list-tabs" id="subnav" role="navigation">
+			<ul>
+				<li>
+					<a href="#post-topic-reply"><?php _e( 'Reply', 'buddypress' ); ?></a>
+				</li>
+
+				<?php if ( bp_forums_has_directory() ) : ?>
+
+					<li>
+						<a href="<?php bp_forums_directory_permalink(); ?>"><?php _e( 'Forum Directory', 'buddypress'); ?></a>
+					</li>
+
+				<?php endif; ?>
+
+			</ul>
+		</div>
+
+		<div id="topic-meta">
+			<h3><?php _e( 'Edit:', 'buddypress' ); ?> <?php bp_the_topic_title(); ?> (<?php bp_the_topic_total_post_count(); ?>)</h3>
+
+			<?php if ( bp_group_is_admin() || bp_group_is_mod() || bp_get_the_topic_is_mine() ) : ?>
+
+				<div class="last admin-links">
+
+					<?php bp_the_topic_admin_links(); ?>
+
+				</div>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_group_forum_topic_meta' ); ?>
+
+		</div>
+
+		<?php if ( bp_group_is_member() ) : ?>
+
+			<?php if ( bp_is_edit_topic() ) : ?>
+
+				<div id="edit-topic">
+
+					<?php do_action( 'bp_group_before_edit_forum_topic' ); ?>
+
+					<label for="topic_title"><?php _e( 'Title:', 'buddypress' ); ?></label>
+					<input type="text" name="topic_title" id="topic_title" value="<?php bp_the_topic_title(); ?>" />
+
+					<label for="topic_text"><?php _e( 'Content:', 'buddypress' ); ?></label>
+					<textarea name="topic_text" id="topic_text"><?php bp_the_topic_text(); ?></textarea>
+
+					<?php do_action( 'bp_group_after_edit_forum_topic' ); ?>
+
+					<p class="submit"><input type="submit" name="save_changes" id="save_changes" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" /></p>
+
+					<?php wp_nonce_field( 'bp_forums_edit_topic' ); ?>
+
+				</div>
+
+			<?php else : ?>
+
+				<div id="edit-post">
+
+					<?php do_action( 'bp_group_before_edit_forum_post' ); ?>
+
+					<textarea name="post_text" id="post_text"><?php bp_the_topic_post_edit_text(); ?></textarea>
+
+					<?php do_action( 'bp_group_after_edit_forum_post' ) ?>
+
+					<p class="submit"><input type="submit" name="save_changes" id="save_changes" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" /></p>
+
+					<?php wp_nonce_field( 'bp_forums_edit_post' ); ?>
+
+				</div>
+
+			<?php endif; ?>
+
+		<?php endif; ?>
+
+	</form><!-- #forum-topic-form -->
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'This topic does not exist.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_group_forum_edit_form' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum-header.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum-header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum-header.php	(revision 5057)
@@ -0,0 +1,35 @@
+<?php do_action( 'bp_before_forum_header' ); ?>
+
+<div id="item-header-avatar">
+	<a href="<?php bp_forum_permalink(); ?>" title="<?php bp_get_forum_name(); ?>">
+
+		<?php //bp_forum_avatar(); ?>
+
+	</a>
+</div><!-- #item-header-avatar -->
+
+<div id="item-header-content">
+	<h2><a href="<?php bp_forum_permalink(); ?>" title="<?php bp_forum_name(); ?>"><?php bp_forum_name(); ?></a></h2>
+	<span class="highlight"><?php //bp_forum_type(); ?></span> <span class="activity"><?php printf( __( 'active %s', 'buddypress' ), '' ); //bp_get_forum_last_active() ); ?></span>
+
+	<?php do_action( 'bp_before_forum_header_meta' ); ?>
+
+	<div id="item-meta">
+
+		<?php //bp_forum_description(); ?>
+
+		<div id="item-buttons">
+
+			<?php do_action( 'bp_forum_header_actions' ); ?>
+
+		</div><!-- #item-buttons -->
+
+		<?php do_action( 'bp_forum_header_meta' ); ?>
+
+	</div>
+</div><!-- #item-header-content -->
+
+<?php
+do_action( 'bp_after_forum_header' );
+do_action( 'template_notices' );
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/single/forum.php	(revision 5057)
@@ -0,0 +1,65 @@
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_directory_forums_content' ); ?>
+
+				<div id="item-header" role="complementary">
+
+					<?php locate_template( array( 'forums/single/forum-header.php' ), true ); ?>
+
+				</div><!-- #item-header -->
+
+				<div id="item-nav">
+					<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+						<ul>
+
+							<li>
+								<a href="#post-new" class="show-hide-new"><?php _e( 'New Topic', 'buddypress' ); ?></a>
+							</li>
+
+							<?php if ( bp_forums_has_directory() ) : ?>
+
+								<li>
+									<a href="<?php bp_forums_directory_permalink() ?>"><?php _e( 'Forum Directory', 'buddypress'); ?></a>
+								</li>
+
+							<?php endif; ?>
+
+							<?php do_action( 'bp_forums_directory_group_sub_types' ); ?>
+
+							<li id="forums-order-select" class="last filter">
+
+								<label for="forums-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+								<select id="forums-order-by">
+									<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+									<option value="popular"><?php _e( 'Most Posts', 'buddypress' ); ?></option>
+									<option value="unreplied"><?php _e( 'Unreplied', 'buddypress' ); ?></option>
+
+									<?php do_action( 'bp_forums_directory_order_options' ); ?>
+
+								</select>
+							</li>
+						</ul>
+					</div>
+				</div><!-- #item-nav -->
+
+				<div id="item-body">
+
+					<div id="forums-dir-list" class="forums dir-list" role="main">
+
+						<?php locate_template( array( 'forums/forums-loop.php' ), true ); ?>
+
+					</div>
+
+					<?php do_action( 'bp_directory_forums_content' ); ?>
+
+				</div>
+			</div><!-- #new-topic-post -->
+		</div><!-- .padder -->
+
+	<?php do_action( 'bp_after_directory_forums_content' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/forums/single/topic.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/forums/single/topic.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/forums/single/topic.php	(revision 5057)
@@ -0,0 +1,195 @@
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_group_home_content' ) ?>
+
+			<?php if ( bp_has_forum_topic_posts() ) : ?>
+
+				<div id="item-header" role="complementary">
+
+					<?php locate_template( array( 'forums/single/forum-header.php' ), true ); ?>
+
+				</div><!-- #item-header -->
+
+				<div id="item-nav">
+					<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+						<ul>
+
+							<?php bp_get_options_nav(); ?>
+
+							<?php do_action( 'bp_forum_options_nav' ); ?>
+
+						</ul>
+					</div>
+				</div><!-- #item-nav -->
+
+				<div id="item-body">
+
+					<?php do_action( 'bp_before_group_forum_topic' ); ?>
+
+					<form action="<?php bp_forum_topic_action() ?>" method="post" id="forum-topic-form" class="standard-form">
+
+						<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+							<ul>
+								<li>
+									<a href="#post-topic-reply" class="show-hide-new"><?php _e( 'New Reply', 'buddypress' ) ?></a>
+								</li>
+
+								<?php if ( bp_forums_has_directory() ) : ?>
+
+									<li>
+										<a href="<?php bp_forums_directory_permalink() ?>"><?php _e( 'Forum Directory', 'buddypress') ?></a>
+									</li>
+
+								<?php endif; ?>
+
+							</ul>
+						</div>
+
+						<div id="topic-meta">
+							<h3><?php bp_the_topic_title() ?> (<?php bp_the_topic_total_post_count() ?>)</h3>
+
+							<?php if ( is_super_admin() || current_user_can( 'moderate' ) ) : ?>
+
+								<div class="last admin-links">
+
+									<?php bp_the_topic_admin_links(); ?>
+
+								</div>
+
+							<?php endif; ?>
+
+							<?php do_action( 'bp_group_forum_topic_meta' ); ?>
+
+						</div>
+
+						<div class="pagination no-ajax">
+
+							<div id="post-count-top" class="pag-count">
+
+								<?php bp_the_topic_pagination_count() ?>
+
+							</div>
+
+							<div class="pagination-links" id="topic-pag-top">
+
+								<?php bp_the_topic_pagination() ?>
+
+							</div>
+
+						</div>
+
+						<?php do_action( 'bp_before_group_forum_topic_posts' ) ?>
+
+						<ul id="topic-post-list" class="item-list" role="main">
+							<?php while ( bp_forum_topic_posts() ) : bp_the_forum_topic_post(); ?>
+
+								<li id="post-<?php bp_the_topic_post_id() ?>" class="<?php bp_the_topic_post_css_class() ?>">
+									<div class="poster-meta">
+										<a href="<?php bp_the_topic_post_poster_link() ?>">
+
+											<?php bp_the_topic_post_poster_avatar( 'width=40&height=40' ); ?>
+
+										</a>
+
+										<?php echo sprintf( __( '%s said %s ago:', 'buddypress' ), bp_get_the_topic_post_poster_name(), bp_get_the_topic_post_time_since() ) ?>
+
+									</div>
+
+									<div class="post-content">
+
+										<?php bp_the_topic_post_content() ?>
+
+									</div>
+
+									<div class="admin-links">
+
+										<?php if ( is_super_admin() || current_user_can( 'moderate' ) ) : ?>
+
+											<?php bp_the_topic_post_admin_links() ?>
+
+										<?php endif; ?>
+
+										<?php do_action( 'bp_group_forum_post_meta' ); ?>
+
+										<a href="#post-<?php bp_the_topic_post_id() ?>" title="<?php _e( 'Permanent link to this post', 'buddypress' ) ?>">#</a>
+									</div>
+								</li>
+
+							<?php endwhile; ?>
+						</ul><!-- #topic-post-list -->
+
+						<?php do_action( 'bp_after_group_forum_topic_posts' ) ?>
+
+						<div class="pagination no-ajax">
+
+							<div id="post-count-bottom" class="pag-count">
+
+								<?php bp_the_topic_pagination_count() ?>
+
+							</div>
+
+							<div class="pagination-links" id="topic-pag-bottom">
+
+								<?php bp_the_topic_pagination() ?>
+
+							</div>
+
+						</div>
+
+						<?php if ( is_user_logged_in() ) : ?>
+
+							<?php if ( bp_get_the_topic_is_last_page() ) : ?>
+
+								<?php if ( bp_get_the_topic_is_topic_open() ) : ?>
+
+									<div id="post-topic-reply">
+										<p id="post-reply"></p>
+
+										<?php do_action( 'groups_forum_new_reply_before' ) ?>
+
+										<h4><?php _e( 'Add a reply:', 'buddypress' ) ?></h4>
+
+										<textarea name="reply_text" id="reply_text"></textarea>
+
+										<div class="submit">
+											<input type="submit" name="submit_reply" id="submit" value="<?php _e( 'Post Reply', 'buddypress' ) ?>" />
+										</div>
+
+										<?php do_action( 'groups_forum_new_reply_after' ) ?>
+
+										<?php wp_nonce_field( 'bp_forums_new_reply' ) ?>
+									</div>
+
+								<?php else : ?>
+
+									<div id="message" class="info">
+										<p><?php _e( 'This topic is closed, replies are no longer accepted.', 'buddypress' ) ?></p>
+									</div>
+
+								<?php endif; ?>
+
+							<?php endif; ?>
+
+						<?php endif; ?>
+
+					</form><!-- #forum-topic-form -->
+				</div>
+
+			<?php else: ?>
+
+				<div id="message" class="info">
+					<p><?php _e( 'There are no posts for this topic.', 'buddypress' ) ?></p>
+				</div>
+
+			<?php endif;?>
+
+		</div>
+	</div>
+
+<?php do_action( 'bp_after_group_forum_topic' ) ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/functions.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/functions.php	(revision 5057)
@@ -0,0 +1,725 @@
+<?php
+/**
+ * BP-Default theme functions and definitions
+ *
+ * Sets up the theme and provides some helper functions. Some helper functions
+ * are used in the theme as custom template tags. Others are attached to action and
+ * filter hooks in WordPress and BuddyPress to change core functionality.
+ *
+ * The first function, bp_dtheme_setup(), sets up the theme by registering support
+ * for various features in WordPress, such as post thumbnails and navigation menus, and
+ * for BuddyPress, action buttons and javascript localisation.
+ *
+ * When using a child theme (see http://codex.wordpress.org/Theme_Development, http://codex.wordpress.org/Child_Themes
+ * and http://codex.buddypress.org/theme-development/building-a-buddypress-child-theme/), you can override
+ * certain functions (those wrapped in a function_exists() call) by defining them first in your
+ * child theme's functions.php file. The child theme's functions.php file is included before the
+ * parent theme's file, so the child theme functions would be used.
+ *
+ * Functions that are not pluggable (not wrapped in function_exists()) are instead attached
+ * to a filter or action hook. The hook can be removed by using remove_action() or
+ * remove_filter() and you can attach your own function to the hook.
+ *
+ * For more information on hooks, actions, and filters, see http://codex.wordpress.org/Plugin_API.
+ *
+ * @package BuddyPress
+ * @subpackage BP-Default
+ * @since 1.2
+ */
+
+if ( !function_exists( 'bp_is_active' ) )
+	return;
+
+// If BuddyPress is not activated, switch back to the default WP theme
+if ( !defined( 'BP_VERSION' ) )
+	switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
+
+/**
+ * Set the content width based on the theme's design and stylesheet.
+ *
+ * Used to set the width of images and content. Should be equal to the width the theme
+ * is designed for, generally via the style.css stylesheet.
+ */
+if ( ! isset( $content_width ) )
+	$content_width = 591;
+
+if ( !function_exists( 'bp_dtheme_setup' ) ) :
+/**
+ * Sets up theme defaults and registers support for various WordPress and BuddyPress features.
+ *
+ * Note that this function is hooked into the after_setup_theme hook, which runs
+ * before the init hook. The init hook is too late for some features, such as indicating
+ * support post thumbnails.
+ *
+ * To override bp_dtheme_setup() in a child theme, add your own bp_dtheme_setup to your child theme's
+ * functions.php file.
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @since 1.5
+ */
+function bp_dtheme_setup() {
+	global $bp;
+
+	// Load the AJAX functions for the theme
+	require( TEMPLATEPATH . '/_inc/ajax.php' );
+
+	// This theme styles the visual editor with editor-style.css to match the theme style.
+	add_editor_style();
+
+	// This theme uses post thumbnails
+	add_theme_support( 'post-thumbnails' );
+
+	// Add default posts and comments RSS feed links to head
+	add_theme_support( 'automatic-feed-links' );
+
+	// Add responsive layout support to bp-default without forcing child
+	// themes to inherit it if they don't want to
+	add_theme_support( 'bp-default-responsive' );
+
+	// This theme uses wp_nav_menu() in one location.
+	register_nav_menus( array(
+		'primary' => __( 'Primary Navigation', 'buddypress' ),
+	) );
+
+	// This theme allows users to set a custom background
+	add_custom_background( 'bp_dtheme_custom_background_style' );
+
+	// Add custom header support if allowed
+	if ( !defined( 'BP_DTHEME_DISABLE_CUSTOM_HEADER' ) ) {
+		define( 'HEADER_TEXTCOLOR', 'FFFFFF' );
+
+		// The height and width of your custom header. You can hook into the theme's own filters to change these values.
+		// Add a filter to bp_dtheme_header_image_width and bp_dtheme_header_image_height to change these values.
+		define( 'HEADER_IMAGE_WIDTH',  apply_filters( 'bp_dtheme_header_image_width',  1250 ) );
+		define( 'HEADER_IMAGE_HEIGHT', apply_filters( 'bp_dtheme_header_image_height', 125  ) );
+
+		// We'll be using post thumbnails for custom header images on posts and pages. We want them to be 1250 pixels wide by 125 pixels tall.
+		// Larger images will be auto-cropped to fit, smaller ones will be ignored.
+		set_post_thumbnail_size( HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT, true );
+
+		// Add a way for the custom header to be styled in the admin panel that controls custom headers.
+		add_custom_image_header( 'bp_dtheme_header_style', 'bp_dtheme_admin_header_style' );
+	}
+
+	if ( !is_admin() ) {
+		// Register buttons for the relevant component templates
+		// Friends button
+		if ( bp_is_active( 'friends' ) )
+			add_action( 'bp_member_header_actions',    'bp_add_friend_button' );
+
+		// Activity button
+		if ( bp_is_active( 'activity' ) )
+			add_action( 'bp_member_header_actions',    'bp_send_public_message_button' );
+
+		// Messages button
+		if ( bp_is_active( 'messages' ) )
+			add_action( 'bp_member_header_actions',    'bp_send_private_message_button' );
+
+		// Group buttons
+		if ( bp_is_active( 'groups' ) ) {
+			add_action( 'bp_group_header_actions',     'bp_group_join_button' );
+			add_action( 'bp_group_header_actions',     'bp_group_new_topic_button' );
+			add_action( 'bp_directory_groups_actions', 'bp_group_join_button' );
+		}
+
+		// Blog button
+		if ( bp_is_active( 'blogs' ) )
+			add_action( 'bp_directory_blogs_actions',  'bp_blogs_visit_blog_button' );
+	}
+}
+add_action( 'after_setup_theme', 'bp_dtheme_setup' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_enqueue_scripts' ) ) :
+/**
+ * Enqueue theme javascript safely
+ *
+ * @see http://codex.wordpress.org/Function_Reference/wp_enqueue_script
+ * @since 1.5
+ */
+function bp_dtheme_enqueue_scripts() {
+	// Bump this when changes are made to bust cache
+	$version = '20110830';
+
+	// Enqueue the global JS - Ajax will not work without it
+	wp_enqueue_script( 'dtheme-ajax-js', get_template_directory_uri() . '/_inc/global.js', array( 'jquery' ), $version );
+
+	// Add words that we need to use in JS to the end of the page so they can be translated and still used.
+	$params = array(
+		'my_favs'           => __( 'My Favorites', 'buddypress' ),
+		'accepted'          => __( 'Accepted', 'buddypress' ),
+		'rejected'          => __( 'Rejected', 'buddypress' ),
+		'show_all_comments' => __( 'Show all comments for this thread', 'buddypress' ),
+		'show_all'          => __( 'Show all', 'buddypress' ),
+		'comments'          => __( 'comments', 'buddypress' ),
+		'close'             => __( 'Close', 'buddypress' ),
+		'view'              => __( 'View', 'buddypress' )
+	);
+
+	wp_localize_script( 'dtheme-ajax-js', 'BP_DTheme', $params );
+}
+add_action( 'wp_enqueue_scripts', 'bp_dtheme_enqueue_scripts' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_enqueue_styles' ) ) :
+/**
+ * Enqueue theme CSS safely
+ *
+ * For maximum flexibility, BuddyPress Default's stylesheet is enqueued in the following fashion.
+ * If you're building a child theme of bp-default, you can prevent this enqueuing by overriding
+ * this function in your child theme's functions.php:
+ *   if ( !function_exists( 'bp_dtheme_enqueue_styles' ) ) :
+ *       function bp_dtheme_enqueue_styles() {}
+ *   endif;
+ *
+ * @see http://codex.wordpress.org/Function_Reference/wp_enqueue_style
+ * @see http://codex.buddypress.org/releases/1-5-developer-and-designer-information/
+ * @since 1.5
+ */
+function bp_dtheme_enqueue_styles() {
+	// Bump this when changes are made to bust cache
+	$version = '20110830';
+
+	// Default CSS
+	wp_enqueue_style( 'bp-default-main', get_template_directory_uri() . '/_inc/css/default.css', array(), $version );
+
+	// Default CSS RTL
+	if ( is_rtl() )
+		wp_enqueue_style( 'bp-default-main-rtl',  get_template_directory_uri() . '/_inc/css/default-rtl.css', array( 'bp-default-main' ), $version );
+
+	// Responsive layout
+	if ( current_theme_supports( 'bp-default-responsive' ) ) {
+		wp_enqueue_style( 'bp-default-responsive', get_template_directory_uri() . '/_inc/css/responsive.css', array( 'bp-default-main' ), $version );
+
+		if ( is_rtl() )
+			wp_enqueue_style( 'bp-default-responsive-rtl', get_template_directory_uri() . '/_inc/css/responsive-rtl.css', array( 'bp-default-responsive' ), $version );
+	}
+}
+add_action( 'wp_print_styles', 'bp_dtheme_enqueue_styles' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_admin_header_style' ) ) :
+/**
+ * Styles the header image displayed on the Appearance > Header admin panel.
+ *
+ * Referenced via add_custom_image_header() in bp_dtheme_setup().
+ *
+ * @since 1.2
+ */
+function bp_dtheme_admin_header_style() {
+?>
+	<style type="text/css">
+		#headimg {
+			position: relative;
+			color: #fff;
+			background: url(<?php header_image() ?>);
+			-moz-border-radius-bottomleft: 6px;
+			-webkit-border-bottom-left-radius: 6px;
+			-moz-border-radius-bottomright: 6px;
+			-webkit-border-bottom-right-radius: 6px;
+			margin-bottom: 20px;
+			height: 125px;
+		}
+
+		#headimg h1{
+			position: absolute;
+			bottom: 15px;
+			left: 15px;
+			width: 44%;
+			margin: 0;
+			font-family: Arial, Tahoma, sans-serif;
+		}
+		#headimg h1 a{
+			color:#<?php header_textcolor() ?>;
+			text-decoration: none;
+			border-bottom: none;
+		}
+		#headimg #desc{
+			color:#<?php header_textcolor() ?>;
+			font-size:1em;
+			margin-top:-0.5em;
+		}
+
+		#desc {
+			display: none;
+		}
+
+		<?php if ( 'blank' == get_header_textcolor() ) { ?>
+		#headimg h1, #headimg #desc {
+			display: none;
+		}
+		#headimg h1 a, #headimg #desc {
+			color:#<?php echo HEADER_TEXTCOLOR ?>;
+		}
+		<?php } ?>
+	</style>
+<?php
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_custom_background_style' ) ) :
+/**
+ * The style for the custom background image or colour.
+ *
+ * Referenced via add_custom_background() in bp_dtheme_setup().
+ *
+ * @see _custom_background_cb()
+ * @since 1.5
+ */
+function bp_dtheme_custom_background_style() {
+	$background = get_background_image();
+	$color = get_background_color();
+	if ( ! $background && ! $color )
+		return;
+
+	$style = $color ? "background-color: #$color;" : '';
+
+	if ( $style && !$background ) {
+		$style .= ' background-image: none;';
+
+	} elseif ( $background ) {
+		$image = " background-image: url('$background');";
+
+		$repeat = get_theme_mod( 'background_repeat', 'repeat' );
+		if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
+			$repeat = 'repeat';
+		$repeat = " background-repeat: $repeat;";
+
+		$position = get_theme_mod( 'background_position_x', 'left' );
+		if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
+			$position = 'left';
+		$position = " background-position: top $position;";
+
+		$attachment = get_theme_mod( 'background_attachment', 'scroll' );
+		if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
+			$attachment = 'scroll';
+		$attachment = " background-attachment: $attachment;";
+
+		$style .= $image . $repeat . $position . $attachment;
+	}
+?>
+	<style type="text/css">
+		body { <?php echo trim( $style ); ?> }
+	</style>
+<?php
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_header_style' ) ) :
+/**
+ * The styles for the post thumbnails / custom page headers.
+ *
+ * Referenced via add_custom_image_header() in bp_dtheme_setup().
+ *
+ * @global WP_Query $post The current WP_Query object for the current post or page
+ * @since 1.2
+ */
+function bp_dtheme_header_style() {
+	global $post;
+
+	$header_image = '';
+
+	if ( is_singular() && current_theme_supports( 'post-thumbnails' ) && has_post_thumbnail( $post->ID ) ) {
+		$image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'post-thumbnail' );
+
+		// $src, $width, $height
+		if ( !empty( $image ) && $image[1] >= HEADER_IMAGE_WIDTH )
+			$header_image = $image[0];
+
+	} else {
+		$header_image = get_header_image();
+	}
+
+	if ( empty( $header_image ) )
+		return;
+?>
+
+	<style type="text/css">
+		#header { background-image: url(<?php echo $header_image ?>); }
+		<?php if ( 'blank' == get_header_textcolor() ) { ?>
+		#header h1, #header #desc { display: none; }
+		<?php } else { ?>
+		#header h1 a, #desc { color:#<?php header_textcolor() ?>; }
+		<?php } ?>
+	</style>
+
+<?php
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_widgets_init' ) ) :
+/**
+ * Register widgetised areas, including one sidebar and four widget-ready columns in the footer.
+ *
+ * To override bp_dtheme_widgets_init() in a child theme, remove the action hook and add your own
+ * function tied to the init hook.
+ *
+ * @since 1.5
+ */
+function bp_dtheme_widgets_init() {
+	// Register the widget columns
+	// Area 1, located in the sidebar. Empty by default.
+	register_sidebar( array(
+		'name'          => 'Sidebar',
+		'id'            => 'sidebar-1',
+		'description'   => __( 'The sidebar widget area', 'buddypress' ),
+		'before_widget' => '<div id="%1$s" class="widget %2$s">',
+		'after_widget'  => '</div>',
+		'before_title'  => '<h3 class="widgettitle">',
+		'after_title'   => '</h3>'
+	) );
+
+	// Area 2, located in the footer. Empty by default.
+	register_sidebar( array(
+		'name' => __( 'First Footer Widget Area', 'buddypress' ),
+		'id' => 'first-footer-widget-area',
+		'description' => __( 'The first footer widget area', 'buddypress' ),
+		'before_widget' => '<li id="%1$s" class="widget %2$s">',
+		'after_widget' => '</li>',
+		'before_title' => '<h3 class="widgettitle">',
+		'after_title' => '</h3>',
+	) );
+
+	// Area 3, located in the footer. Empty by default.
+	register_sidebar( array(
+		'name' => __( 'Second Footer Widget Area', 'buddypress' ),
+		'id' => 'second-footer-widget-area',
+		'description' => __( 'The second footer widget area', 'buddypress' ),
+		'before_widget' => '<li id="%1$s" class="widget %2$s">',
+		'after_widget' => '</li>',
+		'before_title' => '<h3 class="widgettitle">',
+		'after_title' => '</h3>',
+	) );
+
+	// Area 4, located in the footer. Empty by default.
+	register_sidebar( array(
+		'name' => __( 'Third Footer Widget Area', 'buddypress' ),
+		'id' => 'third-footer-widget-area',
+		'description' => __( 'The third footer widget area', 'buddypress' ),
+		'before_widget' => '<li id="%1$s" class="widget %2$s">',
+		'after_widget' => '</li>',
+		'before_title' => '<h3 class="widgettitle">',
+		'after_title' => '</h3>',
+	) );
+
+	// Area 5, located in the footer. Empty by default.
+	register_sidebar( array(
+		'name' => __( 'Fourth Footer Widget Area', 'buddypress' ),
+		'id' => 'fourth-footer-widget-area',
+		'description' => __( 'The fourth footer widget area', 'buddypress' ),
+		'before_widget' => '<li id="%1$s" class="widget %2$s">',
+		'after_widget' => '</li>',
+		'before_title' => '<h3 class="widgettitle">',
+		'after_title' => '</h3>',
+	) );
+}
+add_action( 'widgets_init', 'bp_dtheme_widgets_init' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_blog_comments' ) ) :
+/**
+ * Template for comments and pingbacks.
+ *
+ * To override this walker in a child theme without modifying the comments template
+ * simply create your own bp_dtheme_blog_comments(), and that function will be used instead.
+ *
+ * Used as a callback by wp_list_comments() for displaying the comments.
+ *
+ * @param mixed $comment Comment record from database
+ * @param array $args Arguments from wp_list_comments() call
+ * @param int $depth Comment nesting level
+ * @see wp_list_comments()
+ * @since 1.2
+ */
+function bp_dtheme_blog_comments( $comment, $args, $depth ) {
+	$GLOBALS['comment'] = $comment;
+
+	if ( 'pingback' == $comment->comment_type )
+		return false;
+
+	if ( 1 == $depth )
+		$avatar_size = 50;
+	else
+		$avatar_size = 25;
+	?>
+
+	<li <?php comment_class() ?> id="comment-<?php comment_ID() ?>">
+		<div class="comment-avatar-box">
+			<div class="avb">
+				<a href="<?php echo get_comment_author_url() ?>" rel="nofollow">
+					<?php if ( $comment->user_id ) : ?>
+						<?php echo bp_core_fetch_avatar( array( 'item_id' => $comment->user_id, 'width' => $avatar_size, 'height' => $avatar_size, 'email' => $comment->comment_author_email ) ) ?>
+					<?php else : ?>
+						<?php echo get_avatar( $comment, $avatar_size ) ?>
+					<?php endif; ?>
+				</a>
+			</div>
+		</div>
+
+		<div class="comment-content">
+			<div class="comment-meta">
+				<p>
+					<?php
+						/* translators: 1: comment author url, 2: comment author name, 3: comment permalink, 4: comment date/timestamp*/
+						printf( __( '<a href="%1$s" rel="nofollow">%2$s</a> said on <a href="%3$s"><span class="time-since">%4$s</span></a>', 'buddypress' ), get_comment_author_url(), get_comment_author(), get_comment_link(), get_comment_date() );
+					?>
+				</p>
+			</div>
+
+			<div class="comment-entry">
+				<?php if ( $comment->comment_approved == '0' ) : ?>
+				 	<em class="moderate"><?php _e( 'Your comment is awaiting moderation.', 'buddypress' ); ?></em>
+				<?php endif; ?>
+
+				<?php comment_text() ?>
+			</div>
+
+			<div class="comment-options">
+					<?php if ( comments_open() ) : ?>
+						<?php comment_reply_link( array( 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ); ?>
+					<?php endif; ?>
+
+					<?php if ( current_user_can( 'edit_comment', $comment->comment_ID ) ) : ?>
+						<?php printf( '<a class="button comment-edit-link bp-secondary-action" href="%1$s" title="%2$s">%3$s</a> ', get_edit_comment_link( $comment->comment_ID ), esc_attr__( 'Edit comment', 'buddypress' ), __( 'Edit', 'buddypress' ) ) ?>
+					<?php endif; ?>
+
+			</div>
+
+		</div>
+
+<?php
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_page_on_front' ) ) :
+/**
+ * Return the ID of a page set as the home page.
+ *
+ * @return false|int ID of page set as the home page
+ * @since 1.2
+ */
+function bp_dtheme_page_on_front() {
+	if ( 'page' != get_option( 'show_on_front' ) )
+		return false;
+
+	return apply_filters( 'bp_dtheme_page_on_front', get_option( 'page_on_front' ) );
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_activity_secondary_avatars' ) ) :
+/**
+ * Add secondary avatar image to this activity stream's record, if supported.
+ *
+ * @param string $action The text of this activity
+ * @param BP_Activity_Activity $activity Activity object
+ * @package BuddyPress Theme
+ * @return string
+ * @since 1.2.6
+ */
+function bp_dtheme_activity_secondary_avatars( $action, $activity ) {
+	switch ( $activity->component ) {
+		case 'groups' :
+		case 'friends' :
+			// Only insert avatar if one exists
+			if ( $secondary_avatar = bp_get_activity_secondary_avatar() ) {
+				$reverse_content = strrev( $action );
+				$position        = strpos( $reverse_content, 'a<' );
+				$action          = substr_replace( $action, $secondary_avatar, -$position - 2, 0 );
+			}
+			break;
+	}
+
+	return $action;
+}
+add_filter( 'bp_get_activity_action_pre_meta', 'bp_dtheme_activity_secondary_avatars', 10, 2 );
+endif;
+
+if ( !function_exists( 'bp_dtheme_show_notice' ) ) :
+/**
+ * Show a notice when the theme is activated - workaround by Ozh (http://old.nabble.com/Activation-hook-exist-for-themes--td25211004.html)
+ *
+ * @since 1.2
+ */
+function bp_dtheme_show_notice() {
+	global $pagenow;
+
+	// Bail if bp-default theme was not just activated
+	if ( empty( $_GET['activated'] ) || ( 'themes.php' != $pagenow ) || !is_admin() )
+		return;
+
+	?>
+
+	<div id="message" class="updated fade">
+		<p><?php printf( __( 'Theme activated! This theme contains <a href="%s">custom header image</a> support and <a href="%s">sidebar widgets</a>.', 'buddypress' ), admin_url( 'themes.php?page=custom-header' ), admin_url( 'widgets.php' ) ) ?></p>
+	</div>
+
+	<style type="text/css">#message2, #message0 { display: none; }</style>
+
+	<?php
+}
+add_action( 'admin_notices', 'bp_dtheme_show_notice' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_main_nav' ) ) :
+/**
+ * wp_nav_menu() callback from the main navigation in header.php
+ *
+ * Used when the custom menus haven't been configured.
+ *
+ * @global object $bp Global BuddyPress settings object
+ * @param array Menu arguments from wp_nav_menu()
+ * @see wp_nav_menu()
+ * @since 1.5
+ */
+function bp_dtheme_main_nav( $args ) {
+	global $bp;
+
+	$pages_args = array(
+		'depth'      => 0,
+		'echo'       => false,
+		'exclude'    => '',
+		'title_li'   => ''
+	);
+	$menu = wp_page_menu( $pages_args );
+	$menu = str_replace( array( '<div class="menu"><ul>', '</ul></div>' ), array( '<ul id="nav">', '</ul><!-- #nav -->' ), $menu );
+	echo $menu;
+
+	do_action( 'bp_nav_items' );
+}
+endif;
+
+if ( !function_exists( 'bp_dtheme_page_menu_args' ) ) :
+/**
+ * Get our wp_nav_menu() fallback, bp_dtheme_main_nav(), to show a home link.
+ *
+ * @param array $args Default values for wp_page_menu()
+ * @see wp_page_menu()
+ * @since 1.5
+ */
+function bp_dtheme_page_menu_args( $args ) {
+	$args['show_home'] = true;
+	return $args;
+}
+add_filter( 'wp_page_menu_args', 'bp_dtheme_page_menu_args' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_comment_form' ) ) :
+/**
+ * Applies BuddyPress customisations to the post comment form.
+ *
+ * @global string $user_identity The display name of the user
+ * @param array $default_labels The default options for strings, fields etc in the form
+ * @see comment_form()
+ * @since 1.5
+ */
+function bp_dtheme_comment_form( $default_labels ) {
+	global $user_identity;
+
+	$commenter = wp_get_current_commenter();
+	$req = get_option( 'require_name_email' );
+	$aria_req = ( $req ? " aria-required='true'" : '' );
+	$fields =  array(
+		'author' => '<p class="comment-form-author">' . '<label for="author">' . __( 'Name', 'buddypress' ) . ( $req ? '<span class="required"> *</span>' : '' ) . '</label> ' .
+		            '<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' /></p>',
+		'email'  => '<p class="comment-form-email"><label for="email">' . __( 'Email', 'buddypress' ) . ( $req ? '<span class="required"> *</span>' : '' ) . '</label> ' .
+		            '<input id="email" name="email" type="text" value="' . esc_attr(  $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' /></p>',
+		'url'    => '<p class="comment-form-url"><label for="url">' . __( 'Website', 'buddypress' ) . '</label>' .
+		            '<input id="url" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" /></p>',
+	);
+
+	$new_labels = array(
+		'comment_field'  => '<p class="form-textarea"><textarea name="comment" id="comment" cols="60" rows="10" aria-required="true"></textarea></p>',
+		'fields'         => apply_filters( 'comment_form_default_fields', $fields ),
+		'logged_in_as'   => '',
+		'must_log_in'    => '<p class="alert">' . sprintf( __( 'You must be <a href="%1$s">logged in</a> to post a comment.', 'buddypress' ), wp_login_url( get_permalink() ) )	. '</p>',
+		'title_reply'    => __( 'Leave a reply', 'buddypress' )
+	);
+
+	return apply_filters( 'bp_dtheme_comment_form', array_merge( $default_labels, $new_labels ) );
+}
+add_filter( 'comment_form_defaults', 'bp_dtheme_comment_form', 10 );
+endif;
+
+if ( !function_exists( 'bp_dtheme_before_comment_form' ) ) :
+/**
+ * Adds the user's avatar before the comment form box.
+ *
+ * The 'comment_form_top' action is used to insert our HTML within <div id="reply">
+ * so that the nested comments comment-reply javascript moves the entirety of the comment reply area.
+ *
+ * @see comment_form()
+ * @since 1.5
+ */
+function bp_dtheme_before_comment_form() {
+?>
+	<div class="comment-avatar-box">
+		<div class="avb">
+			<?php if ( bp_loggedin_user_id() ) : ?>
+				<a href="<?php echo bp_loggedin_user_domain() ?>">
+					<?php echo get_avatar( bp_loggedin_user_id(), 50 ) ?>
+				</a>
+			<?php else : ?>
+				<?php echo get_avatar( 0, 50 ) ?>
+			<?php endif; ?>
+		</div>
+	</div>
+
+	<div class="comment-content standard-form">
+<?php
+}
+add_action( 'comment_form_top', 'bp_dtheme_before_comment_form' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_after_comment_form' ) ) :
+/**
+ * Closes tags opened in bp_dtheme_before_comment_form().
+ *
+ * @see bp_dtheme_before_comment_form()
+ * @see comment_form()
+ * @since 1.5
+ */
+function bp_dtheme_after_comment_form() {
+?>
+
+	</div><!-- .comment-content standard-form -->
+
+<?php
+}
+add_action( 'comment_form', 'bp_dtheme_after_comment_form' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_sidebar_login_redirect_to' ) ) :
+/**
+ * Adds a hidden "redirect_to" input field to the sidebar login form.
+ *
+ * @since 1.5
+ */
+function bp_dtheme_sidebar_login_redirect_to() {
+	$redirect_to = apply_filters( 'bp_no_access_redirect', !empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '' );
+?>
+	<input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
+<?php
+}
+add_action( 'bp_sidebar_login_form', 'bp_dtheme_sidebar_login_redirect_to' );
+endif;
+
+if ( !function_exists( 'bp_dtheme_content_nav' ) ) :
+/**
+ * Display navigation to next/previous pages when applicable
+ *
+ * @global unknown $wp_query
+ * @param string $nav_id DOM ID for this navigation
+ * @since 1.5
+ */
+function bp_dtheme_content_nav( $nav_id ) {
+	global $wp_query;
+
+	if ( $wp_query->max_num_pages > 1 ) : ?>
+		<div id="<?php echo $nav_id; ?>" class="navigation">
+			<div class="alignleft"><?php next_posts_link( __( '&larr; Previous Entries', 'buddypress' ) ); ?></div>
+			<div class="alignright"><?php previous_posts_link( __( 'Next Entries &rarr;', 'buddypress' ) ); ?></div>
+		</div><!-- #<?php echo $nav_id; ?> -->
+	<?php endif;
+}
+endif;
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/create.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/create.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/create.php	(revision 5057)
@@ -0,0 +1,318 @@
+<?php
+
+/**
+ * BuddyPress - Create Group
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<form action="<?php bp_group_creation_form_action(); ?>" method="post" id="create-group-form" class="standard-form" enctype="multipart/form-data">
+			<h3><?php _e( 'Create a Group', 'buddypress' ); ?> &nbsp;<a class="button" href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ); ?>"><?php _e( 'Groups Directory', 'buddypress' ); ?></a></h3>
+
+			<?php do_action( 'bp_before_create_group' ); ?>
+
+			<div class="item-list-tabs no-ajax" id="group-create-tabs" role="navigation">
+				<ul>
+
+					<?php bp_group_creation_tabs(); ?>
+
+				</ul>
+			</div>
+
+			<?php do_action( 'template_notices' ); ?>
+
+			<div class="item-body" id="group-create-body">
+
+				<?php /* Group creation step 1: Basic group details */ ?>
+				<?php if ( bp_is_group_creation_step( 'group-details' ) ) : ?>
+
+					<?php do_action( 'bp_before_group_details_creation_step' ); ?>
+
+					<label for="group-name"><?php _e( 'Group Name (required)', 'buddypress' ); ?></label>
+					<input type="text" name="group-name" id="group-name" aria-required="true" value="<?php bp_new_group_name(); ?>" />
+
+					<label for="group-desc"><?php _e( 'Group Description (required)', 'buddypress' ) ?></label>
+					<textarea name="group-desc" id="group-desc" aria-required="true"><?php bp_new_group_description(); ?></textarea>
+
+					<?php
+					do_action( 'bp_after_group_details_creation_step' );
+					do_action( 'groups_custom_group_fields_editable' ); // @Deprecated
+
+					wp_nonce_field( 'groups_create_save_group-details' ); ?>
+
+				<?php endif; ?>
+
+				<?php /* Group creation step 2: Group settings */ ?>
+				<?php if ( bp_is_group_creation_step( 'group-settings' ) ) : ?>
+
+					<?php do_action( 'bp_before_group_settings_creation_step' ); ?>
+
+					<?php if ( bp_is_active( 'forums' ) ) : ?>
+						<?php if ( bp_forums_is_installed_correctly() ) : ?>
+
+							<div class="checkbox">
+								<label><input type="checkbox" name="group-show-forum" id="group-show-forum" value="1"<?php checked( bp_get_new_group_enable_forum(), true, true ); ?> /> <?php _e( 'Enable discussion forum', 'buddypress' ); ?></label>
+							</div>
+
+						<?php else : ?>
+							<?php if ( is_super_admin() ) : ?>
+
+								<div class="checkbox">
+									<label><input type="checkbox" disabled="disabled" name="disabled" id="disabled" value="0" /> <?php printf( __( '<strong>Attention Site Admin:</strong> Group forums require the <a href="%s">correct setup and configuration</a> of a bbPress installation.', 'buddypress' ), bp_get_root_domain() . '/wp-admin/admin.php?page=bb-forums-setup' ); ?></label>
+								</div>
+
+							<?php endif; ?>
+						<?php endif; ?>
+					<?php endif; ?>
+
+					<hr />
+
+					<h4><?php _e( 'Privacy Options', 'buddypress' ); ?></h4>
+
+					<div class="radio">
+						<label><input type="radio" name="group-status" value="public"<?php if ( 'public' == bp_get_new_group_status() || !bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
+							<strong><?php _e( 'This is a public group', 'buddypress' ); ?></strong>
+							<ul>
+								<li><?php _e( 'Any site member can join this group.', 'buddypress' ); ?></li>
+								<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
+								<li><?php _e( 'Group content and activity will be visible to any site member.', 'buddypress' ); ?></li>
+							</ul>
+						</label>
+
+						<label><input type="radio" name="group-status" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
+							<strong><?php _e( 'This is a private group', 'buddypress' ); ?></strong>
+							<ul>
+								<li><?php _e( 'Only users who request membership and are accepted can join the group.', 'buddypress' ); ?></li>
+								<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
+								<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
+							</ul>
+						</label>
+
+						<label><input type="radio" name="group-status" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
+							<strong><?php _e('This is a hidden group', 'buddypress'); ?></strong>
+							<ul>
+								<li><?php _e( 'Only users who are invited can join the group.', 'buddypress' ); ?></li>
+								<li><?php _e( 'This group will not be listed in the groups directory or search results.', 'buddypress' ); ?></li>
+								<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
+							</ul>
+						</label>
+					</div>
+
+					<hr />
+
+					<h4><?php _e( 'Group Invitations', 'buddypress' ); ?></h4>
+
+					<p><?php _e( 'Which members of this group are allowed to invite others?', 'buddypress' ) ?></p>
+
+					<div class="radio">
+						<label>
+							<input type="radio" name="group-invite-status" value="members"<?php bp_group_show_invite_status_setting( 'members' ) ?> />
+							<strong><?php _e( 'All group members', 'buddypress' ) ?></strong>
+						</label>
+
+						<label>
+							<input type="radio" name="group-invite-status" value="mods"<?php bp_group_show_invite_status_setting( 'mods' ) ?> />
+							<strong><?php _e( 'Group admins and mods only', 'buddypress' ) ?></strong>
+						</label>
+
+						<label>
+							<input type="radio" name="group-invite-status" value="admins"<?php bp_group_show_invite_status_setting( 'admins' ) ?> />
+							<strong><?php _e( 'Group admins only', 'buddypress' ) ?></strong>
+						</label>
+					</div>
+
+					<hr />
+
+					<?php do_action( 'bp_after_group_settings_creation_step' ); ?>
+
+					<?php wp_nonce_field( 'groups_create_save_group-settings' ); ?>
+
+				<?php endif; ?>
+
+				<?php /* Group creation step 3: Avatar Uploads */ ?>
+				<?php if ( bp_is_group_creation_step( 'group-avatar' ) ) : ?>
+
+					<?php do_action( 'bp_before_group_avatar_creation_step' ); ?>
+
+					<?php if ( 'upload-image' == bp_get_avatar_admin_step() ) : ?>
+
+						<div class="left-menu">
+
+							<?php bp_new_group_avatar(); ?>
+
+						</div><!-- .left-menu -->
+
+						<div class="main-column">
+							<p><?php _e( "Upload an image to use as an avatar for this group. The image will be shown on the main group page, and in search results.", 'buddypress' ); ?></p>
+
+							<p>
+								<input type="file" name="file" id="file" />
+								<input type="submit" name="upload" id="upload" value="<?php _e( 'Upload Image', 'buddypress' ); ?>" />
+								<input type="hidden" name="action" id="action" value="bp_avatar_upload" />
+							</p>
+
+							<p><?php _e( 'To skip the avatar upload process, hit the "Next Step" button.', 'buddypress' ); ?></p>
+						</div><!-- .main-column -->
+
+					<?php endif; ?>
+
+					<?php if ( 'crop-image' == bp_get_avatar_admin_step() ) : ?>
+
+						<h3><?php _e( 'Crop Group Avatar', 'buddypress' ); ?></h3>
+
+						<img src="<?php bp_avatar_to_crop(); ?>" id="avatar-to-crop" class="avatar" alt="<?php _e( 'Avatar to crop', 'buddypress' ); ?>" />
+
+						<div id="avatar-crop-pane">
+							<img src="<?php bp_avatar_to_crop(); ?>" id="avatar-crop-preview" class="avatar" alt="<?php _e( 'Avatar preview', 'buddypress' ); ?>" />
+						</div>
+
+						<input type="submit" name="avatar-crop-submit" id="avatar-crop-submit" value="<?php _e( 'Crop Image', 'buddypress' ); ?>" />
+
+						<input type="hidden" name="image_src" id="image_src" value="<?php bp_avatar_to_crop_src(); ?>" />
+						<input type="hidden" name="upload" id="upload" />
+						<input type="hidden" id="x" name="x" />
+						<input type="hidden" id="y" name="y" />
+						<input type="hidden" id="w" name="w" />
+						<input type="hidden" id="h" name="h" />
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_after_group_avatar_creation_step' ); ?>
+
+					<?php wp_nonce_field( 'groups_create_save_group-avatar' ); ?>
+
+				<?php endif; ?>
+
+				<?php /* Group creation step 4: Invite friends to group */ ?>
+				<?php if ( bp_is_group_creation_step( 'group-invites' ) ) : ?>
+
+					<?php do_action( 'bp_before_group_invites_creation_step' ); ?>
+
+					<?php if ( bp_is_active( 'friends' ) && bp_get_total_friend_count( bp_loggedin_user_id() ) ) : ?>
+
+						<div class="left-menu">
+
+							<div id="invite-list">
+								<ul>
+									<?php bp_new_group_invite_friend_list(); ?>
+								</ul>
+
+								<?php wp_nonce_field( 'groups_invite_uninvite_user', '_wpnonce_invite_uninvite_user' ); ?>
+							</div>
+
+						</div><!-- .left-menu -->
+
+						<div class="main-column">
+
+							<div id="message" class="info">
+								<p><?php _e('Select people to invite from your friends list.', 'buddypress'); ?></p>
+							</div>
+
+							<?php /* The ID 'friend-list' is important for AJAX support. */ ?>
+							<ul id="friend-list" class="item-list" role="main">
+
+							<?php if ( bp_group_has_invites() ) : ?>
+
+								<?php while ( bp_group_invites() ) : bp_group_the_invite(); ?>
+
+									<li id="<?php bp_group_invite_item_id(); ?>">
+
+										<?php bp_group_invite_user_avatar(); ?>
+
+										<h4><?php bp_group_invite_user_link(); ?></h4>
+										<span class="activity"><?php bp_group_invite_user_last_active(); ?></span>
+
+										<div class="action">
+											<a class="remove" href="<?php bp_group_invite_user_remove_invite_url(); ?>" id="<?php bp_group_invite_item_id(); ?>"><?php _e( 'Remove Invite', 'buddypress' ); ?></a>
+										</div>
+									</li>
+
+								<?php endwhile; ?>
+
+								<?php wp_nonce_field( 'groups_send_invites', '_wpnonce_send_invites' ); ?>
+
+							<?php endif; ?>
+
+							</ul>
+
+						</div><!-- .main-column -->
+
+					<?php else : ?>
+
+						<div id="message" class="info">
+							<p><?php _e( 'Once you have built up friend connections you will be able to invite others to your group. You can send invites any time in the future by selecting the "Send Invites" option when viewing your new group.', 'buddypress' ); ?></p>
+						</div>
+
+					<?php endif; ?>
+
+					<?php wp_nonce_field( 'groups_create_save_group-invites' ); ?>
+
+					<?php do_action( 'bp_after_group_invites_creation_step' ); ?>
+
+				<?php endif; ?>
+
+				<?php do_action( 'groups_custom_create_steps' ); // Allow plugins to add custom group creation steps ?>
+
+				<?php do_action( 'bp_before_group_creation_step_buttons' ); ?>
+
+				<?php if ( 'crop-image' != bp_get_avatar_admin_step() ) : ?>
+
+					<div class="submit" id="previous-next">
+
+						<?php /* Previous Button */ ?>
+						<?php if ( !bp_is_first_group_creation_step() ) : ?>
+
+							<input type="button" value="<?php _e( 'Back to Previous Step', 'buddypress' ); ?>" id="group-creation-previous" name="previous" onclick="location.href='<?php bp_group_creation_previous_link(); ?>'" />
+
+						<?php endif; ?>
+
+						<?php /* Next Button */ ?>
+						<?php if ( !bp_is_last_group_creation_step() && !bp_is_first_group_creation_step() ) : ?>
+
+							<input type="submit" value="<?php _e( 'Next Step', 'buddypress' ); ?>" id="group-creation-next" name="save" />
+
+						<?php endif;?>
+
+						<?php /* Create Button */ ?>
+						<?php if ( bp_is_first_group_creation_step() ) : ?>
+
+							<input type="submit" value="<?php _e( 'Create Group and Continue', 'buddypress' ); ?>" id="group-creation-create" name="save" />
+
+						<?php endif; ?>
+
+						<?php /* Finish Button */ ?>
+						<?php if ( bp_is_last_group_creation_step() ) : ?>
+
+							<input type="submit" value="<?php _e( 'Finish', 'buddypress' ); ?>" id="group-creation-finish" name="save" />
+
+						<?php endif; ?>
+					</div>
+
+				<?php endif;?>
+
+				<?php do_action( 'bp_after_group_creation_step_buttons' ); ?>
+
+				<?php /* Don't leave out this hidden field */ ?>
+				<input type="hidden" name="group_id" id="group_id" value="<?php bp_new_group_id(); ?>" />
+
+				<?php do_action( 'bp_directory_groups_content' ); ?>
+
+			</div><!-- .item-body -->
+
+			<?php do_action( 'bp_after_create_group' ); ?>
+
+		</form>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/groups-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/groups-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/groups-loop.php	(revision 5057)
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * BuddyPress - Groups Loop
+ *
+ * Querystring is set via AJAX in _inc/ajax.php - bp_dtheme_object_filter()
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_groups_loop' ); ?>
+
+<?php if ( bp_has_groups( bp_ajax_querystring( 'groups' ) ) ) : ?>
+
+	<div id="pag-top" class="pagination">
+
+		<div class="pag-count" id="group-dir-count-top">
+
+			<?php bp_groups_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="group-dir-pag-top">
+
+			<?php bp_groups_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_before_directory_groups_list' ); ?>
+
+	<ul id="groups-list" class="item-list" role="main">
+
+	<?php while ( bp_groups() ) : bp_the_group(); ?>
+
+		<li>
+			<div class="item-avatar">
+				<a href="<?php bp_group_permalink(); ?>"><?php bp_group_avatar( 'type=thumb&width=50&height=50' ); ?></a>
+			</div>
+
+			<div class="item">
+				<div class="item-title"><a href="<?php bp_group_permalink(); ?>"><?php bp_group_name(); ?></a></div>
+				<div class="item-meta"><span class="activity"><?php printf( __( 'active %s', 'buddypress' ), bp_get_group_last_active() ); ?></span></div>
+
+				<div class="item-desc"><?php bp_group_description_excerpt(); ?></div>
+
+				<?php do_action( 'bp_directory_groups_item' ); ?>
+
+			</div>
+
+			<div class="action">
+
+				<?php do_action( 'bp_directory_groups_actions' ); ?>
+
+				<div class="meta">
+
+					<?php bp_group_type(); ?> / <?php bp_group_member_count(); ?>
+
+				</div>
+
+			</div>
+
+			<div class="clear"></div>
+		</li>
+
+	<?php endwhile; ?>
+
+	</ul>
+
+	<?php do_action( 'bp_after_directory_groups_list' ); ?>
+
+	<div id="pag-bottom" class="pagination">
+
+		<div class="pag-count" id="group-dir-count-bottom">
+
+			<?php bp_groups_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="group-dir-pag-bottom">
+
+			<?php bp_groups_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'There were no groups found.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_groups_loop' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/index.php	(revision 5057)
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * BuddyPress - Groups Directory
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_groups_page' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_directory_groups' ); ?>
+
+		<form action="" method="post" id="groups-directory-form" class="dir-form">
+
+			<h3><?php _e( 'Groups Directory', 'buddypress' ); ?><?php if ( is_user_logged_in() && bp_user_can_create_groups() ) : ?> &nbsp;<a class="button" href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/create' ); ?>"><?php _e( 'Create a Group', 'buddypress' ); ?></a><?php endif; ?></h3>
+
+			<?php do_action( 'bp_before_directory_groups_content' ); ?>
+
+			<div id="group-dir-search" class="dir-search" role="search">
+
+				<?php bp_directory_groups_search_form() ?>
+
+			</div><!-- #group-dir-search -->
+
+			<?php do_action( 'template_notices' ); ?>
+
+			<div class="item-list-tabs" role="navigation">
+				<ul>
+					<li class="selected" id="groups-all"><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ); ?>"><?php printf( __( 'All Groups <span>%s</span>', 'buddypress' ), bp_get_total_group_count() ); ?></a></li>
+
+					<?php if ( is_user_logged_in() && bp_get_total_group_count_for_user( bp_loggedin_user_id() ) ) : ?>
+
+						<li id="groups-personal"><a href="<?php echo trailingslashit( bp_loggedin_user_domain() . bp_get_groups_slug() . '/my-groups' ); ?>"><?php printf( __( 'My Groups <span>%s</span>', 'buddypress' ), bp_get_total_group_count_for_user( bp_loggedin_user_id() ) ); ?></a></li>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_groups_directory_group_filter' ); ?>
+
+				</ul>
+			</div><!-- .item-list-tabs -->
+
+			<div class="item-list-tabs" id="subnav" role="navigation">
+				<ul>
+
+					<?php do_action( 'bp_groups_directory_group_types' ); ?>
+
+					<li id="groups-order-select" class="last filter">
+
+						<label for="groups-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+						<select id="groups-order-by">
+							<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+							<option value="popular"><?php _e( 'Most Members', 'buddypress' ); ?></option>
+							<option value="newest"><?php _e( 'Newly Created', 'buddypress' ); ?></option>
+							<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+							<?php do_action( 'bp_groups_directory_order_options' ); ?>
+
+						</select>
+					</li>
+				</ul>
+			</div>
+
+			<div id="groups-dir-list" class="groups dir-list">
+
+				<?php locate_template( array( 'groups/groups-loop.php' ), true ); ?>
+
+			</div><!-- #groups-dir-list -->
+
+			<?php do_action( 'bp_directory_groups_content' ); ?>
+
+			<?php wp_nonce_field( 'directory_groups', '_wpnonce-groups-filter' ); ?>
+
+			<?php do_action( 'bp_after_directory_groups_content' ); ?>
+
+		</form><!-- #groups-directory-form -->
+
+		<?php do_action( 'bp_after_directory_groups' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_groups_page' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/activity.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/activity.php	(revision 5057)
@@ -0,0 +1,39 @@
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+		<li class="feed"><a href="<?php bp_group_activity_feed_link() ?>" title="<?php _e( 'RSS Feed', 'buddypress' ); ?>"><?php _e( 'RSS', 'buddypress' ) ?></a></li>
+
+		<?php do_action( 'bp_group_activity_syndication_options' ) ?>
+
+		<li id="activity-filter-select" class="last">
+			<label for="activity-filter-by"><?php _e( 'Show:', 'buddypress' ); ?></label> 
+			<select id="activity-filter-by">
+				<option value="-1"><?php _e( 'Everything', 'buddypress' ) ?></option>
+				<option value="activity_update"><?php _e( 'Updates', 'buddypress' ) ?></option>
+
+				<?php if ( bp_is_active( 'forums' ) ) : ?>
+					<option value="new_forum_topic"><?php _e( 'Forum Topics', 'buddypress' ) ?></option>
+					<option value="new_forum_post"><?php _e( 'Forum Replies', 'buddypress' ) ?></option>
+				<?php endif; ?>
+
+				<option value="joined_group"><?php _e( 'Group Memberships', 'buddypress' ) ?></option>
+
+				<?php do_action( 'bp_group_activity_filter_options' ) ?>
+			</select>
+		</li>
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php do_action( 'bp_before_group_activity_post_form' ) ?>
+
+<?php if ( is_user_logged_in() && bp_group_is_member() ) : ?>
+	<?php locate_template( array( 'activity/post-form.php'), true ) ?>
+<?php endif; ?>
+
+<?php do_action( 'bp_after_group_activity_post_form' ) ?>
+<?php do_action( 'bp_before_group_activity_content' ) ?>
+
+<div class="activity single-group" role="main">
+	<?php locate_template( array( 'activity/activity-loop.php' ), true ) ?>
+</div><!-- .activity.single-group -->
+
+<?php do_action( 'bp_after_group_activity_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/admin.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/admin.php	(revision 5057)
@@ -0,0 +1,373 @@
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+		<?php bp_group_admin_tabs(); ?>
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<form action="<?php bp_group_admin_form_action() ?>" name="group-settings-form" id="group-settings-form" class="standard-form" method="post" enctype="multipart/form-data" role="main">
+
+<?php do_action( 'bp_before_group_admin_content' ) ?>
+
+<?php /* Edit Group Details */ ?>
+<?php if ( bp_is_group_admin_screen( 'edit-details' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_details_admin' ); ?>
+
+	<label for="group-name"><?php _e( 'Group Name (required)', 'buddypress' ); ?></label>
+	<input type="text" name="group-name" id="group-name" value="<?php bp_group_name() ?>" aria-required="true" />
+
+	<label for="group-desc"><?php _e( 'Group Description (required)', 'buddypress' ); ?></label>
+	<textarea name="group-desc" id="group-desc" aria-required="true"><?php bp_group_description_editable() ?></textarea>
+
+	<?php do_action( 'groups_custom_group_fields_editable' ) ?>
+
+	<p>
+		<label for="group-notifiy-members"><?php _e( 'Notify group members of changes via email', 'buddypress' ); ?></label>
+		<input type="radio" name="group-notify-members" value="1" /> <?php _e( 'Yes', 'buddypress' ); ?>&nbsp;
+		<input type="radio" name="group-notify-members" value="0" checked="checked" /> <?php _e( 'No', 'buddypress' ); ?>&nbsp;
+	</p>
+
+	<?php do_action( 'bp_after_group_details_admin' ); ?>
+
+	<p><input type="submit" value="<?php _e( 'Save Changes', 'buddypress' ) ?>" id="save" name="save" /></p>
+	<?php wp_nonce_field( 'groups_edit_group_details' ) ?>
+
+<?php endif; ?>
+
+<?php /* Manage Group Settings */ ?>
+<?php if ( bp_is_group_admin_screen( 'group-settings' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_settings_admin' ); ?>
+
+	<?php if ( bp_is_active( 'forums' ) ) : ?>
+
+		<?php if ( bp_forums_is_installed_correctly() ) : ?>
+
+			<div class="checkbox">
+				<label><input type="checkbox" name="group-show-forum" id="group-show-forum" value="1"<?php bp_group_show_forum_setting() ?> /> <?php _e( 'Enable discussion forum', 'buddypress' ) ?></label>
+			</div>
+
+			<hr />
+
+		<?php endif; ?>
+
+	<?php endif; ?>
+
+	<h4><?php _e( 'Privacy Options', 'buddypress' ); ?></h4>
+
+	<div class="radio">
+		<label>
+			<input type="radio" name="group-status" value="public"<?php bp_group_show_status_setting( 'public' ) ?> />
+			<strong><?php _e( 'This is a public group', 'buddypress' ) ?></strong>
+			<ul>
+				<li><?php _e( 'Any site member can join this group.', 'buddypress' ) ?></li>
+				<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ) ?></li>
+				<li><?php _e( 'Group content and activity will be visible to any site member.', 'buddypress' ) ?></li>
+			</ul>
+		</label>
+
+		<label>
+			<input type="radio" name="group-status" value="private"<?php bp_group_show_status_setting( 'private' ) ?> />
+			<strong><?php _e( 'This is a private group', 'buddypress' ) ?></strong>
+			<ul>
+				<li><?php _e( 'Only users who request membership and are accepted can join the group.', 'buddypress' ) ?></li>
+				<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ) ?></li>
+				<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ) ?></li>
+			</ul>
+		</label>
+
+		<label>
+			<input type="radio" name="group-status" value="hidden"<?php bp_group_show_status_setting( 'hidden' ) ?> />
+			<strong><?php _e( 'This is a hidden group', 'buddypress' ) ?></strong>
+			<ul>
+				<li><?php _e( 'Only users who are invited can join the group.', 'buddypress' ) ?></li>
+				<li><?php _e( 'This group will not be listed in the groups directory or search results.', 'buddypress' ) ?></li>
+				<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ) ?></li>
+			</ul>
+		</label>
+	</div>
+
+	<hr /> 
+	 
+	<h4><?php _e( 'Group Invitations', 'buddypress' ); ?></h4> 
+
+	<p><?php _e( 'Which members of this group are allowed to invite others?', 'buddypress' ) ?></p> 
+
+	<div class="radio"> 
+		<label> 
+			<input type="radio" name="group-invite-status" value="members"<?php bp_group_show_invite_status_setting( 'members' ) ?> /> 
+			<strong><?php _e( 'All group members', 'buddypress' ) ?></strong> 
+		</label> 
+
+		<label> 
+			<input type="radio" name="group-invite-status" value="mods"<?php bp_group_show_invite_status_setting( 'mods' ) ?> /> 
+			<strong><?php _e( 'Group admins and mods only', 'buddypress' ) ?></strong> 
+		</label>
+		
+		<label> 
+			<input type="radio" name="group-invite-status" value="admins"<?php bp_group_show_invite_status_setting( 'admins' ) ?> /> 
+			<strong><?php _e( 'Group admins only', 'buddypress' ) ?></strong> 
+		</label> 
+ 	</div> 
+
+	<hr /> 
+
+	<?php do_action( 'bp_after_group_settings_admin' ); ?>
+
+	<p><input type="submit" value="<?php _e( 'Save Changes', 'buddypress' ) ?>" id="save" name="save" /></p>
+	<?php wp_nonce_field( 'groups_edit_group_settings' ) ?>
+
+<?php endif; ?>
+
+<?php /* Group Avatar Settings */ ?>
+<?php if ( bp_is_group_admin_screen( 'group-avatar' ) ) : ?>
+
+	<?php if ( 'upload-image' == bp_get_avatar_admin_step() ) : ?>
+
+			<p><?php _e("Upload an image to use as an avatar for this group. The image will be shown on the main group page, and in search results.", 'buddypress') ?></p>
+
+			<p>
+				<input type="file" name="file" id="file" />
+				<input type="submit" name="upload" id="upload" value="<?php _e( 'Upload Image', 'buddypress' ) ?>" />
+				<input type="hidden" name="action" id="action" value="bp_avatar_upload" />
+			</p>
+
+			<?php if ( bp_get_group_has_avatar() ) : ?>
+
+				<p><?php _e( "If you'd like to remove the existing avatar but not upload a new one, please use the delete avatar button.", 'buddypress' ) ?></p>
+
+				<?php bp_button( array( 'id' => 'delete_group_avatar', 'component' => 'groups', 'wrapper_id' => 'delete-group-avatar-button', 'link_class' => 'edit', 'link_href' => bp_get_group_avatar_delete_link(), 'link_title' => __( 'Delete Avatar', 'buddypress' ), 'link_text' => __( 'Delete Avatar', 'buddypress' ) ) ); ?>
+
+			<?php endif; ?>
+
+			<?php wp_nonce_field( 'bp_avatar_upload' ) ?>
+
+	<?php endif; ?>
+
+	<?php if ( 'crop-image' == bp_get_avatar_admin_step() ) : ?>
+
+		<h3><?php _e( 'Crop Avatar', 'buddypress' ) ?></h3>
+
+		<img src="<?php bp_avatar_to_crop() ?>" id="avatar-to-crop" class="avatar" alt="<?php _e( 'Avatar to crop', 'buddypress' ) ?>" />
+
+		<div id="avatar-crop-pane">
+			<img src="<?php bp_avatar_to_crop() ?>" id="avatar-crop-preview" class="avatar" alt="<?php _e( 'Avatar preview', 'buddypress' ) ?>" />
+		</div>
+
+		<input type="submit" name="avatar-crop-submit" id="avatar-crop-submit" value="<?php _e( 'Crop Image', 'buddypress' ) ?>" />
+
+		<input type="hidden" name="image_src" id="image_src" value="<?php bp_avatar_to_crop_src() ?>" />
+		<input type="hidden" id="x" name="x" />
+		<input type="hidden" id="y" name="y" />
+		<input type="hidden" id="w" name="w" />
+		<input type="hidden" id="h" name="h" />
+
+		<?php wp_nonce_field( 'bp_avatar_cropstore' ) ?>
+
+	<?php endif; ?>
+
+<?php endif; ?>
+
+<?php /* Manage Group Members */ ?>
+<?php if ( bp_is_group_admin_screen( 'manage-members' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_manage_members_admin' ); ?>
+	
+	<div class="bp-widget">
+		<h4><?php _e( 'Administrators', 'buddypress' ); ?></h4>
+
+		<?php if ( bp_has_members( '&include='. bp_group_admin_ids() ) ) : ?>
+		
+		<ul id="admins-list" class="item-list single-line>">
+			
+			<?php while ( bp_members() ) : bp_the_member(); ?>
+			<li>
+				<?php echo bp_core_fetch_avatar( array( 'item_id' => bp_get_member_user_id(), 'type' => 'thumb', 'width' => 30, 'height' => 30, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+				<h5>
+					<a href="<?php bp_member_permalink(); ?>"> <?php bp_member_name(); ?></a>
+					<span class="small">
+						<a class="button confirm admin-demote-to-member" href="<?php bp_group_member_demote_link( bp_get_member_user_id() ) ?>"><?php _e( 'Demote to Member', 'buddypress' ) ?></a>
+					</span>			
+				</h5>		
+			</li>
+			<?php endwhile; ?>
+		
+		</ul>
+		
+		<?php endif; ?>
+
+	</div>
+	
+	<?php if ( bp_group_has_moderators() ) : ?>
+		<div class="bp-widget">
+			<h4><?php _e( 'Moderators', 'buddypress' ) ?></h4>		
+			
+			<?php if ( bp_has_members( '&include=' . bp_group_mod_ids() ) ) : ?>
+				<ul id="mods-list" class="item-list">
+				
+					<?php while ( bp_members() ) : bp_the_member(); ?>					
+					<li>
+						<?php echo bp_core_fetch_avatar( array( 'item_id' => bp_get_member_user_id(), 'type' => 'thumb', 'width' => 30, 'height' => 30, 'alt' => __( 'Profile picture of %s', 'buddypress' ) ) ) ?>
+						<h5>
+							<a href="<?php bp_member_permalink(); ?>"> <?php bp_member_name(); ?></a>
+							<span class="small">
+								<a href="<?php bp_group_member_promote_admin_link( array( 'user_id' => bp_get_member_user_id() ) ) ?>" class="button confirm mod-promote-to-admin" title="<?php _e( 'Promote to Admin', 'buddypress' ); ?>"><?php _e( 'Promote to Admin', 'buddypress' ); ?></a>
+								<a class="button confirm mod-demote-to-member" href="<?php bp_group_member_demote_link( bp_get_member_user_id() ) ?>"><?php _e( 'Demote to Member', 'buddypress' ) ?></a>
+							</span>		
+						</h5>		
+					</li>	
+					<?php endwhile; ?>			
+				
+				</ul>
+			
+			<?php endif; ?>
+		</div>
+	<?php endif ?>
+
+
+	<div class="bp-widget">
+		<h4><?php _e("Members", "buddypress"); ?></h4>
+
+		<?php if ( bp_group_has_members( 'per_page=15&exclude_banned=false' ) ) : ?>
+
+			<?php if ( bp_group_member_needs_pagination() ) : ?>
+
+				<div class="pagination no-ajax">
+
+					<div id="member-count" class="pag-count">
+						<?php bp_group_member_pagination_count() ?>
+					</div>
+
+					<div id="member-admin-pagination" class="pagination-links">
+						<?php bp_group_member_admin_pagination() ?>
+					</div>
+
+				</div>
+
+			<?php endif; ?>
+
+			<ul id="members-list" class="item-list single-line">
+				<?php while ( bp_group_members() ) : bp_group_the_member(); ?>
+
+					<li class="<?php bp_group_member_css_class(); ?>">
+						<?php bp_group_member_avatar_mini() ?>
+
+						<h5>
+							<?php bp_group_member_link() ?>
+
+							<?php if ( bp_get_group_member_is_banned() ) _e( '(banned)', 'buddypress'); ?>
+
+							<span class="small">
+
+							<?php if ( bp_get_group_member_is_banned() ) : ?>
+
+								<a href="<?php bp_group_member_unban_link() ?>" class="button confirm member-unban" title="<?php _e( 'Unban this member', 'buddypress' ) ?>"><?php _e( 'Remove Ban', 'buddypress' ); ?></a>
+
+							<?php else : ?>
+
+								<a href="<?php bp_group_member_ban_link() ?>" class="button confirm member-ban" title="<?php _e( 'Kick and ban this member', 'buddypress' ); ?>"><?php _e( 'Kick &amp; Ban', 'buddypress' ); ?></a>
+								<a href="<?php bp_group_member_promote_mod_link() ?>" class="button confirm member-promote-to-mod" title="<?php _e( 'Promote to Mod', 'buddypress' ); ?>"><?php _e( 'Promote to Mod', 'buddypress' ); ?></a>
+								<a href="<?php bp_group_member_promote_admin_link() ?>" class="button confirm member-promote-to-admin" title="<?php _e( 'Promote to Admin', 'buddypress' ); ?>"><?php _e( 'Promote to Admin', 'buddypress' ); ?></a>
+
+							<?php endif; ?>
+
+								<a href="<?php bp_group_member_remove_link() ?>" class="button confirm" title="<?php _e( 'Remove this member', 'buddypress' ); ?>"><?php _e( 'Remove from group', 'buddypress' ); ?></a>
+
+								<?php do_action( 'bp_group_manage_members_admin_item' ); ?>
+
+							</span>
+						</h5>
+					</li>
+
+				<?php endwhile; ?>
+			</ul>
+
+		<?php else: ?>
+
+			<div id="message" class="info">
+				<p><?php _e( 'This group has no members.', 'buddypress' ); ?></p>
+			</div>
+
+		<?php endif; ?>
+
+	</div>
+
+	<?php do_action( 'bp_after_group_manage_members_admin' ); ?>
+
+<?php endif; ?>
+
+<?php /* Manage Membership Requests */ ?>
+<?php if ( bp_is_group_admin_screen( 'membership-requests' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_membership_requests_admin' ); ?>
+
+	<?php if ( bp_group_has_membership_requests() ) : ?>
+
+		<ul id="request-list" class="item-list">
+			<?php while ( bp_group_membership_requests() ) : bp_group_the_membership_request(); ?>
+
+				<li>
+					<?php bp_group_request_user_avatar_thumb() ?>
+					<h4><?php bp_group_request_user_link() ?> <span class="comments"><?php bp_group_request_comment() ?></span></h4>
+					<span class="activity"><?php bp_group_request_time_since_requested() ?></span>
+
+					<?php do_action( 'bp_group_membership_requests_admin_item' ); ?>
+
+					<div class="action">
+
+						<?php bp_button( array( 'id' => 'group_membership_accept', 'component' => 'groups', 'wrapper_class' => 'accept', 'link_href' => bp_get_group_request_accept_link(), 'link_title' => __( 'Accept', 'buddypress' ), 'link_text' => __( 'Accept', 'buddypress' ) ) ); ?>
+
+						<?php bp_button( array( 'id' => 'group_membership_reject', 'component' => 'groups', 'wrapper_class' => 'reject', 'link_href' => bp_get_group_request_reject_link(), 'link_title' => __( 'Reject', 'buddypress' ), 'link_text' => __( 'Reject', 'buddypress' ) ) ); ?>
+
+						<?php do_action( 'bp_group_membership_requests_admin_item_action' ); ?>
+
+					</div>
+				</li>
+
+			<?php endwhile; ?>
+		</ul>
+
+	<?php else: ?>
+
+		<div id="message" class="info">
+			<p><?php _e( 'There are no pending membership requests.', 'buddypress' ); ?></p>
+		</div>
+
+	<?php endif; ?>
+
+	<?php do_action( 'bp_after_group_membership_requests_admin' ); ?>
+
+<?php endif; ?>
+
+<?php do_action( 'groups_custom_edit_steps' ) // Allow plugins to add custom group edit screens ?>
+
+<?php /* Delete Group Option */ ?>
+<?php if ( bp_is_group_admin_screen( 'delete-group' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_delete_admin' ); ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'WARNING: Deleting this group will completely remove ALL content associated with it. There is no way back, please be careful with this option.', 'buddypress' ); ?></p>
+	</div>
+
+	<label><input type="checkbox" name="delete-group-understand" id="delete-group-understand" value="1" onclick="if(this.checked) { document.getElementById('delete-group-button').disabled = ''; } else { document.getElementById('delete-group-button').disabled = 'disabled'; }" /> <?php _e( 'I understand the consequences of deleting this group.', 'buddypress' ); ?></label>
+
+	<?php do_action( 'bp_after_group_delete_admin' ); ?>
+
+	<div class="submit">
+		<input type="submit" disabled="disabled" value="<?php _e( 'Delete Group', 'buddypress' ) ?>" id="delete-group-button" name="delete-group-button" />
+	</div>
+
+	<input type="hidden" name="group-id" id="group-id" value="<?php bp_group_id() ?>" />
+
+	<?php wp_nonce_field( 'groups_delete_group' ) ?>
+
+<?php endif; ?>
+
+<?php /* This is important, don't forget it */ ?>
+	<input type="hidden" name="group-id" id="group-id" value="<?php bp_group_id() ?>" />
+
+<?php do_action( 'bp_after_group_admin_content' ) ?>
+
+</form><!-- #group-settings-form -->
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum.php	(revision 5057)
@@ -0,0 +1,93 @@
+<?php
+
+do_action( 'bp_before_group_forum_content' );
+
+if ( bp_is_group_forum_topic_edit() ) :
+	locate_template( array( 'groups/single/forum/edit.php' ), true );
+
+elseif ( bp_is_group_forum_topic() ) :
+	locate_template( array( 'groups/single/forum/topic.php' ), true );
+
+else : ?>
+
+	<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+		<ul>
+
+			<li>
+				<a href="#post-new" class="show-hide-new"><?php _e( 'New Topic', 'buddypress' ) ?></a>
+			</li>
+
+			<?php if ( bp_forums_has_directory() ) : ?>
+
+				<li>
+					<a href="<?php bp_forums_directory_permalink() ?>"><?php _e( 'Forum Directory', 'buddypress') ?></a>
+				</li>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_forums_directory_group_sub_types' ); ?>
+
+			<li id="forums-order-select" class="last filter">
+
+				<label for="forums-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+				<select id="forums-order-by">
+					<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+					<option value="popular"><?php _e( 'Most Posts', 'buddypress' ); ?></option>
+					<option value="unreplied"><?php _e( 'Unreplied', 'buddypress' ); ?></option>
+
+					<?php do_action( 'bp_forums_directory_order_options' ); ?>
+
+				</select>
+			</li>
+		</ul>
+	</div>
+
+	<div class="forums single-forum" role="main">
+
+		<?php locate_template( array( 'forums/forums-loop.php' ), true ) ?>
+
+	</div><!-- .forums.single-forum -->
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_group_forum_content' ) ?>
+
+<?php if ( !bp_is_group_forum_topic_edit() && !bp_is_group_forum_topic() ) : ?>
+
+	<?php if ( !bp_group_is_user_banned() && ( ( is_user_logged_in() && 'public' == bp_get_group_status() ) || bp_group_is_member() ) ) : ?>
+
+		<form action="" method="post" id="forum-topic-form" class="standard-form">
+			<div id="new-topic-post">
+
+				<?php do_action( 'bp_before_group_forum_post_new' ) ?>
+
+				<?php if ( bp_groups_auto_join() && !bp_group_is_member() ) : ?>
+					<p><?php _e( 'You will auto join this group when you start a new topic.', 'buddypress' ) ?></p>
+				<?php endif; ?>
+
+				<p id="post-new"></p>
+				<h4><?php _e( 'Post a New Topic:', 'buddypress' ) ?></h4>
+
+				<label><?php _e( 'Title:', 'buddypress' ) ?></label>
+				<input type="text" name="topic_title" id="topic_title" value="" />
+
+				<label><?php _e( 'Content:', 'buddypress' ) ?></label>
+				<textarea name="topic_text" id="topic_text"></textarea>
+
+				<label><?php _e( 'Tags (comma separated):', 'buddypress' ) ?></label>
+				<input type="text" name="topic_tags" id="topic_tags" value="" />
+
+				<?php do_action( 'bp_after_group_forum_post_new' ) ?>
+
+				<div class="submit">
+					<input type="submit" name="submit_topic" id="submit" value="<?php _e( 'Post Topic', 'buddypress' ) ?>" />
+				</div>
+
+				<?php wp_nonce_field( 'bp_forums_new_topic' ) ?>
+			</div><!-- #new-topic-post -->
+		</form><!-- #forum-topic-form -->
+
+	<?php endif; ?>
+
+<?php endif; ?>
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/edit.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/edit.php	(revision 5057)
@@ -0,0 +1,96 @@
+<?php do_action( 'bp_before_group_forum_edit_form' ); ?>
+
+<?php if ( bp_has_forum_topic_posts() ) : ?>
+
+	<form action="<?php bp_forum_topic_action(); ?>" method="post" id="forum-topic-form" class="standard-form">
+
+		<div class="item-list-tabs" id="subnav" role="navigation">
+			<ul>
+				<li>
+					<a href="#post-topic-reply"><?php _e( 'Reply', 'buddypress' ); ?></a>
+				</li>
+
+				<?php if ( bp_forums_has_directory() ) : ?>
+
+					<li>
+						<a href="<?php bp_forums_directory_permalink(); ?>"><?php _e( 'Forum Directory', 'buddypress'); ?></a>
+					</li>
+
+				<?php endif; ?>
+
+			</ul>
+		</div>
+
+		<div id="topic-meta">
+			<h3><?php _e( 'Edit:', 'buddypress' ); ?> <?php bp_the_topic_title(); ?> (<?php bp_the_topic_total_post_count(); ?>)</h3>
+
+			<?php if ( bp_group_is_admin() || bp_group_is_mod() || bp_get_the_topic_is_mine() ) : ?>
+
+				<div class="last admin-links">
+
+					<?php bp_the_topic_admin_links(); ?>
+
+				</div>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_group_forum_topic_meta' ); ?>
+
+		</div>
+
+		<?php if ( bp_group_is_member() ) : ?>
+
+			<?php if ( bp_is_edit_topic() ) : ?>
+
+				<div id="edit-topic">
+
+					<?php do_action( 'bp_group_before_edit_forum_topic' ); ?>
+
+					<label for="topic_title"><?php _e( 'Title:', 'buddypress' ); ?></label>
+					<input type="text" name="topic_title" id="topic_title" value="<?php bp_the_topic_title(); ?>" />
+
+					<label for="topic_text"><?php _e( 'Content:', 'buddypress' ); ?></label>
+					<textarea name="topic_text" id="topic_text"><?php bp_the_topic_text(); ?></textarea>
+
+					<label><?php _e( 'Tags (comma separated):', 'buddypress' ) ?></label>
+					<input type="text" name="topic_tags" id="topic_tags" value="<?php bp_forum_topic_tag_list() ?>" />
+
+					<?php do_action( 'bp_group_after_edit_forum_topic' ); ?>
+
+					<p class="submit"><input type="submit" name="save_changes" id="save_changes" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" /></p>
+
+					<?php wp_nonce_field( 'bp_forums_edit_topic' ); ?>
+
+				</div>
+
+			<?php else : ?>
+
+				<div id="edit-post">
+
+					<?php do_action( 'bp_group_before_edit_forum_post' ); ?>
+
+					<textarea name="post_text" id="post_text"><?php bp_the_topic_post_edit_text(); ?></textarea>
+
+					<?php do_action( 'bp_group_after_edit_forum_post' ) ?>
+
+					<p class="submit"><input type="submit" name="save_changes" id="save_changes" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" /></p>
+
+					<?php wp_nonce_field( 'bp_forums_edit_post' ); ?>
+
+				</div>
+
+			<?php endif; ?>
+
+		<?php endif; ?>
+
+	</form><!-- #forum-topic-form -->
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'This topic does not exist.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_group_forum_edit_form' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/topic.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/topic.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/forum/topic.php	(revision 5057)
@@ -0,0 +1,162 @@
+<?php do_action( 'bp_before_group_forum_topic' ); ?>
+
+<?php if ( bp_has_forum_topic_posts() ) : ?>
+
+	<form action="<?php bp_forum_topic_action() ?>" method="post" id="forum-topic-form" class="standard-form">
+
+		<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+			<ul>
+				<li>
+					<a href="#post-topic-reply" class="show-hide-new"><?php _e( 'New Reply', 'buddypress' ) ?></a>
+				</li>
+
+				<?php if ( bp_forums_has_directory() ) : ?>
+
+					<li>
+						<a href="<?php bp_forums_directory_permalink() ?>"><?php _e( 'Forum Directory', 'buddypress') ?></a>
+					</li>
+
+				<?php endif; ?>
+
+			</ul>
+		</div>
+
+		<div id="topic-meta">
+			<h3><?php bp_the_topic_title() ?> (<?php bp_the_topic_total_post_count() ?>)</h3>
+
+			<?php if ( bp_forum_topic_has_tags() ) : ?>
+
+				<div class="topic-tags">
+
+					<?php _e( 'Topic tags:', 'buddypress' ) ?> <?php bp_forum_topic_tag_list() ?>
+
+				</div>
+
+			<?php endif; ?>
+
+			<?php if ( bp_group_is_admin() || bp_group_is_mod() || bp_get_the_topic_is_mine() ) : ?>
+
+				<div class="last admin-links">
+
+					<?php bp_the_topic_admin_links() ?>
+
+				</div>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_group_forum_topic_meta' ); ?>
+
+		</div>
+
+		<div class="pagination no-ajax">
+
+			<div id="post-count-top" class="pag-count">
+
+				<?php bp_the_topic_pagination_count() ?>
+
+			</div>
+
+			<div class="pagination-links" id="topic-pag-top">
+
+				<?php bp_the_topic_pagination() ?>
+
+			</div>
+
+		</div>
+
+		<?php do_action( 'bp_before_group_forum_topic_posts' ) ?>
+
+		<ul id="topic-post-list" class="item-list" role="main">
+			<?php while ( bp_forum_topic_posts() ) : bp_the_forum_topic_post(); ?>
+
+				<li id="post-<?php bp_the_topic_post_id() ?>" class="<?php bp_the_topic_post_css_class() ?>">
+					<div class="poster-meta">
+						<a href="<?php bp_the_topic_post_poster_link() ?>">
+							<?php bp_the_topic_post_poster_avatar( 'width=40&height=40' ) ?>
+						</a>
+						<?php echo sprintf( __( '%1$s said %2$s:', 'buddypress' ), bp_get_the_topic_post_poster_name(), bp_get_the_topic_post_time_since() ) ?>
+					</div>
+
+					<div class="post-content">
+						<?php bp_the_topic_post_content() ?>
+					</div>
+
+					<div class="admin-links">
+						<?php if ( bp_group_is_admin() || bp_group_is_mod() || bp_get_the_topic_post_is_mine() ) : ?>
+							<?php bp_the_topic_post_admin_links() ?>
+						<?php endif; ?>
+
+						<?php do_action( 'bp_group_forum_post_meta' ); ?>
+
+						<a href="#post-<?php bp_the_topic_post_id() ?>" title="<?php _e( 'Permanent link to this post', 'buddypress' ) ?>">#</a>
+					</div>
+				</li>
+
+			<?php endwhile; ?>
+		</ul><!-- #topic-post-list -->
+
+		<?php do_action( 'bp_after_group_forum_topic_posts' ) ?>
+
+		<div class="pagination no-ajax">
+
+			<div id="post-count-bottom" class="pag-count">
+				<?php bp_the_topic_pagination_count() ?>
+			</div>
+
+			<div class="pagination-links" id="topic-pag-bottom">
+				<?php bp_the_topic_pagination() ?>
+			</div>
+
+		</div>
+
+		<?php if ( ( is_user_logged_in() && 'public' == bp_get_group_status() ) || bp_group_is_member() ) : ?>
+
+			<?php if ( bp_get_the_topic_is_last_page() ) : ?>
+
+				<?php if ( bp_get_the_topic_is_topic_open() && !bp_group_is_user_banned() ) : ?>
+
+					<div id="post-topic-reply">
+						<p id="post-reply"></p>
+
+						<?php if ( bp_groups_auto_join() && !bp_group_is_member() ) : ?>
+							<p><?php _e( 'You will auto join this group when you reply to this topic.', 'buddypress' ) ?></p>
+						<?php endif; ?>
+
+						<?php do_action( 'groups_forum_new_reply_before' ) ?>
+
+						<h4><?php _e( 'Add a reply:', 'buddypress' ) ?></h4>
+
+						<textarea name="reply_text" id="reply_text"></textarea>
+
+						<div class="submit">
+							<input type="submit" name="submit_reply" id="submit" value="<?php _e( 'Post Reply', 'buddypress' ) ?>" />
+						</div>
+
+						<?php do_action( 'groups_forum_new_reply_after' ) ?>
+
+						<?php wp_nonce_field( 'bp_forums_new_reply' ) ?>
+					</div>
+
+				<?php elseif ( !bp_group_is_user_banned() ) : ?>
+
+					<div id="message" class="info">
+						<p><?php _e( 'This topic is closed, replies are no longer accepted.', 'buddypress' ) ?></p>
+					</div>
+
+				<?php endif; ?>
+
+			<?php endif; ?>
+
+		<?php endif; ?>
+
+	</form><!-- #forum-topic-form -->
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'There are no posts for this topic.', 'buddypress' ) ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_group_forum_topic' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/group-header.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/group-header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/group-header.php	(revision 5057)
@@ -0,0 +1,64 @@
+<?php
+
+do_action( 'bp_before_group_header' );
+
+?>
+
+<div id="item-actions">
+
+	<?php if ( bp_group_is_visible() ) : ?>
+
+		<h3><?php _e( 'Group Admins', 'buddypress' ); ?></h3>
+
+		<?php bp_group_list_admins();
+
+		do_action( 'bp_after_group_menu_admins' );
+
+		if ( bp_group_has_moderators() ) :
+			do_action( 'bp_before_group_menu_mods' ); ?>
+
+			<h3><?php _e( 'Group Mods' , 'buddypress' ) ?></h3>
+
+			<?php bp_group_list_mods();
+
+			do_action( 'bp_after_group_menu_mods' );
+
+		endif;
+
+	endif; ?>
+
+</div><!-- #item-actions -->
+
+<div id="item-header-avatar">
+	<a href="<?php bp_group_permalink(); ?>" title="<?php bp_group_name(); ?>">
+
+		<?php bp_group_avatar(); ?>
+
+	</a>
+</div><!-- #item-header-avatar -->
+
+<div id="item-header-content">
+	<h2><a href="<?php bp_group_permalink(); ?>" title="<?php bp_group_name(); ?>"><?php bp_group_name(); ?></a></h2>
+	<span class="highlight"><?php bp_group_type(); ?></span> <span class="activity"><?php printf( __( 'active %s', 'buddypress' ), bp_get_group_last_active() ); ?></span>
+
+	<?php do_action( 'bp_before_group_header_meta' ); ?>
+
+	<div id="item-meta">
+
+		<?php bp_group_description(); ?>
+
+		<div id="item-buttons">
+
+			<?php do_action( 'bp_group_header_actions' ); ?>
+
+		</div><!-- #item-buttons -->
+
+		<?php do_action( 'bp_group_header_meta' ); ?>
+
+	</div>
+</div><!-- #item-header-content -->
+
+<?php
+do_action( 'bp_after_group_header' );
+do_action( 'template_notices' );
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/home.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/home.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/home.php	(revision 5057)
@@ -0,0 +1,82 @@
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php if ( bp_has_groups() ) : while ( bp_groups() ) : bp_the_group(); ?>
+
+			<?php do_action( 'bp_before_group_home_content' ) ?>
+
+			<div id="item-header" role="complementary">
+
+				<?php locate_template( array( 'groups/single/group-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_options_nav(); ?>
+
+						<?php do_action( 'bp_group_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body">
+
+				<?php do_action( 'bp_before_group_body' );
+
+				if ( bp_is_group_admin_page() && bp_group_is_visible() ) :
+					locate_template( array( 'groups/single/admin.php' ), true );
+
+				elseif ( bp_is_group_members() && bp_group_is_visible() ) :
+					locate_template( array( 'groups/single/members.php' ), true );
+
+				elseif ( bp_is_group_invites() && bp_group_is_visible() ) :
+					locate_template( array( 'groups/single/send-invites.php' ), true );
+
+					elseif ( bp_is_group_forum() && bp_group_is_visible() && bp_is_active( 'forums' ) && bp_forums_is_installed_correctly() ) :
+						locate_template( array( 'groups/single/forum.php' ), true );
+
+				elseif ( bp_is_group_membership_request() ) :
+					locate_template( array( 'groups/single/request-membership.php' ), true );
+
+				elseif ( bp_group_is_visible() && bp_is_active( 'activity' ) ) :
+					locate_template( array( 'groups/single/activity.php' ), true );
+
+				elseif ( bp_group_is_visible() ) :
+					locate_template( array( 'groups/single/members.php' ), true );
+
+				elseif ( !bp_group_is_visible() ) :
+					// The group is not visible, show the status message
+
+					do_action( 'bp_before_group_status_message' ); ?>
+
+					<div id="message" class="info">
+						<p><?php bp_group_status_message(); ?></p>
+					</div>
+
+					<?php do_action( 'bp_after_group_status_message' );
+
+				else :
+					// If nothing sticks, just load a group front template if one exists.
+					locate_template( array( 'groups/single/front.php' ), true );
+
+				endif;
+
+				do_action( 'bp_after_group_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_group_home_content' ); ?>
+
+			<?php endwhile; endif; ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/members.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/members.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/members.php	(revision 5057)
@@ -0,0 +1,107 @@
+<?php if ( bp_group_has_members( 'exclude_admins_mods=0' ) ) : ?>
+
+	<?php do_action( 'bp_before_group_members_content' ); ?>
+
+	<div class="item-list-tabs" id="subnav" role="navigation">
+		<ul>
+
+			<?php do_action( 'bp_members_directory_member_sub_types' ); ?>
+
+			<li id="members-order-select" class="last filter">
+
+				<label for="members-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+				<select id="members-order-by">
+					<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+					<option value="newest"><?php _e( 'Newest Registered', 'buddypress' ); ?></option>
+
+					<?php if ( bp_is_active( 'xprofile' ) ) : ?>
+
+						<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_members_directory_order_options' ); ?>
+
+				</select>
+			</li>
+		</ul>
+	</div>
+
+	<div id="pag-top" class="pagination no-ajax">
+
+		<div class="pag-count" id="member-count-top">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-pag-top">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_before_group_members_list' ); ?>
+
+	<ul id="member-list" class="item-list" role="main">
+
+		<?php while ( bp_group_members() ) : bp_group_the_member(); ?>
+
+			<li>
+				<a href="<?php bp_group_member_domain(); ?>">
+
+					<?php bp_group_member_avatar_thumb(); ?>
+
+				</a>
+
+				<h5><?php bp_group_member_link(); ?></h5>
+				<span class="activity"><?php bp_group_member_joined_since(); ?></span>
+
+				<?php do_action( 'bp_group_members_list_item' ); ?>
+
+				<?php if ( bp_is_active( 'friends' ) ) : ?>
+
+					<div class="action">
+
+						<?php bp_add_friend_button( bp_get_group_member_id(), bp_get_group_member_is_friend() ); ?>
+
+						<?php do_action( 'bp_group_members_list_item_action' ); ?>
+
+					</div>
+
+				<?php endif; ?>
+			</li>
+
+		<?php endwhile; ?>
+
+	</ul>
+
+	<?php do_action( 'bp_after_group_members_list' ); ?>
+
+	<div id="pag-bottom" class="pagination">
+
+		<div class="pag-count" id="member-count-bottom">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-pag-bottom">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_after_group_members_content' ); ?>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'This group has no members.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/plugins.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/plugins.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/plugins.php	(revision 5057)
@@ -0,0 +1,41 @@
+<?php get_header( 'buddypress' ) ?>
+
+	<div id="content">
+		<div class="padder">
+			<?php if ( bp_has_groups() ) : while ( bp_groups() ) : bp_the_group(); ?>
+
+			<?php do_action( 'bp_before_group_plugin_template' ) ?>
+
+			<div id="item-header">
+				<?php locate_template( array( 'groups/single/group-header.php' ), true ) ?>
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+						<?php bp_get_options_nav() ?>
+
+						<?php do_action( 'bp_group_plugin_options_nav' ) ?>
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body">
+
+				<?php do_action( 'bp_before_group_body' ) ?>
+
+				<?php do_action( 'bp_template_content' ) ?>
+
+				<?php do_action( 'bp_after_group_body' ) ?>
+			</div><!-- #item-body -->
+
+			<?php endwhile; endif; ?>
+
+			<?php do_action( 'bp_after_group_plugin_template' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar( 'buddypress' ) ?>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/request-membership.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/request-membership.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/request-membership.php	(revision 5057)
@@ -0,0 +1,18 @@
+<?php do_action( 'bp_before_group_request_membership_content' ) ?>
+
+<?php if ( !bp_group_has_requested_membership() ) : ?>
+	<p><?php printf( __( "You are requesting to become a member of the group '%s'.", "buddypress" ), bp_get_group_name( false ) ); ?></p>
+
+	<form action="<?php bp_group_form_action('request-membership') ?>" method="post" name="request-membership-form" id="request-membership-form" class="standard-form">
+		<label for="group-request-membership-comments"><?php _e( 'Comments (optional)', 'buddypress' ); ?></label>
+		<textarea name="group-request-membership-comments" id="group-request-membership-comments"></textarea>
+
+		<?php do_action( 'bp_group_request_membership_content' ) ?>
+
+		<p><input type="submit" name="group-request-send" id="group-request-send" value="<?php _e( 'Send Request', 'buddypress' ) ?>" />
+
+		<?php wp_nonce_field( 'groups_request_membership' ) ?>
+	</form><!-- #request-membership-form -->
+<?php endif; ?>
+
+<?php do_action( 'bp_after_group_request_membership_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/groups/single/send-invites.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/groups/single/send-invites.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/groups/single/send-invites.php	(revision 5057)
@@ -0,0 +1,78 @@
+<?php do_action( 'bp_before_group_send_invites_content' ) ?>
+
+<?php if ( bp_get_total_friend_count( bp_loggedin_user_id() ) ) : ?>
+
+	<form action="<?php bp_group_send_invite_form_action() ?>" method="post" id="send-invite-form" class="standard-form" role="main">
+
+		<div class="left-menu">
+
+			<div id="invite-list">
+				<ul>
+					<?php bp_new_group_invite_friend_list() ?>
+				</ul>
+
+				<?php wp_nonce_field( 'groups_invite_uninvite_user', '_wpnonce_invite_uninvite_user' ) ?>
+			</div>
+
+		</div><!-- .left-menu -->
+
+		<div class="main-column">
+
+			<div id="message" class="info">
+				<p><?php _e('Select people to invite from your friends list.', 'buddypress'); ?></p>
+			</div>
+
+			<?php do_action( 'bp_before_group_send_invites_list' ) ?>
+
+			<?php /* The ID 'friend-list' is important for AJAX support. */ ?>
+			<ul id="friend-list" class="item-list">
+			<?php if ( bp_group_has_invites() ) : ?>
+
+				<?php while ( bp_group_invites() ) : bp_group_the_invite(); ?>
+
+					<li id="<?php bp_group_invite_item_id() ?>">
+						<?php bp_group_invite_user_avatar() ?>
+
+						<h4><?php bp_group_invite_user_link() ?></h4>
+						<span class="activity"><?php bp_group_invite_user_last_active() ?></span>
+
+						<?php do_action( 'bp_group_send_invites_item' ) ?>
+
+						<div class="action">
+							<a class="button remove" href="<?php bp_group_invite_user_remove_invite_url() ?>" id="<?php bp_group_invite_item_id() ?>"><?php _e( 'Remove Invite', 'buddypress' ) ?></a>
+
+							<?php do_action( 'bp_group_send_invites_item_action' ) ?>
+						</div>
+					</li>
+
+				<?php endwhile; ?>
+
+			<?php endif; ?>
+			</ul><!-- #friend-list -->
+
+			<?php do_action( 'bp_after_group_send_invites_list' ) ?>
+
+		</div><!-- .main-column -->
+
+		<div class="clear"></div>
+
+		<div class="submit">
+			<input type="submit" name="submit" id="submit" value="<?php _e( 'Send Invites', 'buddypress' ) ?>" />
+		</div>
+
+		<?php wp_nonce_field( 'groups_send_invites', '_wpnonce_send_invites') ?>
+
+		<?php /* This is important, don't forget it */ ?>
+		<input type="hidden" name="group_id" id="group_id" value="<?php bp_group_id() ?>" />
+
+	</form><!-- #send-invite-form -->
+
+<?php else : ?>
+
+	<div id="message" class="info" role="main">
+		<p><?php _e( 'Once you have built up friend connections you will be able to invite others to your group. You can send invites any time in the future by selecting the "Send Invites" option when viewing your new group.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_group_send_invites_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/header.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/header.php	(revision 5057)
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
+	<head profile="http://gmpg.org/xfn/11">
+		<meta http-equiv="Content-Type" content="<?php bloginfo( 'html_type' ) ?>; charset=<?php bloginfo( 'charset' ) ?>" />
+		<title><?php wp_title( '|', true, 'right' ); bloginfo( 'name' ); ?></title>
+
+		<?php do_action( 'bp_head' ) ?>
+
+		<link rel="pingback" href="<?php bloginfo( 'pingback_url' ) ?>" />
+
+		<?php
+			if ( is_singular() && bp_is_blog_page() && get_option( 'thread_comments' ) )
+				wp_enqueue_script( 'comment-reply' );
+
+			wp_head();
+		?>
+	</head>
+
+	<body <?php body_class() ?> id="bp-default">
+
+		<?php do_action( 'bp_before_header' ) ?>
+
+		<div id="header">
+			<div id="search-bar" role="search">
+				<div class="padder">
+					<h1 id="logo" role="banner"><a href="<?php echo home_url(); ?>" title="<?php _ex( 'Home', 'Home page banner link title', 'buddypress' ); ?>"><?php bp_site_name(); ?></a></h1>
+
+						<form action="<?php echo bp_search_form_action() ?>" method="post" id="search-form">
+							<label for="search-terms" class="accessibly-hidden"><?php _e( 'Search for:', 'buddypress' ); ?></label>
+							<input type="text" id="search-terms" name="search-terms" value="<?php echo isset( $_REQUEST['s'] ) ? esc_attr( $_REQUEST['s'] ) : ''; ?>" />
+
+							<?php echo bp_search_form_type_select() ?>
+
+							<input type="submit" name="search-submit" id="search-submit" value="<?php _e( 'Search', 'buddypress' ) ?>" />
+
+							<?php wp_nonce_field( 'bp_search_form' ) ?>
+
+						</form><!-- #search-form -->
+
+				<?php do_action( 'bp_search_login_bar' ) ?>
+
+				</div><!-- .padder -->
+			</div><!-- #search-bar -->
+
+			<div id="navigation" role="navigation">
+				<?php wp_nav_menu( array( 'container' => false, 'menu_id' => 'nav', 'theme_location' => 'primary', 'fallback_cb' => 'bp_dtheme_main_nav' ) ); ?>
+			</div>
+
+			<?php do_action( 'bp_header' ) ?>
+
+		</div><!-- #header -->
+
+		<?php do_action( 'bp_after_header' ) ?>
+		<?php do_action( 'bp_before_container' ) ?>
+
+		<div id="container">
Index: /tags/1.5-beta-3/bp-themes/bp-default/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/index.php	(revision 5057)
@@ -0,0 +1,69 @@
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_blog_home' ) ?>
+
+		<?php do_action( 'template_notices' ) ?>
+
+		<div class="page" id="blog-latest" role="main">
+
+			<?php if ( have_posts() ) : ?>
+
+				<?php bp_dtheme_content_nav( 'nav-above' ); ?>
+
+				<?php while (have_posts()) : the_post(); ?>
+
+					<?php do_action( 'bp_before_blog_post' ) ?>
+
+					<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+						<div class="author-box">
+							<?php echo get_avatar( get_the_author_meta( 'user_email' ), '50' ); ?>
+							<p><?php printf( _x( 'by %s', 'Post written by...', 'buddypress' ), bp_core_get_userlink( $post->post_author ) ) ?></p>
+
+							<?php if ( is_sticky() ) : ?>
+								<span class="activity sticky-post"><?php _ex( 'Featured', 'Sticky post', 'buddypress' ); ?></span>
+							<?php endif; ?>
+						</div>
+
+						<div class="post-content">
+							<h2 class="posttitle"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php _e( 'Permanent Link to', 'buddypress' ) ?> <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+
+							<p class="date"><?php printf( __( '%1$s <span>in %2$s</span>', 'buddypress' ), get_the_date(), get_the_category_list( ', ' ) ); ?></p>
+
+							<div class="entry">
+								<?php the_content( __( 'Read the rest of this entry &rarr;', 'buddypress' ) ); ?>
+								<?php wp_link_pages( array( 'before' => '<div class="page-link"><p>' . __( 'Pages: ', 'buddypress' ), 'after' => '</p></div>', 'next_or_number' => 'number' ) ); ?>
+							</div>
+
+							<p class="postmetadata"><?php the_tags( '<span class="tags">' . __( 'Tags: ', 'buddypress' ), ', ', '</span>' ); ?> <span class="comments"><?php comments_popup_link( __( 'No Comments &#187;', 'buddypress' ), __( '1 Comment &#187;', 'buddypress' ), __( '% Comments &#187;', 'buddypress' ) ); ?></span></p>
+						</div>
+
+					</div>
+
+					<?php do_action( 'bp_after_blog_post' ) ?>
+
+				<?php endwhile; ?>
+
+				<?php bp_dtheme_content_nav( 'nav-below' ); ?>
+
+			<?php else : ?>
+
+				<h2 class="center"><?php _e( 'Not Found', 'buddypress' ) ?></h2>
+				<p class="center"><?php _e( 'Sorry, but you are looking for something that isn\'t here.', 'buddypress' ) ?></p>
+
+				<?php get_search_form() ?>
+
+			<?php endif; ?>
+		</div>
+
+		<?php do_action( 'bp_after_blog_home' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar() ?>
+
+<?php get_footer() ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/license.txt
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/license.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/license.txt	(revision 5057)
@@ -0,0 +1,280 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/links.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/links.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/links.php	(revision 5057)
@@ -0,0 +1,29 @@
+<?php
+/*
+Template Name: Links
+*/
+?>
+
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_blog_links' ) ?>
+
+		<div class="page" id="blog-latest" role="main">
+
+			<h2 class="pagetitle"><?php _e( 'Links', 'buddypress' ) ?></h2>
+
+			<ul id="links-list">
+				<?php wp_list_bookmarks(); ?>
+			</ul>
+
+		</div>
+
+		<?php do_action( 'bp_after_blog_links' ) ?>
+
+		</div>
+	</div>
+
+<?php get_footer(); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/index.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/index.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/index.php	(revision 5057)
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * BuddyPress - Members Directory
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<?php do_action( 'bp_before_directory_members_page' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_directory_members' ); ?>
+
+		<form action="" method="post" id="members-directory-form" class="dir-form">
+
+			<h3><?php _e( 'Members Directory', 'buddypress' ); ?></h3>
+
+			<?php do_action( 'bp_before_directory_members_content' ); ?>
+
+			<div id="members-dir-search" class="dir-search" role="search">
+
+				<?php bp_directory_members_search_form(); ?>
+
+			</div><!-- #members-dir-search -->
+
+			<div class="item-list-tabs" role="navigation">
+				<ul>
+					<li class="selected" id="members-all"><a href="<?php echo trailingslashit( bp_get_root_domain() . '/' . bp_get_members_root_slug() ); ?>"><?php printf( __( 'All Members <span>%s</span>', 'buddypress' ), bp_get_total_member_count() ); ?></a></li>
+
+					<?php if ( is_user_logged_in() && bp_is_active( 'friends' ) && bp_get_total_friend_count( bp_loggedin_user_id() ) ) : ?>
+
+						<li id="members-personal"><a href="<?php echo bp_loggedin_user_domain() . bp_get_friends_slug() . '/my-friends/' ?>"><?php printf( __( 'My Friends <span>%s</span>', 'buddypress' ), bp_get_total_friend_count( bp_loggedin_user_id() ) ); ?></a></li>
+
+					<?php endif; ?>
+
+					<?php do_action( 'bp_members_directory_member_types' ); ?>
+
+				</ul>
+			</div><!-- .item-list-tabs -->
+
+			<div class="item-list-tabs" id="subnav" role="navigation">
+				<ul>
+
+					<?php do_action( 'bp_members_directory_member_sub_types' ); ?>
+
+					<li id="members-order-select" class="last filter">
+
+						<label for="members-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+						<select id="members-order-by">
+							<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+							<option value="newest"><?php _e( 'Newest Registered', 'buddypress' ); ?></option>
+
+							<?php if ( bp_is_active( 'xprofile' ) ) : ?>
+
+								<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+							<?php endif; ?>
+
+							<?php do_action( 'bp_members_directory_order_options' ); ?>
+
+						</select>
+					</li>
+				</ul>
+			</div>
+
+			<div id="members-dir-list" class="members dir-list">
+
+				<?php locate_template( array( 'members/members-loop.php' ), true ); ?>
+
+			</div><!-- #members-dir-list -->
+
+			<?php do_action( 'bp_directory_members_content' ); ?>
+
+			<?php wp_nonce_field( 'directory_members', '_wpnonce-member-filter' ); ?>
+
+			<?php do_action( 'bp_after_directory_members_content' ); ?>
+
+		</form><!-- #members-directory-form -->
+
+		<?php do_action( 'bp_after_directory_members' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php do_action( 'bp_after_directory_members_page' ); ?>
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/members-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/members-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/members-loop.php	(revision 5057)
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * BuddyPress - Members Loop
+ *
+ * Querystring is set via AJAX in _inc/ajax.php - bp_dtheme_object_filter()
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_members_loop' ); ?>
+
+<?php if ( bp_has_members( bp_ajax_querystring( 'members' ) ) ) : ?>
+
+	<div id="pag-top" class="pagination">
+
+		<div class="pag-count" id="member-dir-count-top">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-dir-pag-top">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+	<?php do_action( 'bp_before_directory_members_list' ); ?>
+
+	<ul id="members-list" class="item-list" role="main">
+
+	<?php while ( bp_members() ) : bp_the_member(); ?>
+
+		<li>
+			<div class="item-avatar">
+				<a href="<?php bp_member_permalink(); ?>"><?php bp_member_avatar(); ?></a>
+			</div>
+
+			<div class="item">
+				<div class="item-title">
+					<a href="<?php bp_member_permalink(); ?>"><?php bp_member_name(); ?></a>
+
+					<?php if ( bp_get_member_latest_update() ) : ?>
+
+						<span class="update"> <?php bp_member_latest_update(); ?></span>
+
+					<?php endif; ?>
+
+				</div>
+
+				<div class="item-meta"><span class="activity"><?php bp_member_last_active(); ?></span></div>
+
+				<?php do_action( 'bp_directory_members_item' ); ?>
+
+				<?php
+				 /***
+				  * If you want to show specific profile fields here you can,
+				  * but it'll add an extra query for each member in the loop
+				  * (only one regardless of the number of fields you show):
+				  *
+				  * bp_member_profile_data( 'field=the field name' );
+				  */
+				?>
+			</div>
+
+			<div class="action">
+
+				<?php do_action( 'bp_directory_members_actions' ); ?>
+
+			</div>
+
+			<div class="clear"></div>
+		</li>
+
+	<?php endwhile; ?>
+
+	</ul>
+
+	<?php do_action( 'bp_after_directory_members_list' ); ?>
+
+	<?php bp_member_hidden_fields(); ?>
+
+	<div id="pag-bottom" class="pagination">
+
+		<div class="pag-count" id="member-dir-count-bottom">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-dir-pag-bottom">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( "Sorry, no members were found.", 'buddypress' ); ?></p>
+	</div>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_members_loop' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity.php	(revision 5057)
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * BuddyPress - Users Activity
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+
+		<?php bp_get_options_nav() ?>
+
+		<li id="activity-filter-select" class="last">
+			<label for="activity-filter-by"><?php _e( 'Show:', 'buddypress' ); ?></label>
+			<select id="activity-filter-by">
+				<option value="-1"><?php _e( 'Everything', 'buddypress' ) ?></option>
+				<option value="activity_update"><?php _e( 'Updates', 'buddypress' ) ?></option>
+
+				<?php
+				if ( !bp_is_current_action( 'groups' ) ) :
+					if ( bp_is_active( 'blogs' ) ) : ?>
+
+						<option value="new_blog_post"><?php _e( 'Posts', 'buddypress' ) ?></option>
+						<option value="new_blog_comment"><?php _e( 'Comments', 'buddypress' ) ?></option>
+
+					<?php
+					endif;
+
+					if ( bp_is_active( 'friends' ) ) : ?>
+
+						<option value="friendship_accepted,friendship_created"><?php _e( 'Friendships', 'buddypress' ) ?></option>
+
+					<?php endif;
+
+				endif;
+
+				if ( bp_is_active( 'forums' ) ) : ?>
+
+					<option value="new_forum_topic"><?php _e( 'Forum Topics', 'buddypress' ) ?></option>
+					<option value="new_forum_post"><?php _e( 'Forum Replies', 'buddypress' ) ?></option>
+
+				<?php endif;
+
+				if ( bp_is_active( 'groups' ) ) : ?>
+
+					<option value="created_group"><?php _e( 'New Groups', 'buddypress' ) ?></option>
+					<option value="joined_group"><?php _e( 'Group Memberships', 'buddypress' ) ?></option>
+
+				<?php endif;
+
+				do_action( 'bp_member_activity_filter_options' ); ?>
+
+			</select>
+		</li>
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php do_action( 'bp_before_member_activity_post_form' ); ?>
+
+<?php
+if ( is_user_logged_in() && bp_is_my_profile() && ( !bp_current_action() || bp_is_current_action( 'just-me' ) ) )
+	locate_template( array( 'activity/post-form.php'), true );
+
+do_action( 'bp_after_member_activity_post_form' );
+do_action( 'bp_before_member_activity_content' ); ?>
+
+<div class="activity" role="main">
+
+	<?php locate_template( array( 'activity/activity-loop.php' ), true ); ?>
+
+</div><!-- .activity -->
+
+<?php do_action( 'bp_after_member_activity_content' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity/permalink.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity/permalink.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/activity/permalink.php	(revision 5057)
@@ -0,0 +1,17 @@
+<?php get_header( 'buddypress' ) ?>
+
+<div class="activity no-ajax" role="main">
+	<?php if ( bp_has_activities( 'display_comments=threaded&show_hidden=true&include=' . bp_current_action() ) ) : ?>
+
+		<ul id="activity-stream" class="activity-list item-list">
+		<?php while ( bp_activities() ) : bp_the_activity(); ?>
+
+			<?php locate_template( array( 'activity/entry.php' ), true ) ?>
+
+		<?php endwhile; ?>
+		</ul>
+
+	<?php endif; ?>
+</div>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/blogs.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/blogs.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/blogs.php	(revision 5057)
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * BuddyPress - Users Blogs
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs" id="subnav" role="navigation">
+	<ul>
+
+		<?php bp_get_options_nav(); ?>
+
+		<li id="blogs-order-select" class="last filter">
+
+			<label for="blogs-all"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+			<select id="blogs-all">
+				<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+				<option value="newest"><?php _e( 'Newest', 'buddypress' ); ?></option>
+				<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+				<?php do_action( 'bp_member_blog_order_options' ); ?>
+
+			</select>
+		</li>
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php do_action( 'bp_before_member_blogs_content' ); ?>
+
+<div class="blogs myblogs" role="main">
+
+	<?php locate_template( array( 'blogs/blogs-loop.php' ), true ); ?>
+
+</div><!-- .blogs.myblogs -->
+
+<?php do_action( 'bp_after_member_blogs_content' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums.php	(revision 5057)
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * BuddyPress - Users Forums
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+		<?php bp_get_options_nav() ?>
+
+		<li id="forums-order-select" class="last filter">
+
+			<label for="forums-order-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+			<select id="forums-order-by">
+				<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+				<option value="popular"><?php _e( 'Most Posts', 'buddypress' ); ?></option>
+				<option value="unreplied"><?php _e( 'Unreplied', 'buddypress' ); ?></option>
+
+				<?php do_action( 'bp_forums_directory_order_options' ); ?>
+
+			</select>
+		</li>
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php
+
+if ( bp_is_current_action( 'favorites' ) ) :
+	locate_template( array( 'members/single/forums/topics.php' ), true );
+
+else :
+	do_action( 'bp_before_member_forums_content' ); ?>
+
+	<div class="forums myforums">
+
+		<?php locate_template( array( 'forums/forums-loop.php' ), true ); ?>
+
+	</div>
+
+	<?php do_action( 'bp_after_member_forums_content' ); ?>
+
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums/topics.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums/topics.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/forums/topics.php	(revision 5057)
@@ -0,0 +1,7 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends.php	(revision 5057)
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * BuddyPress - Users Friends
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+		<?php if ( bp_is_my_profile() ) bp_get_options_nav(); ?>
+
+		<?php if ( !bp_is_current_action( 'requests' ) ) : ?>
+
+			<li id="members-order-select" class="last filter">
+
+				<label for="members-all"><?php _e( 'Order By:', 'buddypress' ) ?></label>
+				<select id="members-all">
+					<option value="active"><?php _e( 'Last Active', 'buddypress' ) ?></option>
+					<option value="newest"><?php _e( 'Newest Registered', 'buddypress' ) ?></option>
+					<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ) ?></option>
+
+					<?php do_action( 'bp_member_blog_order_options' ) ?>
+
+				</select>
+			</li>
+
+		<?php endif; ?>
+
+	</ul>
+</div>
+
+<?php
+
+if ( bp_is_current_action( 'requests' ) ) :
+	 locate_template( array( 'members/single/friends/requests.php' ), true );
+
+else :
+	do_action( 'bp_before_member_friends_content' ); ?>
+
+	<div class="members friends">
+
+		<?php locate_template( array( 'members/members-loop.php' ), true ); ?>
+
+	</div><!-- .members.friends -->
+
+	<?php do_action( 'bp_after_member_friends_content' ); ?>
+
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends/requests.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends/requests.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/friends/requests.php	(revision 5057)
@@ -0,0 +1,73 @@
+<?php do_action( 'bp_before_member_friend_requests_content' ) ?>
+
+<?php if ( bp_has_members( 'include=' . bp_get_friendship_requests() ) ) : ?>
+
+	<div id="pag-top" class="pagination no-ajax">
+
+		<div class="pag-count" id="member-dir-count-top">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-dir-pag-top">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+	<ul id="friend-list" class="item-list" role="main">
+		<?php while ( bp_members() ) : bp_the_member(); ?>
+
+			<li id="friendship-<?php bp_friend_friendship_id() ?>">
+				<div class="item-avatar">
+					<a href="<?php bp_member_link() ?>"><?php bp_member_avatar() ?></a>
+				</div>
+
+				<div class="item">
+					<div class="item-title"><a href="<?php bp_member_link() ?>"><?php bp_member_name() ?></a></div>
+					<div class="item-meta"><span class="activity"><?php bp_member_last_active() ?></span></div>
+				</div>
+
+				<?php do_action( 'bp_friend_requests_item' ) ?>
+
+				<div class="action">
+					<a class="button accept" href="<?php bp_friend_accept_request_link() ?>"><?php _e( 'Accept', 'buddypress' ); ?></a> &nbsp;
+					<a class="button reject" href="<?php bp_friend_reject_request_link() ?>"><?php _e( 'Reject', 'buddypress' ); ?></a>
+
+					<?php do_action( 'bp_friend_requests_item_action' ) ?>
+				</div>
+			</li>
+
+		<?php endwhile; ?>
+	</ul>
+
+	<?php do_action( 'bp_friend_requests_content' ) ?>
+
+	<div id="pag-bottom" class="pagination no-ajax">
+
+		<div class="pag-count" id="member-dir-count-bottom">
+
+			<?php bp_members_pagination_count(); ?>
+
+		</div>
+
+		<div class="pagination-links" id="member-dir-pag-bottom">
+
+			<?php bp_members_pagination_links(); ?>
+
+		</div>
+
+	</div>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'You have no pending friendship requests.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_member_friend_requests_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups.php	(revision 5057)
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * BuddyPress - Users Groups
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+		<?php if ( bp_is_my_profile() ) bp_get_options_nav(); ?>
+
+		<?php if ( !bp_is_current_action( 'invites' ) ) : ?>
+
+			<li id="groups-order-select" class="last filter">
+
+				<label for="groups-sort-by"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+				<select id="groups-sort-by">
+					<option value="active"><?php _e( 'Last Active', 'buddypress' ); ?></option>
+					<option value="popular"><?php _e( 'Most Members', 'buddypress' ); ?></option>
+					<option value="newest"><?php _e( 'Newly Created', 'buddypress' ); ?></option>
+					<option value="alphabetical"><?php _e( 'Alphabetical', 'buddypress' ); ?></option>
+
+					<?php do_action( 'bp_member_group_order_options' ) ?>
+
+				</select>
+			</li>
+
+		<?php endif; ?>
+
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php
+
+if ( bp_is_current_action( 'invites' ) ) :
+	locate_template( array( 'members/single/groups/invites.php' ), true );
+
+else :
+	do_action( 'bp_before_member_groups_content' ); ?>
+
+	<div class="groups mygroups">
+
+		<?php locate_template( array( 'groups/groups-loop.php' ), true ); ?>
+
+	</div>
+
+	<?php do_action( 'bp_after_member_groups_content' ); ?>
+
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups/invites.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups/invites.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/groups/invites.php	(revision 5057)
@@ -0,0 +1,42 @@
+<?php do_action( 'bp_before_group_invites_content' ) ?>
+
+<?php if ( bp_has_groups( 'type=invites&user_id=' . bp_loggedin_user_id() ) ) : ?>
+
+	<ul id="group-list" class="invites item-list" role="main">
+
+		<?php while ( bp_groups() ) : bp_the_group(); ?>
+
+			<li>
+				<div class="item-avatar">
+					<a href="<?php bp_group_permalink() ?>"><?php bp_group_avatar( 'type=thumb&width=50&height=50' ) ?></a>
+				</div>
+
+				<h4><a href="<?php bp_group_permalink() ?>"><?php bp_group_name() ?></a><span class="small"> - <?php printf( __( '%s members', 'buddypress' ), bp_group_total_members( false ) ) ?></span></h4>
+
+				<p class="desc">
+					<?php bp_group_description_excerpt() ?>
+				</p>
+
+				<?php do_action( 'bp_group_invites_item' ) ?>
+
+				<div class="action">
+					<a class="button accept" href="<?php bp_group_accept_invite_link() ?>"><?php _e( 'Accept', 'buddypress' ) ?></a> &nbsp;
+					<a class="button reject confirm" href="<?php bp_group_reject_invite_link() ?>"><?php _e( 'Reject', 'buddypress' ) ?></a>
+
+					<?php do_action( 'bp_group_invites_item_action' ) ?>
+
+				</div>
+			</li>
+
+		<?php endwhile; ?>
+	</ul>
+
+<?php else: ?>
+
+	<div id="message" class="info" role="main">
+		<p><?php _e( 'You have no outstanding group invites.', 'buddypress' ) ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_group_invites_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/home.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/home.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/home.php	(revision 5057)
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * BuddyPress - Users Home
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_member_home_content' ); ?>
+
+			<div id="item-header" role="complementary">
+
+				<?php locate_template( array( 'members/single/member-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_displayed_user_nav(); ?>
+
+						<?php do_action( 'bp_member_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body">
+
+				<?php do_action( 'bp_before_member_body' );
+
+				if ( bp_is_user_activity() || !bp_current_component() ) :
+					locate_template( array( 'members/single/activity.php' ), true );
+
+				 elseif ( bp_is_user_blogs() ) :
+					locate_template( array( 'members/single/blogs.php'    ), true );
+
+				elseif ( bp_is_user_friends() ) :
+					locate_template( array( 'members/single/friends.php'  ), true );
+
+				elseif ( bp_is_user_groups() ) :
+					locate_template( array( 'members/single/groups.php'   ), true );
+
+				elseif ( bp_is_user_messages() ) :
+					locate_template( array( 'members/single/messages.php' ), true );
+
+				elseif ( bp_is_user_profile() ) :
+					locate_template( array( 'members/single/profile.php'  ), true );
+
+				elseif ( bp_is_user_forums() ) :
+					locate_template( array( 'members/single/forums.php'  ), true );
+
+				// If nothing sticks, load a generic template
+				else :
+					locate_template( array( 'members/single/front.php'    ), true );
+
+				endif;
+
+				do_action( 'bp_after_member_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_member_home_content' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/member-header.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/member-header.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/member-header.php	(revision 5057)
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * BuddyPress - Users Header
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php do_action( 'bp_before_member_header' ); ?>
+
+<div id="item-header-avatar">
+	<a href="<?php bp_user_link(); ?>">
+
+		<?php bp_displayed_user_avatar( 'type=full' ); ?>
+
+	</a>
+</div><!-- #item-header-avatar -->
+
+<div id="item-header-content">
+
+	<h2>
+		<a href="<?php bp_displayed_user_link(); ?>"><?php bp_displayed_user_fullname(); ?></a>
+	</h2>
+
+	<span class="user-nicename">@<?php bp_displayed_user_username(); ?></span>
+	<span class="activity"><?php bp_last_activity( bp_displayed_user_id() ); ?></span>
+
+	<?php do_action( 'bp_before_member_header_meta' ); ?>
+
+	<div id="item-meta">
+
+		<?php if ( bp_is_active( 'activity' ) ) : ?>
+
+			<div id="latest-update">
+
+				<?php bp_activity_latest_update( bp_displayed_user_id() ); ?>
+
+			</div>
+
+		<?php endif; ?>
+
+		<div id="item-buttons">
+
+			<?php do_action( 'bp_member_header_actions' ); ?>
+
+		</div><!-- #item-buttons -->
+
+		<?php
+		/***
+		 * If you'd like to show specific profile fields here use:
+		 * bp_profile_field_data( 'field=About Me' ); -- Pass the name of the field
+		 */
+		 do_action( 'bp_profile_header_meta' );
+
+		 ?>
+
+	</div><!-- #item-meta -->
+
+</div><!-- #item-header-content -->
+
+<?php do_action( 'bp_after_member_header' ); ?>
+
+<?php do_action( 'template_notices' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages.php	(revision 5057)
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * BuddyPress - Users Messages
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+	<ul>
+
+		<?php bp_get_options_nav(); ?>
+
+	</ul>
+</div><!-- .item-list-tabs -->
+
+<?php
+
+	if ( bp_is_current_action( 'compose' ) ) :
+		locate_template( array( 'members/single/messages/compose.php' ), true );
+
+	elseif ( bp_is_current_action( 'view' ) ) :
+		locate_template( array( 'members/single/messages/single.php' ), true );
+
+	else :
+		do_action( 'bp_before_member_messages_content' ); ?>
+
+	<div class="messages" role="main">
+
+		<?php
+			if ( bp_is_current_action( 'notices' ) )
+				locate_template( array( 'members/single/messages/notices-loop.php' ), true );
+			else
+				locate_template( array( 'members/single/messages/messages-loop.php' ), true );
+		?>
+
+	</div><!-- .messages -->
+
+	<?php do_action( 'bp_after_member_messages_content' ); ?>
+
+<?php endif; ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/compose.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/compose.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/compose.php	(revision 5057)
@@ -0,0 +1,37 @@
+<form action="<?php bp_messages_form_action('compose') ?>" method="post" id="send_message_form" class="standard-form" role="main">
+
+	<?php do_action( 'bp_before_messages_compose_content' ) ?>
+
+	<label for="send-to-input"><?php _e("Send To (Username or Friend's Name)", 'buddypress') ?></label>
+	<ul class="first acfb-holder">
+		<li>
+			<?php bp_message_get_recipient_tabs() ?>
+			<input type="text" name="send-to-input" class="send-to-input" id="send-to-input" />
+		</li>
+	</ul>
+
+	<?php if ( is_super_admin() ) : ?>
+		<input type="checkbox" id="send-notice" name="send-notice" value="1" /> <?php _e( "This is a notice to all users.", "buddypress" ) ?>
+	<?php endif; ?>
+
+	<label for="subject"><?php _e( 'Subject', 'buddypress') ?></label>
+	<input type="text" name="subject" id="subject" value="<?php bp_messages_subject_value() ?>" />
+
+	<label for="content"><?php _e( 'Message', 'buddypress') ?></label>
+	<textarea name="content" id="message_content" rows="15" cols="40"><?php bp_messages_content_value() ?></textarea>
+
+	<input type="hidden" name="send_to_usernames" id="send-to-usernames" value="<?php bp_message_get_recipient_usernames(); ?>" class="<?php bp_message_get_recipient_usernames() ?>" />
+
+	<?php do_action( 'bp_after_messages_compose_content' ) ?>
+
+	<div class="submit">
+		<input type="submit" value="<?php _e( "Send Message", 'buddypress' ) ?>" name="send" id="send" />
+	</div>
+
+	<?php wp_nonce_field( 'messages_send_message' ) ?>
+</form>
+
+<script type="text/javascript">
+	document.getElementById("send-to-input").focus();
+</script>
+
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/messages-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/messages-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/messages-loop.php	(revision 5057)
@@ -0,0 +1,73 @@
+<?php do_action( 'bp_before_member_messages_loop' ) ?>
+
+<?php if ( bp_has_message_threads() ) : ?>
+
+	<div class="pagination no-ajax" id="user-pag">
+
+		<div class="pag-count" id="messages-dir-count">
+			<?php bp_messages_pagination_count() ?>
+		</div>
+
+		<div class="pagination-links" id="messages-dir-pag">
+			<?php bp_messages_pagination() ?>
+		</div>
+
+	</div><!-- .pagination -->
+
+	<?php do_action( 'bp_after_member_messages_pagination' ) ?>
+	<?php do_action( 'bp_before_member_messages_threads' ) ?>
+
+	<table id="message-threads" class="messages-notices">
+		<?php while ( bp_message_threads() ) : bp_message_thread(); ?>
+
+			<tr id="m-<?php bp_message_thread_id() ?>" class="<?php bp_message_css_class(); ?><?php if ( bp_message_thread_has_unread() ) : ?> unread"<?php else: ?> read"<?php endif; ?>>
+				<td width="1%" class="thread-count">
+					<span class="unread-count"><?php bp_message_thread_unread_count() ?></span>
+				</td>
+				<td width="1%" class="thread-avatar"><?php bp_message_thread_avatar() ?></td>
+
+				<?php if ( 'sentbox' != bp_current_action() ) : ?>
+					<td width="30%" class="thread-from">
+						<?php _e( 'From:', 'buddypress' ); ?> <?php bp_message_thread_from() ?><br />
+						<span class="activity"><?php bp_message_thread_last_post_date() ?></span>
+					</td>
+				<?php else: ?>
+					<td width="30%" class="thread-from">
+						<?php _e( 'To:', 'buddypress' ); ?> <?php bp_message_thread_to() ?><br />
+						<span class="activity"><?php bp_message_thread_last_post_date() ?></span>
+					</td>
+				<?php endif; ?>
+
+				<td width="50%" class="thread-info">
+					<p><a href="<?php bp_message_thread_view_link() ?>" title="<?php _e( "View Message", "buddypress" ); ?>"><?php bp_message_thread_subject() ?></a></p>
+					<p class="thread-excerpt"><?php bp_message_thread_excerpt() ?></p>
+				</td>
+
+				<?php do_action( 'bp_messages_inbox_list_item' ) ?>
+
+				<td width="13%" class="thread-options">
+					<input type="checkbox" name="message_ids[]" value="<?php bp_message_thread_id() ?>" />
+					<a class="button confirm" href="<?php bp_message_thread_delete_link() ?>" title="<?php _e( "Delete Message", "buddypress" ); ?>"><?php _e( 'Delete', 'buddypress' ) ?></a> &nbsp;
+				</td>
+			</tr>
+
+		<?php endwhile; ?>
+	</table><!-- #message-threads -->
+
+	<div class="messages-options-nav">
+		<?php bp_messages_options() ?>
+	</div><!-- .messages-options-nav -->
+
+	<?php do_action( 'bp_after_member_messages_threads' ) ?>
+
+	<?php do_action( 'bp_after_member_messages_options' ) ?>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'Sorry, no messages were found.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_member_messages_loop' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/notices-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/notices-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/notices-loop.php	(revision 5057)
@@ -0,0 +1,54 @@
+<?php do_action( 'bp_before_notices_loop' ) ?>
+
+<?php if ( bp_has_message_threads() ) : ?>
+
+	<div class="pagination" id="user-pag">
+
+		<div class="pag-count" id="messages-dir-count">
+			<?php bp_messages_pagination_count() ?>
+		</div>
+
+		<div class="pagination-links" id="messages-dir-pag">
+			<?php bp_messages_pagination() ?>
+		</div>
+
+	</div><!-- .pagination -->
+
+	<?php do_action( 'bp_after_notices_pagination' ) ?>
+	<?php do_action( 'bp_before_notices' ) ?>
+
+	<table id="message-threads" class="messages-notices">
+		<?php while ( bp_message_threads() ) : bp_message_thread(); ?>
+			<tr id="notice-<?php bp_message_notice_id() ?>" class="<?php bp_message_css_class(); ?>">
+				<td width="1%">
+				</td>
+				<td width="38%">
+					<strong><?php bp_message_notice_subject() ?></strong>
+					<?php bp_message_notice_text() ?>
+				</td>
+				<td width="21%">
+					<strong><?php bp_message_is_active_notice() ?></strong>
+					<span class="activity"><?php _e("Sent:", "buddypress"); ?> <?php bp_message_notice_post_date() ?></span>
+				</td>
+
+				<?php do_action( 'bp_notices_list_item' ) ?>
+
+				<td width="10%">
+					<a class="button" href="<?php bp_message_activate_deactivate_link() ?>" class="confirm"><?php bp_message_activate_deactivate_text() ?></a>
+					<a class="button" href="<?php bp_message_notice_delete_link() ?>" class="confirm" title="<?php _e( "Delete Message", "buddypress" ); ?>">x</a>
+				</td>
+			</tr>
+		<?php endwhile; ?>
+	</table><!-- #message-threads -->
+
+	<?php do_action( 'bp_after_notices' ) ?>
+
+<?php else: ?>
+
+	<div id="message" class="info">
+		<p><?php _e( 'Sorry, no notices were found.', 'buddypress' ); ?></p>
+	</div>
+
+<?php endif;?>
+
+<?php do_action( 'bp_after_notices_loop' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/single.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/single.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/messages/single.php	(revision 5057)
@@ -0,0 +1,110 @@
+<div id="message-thread" role="main">
+
+	<?php do_action( 'bp_before_message_thread_content' ) ?>
+
+	<?php if ( bp_thread_has_messages() ) : ?>
+
+		<h3 id="message-subject"><?php bp_the_thread_subject() ?></h3>
+
+		<p id="message-recipients">
+			<span class="highlight">
+
+				<?php if ( !bp_get_the_thread_recipients() ) : ?>
+
+					<?php _e( 'You are alone in this conversation.', 'buddypress' ); ?>
+
+				<?php else : ?>
+
+					<?php printf( __( 'Conversation between %s and you.', 'buddypress' ), bp_get_the_thread_recipients() ) ?>
+
+				<?php endif; ?>
+
+			</span>
+
+			<a class="button confirm" href="<?php bp_the_thread_delete_link() ?>" title="<?php _e( "Delete Message", "buddypress" ); ?>"><?php _e( 'Delete', 'buddypress' ) ?></a> &nbsp;
+		</p>
+
+		<?php do_action( 'bp_before_message_thread_list' ) ?>
+
+		<?php while ( bp_thread_messages() ) : bp_thread_the_message(); ?>
+
+			<div class="message-box <?php bp_the_thread_message_alt_class(); ?>">
+
+				<div class="message-metadata">
+
+					<?php do_action( 'bp_before_message_meta' ) ?>
+
+					<?php bp_the_thread_message_sender_avatar( 'type=thumb&width=30&height=30' ) ?>
+					<strong><a href="<?php bp_the_thread_message_sender_link() ?>" title="<?php bp_the_thread_message_sender_name() ?>"><?php bp_the_thread_message_sender_name() ?></a> <span class="activity"><?php bp_the_thread_message_time_since() ?></span></strong>
+
+					<?php do_action( 'bp_after_message_meta' ) ?>
+
+				</div><!-- .message-metadata -->
+
+				<?php do_action( 'bp_before_message_content' ) ?>
+
+				<div class="message-content">
+
+					<?php bp_the_thread_message_content() ?>
+
+				</div><!-- .message-content -->
+
+				<?php do_action( 'bp_after_message_content' ) ?>
+
+				<div class="clear"></div>
+
+			</div><!-- .message-box -->
+
+		<?php endwhile; ?>
+
+		<?php do_action( 'bp_after_message_thread_list' ) ?>
+
+		<?php do_action( 'bp_before_message_thread_reply' ) ?>
+
+		<form id="send-reply" action="<?php bp_messages_form_action() ?>" method="post" class="standard-form">
+
+			<div class="message-box">
+
+				<div class="message-metadata">
+
+					<?php do_action( 'bp_before_message_meta' ) ?>
+
+					<div class="avatar-box">
+						<?php bp_loggedin_user_avatar( 'type=thumb&height=30&width=30' ) ?>
+
+						<strong><?php _e( 'Send a Reply', 'buddypress' ) ?></strong>
+					</div>
+
+					<?php do_action( 'bp_after_message_meta' ) ?>
+
+				</div><!-- .message-metadata -->
+
+				<div class="message-content">
+
+					<?php do_action( 'bp_before_message_reply_box' ) ?>
+
+					<textarea name="content" id="message_content" rows="15" cols="40"></textarea>
+
+					<?php do_action( 'bp_after_message_reply_box' ) ?>
+
+					<div class="submit">
+						<input type="submit" name="send" value="<?php _e( 'Send Reply', 'buddypress' ) ?>" id="send_reply_button"/>
+					</div>
+
+					<input type="hidden" id="thread_id" name="thread_id" value="<?php bp_the_thread_id(); ?>" />
+					<input type="hidden" id="messages_order" name="messages_order" value="<?php bp_thread_messages_order(); ?>" />
+					<?php wp_nonce_field( 'messages_send_message', 'send_message_nonce' ) ?>
+
+				</div><!-- .message-content -->
+
+			</div><!-- .message-box -->
+
+		</form><!-- #send-reply -->
+
+		<?php do_action( 'bp_after_message_thread_reply' ) ?>
+
+	<?php endif; ?>
+
+	<?php do_action( 'bp_after_message_thread_content' ) ?>
+
+</div>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/plugins.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/plugins.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/plugins.php	(revision 5057)
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * BuddyPress - Users Plugins
+ *
+ * This is a fallback file that external plugins can use if the template they
+ * need is not installed in the current theme. Use the actions in this template
+ * to output everything your plugin needs.
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_member_plugin_template' ); ?>
+
+			<div id="item-header">
+
+				<?php locate_template( array( 'members/single/member-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_displayed_user_nav(); ?>
+
+						<?php do_action( 'bp_member_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body" role="main">
+
+				<?php do_action( 'bp_before_member_body' ); ?>
+
+				<div class="item-list-tabs no-ajax" id="subnav">
+					<ul>
+
+						<?php bp_get_options_nav(); ?>
+
+						<?php do_action( 'bp_member_plugin_options_nav' ); ?>
+
+					</ul>
+				</div><!-- .item-list-tabs -->
+
+				<h3><?php do_action( 'bp_template_title' ); ?></h3>
+
+				<?php do_action( 'bp_template_content' ); ?>
+
+				<?php do_action( 'bp_after_member_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_member_plugin_template' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ); ?>
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile.php	(revision 5057)
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * BuddyPress - Users Profile
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+
+?>
+
+<?php if ( bp_is_my_profile() ) : ?>
+
+	<div class="item-list-tabs no-ajax" id="subnav" role="navigation">
+		<ul>
+
+			<?php bp_get_options_nav(); ?>
+
+		</ul>
+	</div><!-- .item-list-tabs -->
+
+<?php endif; ?>
+
+<?php do_action( 'bp_before_profile_content' ); ?>
+
+<div class="profile" role="main">
+
+	<?php
+		// Profile Edit
+		if ( bp_is_current_action( 'edit' ) )
+			locate_template( array( 'members/single/profile/edit.php' ), true );
+
+		// Change Avatar
+		elseif ( bp_is_current_action( 'change-avatar' ) )
+			locate_template( array( 'members/single/profile/change-avatar.php' ), true );
+
+		// Display XProfile
+		elseif ( bp_is_active( 'xprofile' ) )
+			locate_template( array( 'members/single/profile/profile-loop.php' ), true );
+
+		// Display WordPress profile (fallback)
+		else
+			locate_template( array( 'members/single/profile/profile-wp.php' ), true );
+	?>
+
+</div><!-- .profile -->
+
+<?php do_action( 'bp_after_profile_content' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/change-avatar.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/change-avatar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/change-avatar.php	(revision 5057)
@@ -0,0 +1,59 @@
+<h4><?php _e( 'Change Avatar', 'buddypress' ) ?></h4>
+
+<?php do_action( 'bp_before_profile_avatar_upload_content' ) ?>
+
+<?php if ( !(int)bp_get_option( 'bp-disable-avatar-uploads' ) ) : ?>
+
+	<p><?php _e( 'Your avatar will be used on your profile and throughout the site. If there is a <a href="http://gravatar.com">Gravatar</a> associated with your account email we will use that, or you can upload an image from your computer.', 'buddypress') ?></p>
+
+	<form action="" method="post" id="avatar-upload-form" class="standard-form" enctype="multipart/form-data">
+
+		<?php if ( 'upload-image' == bp_get_avatar_admin_step() ) : ?>
+
+			<?php wp_nonce_field( 'bp_avatar_upload' ) ?>
+			<p><?php _e( 'Click below to select a JPG, GIF or PNG format photo from your computer and then click \'Upload Image\' to proceed.', 'buddypress' ) ?></p>
+
+			<p id="avatar-upload">
+				<input type="file" name="file" id="file" />
+				<input type="submit" name="upload" id="upload" value="<?php _e( 'Upload Image', 'buddypress' ) ?>" />
+				<input type="hidden" name="action" id="action" value="bp_avatar_upload" />
+			</p>
+
+			<?php if ( bp_get_user_has_avatar() ) : ?>
+				<p><?php _e( "If you'd like to delete your current avatar but not upload a new one, please use the delete avatar button.", 'buddypress' ) ?></p>
+				<p><a class="button edit" href="<?php bp_avatar_delete_link() ?>" title="<?php _e( 'Delete Avatar', 'buddypress' ) ?>"><?php _e( 'Delete My Avatar', 'buddypress' ) ?></a></p>
+			<?php endif; ?>
+
+		<?php endif; ?>
+
+		<?php if ( 'crop-image' == bp_get_avatar_admin_step() ) : ?>
+
+			<h5><?php _e( 'Crop Your New Avatar', 'buddypress' ) ?></h5>
+
+			<img src="<?php bp_avatar_to_crop() ?>" id="avatar-to-crop" class="avatar" alt="<?php _e( 'Avatar to crop', 'buddypress' ) ?>" />
+
+			<div id="avatar-crop-pane">
+				<img src="<?php bp_avatar_to_crop() ?>" id="avatar-crop-preview" class="avatar" alt="<?php _e( 'Avatar preview', 'buddypress' ) ?>" />
+			</div>
+
+			<input type="submit" name="avatar-crop-submit" id="avatar-crop-submit" value="<?php _e( 'Crop Image', 'buddypress' ) ?>" />
+
+			<input type="hidden" name="image_src" id="image_src" value="<?php bp_avatar_to_crop_src() ?>" />
+			<input type="hidden" id="x" name="x" />
+			<input type="hidden" id="y" name="y" />
+			<input type="hidden" id="w" name="w" />
+			<input type="hidden" id="h" name="h" />
+
+			<?php wp_nonce_field( 'bp_avatar_cropstore' ) ?>
+
+		<?php endif; ?>
+
+	</form>
+
+<?php else : ?>
+
+	<p><?php _e( 'Your avatar will be used on your profile and throughout the site. To change your avatar, please create an account with <a href="http://gravatar.com">Gravatar</a> using the same email address as you used to register with this site.', 'buddypress' ) ?></p>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_profile_avatar_upload_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/edit.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/edit.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/edit.php	(revision 5057)
@@ -0,0 +1,137 @@
+<?php do_action( 'bp_before_profile_edit_content' );
+
+if ( bp_has_profile( 'profile_group_id=' . bp_get_current_profile_group_id() ) ) :
+	while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
+
+<form action="<?php bp_the_profile_group_edit_form_action(); ?>" method="post" id="profile-edit-form" class="standard-form <?php bp_the_profile_group_slug(); ?>">
+
+	<?php do_action( 'bp_before_profile_field_content' ); ?>
+
+		<h4><?php printf( __( "Editing '%s' Profile Group", "buddypress" ), bp_get_the_profile_group_name() ); ?></h4>
+
+		<ul class="button-nav">
+
+			<?php bp_profile_group_tabs(); ?>
+
+		</ul>
+
+		<div class="clear"></div>
+
+		<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
+
+			<div<?php bp_field_css_class( 'editfield' ) ?>>
+
+				<?php if ( 'textbox' == bp_get_the_profile_field_type() ) : ?>
+
+					<label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
+					<input type="text" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" value="<?php bp_the_profile_field_edit_value(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>/>
+
+				<?php endif; ?>
+
+				<?php if ( 'textarea' == bp_get_the_profile_field_type() ) : ?>
+
+					<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
+					<textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>><?php bp_the_profile_field_edit_value(); ?></textarea>
+
+				<?php endif; ?>
+
+				<?php if ( 'selectbox' == bp_get_the_profile_field_type() ) : ?>
+
+					<label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
+					<select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
+						<?php bp_the_profile_field_options() ?>
+					</select>
+
+				<?php endif; ?>
+
+				<?php if ( 'multiselectbox' == bp_get_the_profile_field_type() ) : ?>
+
+					<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
+					<select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" multiple="multiple" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
+
+						<?php bp_the_profile_field_options(); ?>
+
+					</select>
+
+					<?php if ( !bp_get_the_profile_field_is_required() ) : ?>
+
+						<a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a>
+
+					<?php endif; ?>
+
+				<?php endif; ?>
+
+				<?php if ( 'radio' == bp_get_the_profile_field_type() ) : ?>
+
+					<div class="radio">
+						<span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
+
+						<?php bp_the_profile_field_options(); ?>
+
+						<?php if ( !bp_get_the_profile_field_is_required() ) : ?>
+
+							<a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a>
+
+						<?php endif; ?>
+					</div>
+
+				<?php endif; ?>
+
+				<?php if ( 'checkbox' == bp_get_the_profile_field_type() ) : ?>
+
+					<div class="checkbox">
+						<span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
+
+						<?php bp_the_profile_field_options(); ?>
+					</div>
+
+				<?php endif; ?>
+
+				<?php if ( 'datebox' == bp_get_the_profile_field_type() ) : ?>
+
+					<div class="datebox">
+						<label for="<?php bp_the_profile_field_input_name(); ?>_day"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
+
+						<select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
+
+							<?php bp_the_profile_field_options( 'type=day' ); ?>
+
+						</select>
+
+						<select name="<?php bp_the_profile_field_input_name() ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
+
+							<?php bp_the_profile_field_options( 'type=month' ); ?>
+
+						</select>
+
+						<select name="<?php bp_the_profile_field_input_name() ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
+
+							<?php bp_the_profile_field_options( 'type=year' ); ?>
+
+						</select>
+					</div>
+
+				<?php endif; ?>
+
+				<?php do_action( 'bp_custom_profile_edit_fields' ); ?>
+
+				<p class="description"><?php bp_the_profile_field_description(); ?></p>
+			</div>
+
+		<?php endwhile; ?>
+
+	<?php do_action( 'bp_after_profile_field_content' ); ?>
+
+	<div class="submit">
+		<input type="submit" name="profile-group-edit-submit" id="profile-group-edit-submit" value="<?php _e( 'Save Changes', 'buddypress' ); ?> " />
+	</div>
+
+	<input type="hidden" name="field_ids" id="field_ids" value="<?php bp_the_profile_group_field_ids(); ?>" />
+
+	<?php wp_nonce_field( 'bp_xprofile_edit' ); ?>
+
+</form>
+
+<?php endwhile; endif; ?>
+
+<?php do_action( 'bp_after_profile_edit_content' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-loop.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-loop.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-loop.php	(revision 5057)
@@ -0,0 +1,48 @@
+<?php do_action( 'bp_before_profile_loop_content' ); ?>
+
+<?php if ( bp_has_profile() ) : ?>
+
+	<?php while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
+
+		<?php if ( bp_profile_group_has_fields() ) : ?>
+
+			<?php do_action( 'bp_before_profile_field_content' ); ?>
+
+			<div class="bp-widget <?php bp_the_profile_group_slug(); ?>">
+
+				<h4><?php bp_the_profile_group_name(); ?></h4>
+
+				<table class="profile-fields">
+
+					<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
+
+						<?php if ( bp_field_has_data() ) : ?>
+
+							<tr<?php bp_field_css_class(); ?>>
+
+								<td class="label"><?php bp_the_profile_field_name(); ?></td>
+
+								<td class="data"><?php bp_the_profile_field_value(); ?></td>
+
+							</tr>
+
+						<?php endif; ?>
+
+						<?php do_action( 'bp_profile_field_item' ); ?>
+
+					<?php endwhile; ?>
+
+				</table>
+			</div>
+
+			<?php do_action( 'bp_after_profile_field_content' ); ?>
+
+		<?php endif; ?>
+
+	<?php endwhile; ?>
+
+	<?php do_action( 'bp_profile_field_buttons' ); ?>
+
+<?php endif; ?>
+
+<?php do_action( 'bp_after_profile_loop_content' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-wp.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-wp.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/profile/profile-wp.php	(revision 5057)
@@ -0,0 +1,73 @@
+<?php do_action( 'bp_before_profile_loop_content' ) ?>
+
+<?php $ud = get_userdata( bp_displayed_user_id() ); ?>
+
+<?php do_action( 'bp_before_profile_field_content' ) ?>
+
+	<div class="bp-widget wp-profile">
+		<h4><?php bp_is_my_profile() ? _e( 'My Profile', 'buddypress' ) : printf( __( "%s's Profile", 'buddypress' ), bp_get_displayed_user_fullname() ); ?></h4>
+
+		<table class="wp-profile-fields">
+
+			<?php if ( $ud->display_name ) : ?>
+
+				<tr id="wp_displayname">
+					<td class="label"><?php _e( 'Name', 'buddypress' ); ?></td>
+					<td class="data"><?php echo $ud->display_name; ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( $ud->user_description ) : ?>
+
+				<tr id="wp_desc">
+					<td class="label"><?php _e( 'About Me', 'buddypress' ); ?></td>
+					<td class="data"><?php echo $ud->user_description; ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( $ud->user_url ) : ?>
+
+				<tr id="wp_website">
+					<td class="label"><?php _e( 'Website', 'buddypress' ); ?></td>
+					<td class="data"><?php echo make_clickable( $ud->user_url ); ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( $ud->jabber ) : ?>
+
+				<tr id="wp_jabber">
+					<td class="label"><?php _e( 'Jabber', 'buddypress' ); ?></td>
+					<td class="data"><?php echo $ud->jabber; ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( $ud->aim ) : ?>
+
+				<tr id="wp_aim">
+					<td class="label"><?php _e( 'AOL Messenger', 'buddypress' ); ?></td>
+					<td class="data"><?php echo $ud->aim; ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+			<?php if ( $ud->yim ) : ?>
+
+				<tr id="wp_yim">
+					<td class="label"><?php _e( 'Yahoo Messenger', 'buddypress' ); ?></td>
+					<td class="data"><?php echo $ud->yim; ?></td>
+				</tr>
+
+			<?php endif; ?>
+
+		</table>
+	</div>
+
+<?php do_action( 'bp_after_profile_field_content' ) ?>
+
+<?php do_action( 'bp_profile_field_buttons' ) ?>
+
+<?php do_action( 'bp_after_profile_loop_content' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/delete-account.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/delete-account.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/delete-account.php	(revision 5057)
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * BuddyPress Delete Account
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+?>
+
+<?php get_header( 'buddypress' ) ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_member_settings_template' ); ?>
+
+			<div id="item-header">
+
+				<?php locate_template( array( 'members/single/member-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_displayed_user_nav(); ?>
+
+						<?php do_action( 'bp_member_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body" role="main">
+
+				<?php do_action( 'bp_before_member_body' ); ?>
+
+				<div class="item-list-tabs no-ajax" id="subnav">
+					<ul>
+
+						<?php bp_get_options_nav(); ?>
+
+						<?php do_action( 'bp_member_plugin_options_nav' ); ?>
+
+					</ul>
+				</div><!-- .item-list-tabs -->
+
+				<h3><?php _e( 'Delete Account', 'buddypress' ); ?></h3>
+
+				<form action="<?php echo bp_displayed_user_domain() . bp_get_settings_slug() . '/delete-account'; ?>" name="account-delete-form" id="account-delete-form" class="standard-form" method="post">
+
+					<div id="message" class="info">
+						<p><?php _e( 'WARNING: Deleting your account will completely remove ALL content associated with it. There is no way back, please be careful with this option.', 'buddypress' ); ?></p>
+					</div>
+
+					<input type="checkbox" name="delete-account-understand" id="delete-account-understand" value="1" onclick="if(this.checked) { document.getElementById('delete-account-button').disabled = ''; } else { document.getElementById('delete-account-button').disabled = 'disabled'; }" /> <?php _e( 'I understand the consequences of deleting my account.', 'buddypress' ); ?>
+
+					<?php do_action( 'bp_members_delete_account_before_submit' ); ?>
+
+					<div class="submit">
+						<input type="submit" disabled="disabled" value="<?php _e( 'Delete My Account', 'buddypress' ) ?>" id="delete-account-button" name="delete-account-button" />
+					</div>
+
+					<?php do_action( 'bp_members_delete_account_after_submit' ); ?>
+
+					<?php wp_nonce_field( 'delete-account' ); ?>
+				</form>
+
+				<?php do_action( 'bp_after_member_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_member_settings_template' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ) ?>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/general.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/general.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/general.php	(revision 5057)
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * BuddyPress Member Settings
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+?>
+
+<?php get_header( 'buddypress' ) ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_member_settings_template' ); ?>
+
+			<div id="item-header">
+
+				<?php locate_template( array( 'members/single/member-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_displayed_user_nav(); ?>
+
+						<?php do_action( 'bp_member_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body" role="main">
+
+				<?php do_action( 'bp_before_member_body' ); ?>
+
+				<div class="item-list-tabs no-ajax" id="subnav">
+					<ul>
+
+						<?php bp_get_options_nav(); ?>
+
+						<?php do_action( 'bp_member_plugin_options_nav' ); ?>
+
+					</ul>
+				</div><!-- .item-list-tabs -->
+
+				<h3><?php _e( 'General Settings', 'buddypress' ); ?></h3>
+
+				<?php do_action( 'bp_template_content' ) ?>
+
+				<form action="<?php echo bp_displayed_user_domain() . bp_get_settings_slug() . '/general'; ?>" method="post" class="standard-form" id="settings-form">
+
+					<label for="pwd"><?php _e( 'Current Password <span>(required to update email or change current password)</span>', 'buddypress' ); ?></label>
+					<input type="password" name="pwd" id="pwd" size="16" value="" class="settings-input small" /> &nbsp;<a href="<?php echo site_url( add_query_arg( array( 'action' => 'lostpassword' ), 'wp-login.php' ), 'login' ); ?>" title="<?php _e( 'Password Lost and Found', 'buddypress' ); ?>"><?php _e( 'Lost your password?', 'buddypress' ); ?></a>
+
+					<label for="email"><?php _e( 'Account Email', 'buddypress' ); ?></label>
+					<input type="text" name="email" id="email" value="<?php echo bp_get_displayed_user_email(); ?>" class="settings-input" />
+
+					<label for="pass1"><?php _e( 'Change Password <span>(leave blank for no change)</span>', 'buddypress' ); ?></label>
+					<input type="password" name="pass1" id="pass1" size="16" value="" class="settings-input small" /> &nbsp;<?php _e( 'New Password', 'buddypress' ); ?><br />
+					<input type="password" name="pass2" id="pass2" size="16" value="" class="settings-input small" /> &nbsp;<?php _e( 'Repeat New Password', 'buddypress' ); ?>
+
+					<?php do_action( 'bp_core_general_settings_before_submit' ); ?>
+
+					<div class="submit">
+						<input type="submit" name="submit" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" id="submit" class="auto" />
+					</div>
+
+					<?php do_action( 'bp_core_general_settings_after_submit' ); ?>
+
+					<?php wp_nonce_field( 'bp_settings_general' ); ?>
+
+				</form>
+
+				<?php do_action( 'bp_after_member_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_member_settings_template' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ) ?>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/notifications.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/notifications.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/members/single/settings/notifications.php	(revision 5057)
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * BuddyPress Notification Settings
+ *
+ * @package BuddyPress
+ * @subpackage bp-default
+ */
+?>
+
+<?php get_header( 'buddypress' ) ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_member_settings_template' ); ?>
+
+			<div id="item-header">
+
+				<?php locate_template( array( 'members/single/member-header.php' ), true ); ?>
+
+			</div><!-- #item-header -->
+
+			<div id="item-nav">
+				<div class="item-list-tabs no-ajax" id="object-nav" role="navigation">
+					<ul>
+
+						<?php bp_get_displayed_user_nav(); ?>
+
+						<?php do_action( 'bp_member_options_nav' ); ?>
+
+					</ul>
+				</div>
+			</div><!-- #item-nav -->
+
+			<div id="item-body" role="main">
+
+				<?php do_action( 'bp_before_member_body' ); ?>
+
+				<div class="item-list-tabs no-ajax" id="subnav">
+					<ul>
+
+						<?php bp_get_options_nav(); ?>
+
+						<?php do_action( 'bp_member_plugin_options_nav' ); ?>
+
+					</ul>
+				</div><!-- .item-list-tabs -->
+
+				<h3><?php _e( 'Email Notification', 'buddypress' ); ?></h3>
+
+				<?php do_action( 'bp_template_content' ) ?>
+
+				<form action="<?php echo bp_displayed_user_domain() . bp_get_settings_slug() . '/notifications'; ?>" method="post" class="standard-form" id="settings-form">
+					<p><?php _e( 'Send a notification by email when:', 'buddypress' ); ?></p>
+
+					<?php do_action( 'bp_notification_settings' ); ?>
+
+					<?php do_action( 'bp_members_notification_settings_before_submit' ); ?>
+
+					<div class="submit">
+						<input type="submit" name="submit" value="<?php _e( 'Save Changes', 'buddypress' ); ?>" id="submit" class="auto" />
+					</div>
+
+					<?php do_action( 'bp_members_notification_settings_after_submit' ); ?>
+
+					<?php wp_nonce_field('bp_settings_notifications'); ?>
+
+				</form>
+
+				<?php do_action( 'bp_after_member_body' ); ?>
+
+			</div><!-- #item-body -->
+
+			<?php do_action( 'bp_after_member_settings_template' ); ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_sidebar( 'buddypress' ) ?>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/onecolumn-page.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/onecolumn-page.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/onecolumn-page.php	(revision 5057)
@@ -0,0 +1,49 @@
+<?php
+/*
+ * Template Name: One column, no sidebar
+ *
+ * A custom page template without sidebar.
+ *
+ * @package BuddyPress
+ * @subpackage BP_Default
+ * @since 1.5
+ */
+
+get_header() ?>
+
+	<div id="content">
+		<div class="padder one-column">
+
+		<?php do_action( 'bp_before_blog_page' ) ?>
+
+		<div class="page" id="blog-page" role="main">
+
+			<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
+
+				<h2 class="pagetitle"><?php the_title(); ?></h2>
+
+				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+					<div class="entry">
+
+						<?php the_content( __( '<p class="serif">Read the rest of this page &rarr;</p>', 'buddypress' ) ); ?>
+
+						<?php wp_link_pages( array( 'before' => '<div class="page-link"><p>' . __( 'Pages: ', 'buddypress' ), 'after' => '</p></div>', 'next_or_number' => 'number' ) ); ?>
+						<?php edit_post_link( __( 'Edit this page.', 'buddypress' ), '<p class="edit-link">', '</p>'); ?>
+
+					</div>
+
+				</div>
+
+			<?php comments_template(); ?>
+
+			<?php endwhile; endif; ?>
+
+		</div><!-- .page -->
+
+		<?php do_action( 'bp_after_blog_page' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+<?php get_footer(); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/page.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/page.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/page.php	(revision 5057)
@@ -0,0 +1,40 @@
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_blog_page' ) ?>
+
+		<div class="page" id="blog-page" role="main">
+
+			<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
+
+				<h2 class="pagetitle"><?php the_title(); ?></h2>
+
+				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+					<div class="entry">
+
+						<?php the_content( __( '<p class="serif">Read the rest of this page &rarr;</p>', 'buddypress' ) ); ?>
+
+						<?php wp_link_pages( array( 'before' => '<div class="page-link"><p>' . __( 'Pages: ', 'buddypress' ), 'after' => '</p></div>', 'next_or_number' => 'number' ) ); ?>
+						<?php edit_post_link( __( 'Edit this page.', 'buddypress' ), '<p class="edit-link">', '</p>'); ?>
+
+					</div>
+
+				</div>
+
+			<?php comments_template(); ?>
+
+			<?php endwhile; endif; ?>
+
+		</div><!-- .page -->
+
+		<?php do_action( 'bp_after_blog_page' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar() ?>
+
+<?php get_footer(); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/readme.txt
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/readme.txt	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/readme.txt	(revision 5057)
@@ -0,0 +1,22 @@
+--- BuddyPress Default ---
+Clean and stylish, BuddyPress Default lets you build a social network straight out of the box. Make it yours with a custom menu, header image, and background. Along with five widgetized areas (one in the sidebar, four in the footer), BP-Default supports featured images (as custom header images on posts and pages) and is furnished with an optional one-column page template that removes the sidebar, and a stylesheet for the admin Visual Editor.
+
+
+--- Installation ---
+The theme is bundled with BuddyPress. After activating the plugin, BuddyPress Default will be added to the "Appearance > Themes" menu in your WordPress admin area.
+
+
+--- About BuddyPress ---
+Social networking in a box. Build a social network for your company, school, sports team or niche community all based on the power and flexibility of WordPress. BuddyPress will let users register on your site and start creating profiles, posting messages, making connections, creating and interacting in groups and much more.
+
+For help with BuddyPress Default, or for more information about BuddyPress, please visit http://buddypress.org/.
+
+
+--- Building a BuddyPress theme ---
+If you want to make a custom theme based on BuddyPress Default, DO NOT copy and edit it. By doing this you will make updates and maintenance much harder for yourself. Instead, please review this codex page for instructions on how to build a BuddyPress child theme:
+
+http://codex.buddypress.org/how-to-guides/building-a-buddypress-child-theme/
+
+
+--- Changelog ---
+A list of changes is available at http://codex.buddypress.org/theme-development/bp-default-theme-changelog/.
Index: /tags/1.5-beta-3/bp-themes/bp-default/registration/activate.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/registration/activate.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/registration/activate.php	(revision 5057)
@@ -0,0 +1,54 @@
+<?php get_header( 'buddypress' ); ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_activation_page' ) ?>
+
+		<div class="page" id="activate-page">
+
+			<?php if ( bp_account_was_activated() ) : ?>
+
+				<h2 class="widgettitle"><?php _e( 'Account Activated', 'buddypress' ) ?></h2>
+
+				<?php do_action( 'bp_before_activate_content' ) ?>
+
+				<?php if ( isset( $_GET['e'] ) ) : ?>
+					<p><?php _e( 'Your account was activated successfully! Your account details have been sent to you in a separate email.', 'buddypress' ) ?></p>
+				<?php else : ?>
+					<p><?php _e( 'Your account was activated successfully! You can now log in with the username and password you provided when you signed up.', 'buddypress' ) ?></p>
+				<?php endif; ?>
+
+			<?php else : ?>
+
+				<h3><?php _e( 'Activate your Account', 'buddypress' ) ?></h3>
+
+				<?php do_action( 'bp_before_activate_content' ) ?>
+
+				<p><?php _e( 'Please provide a valid activation key.', 'buddypress' ) ?></p>
+
+				<form action="" method="get" class="standard-form" id="activation-form">
+
+					<label for="key"><?php _e( 'Activation Key:', 'buddypress' ) ?></label>
+					<input type="text" name="key" id="key" value="" />
+
+					<p class="submit">
+						<input type="submit" name="submit" value="<?php _e( 'Activate', 'buddypress' ) ?>" />
+					</p>
+
+				</form>
+
+			<?php endif; ?>
+
+			<?php do_action( 'bp_after_activate_content' ) ?>
+
+		</div><!-- .page -->
+
+		<?php do_action( 'bp_after_activation_page' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar( 'buddypress' ) ?>
+
+<?php get_footer( 'buddypress' ); ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/registration/register.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/registration/register.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/registration/register.php	(revision 5057)
@@ -0,0 +1,269 @@
+<?php get_header( 'buddypress' ) ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_register_page' ) ?>
+
+		<div class="page" id="register-page">
+
+			<form action="" name="signup_form" id="signup_form" class="standard-form" method="post" enctype="multipart/form-data">
+
+			<?php if ( 'registration-disabled' == bp_get_current_signup_step() ) : ?>
+				<?php do_action( 'template_notices' ) ?>
+				<?php do_action( 'bp_before_registration_disabled' ) ?>
+
+					<p><?php _e( 'User registration is currently not allowed.', 'buddypress' ); ?></p>
+
+				<?php do_action( 'bp_after_registration_disabled' ); ?>
+			<?php endif; // registration-disabled signup setp ?>
+
+			<?php if ( 'request-details' == bp_get_current_signup_step() ) : ?>
+
+				<h2><?php _e( 'Create an Account', 'buddypress' ) ?></h2>
+
+				<?php do_action( 'template_notices' ) ?>
+
+				<p><?php _e( 'Registering for this site is easy, just fill in the fields below and we\'ll get a new account set up for you in no time.', 'buddypress' ) ?></p>
+
+				<?php do_action( 'bp_before_account_details_fields' ) ?>
+
+				<div class="register-section" id="basic-details-section">
+
+					<?php /***** Basic Account Details ******/ ?>
+
+					<h4><?php _e( 'Account Details', 'buddypress' ) ?></h4>
+
+					<label for="signup_username"><?php _e( 'Username', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+					<?php do_action( 'bp_signup_username_errors' ) ?>
+					<input type="text" name="signup_username" id="signup_username" value="<?php bp_signup_username_value() ?>" />
+
+					<label for="signup_email"><?php _e( 'Email Address', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+					<?php do_action( 'bp_signup_email_errors' ) ?>
+					<input type="text" name="signup_email" id="signup_email" value="<?php bp_signup_email_value() ?>" />
+
+					<label for="signup_password"><?php _e( 'Choose a Password', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+					<?php do_action( 'bp_signup_password_errors' ) ?>
+					<input type="password" name="signup_password" id="signup_password" value="" />
+
+					<label for="signup_password_confirm"><?php _e( 'Confirm Password', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+					<?php do_action( 'bp_signup_password_confirm_errors' ) ?>
+					<input type="password" name="signup_password_confirm" id="signup_password_confirm" value="" />
+
+				</div><!-- #basic-details-section -->
+
+				<?php do_action( 'bp_after_account_details_fields' ) ?>
+
+				<?php /***** Extra Profile Details ******/ ?>
+
+				<?php if ( bp_is_active( 'xprofile' ) ) : ?>
+
+					<?php do_action( 'bp_before_signup_profile_fields' ) ?>
+
+					<div class="register-section" id="profile-details-section">
+
+						<h4><?php _e( 'Profile Details', 'buddypress' ) ?></h4>
+
+						<?php /* Use the profile field loop to render input fields for the 'base' profile field group */ ?>
+						<?php if ( bp_is_active( 'xprofile' ) ) : if ( bp_has_profile( 'profile_group_id=1' ) ) : while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
+
+						<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
+
+							<div class="editfield">
+
+								<?php if ( 'textbox' == bp_get_the_profile_field_type() ) : ?>
+
+									<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></label>
+									<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+									<input type="text" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" value="<?php bp_the_profile_field_edit_value() ?>" />
+
+								<?php endif; ?>
+
+								<?php if ( 'textarea' == bp_get_the_profile_field_type() ) : ?>
+
+									<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></label>
+									<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+									<textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_edit_value() ?></textarea>
+
+								<?php endif; ?>
+
+								<?php if ( 'selectbox' == bp_get_the_profile_field_type() ) : ?>
+
+									<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></label>
+									<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+									<select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>">
+										<?php bp_the_profile_field_options() ?>
+									</select>
+
+								<?php endif; ?>
+
+								<?php if ( 'multiselectbox' == bp_get_the_profile_field_type() ) : ?>
+
+									<label for="<?php bp_the_profile_field_input_name() ?>"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></label>
+									<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+									<select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" multiple="multiple">
+										<?php bp_the_profile_field_options() ?>
+									</select>
+
+								<?php endif; ?>
+
+								<?php if ( 'radio' == bp_get_the_profile_field_type() ) : ?>
+
+									<div class="radio">
+										<span class="label"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></span>
+
+										<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+										<?php bp_the_profile_field_options() ?>
+
+										<?php if ( !bp_get_the_profile_field_is_required() ) : ?>
+											<a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name() ?>' );"><?php _e( 'Clear', 'buddypress' ) ?></a>
+										<?php endif; ?>
+									</div>
+
+								<?php endif; ?>
+
+								<?php if ( 'checkbox' == bp_get_the_profile_field_type() ) : ?>
+
+									<div class="checkbox">
+										<span class="label"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></span>
+
+										<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+										<?php bp_the_profile_field_options() ?>
+									</div>
+
+								<?php endif; ?>
+
+								<?php if ( 'datebox' == bp_get_the_profile_field_type() ) : ?>
+
+									<div class="datebox">
+										<label for="<?php bp_the_profile_field_input_name() ?>_day"><?php bp_the_profile_field_name() ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ) ?><?php endif; ?></label>
+										<?php do_action( 'bp_' . bp_get_the_profile_field_input_name() . '_errors' ) ?>
+
+										<select name="<?php bp_the_profile_field_input_name() ?>_day" id="<?php bp_the_profile_field_input_name() ?>_day">
+											<?php bp_the_profile_field_options( 'type=day' ) ?>
+										</select>
+
+										<select name="<?php bp_the_profile_field_input_name() ?>_month" id="<?php bp_the_profile_field_input_name() ?>_month">
+											<?php bp_the_profile_field_options( 'type=month' ) ?>
+										</select>
+
+										<select name="<?php bp_the_profile_field_input_name() ?>_year" id="<?php bp_the_profile_field_input_name() ?>_year">
+											<?php bp_the_profile_field_options( 'type=year' ) ?>
+										</select>
+									</div>
+
+								<?php endif; ?>
+
+								<?php do_action( 'bp_custom_profile_edit_fields' ) ?>
+
+								<p class="description"><?php bp_the_profile_field_description() ?></p>
+
+							</div>
+
+						<?php endwhile; ?>
+
+						<input type="hidden" name="signup_profile_field_ids" id="signup_profile_field_ids" value="<?php bp_the_profile_group_field_ids() ?>" />
+
+						<?php endwhile; endif; endif; ?>
+
+					</div><!-- #profile-details-section -->
+
+					<?php do_action( 'bp_after_signup_profile_fields' ) ?>
+
+				<?php endif; ?>
+
+				<?php if ( bp_get_blog_signup_allowed() ) : ?>
+
+					<?php do_action( 'bp_before_blog_details_fields' ) ?>
+
+					<?php /***** Blog Creation Details ******/ ?>
+
+					<div class="register-section" id="blog-details-section">
+
+						<h4><?php _e( 'Blog Details', 'buddypress' ) ?></h4>
+
+						<p><input type="checkbox" name="signup_with_blog" id="signup_with_blog" value="1"<?php if ( (int) bp_get_signup_with_blog_value() ) : ?> checked="checked"<?php endif; ?> /> <?php _e( 'Yes, I\'d like to create a new site', 'buddypress' ) ?></p>
+
+						<div id="blog-details"<?php if ( (int) bp_get_signup_with_blog_value() ) : ?>class="show"<?php endif; ?>>
+
+							<label for="signup_blog_url"><?php _e( 'Blog URL', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+							<?php do_action( 'bp_signup_blog_url_errors' ) ?>
+
+							<?php if ( is_subdomain_install() ) : ?>
+								http:// <input type="text" name="signup_blog_url" id="signup_blog_url" value="<?php bp_signup_blog_url_value() ?>" /> .<?php echo preg_replace( '|^https?://(?:www\.)|', '', site_url() ) ?>
+							<?php else : ?>
+								<?php echo site_url() ?>/ <input type="text" name="signup_blog_url" id="signup_blog_url" value="<?php bp_signup_blog_url_value() ?>" />
+							<?php endif; ?>
+
+							<label for="signup_blog_title"><?php _e( 'Site Title', 'buddypress' ) ?> <?php _e( '(required)', 'buddypress' ) ?></label>
+							<?php do_action( 'bp_signup_blog_title_errors' ) ?>
+							<input type="text" name="signup_blog_title" id="signup_blog_title" value="<?php bp_signup_blog_title_value() ?>" />
+
+							<span class="label"><?php _e( 'I would like my site to appear in search engines, and in public listings around this network.', 'buddypress' ) ?>:</span>
+							<?php do_action( 'bp_signup_blog_privacy_errors' ) ?>
+
+							<label><input type="radio" name="signup_blog_privacy" id="signup_blog_privacy_public" value="public"<?php if ( 'public' == bp_get_signup_blog_privacy_value() || !bp_get_signup_blog_privacy_value() ) : ?> checked="checked"<?php endif; ?> /> <?php _e( 'Yes', 'buddypress' ) ?></label>
+							<label><input type="radio" name="signup_blog_privacy" id="signup_blog_privacy_private" value="private"<?php if ( 'private' == bp_get_signup_blog_privacy_value() ) : ?> checked="checked"<?php endif; ?> /> <?php _e( 'No', 'buddypress' ) ?></label>
+
+						</div>
+
+					</div><!-- #blog-details-section -->
+
+					<?php do_action( 'bp_after_blog_details_fields' ) ?>
+
+				<?php endif; ?>
+
+				<?php do_action( 'bp_before_registration_submit_buttons' ) ?>
+
+				<div class="submit">
+					<input type="submit" name="signup_submit" id="signup_submit" value="<?php _e( 'Complete Sign Up', 'buddypress' ) ?>" />
+				</div>
+
+				<?php do_action( 'bp_after_registration_submit_buttons' ) ?>
+
+				<?php wp_nonce_field( 'bp_new_signup' ) ?>
+
+			<?php endif; // request-details signup step ?>
+
+			<?php if ( 'completed-confirmation' == bp_get_current_signup_step() ) : ?>
+
+				<h2><?php _e( 'Sign Up Complete!', 'buddypress' ) ?></h2>
+
+				<?php do_action( 'template_notices' ) ?>
+				<?php do_action( 'bp_before_registration_confirmed' ) ?>
+
+				<?php if ( bp_registration_needs_activation() ) : ?>
+					<p><?php _e( 'You have successfully created your account! To begin using this site you will need to activate your account via the email we have just sent to your address.', 'buddypress' ) ?></p>
+				<?php else : ?>
+					<p><?php _e( 'You have successfully created your account! Please log in using the username and password you have just created.', 'buddypress' ) ?></p>
+				<?php endif; ?>
+
+				<?php do_action( 'bp_after_registration_confirmed' ) ?>
+
+			<?php endif; // completed-confirmation signup step ?>
+
+			<?php do_action( 'bp_custom_signup_steps' ) ?>
+
+			</form>
+
+		</div>
+
+		<?php do_action( 'bp_after_register_page' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar( 'buddypress' ) ?>
+
+	<script type="text/javascript">
+		jQuery(document).ready( function() {
+			if ( jQuery('div#blog-details').length && !jQuery('div#blog-details').hasClass('show') )
+				jQuery('div#blog-details').toggle();
+
+			jQuery( 'input#signup_with_blog' ).click( function() {
+				jQuery('div#blog-details').fadeOut().toggle();
+			});
+		});
+	</script>
+
+<?php get_footer( 'buddypress' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/rtl.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/rtl.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/rtl.css	(revision 5057)
@@ -0,0 +1,24 @@
+/**
+ * Theme Name: BuddyPress Default
+ * Theme URI: http://buddypress.org/extend/themes/
+ * Description: Clean and stylish, BuddyPress Default lets you build a social network straight out of the box. Make it yours with a custom menu, header image, and background. Along with five widgetized areas (one in the sidebar, four in the footer), BP-Default supports featured images (as custom header images on posts and pages) and is furnished with an optional one-column page template that removes the sidebar, and a stylesheet for the admin Visual Editor.
+ * Version: 1.5-beta-3
+ * Author: the BuddyPress team
+ * Author URI: http://buddypress.org
+ * License: GNU General Public License
+ * License URI: license.txt
+ * Tags: blue, buddypress, custom-background, custom-header, custom-menu, editor-style, featured-image-header, featured-images, fixed-width, light, right-sidebar, rtl-language-support, sticky-post, threaded-comments, translation-ready, two-columns, white
+ *
+ *** IMPORTANT - DO NOT COPY THIS THEME **
+ *
+ * If you want to make a custom theme based on this theme, DO NOT copy and edit it. By
+ * doing this you will make updates and maintainence much harder for yourself.
+ * Instead, please read this codex page on how to build a BuddyPress child theme:
+ *
+ * http://codex.buddypress.org/how-to-guides/building-a-buddypress-child-theme/
+ *
+ ***
+ *
+ * This file intentionally left blank. CSS is enqueued in functions.php
+ *
+ */
Index: /tags/1.5-beta-3/bp-themes/bp-default/search.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/search.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/search.php	(revision 5057)
@@ -0,0 +1,65 @@
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder">
+
+		<?php do_action( 'bp_before_blog_search' ) ?>
+
+		<div class="page" id="blog-search" role="main">
+
+			<h2 class="pagetitle"><?php _e( 'Site', 'buddypress' ) ?></h2>
+
+			<?php if (have_posts()) : ?>
+
+				<h3 class="pagetitle"><?php _e( 'Search Results', 'buddypress' ) ?></h3>
+
+				<?php bp_dtheme_content_nav( 'nav-above' ); ?>
+
+				<?php while (have_posts()) : the_post(); ?>
+
+					<?php do_action( 'bp_before_blog_post' ) ?>
+
+					<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+						<div class="author-box">
+							<?php echo get_avatar( get_the_author_meta( 'email' ), '50' ); ?>
+							<p><?php printf( _x( 'by %s', 'Post written by...', 'buddypress' ), bp_core_get_userlink( $post->post_author ) ) ?></p>
+						</div>
+
+						<div class="post-content">
+							<h2 class="posttitle"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php _e( 'Permanent Link to', 'buddypress' ) ?> <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
+
+							<p class="date"><?php printf( __( '%1$s <span>in %2$s</span>', 'buddypress' ), get_the_date(), get_the_category_list( ', ' ) ); ?></p>
+
+							<div class="entry">
+								<?php the_content( __( 'Read the rest of this entry &rarr;', 'buddypress' ) ); ?>
+							</div>
+
+							<p class="postmetadata"><?php the_tags( '<span class="tags">' . __( 'Tags: ', 'buddypress' ), ', ', '</span>' ); ?> <span class="comments"><?php comments_popup_link( __( 'No Comments &#187;', 'buddypress' ), __( '1 Comment &#187;', 'buddypress' ), __( '% Comments &#187;', 'buddypress' ) ); ?></span></p>
+						</div>
+
+					</div>
+
+					<?php do_action( 'bp_after_blog_post' ) ?>
+
+				<?php endwhile; ?>
+
+				<?php bp_dtheme_content_nav( 'nav-below' ); ?>
+
+			<?php else : ?>
+
+				<h2 class="center"><?php _e( 'No posts found. Try a different search?', 'buddypress' ) ?></h2>
+				<?php get_search_form() ?>
+
+			<?php endif; ?>
+
+		</div>
+
+		<?php do_action( 'bp_after_blog_search' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar() ?>
+
+<?php get_footer() ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/searchform.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/searchform.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/searchform.php	(revision 5057)
@@ -0,0 +1,10 @@
+<?php do_action( 'bp_before_blog_search_form' ) ?>
+
+<form role="search" method="get" id="searchform" action="<?php echo home_url() ?>/">
+	<input type="text" value="<?php the_search_query(); ?>" name="s" id="s" />
+	<input type="submit" id="searchsubmit" value="<?php _e( 'Search', 'buddypress' ) ?>" />
+
+	<?php do_action( 'bp_blog_search_form' ) ?>
+</form>
+
+<?php do_action( 'bp_after_blog_search_form' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/sidebar-footer.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/sidebar-footer.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/sidebar-footer.php	(revision 5057)
@@ -0,0 +1,50 @@
+<?php
+	/* The footer widget area is triggered if any of the areas
+	 * have widgets. So let's check that first.
+	 *
+	 * If none of the sidebars have widgets, then let's bail early.
+	 */
+	if (   ! is_active_sidebar( 'first-footer-widget-area'  )
+		&& ! is_active_sidebar( 'second-footer-widget-area' )
+		&& ! is_active_sidebar( 'third-footer-widget-area'  )
+		&& ! is_active_sidebar( 'fourth-footer-widget-area' )
+	)
+		return;
+	// If we get this far, we have widgets. Let do this.
+?>
+
+			<div id="footer-widget-area" role="complementary">
+
+<?php if ( is_active_sidebar( 'first-footer-widget-area' ) ) : ?>
+				<div id="first" class="widget-area">
+					<ul class="xoxo">
+						<?php dynamic_sidebar( 'first-footer-widget-area' ); ?>
+					</ul>
+				</div><!-- #first .widget-area -->
+<?php endif; ?>
+
+<?php if ( is_active_sidebar( 'second-footer-widget-area' ) ) : ?>
+				<div id="second" class="widget-area">
+					<ul class="xoxo">
+						<?php dynamic_sidebar( 'second-footer-widget-area' ); ?>
+					</ul>
+				</div><!-- #second .widget-area -->
+<?php endif; ?>
+
+<?php if ( is_active_sidebar( 'third-footer-widget-area' ) ) : ?>
+				<div id="third" class="widget-area">
+					<ul class="xoxo">
+						<?php dynamic_sidebar( 'third-footer-widget-area' ); ?>
+					</ul>
+				</div><!-- #third .widget-area -->
+<?php endif; ?>
+
+<?php if ( is_active_sidebar( 'fourth-footer-widget-area' ) ) : ?>
+				<div id="fourth" class="widget-area">
+					<ul class="xoxo">
+						<?php dynamic_sidebar( 'fourth-footer-widget-area' ); ?>
+					</ul>
+				</div><!-- #fourth .widget-area -->
+<?php endif; ?>
+
+			</div><!-- #footer-widget-area -->
Index: /tags/1.5-beta-3/bp-themes/bp-default/sidebar.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/sidebar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/sidebar.php	(revision 5057)
@@ -0,0 +1,76 @@
+<?php do_action( 'bp_before_sidebar' ) ?>
+
+<div id="sidebar" role="complementary">
+	<div class="padder">
+
+	<?php do_action( 'bp_inside_before_sidebar' ) ?>
+
+	<?php if ( is_user_logged_in() ) : ?>
+
+		<?php do_action( 'bp_before_sidebar_me' ) ?>
+
+		<div id="sidebar-me">
+			<a href="<?php echo bp_loggedin_user_domain() ?>">
+				<?php bp_loggedin_user_avatar( 'type=thumb&width=40&height=40' ) ?>
+			</a>
+
+			<h4><?php echo bp_core_get_userlink( bp_loggedin_user_id() ); ?></h4>
+			<a class="button logout" href="<?php echo wp_logout_url( bp_get_root_domain() ) ?>"><?php _e( 'Log Out', 'buddypress' ) ?></a>
+
+			<?php do_action( 'bp_sidebar_me' ) ?>
+		</div>
+
+		<?php do_action( 'bp_after_sidebar_me' ) ?>
+
+		<?php if ( bp_is_active( 'messages' ) ) : ?>
+			<?php bp_message_get_notices(); /* Site wide notices to all users */ ?>
+		<?php endif; ?>
+
+	<?php else : ?>
+
+		<?php do_action( 'bp_before_sidebar_login_form' ) ?>
+
+		<?php if ( bp_get_signup_allowed() ) : ?>
+		
+			<p id="login-text">
+
+				<?php printf( __( 'Please <a href="%s" title="Create an account">create an account</a> to get started.', 'buddypress' ), site_url( bp_get_signup_slug() . '/' ) ) ?>
+
+			</p>
+
+		<?php endif; ?>
+
+		<form name="login-form" id="sidebar-login-form" class="standard-form" action="<?php echo site_url( 'wp-login.php', 'login_post' ) ?>" method="post">
+			<label><?php _e( 'Username', 'buddypress' ) ?><br />
+			<input type="text" name="log" id="sidebar-user-login" class="input" value="<?php if ( isset( $user_login) ) echo esc_attr(stripslashes($user_login)); ?>" tabindex="97" /></label>
+
+			<label><?php _e( 'Password', 'buddypress' ) ?><br />
+			<input type="password" name="pwd" id="sidebar-user-pass" class="input" value="" tabindex="98" /></label>
+
+			<p class="forgetmenot"><label><input name="rememberme" type="checkbox" id="sidebar-rememberme" value="forever" tabindex="99" /> <?php _e( 'Remember Me', 'buddypress' ) ?></label></p>
+
+			<?php do_action( 'bp_sidebar_login_form' ) ?>
+			<input type="submit" name="wp-submit" id="sidebar-wp-submit" value="<?php _e( 'Log In', 'buddypress' ); ?>" tabindex="100" />
+			<input type="hidden" name="testcookie" value="1" />
+		</form>
+
+		<?php do_action( 'bp_after_sidebar_login_form' ) ?>
+
+	<?php endif; ?>
+
+	<?php /* Show forum tags on the forums directory */
+	if ( bp_is_active( 'forums' ) && bp_is_current_component( 'forums' ) && bp_is_directory() ) : ?>
+		<div id="forum-directory-tags" class="widget tags">
+			<h3 class="widgettitle"><?php _e( 'Forum Topic Tags', 'buddypress' ) ?></h3>
+			<div id="tag-text"><?php bp_forums_tag_heat_map(); ?></div>
+		</div>
+	<?php endif; ?>
+
+	<?php dynamic_sidebar( 'sidebar-1' ) ?>
+
+	<?php do_action( 'bp_inside_after_sidebar' ) ?>
+
+	</div><!-- .padder -->
+</div><!-- #sidebar -->
+
+<?php do_action( 'bp_after_sidebar' ) ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/single.php
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/single.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/single.php	(revision 5057)
@@ -0,0 +1,58 @@
+<?php get_header() ?>
+
+	<div id="content">
+		<div class="padder">
+
+			<?php do_action( 'bp_before_blog_single_post' ) ?>
+
+			<div class="page" id="blog-single" role="main">
+
+			<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
+
+				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+
+					<div class="author-box">
+						<?php echo get_avatar( get_the_author_meta( 'user_email' ), '50' ); ?>
+						<p><?php printf( _x( 'by %s', 'Post written by...', 'buddypress' ), str_replace( '<a href=', '<a rel="author" href=', bp_core_get_userlink( $post->post_author ) ) ); ?></p>
+					</div>
+
+					<div class="post-content">
+						<h2 class="posttitle"><?php the_title(); ?></h2>
+
+						<p class="date">
+							<?php printf( __( '%1$s <span>in %2$s</span>', 'buddypress' ), get_the_date(), get_the_category_list( ', ' ) ); ?>
+							<span class="post-utility alignright"><?php edit_post_link( __( 'Edit this entry', 'buddypress' ) ); ?></span>
+						</p>
+
+						<div class="entry">
+							<?php the_content( __( 'Read the rest of this entry &rarr;', 'buddypress' ) ); ?>
+
+							<?php wp_link_pages( array( 'before' => '<div class="page-link"><p>' . __( 'Pages: ', 'buddypress' ), 'after' => '</p></div>', 'next_or_number' => 'number' ) ); ?>
+						</div>
+
+						<p class="postmetadata"><?php the_tags( '<span class="tags">' . __( 'Tags: ', 'buddypress' ), ', ', '</span>' ); ?>&nbsp;</p>
+
+						<div class="alignleft"><?php previous_post_link( '%link', '<span class="meta-nav">' . _x( '&larr;', 'Previous post link', 'buddypress' ) . '</span> %title' ); ?></div>
+						<div class="alignright"><?php next_post_link( '%link', '%title <span class="meta-nav">' . _x( '&rarr;', 'Next post link', 'buddypress' ) . '</span>' ); ?></div>
+					</div>
+
+				</div>
+
+			<?php comments_template(); ?>
+
+			<?php endwhile; else: ?>
+
+				<p><?php _e( 'Sorry, no posts matched your criteria.', 'buddypress' ) ?></p>
+
+			<?php endif; ?>
+
+		</div>
+
+		<?php do_action( 'bp_after_blog_single_post' ) ?>
+
+		</div><!-- .padder -->
+	</div><!-- #content -->
+
+	<?php get_sidebar() ?>
+
+<?php get_footer() ?>
Index: /tags/1.5-beta-3/bp-themes/bp-default/style.css
===================================================================
--- /tags/1.5-beta-3/bp-themes/bp-default/style.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-themes/bp-default/style.css	(revision 5057)
@@ -0,0 +1,24 @@
+/**
+ * Theme Name: BuddyPress Default
+ * Theme URI: http://buddypress.org/extend/themes/
+ * Description: Clean and stylish, BuddyPress Default lets you build a social network straight out of the box. Make it yours with a custom menu, header image, and background. Along with five widgetized areas (one in the sidebar, four in the footer), BP-Default supports featured images (as custom header images on posts and pages) and is furnished with an optional one-column page template that removes the sidebar, and a stylesheet for the admin Visual Editor.
+ * Version: 1.5-beta-3
+ * Author: the BuddyPress team
+ * Author URI: http://buddypress.org
+ * License: GNU General Public License
+ * License URI: license.txt
+ * Tags: blue, buddypress, custom-background, custom-header, custom-menu, editor-style, featured-image-header, featured-images, fixed-width, light, right-sidebar, rtl-language-support, sticky-post, threaded-comments, translation-ready, two-columns, white
+ *
+ *** IMPORTANT - DO NOT COPY THIS THEME **
+ *
+ * If you want to make a custom theme based on this theme, DO NOT copy and edit it. By
+ * doing this you will make updates and maintainence much harder for yourself.
+ * Instead, please read this codex page on how to build a BuddyPress child theme:
+ *
+ * http://codex.buddypress.org/how-to-guides/building-a-buddypress-child-theme/
+ *
+ ***
+ *
+ * This file intentionally left blank. CSS is enqueued in functions.php
+ *
+ */
Index: /tags/1.5-beta-3/bp-xprofile/admin/css/admin.css
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/admin/css/admin.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/admin/css/admin.css	(revision 5057)
@@ -0,0 +1,1 @@
+div#icon-buddypress{background:url(../../../bp-core/images/icons32.png) no-repeat -4px 0;}#tabs-bottom{background:#DFDFDF url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0 0;height:32px;border:1px solid #ccc;-moz-border-radius:0 0 6px 6px;-khtml-border-radius:0 0 6px 6px;-webkit-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}#tabs{position:relative;}p.nofields{margin-top:20px;}ul#field-group-tabs{float:left;padding:0 0 0 15px;display:none;}ul#field-group-tabs li{float:left;margin-bottom:-1px;background-color:transparent;margin-right:8px;}ul#field-group-tabs li.ui-state-hover{background-color:#fafafa;}ul#field-group-tabs li.ui-state-hover a.ui-tab{border-color:#bbb #bbb #ccc #bbb;color:#888;}ul#field-group-tabs li.ui-state-acceptable a.ui-tab{border-color:#5a5 #5a5 #ccc #5a5;color:#8a8;background-image:url(../images/plus.gif);background-color:#efe;}ul#field-group-tabs li.ui-tabs-selected.ui-state-acceptable a.ui-tab{background-color:#ECECEC;background-image:url(../images/cross.gif);}ul#field-group-tabs li.drop-candidate a.ui-tab{background-color:#ffc;border-color:#aa5 #aa5 #ccc #aa5;color:#aa8;}ul#field-group-tabs li a.ui-tab{display:block;margin-top:3px;padding:5px 10px 5px 28px;border:1px solid #ccc;border-bottom:none;-moz-border-radius:6px 6px 0 0;-khtml-border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;text-decoration:none;background:#F4F4F4 url(../images/move-x.gif) 7px 55% no-repeat;color:#888;}ul#field-group-tabs li.ui-tabs-selected a.ui-tab{background-color:#ECECEC;margin-top:-1px;padding:7px 10px 7px 28px;border:1px solid #ccc;border-bottom:1px solid transparent;color:#000;font-weight:bold;}.tab-toolbar{background:#ECECEC url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0 0;border-width:1px 1px 0;-moz-border-radius:6px 6px 0 0;-khtml-border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;margin:-20px -21px 20px -21px;clear:left;padding:10px;border:1px solid #ccc;}.tab-toolbar a.button{padding:5px 10px;}.tab-toolbar a.deletion{color:#f00;border-bottom:1px solid #f00;font-size:11px;text-decoration:none;padding:0 2px;margin-top:-2px;}div.tab-wrapper{background-color:#f9f9f9;clear:left;padding:20px;border-left:1px solid #ccc;border-right:1px solid #ccc;-moz-border-radius:6px 6px 0 0;-khtml-border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;}.tab-wrapper fieldset{position:relative;cursor:default!important;background:#fff;}.tab-wrapper fieldset legend{padding-bottom:15px;font-weight:bold;}.tab-wrapper .xprofile-field{position:relative;}.tab-wrapper fieldset fieldset{position:relative;border:1px solid #ddd;-moz-border-radius:6px;-khtml-border-radius:6px;-webkit-border-radius:6px;border-radius:5px;margin:10px 0 0 0;cursor:move!important;background:#fcfcfc url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0 0;}.tab-wrapper fieldset fieldset legend{position:absolute!important;top:0;left:0;width:80%;padding:30px;}.tab-wrapper fieldset fieldset legend span{position:absolute;top:6px;left:30px;font-weight:bold;width:100%;}.field-group fieldset:hover{background-color:#fafafa;border-color:#ccc;}fieldset div.field-wrapper{padding-left:30px;padding:40px 10px 10px 10px;background:url(../images/move.gif) 6px 6px no-repeat transparent;}fieldset.radio div div,fieldset.checkbox div{float:left;}fieldset.radio div div label,fieldset.checkbox div label{margin-right:20px;}fieldset.clear-value{margin-left:10px;}.field-group div.actions{position:absolute;right:10px;top:6px;}.field-group fieldset:hover div.actions{display:block;}.field-group fieldset.ui-sortable-placeholder{border:1px dashed #999;background-color:#eee;visibility:visible!important;}ul.forTab{list-style:none;padding:0;margin:0 0 0 1em;}ul.forTab li{margin:0 0 1em 0;}ul.forTab li label{display:block;}ul.forTab li input{font-size:1.4em;}p.success{background:green;}p.err{border-top:2px solid red;border-bottom:2px solid red;color:red;padding:5px 0;width:40%;}span.desc,span.signup-description{display:block;font-size:11px;color:#555;}select.multi-select{width:90%;height:10em!important;}ul.multi-checkbox{margin:0 5px 0 0;padding:.5em .9em;height:10em;overflow:auto;list-style:none;border:solid 1px #ccc;width:90%;}ul.multi-checkbox li{padding:0;margin:0;}div.options-box{margin-left:20px!important;margin-right:10px!important;border-left:4px solid #EAF3FA;padding-left:15px;}th a{background:#fff;padding:2px 5px;-moz-border-radius:3px;-khtml-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;top:-2px;}textarea#description{border:1px solid #ddd;width:85%;}
Index: /tags/1.5-beta-3/bp-xprofile/admin/css/admin.dev.css
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/admin/css/admin.dev.css	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/admin/css/admin.dev.css	(revision 5057)
@@ -0,0 +1,266 @@
+/* Profile field group main admin page */
+div#icon-buddypress {
+	background: url( ../../../bp-core/images/icons32.png ) no-repeat -4px 0px;
+}
+
+#tabs-bottom {
+	background: #DFDFDF url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0% 0%;
+	height: 32px;
+	border: 1px solid #ccc;
+	-moz-border-radius: 0 0 6px 6px;
+	-khtml-border-radius: 0 0 6px 6px;
+	-webkit-border-radius: 0 0 6px 6px;
+	border-radius: 0 0 6px 6px;
+}
+
+#tabs {
+	position: relative;
+	}
+	p.nofields {
+		margin-top: 20px;
+	}
+
+	/* Field group tabs */
+	ul#field-group-tabs {
+		float: left;
+		padding: 0 0 0 15px;
+		display: none;
+	}
+		ul#field-group-tabs li {
+			float: left;
+			margin-bottom: -1px;
+			background-color: transparent;
+			margin-right: 8px;
+		}
+		ul#field-group-tabs li.ui-state-hover {
+			background-color: #fafafa;
+		}
+		ul#field-group-tabs li.ui-state-hover a.ui-tab {
+			border-color: #bbb #bbb #ccc #bbb;
+			color: #888;
+		}
+		ul#field-group-tabs li.ui-state-acceptable a.ui-tab {
+			border-color: #5a5 #5a5 #ccc #5a5;
+			color: #8a8;
+			background-image: url(../images/plus.gif);
+			background-color: #efe;
+		}
+			ul#field-group-tabs li.ui-tabs-selected.ui-state-acceptable a.ui-tab {
+				background-color: #ECECEC;
+				background-image: url(../images/cross.gif);
+			}
+		ul#field-group-tabs li.drop-candidate a.ui-tab {
+			background-color: #ffc;
+			border-color: #aa5 #aa5 #ccc #aa5;
+			color: #aa8;
+		}
+		ul#field-group-tabs li a.ui-tab {
+			display: block;
+			margin-top: 3px;
+			padding: 5px 10px 5px 28px;
+			border: 1px solid #ccc;
+			border-bottom: none;
+			-moz-border-radius: 6px 6px 0 0;
+			-khtml-border-radius: 6px 6px 0 0;
+			-webkit-border-radius: 6px 6px 0 0;
+			border-radius: 6px 6px 0 0;
+			text-decoration: none;
+			background: #F4F4F4 url(../images/move-x.gif) 7px 55% no-repeat;
+			color: #888;
+		}
+
+		/* Selected tab */
+		ul#field-group-tabs li.ui-tabs-selected a.ui-tab {
+			background-color: #ECECEC;
+			margin-top: -1px;
+			padding: 7px 10px 7px 28px;
+			border: 1px solid #ccc;
+			border-bottom: 1px solid transparent;
+			color: #000;
+			font-weight: bold;
+		}
+
+	/* Toolbar */
+	.tab-toolbar {
+		background: #ECECEC url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0% 0%;
+		border-width: 1px 1px 0px;
+		-moz-border-radius: 6px 6px 0 0;
+		-khtml-border-radius: 6px 6px 0 0;
+		-webkit-border-radius: 6px 6px 0 0;
+		border-radius: 6px 6px 0 0;
+		margin: -20px -21px 20px -21px;
+		clear: left;
+		padding: 10px;
+		border: 1px solid #ccc;
+	}
+
+	.tab-toolbar a.button {
+		padding: 5px 10px;
+	}
+
+	.tab-toolbar a.deletion {
+		color: #f00;
+		border-bottom: 1px solid #f00;
+		font-size: 11px;
+		text-decoration: none;
+		padding: 0 2px;
+		margin-top: -2px;
+	}
+
+	div.tab-wrapper {
+		background-color: #f9f9f9;
+		clear: left;
+		padding: 20px;
+		border-left: 1px solid #ccc;
+		border-right: 1px solid #ccc;
+		-moz-border-radius: 6px 6px 0 0;
+		-khtml-border-radius: 6px 6px 0 0;
+		-webkit-border-radius: 6px 6px 0 0;
+		border-radius: 6px 6px 0 0;
+	}
+
+	.tab-wrapper fieldset {
+		position: relative;
+		cursor: default !important;
+		background: #fff;
+		}
+		.tab-wrapper fieldset legend {
+			padding-bottom: 15px;
+			font-weight: bold;
+		}
+	.tab-wrapper .xprofile-field {
+		position: relative;
+	}
+	.tab-wrapper fieldset fieldset {
+		position: relative;
+		border: 1px solid #ddd;
+		-moz-border-radius: 6px;
+		-khtml-border-radius: 6px;
+		-webkit-border-radius: 6px;
+		border-radius: 5px;
+		margin: 10px 0 0 0;
+		cursor: move !important;
+		background: #fcfcfc url(../../../../../wp-admin/images/gray-grad.png) repeat-x scroll 0% 0%;
+	}
+	.tab-wrapper fieldset fieldset legend {
+		position: absolute !important;
+		top: 0;
+		left: 0;
+		width: 80%;
+		padding: 30px;
+	}
+	.tab-wrapper fieldset fieldset legend span {
+		position: absolute;
+		top: 6px;
+		left: 30px;
+		font-weight: bold;
+		width: 100%;
+	}
+		.field-group fieldset:hover {
+			background-color: #fafafa;
+			border-color: #ccc;
+		}
+		fieldset div.field-wrapper {
+			padding-left: 30px;
+			padding: 40px 10px 10px 10px;
+			background: url(../images/move.gif) 6px 6px no-repeat transparent;
+		}
+	fieldset.radio div div,
+	fieldset.checkbox div {
+		float: left;
+	}
+	fieldset.radio div div label,
+	fieldset.checkbox div label {
+		margin-right: 20px;
+	}
+
+	fieldset.clear-value {
+		margin-left: 10px;
+	}
+	.field-group div.actions {
+		position: absolute;
+		right: 10px;
+		top: 6px;
+	}
+	.field-group fieldset:hover div.actions {
+		display: block;
+	}
+	.field-group fieldset.ui-sortable-placeholder {
+		border: 1px dashed #999;
+		background-color: #eee;
+		visibility: visible !important;
+	}
+
+	ul.forTab {
+		list-style: none;
+		padding: 0;
+		margin: 0 0 0 1em;
+	}
+		ul.forTab li {
+			margin: 0 0 1em 0;
+		}
+
+			ul.forTab li label {
+				display: block;
+			}
+
+			ul.forTab li input {
+				font-size: 1.4em;
+			}
+
+	p.success { background: green;}
+	p.err {
+		border-top: 2px solid red;
+		border-bottom: 2px solid red;
+		color: red;
+		padding: 5px 0;
+		width: 40%;
+	}
+
+	span.desc, span.signup-description {
+		display: block;
+		font-size: 11px;
+		color: #555;
+	}
+
+	select.multi-select {
+		width:90%;
+		height:10em !important;
+	}
+
+ul.multi-checkbox {
+	margin: 0 5px 0 0px;
+	padding: .5em .9em;
+	height: 10em;
+	overflow: auto;
+	list-style: none;
+	border: solid 1px #ccc;
+	width: 90%;
+}
+
+ul.multi-checkbox li {
+	padding: 0;
+	margin: 0;
+}
+
+div.options-box {
+	margin-left: 20px !important;
+	margin-right: 10px !important;
+	border-left: 4px solid #EAF3FA;
+	padding-left: 15px;
+}
+
+th a {
+	background: #fff;
+	padding: 2px 5px;
+	-moz-border-radius: 3px;
+	-khtml-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+	top: -2px;
+}
+
+textarea#description {
+	border: 1px solid #ddd;
+	width: 85%;
+}
Index: /tags/1.5-beta-3/bp-xprofile/admin/js/admin.dev.js
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/admin/js/admin.dev.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/admin/js/admin.dev.js	(revision 5057)
@@ -0,0 +1,218 @@
+function add_option(forWhat) {
+	var holder = document.getElementById(forWhat + "_more");
+	var theId = document.getElementById(forWhat + '_option_number').value;
+
+	var newDiv = document.createElement('p');
+	newDiv.setAttribute('id', forWhat + '_div' + theId);
+
+	var newOption = document.createElement('input');
+	newOption.setAttribute('type', 'text');
+	newOption.setAttribute('name', forWhat + '_option[' + theId + ']');
+	newOption.setAttribute('id', forWhat + '_option' + theId);
+
+	var label = document.createElement('label');
+	label.setAttribute('for', forWhat + '_option' + theId);
+
+	var txt = document.createTextNode("Option " + theId + ": ");
+	label.appendChild(txt);
+
+	var isDefault = document.createElement('input');
+
+	if(forWhat == 'checkbox' || forWhat == 'multiselectbox') {
+		isDefault.setAttribute('type', 'checkbox');
+		isDefault.setAttribute('name', 'isDefault_' + forWhat + '_option[' + theId + ']');
+	} else {
+		isDefault.setAttribute('type', 'radio');
+		isDefault.setAttribute('name', 'isDefault_' + forWhat + '_option');
+	}
+
+	isDefault.setAttribute('value', theId);
+
+	var label1 = document.createElement('label');
+	var txt1 = document.createTextNode(" Default Value ");
+
+	label1.appendChild(txt1);
+	label1.setAttribute('for', 'isDefault_' + forWhat + '_option[]');
+	toDelete = document.createElement('a');
+
+	toDeleteText = document.createTextNode('[x]');
+	toDelete.setAttribute('href',"javascript:hide('" + forWhat + '_div' + theId + "')");
+
+	toDelete.setAttribute('class','delete');
+
+	toDelete.appendChild(toDeleteText);
+
+	newDiv.appendChild(label);
+	newDiv.appendChild(newOption);
+	newDiv.appendChild(document.createTextNode(" "));
+	newDiv.appendChild(isDefault);
+	newDiv.appendChild(label1);
+	newDiv.appendChild(toDelete);
+	holder.appendChild(newDiv);
+
+
+	theId++
+	document.getElementById(forWhat + "_option_number").value = theId;
+}
+
+function show_options(forWhat) {
+	document.getElementById("radio").style.display = "none";
+	document.getElementById("selectbox").style.display = "none";
+	document.getElementById("multiselectbox").style.display = "none";
+	document.getElementById("checkbox").style.display = "none";
+
+	if(forWhat == "radio")
+		document.getElementById("radio").style.display = "";
+
+	if(forWhat == "selectbox")
+		document.getElementById("selectbox").style.display = "";
+
+	if(forWhat == "multiselectbox")
+		document.getElementById("multiselectbox").style.display = "";
+
+	if(forWhat == "checkbox")
+		document.getElementById("checkbox").style.display = "";
+}
+
+function hide(id) {
+	if ( !document.getElementById(id) ) return false;
+
+	document.getElementById(id).style.display = "none";
+	document.getElementById(id).value = '';
+}
+
+// Set up deleting options ajax
+jQuery(document).ready( function() {
+
+	jQuery("a.ajax-option-delete").click(
+		function() {
+			var theId = this.id.split('-');
+			theId = theId[1];
+
+			jQuery.post( ajaxurl, {
+				action: 'xprofile_delete_option',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce': jQuery("input#_wpnonce").val(),
+
+				'option_id': theId
+			},
+			function(response)
+			{});
+		}
+	);
+});
+
+var fixHelper = function(e, ui) {
+	ui.children().each(function() {
+		jQuery(this).width( jQuery(this).width() );
+	});
+	return ui;
+};
+
+// Main XProfile behavior layer
+jQuery(document).ready( function() {
+	/* Show object if JS is enabled */
+	jQuery( "ul#field-group-tabs" ).show();
+
+	/* Allow reordering of field group tabs */
+	jQuery( "ul#field-group-tabs" ).sortable( {
+		cursor: 'move',
+		axis: 'x',
+		opacity: 0.6,
+		items: 'li',
+		tolerance: 'pointer',
+
+		update: function() {
+			jQuery.post( ajaxurl, {
+				action: 'xprofile_reorder_groups',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce_reorder_groups': jQuery( "input#_wpnonce_reorder_groups" ).val(),
+				'group_order': jQuery(this).sortable( 'serialize' )
+			},
+			function(response){});
+		}
+	}).disableSelection();
+
+	/* Allow reordering of fields within groups */
+	jQuery( "fieldset.field-group" ).sortable({
+		cursor: 'move',
+		opacity: 0.3,
+		items: 'fieldset',
+		tolerance: 'pointer',
+
+		update: function() {
+			jQuery.post( ajaxurl, {
+				action: 'xprofile_reorder_fields',
+				'cookie': encodeURIComponent(document.cookie),
+				'_wpnonce_reorder_fields': jQuery( "input#_wpnonce_reorder_fields" ).val(),
+				'field_order': jQuery(this).sortable( 'serialize' ),
+				'field_group_id': jQuery(this).attr( 'id' )
+			},
+			function(response)
+			{});
+		}
+	})
+
+	/* Disallow text selection */
+	.disableSelection()
+
+	/* Change cursor to move if JS is enabled */
+	.css( 'cursor', 'move' );
+
+	/* tabs init with a custom tab template and an "add" callback filling in the content */
+	var $tab_items;
+	var $tabs = jQuery( "#tabs" ).tabs();
+	set_tab_items( $tabs );
+
+	function set_tab_items( $tabs ) {
+		$tab_items = jQuery( "ul:first li", $tabs ).droppable({
+			accept: ".connectedSortable fieldset",
+			hoverClass: "ui-state-hover",
+			activeClass: "ui-state-acceptable",
+			touch: "pointer",
+			tolerance: "pointer",
+
+			/* When field is dropped on tab */
+			drop: function( ev, ui ) {
+				/* The tab */
+				var $item = jQuery(this);
+
+				/* The tab body */
+				var $list = jQuery( $item.find( 'a' ).attr( 'href' ) ).find( '.connectedSortable' );
+
+				/* Remove helper class */
+				jQuery($item).removeClass( 'drop-candidate' );
+
+				/* Hide field, change selected tab, and show new placement */
+				ui.draggable.hide( 'slow', function() {
+
+					/* Select new tab as current */
+					$tabs.tabs( 'select', $tab_items.index( $item ) );
+
+					/* Show new placement */
+					jQuery(this).appendTo($list).show( 'slow' ).animate( {opacity: "1"}, 500 );
+
+					/* Refresh $list variable */
+					$list = jQuery( $item.find( 'a' ).attr( 'href' ) ).find( '.connectedSortable' );
+					jQuery($list).find( 'p.nofields' ).hide( 'slow' );
+
+					/* Ajax update field locations and orders */
+					jQuery.post( ajaxurl, {
+						action: 'xprofile_reorder_fields',
+						'cookie': encodeURIComponent(document.cookie),
+						'_wpnonce_reorder_fields': jQuery( "input#_wpnonce_reorder_fields" ).val(),
+						'field_order': jQuery( $list ).sortable( 'serialize' ),
+						'field_group_id': jQuery( $list ).attr( 'id' )
+					},
+					function(response){});
+				});
+			},
+			over: function( event, ui ) {
+				jQuery(this).addClass( 'drop-candidate' );
+			},
+			out: function( event, ui ) {
+				jQuery(this).removeClass( 'drop-candidate' );
+			}
+		});
+	}
+});
Index: /tags/1.5-beta-3/bp-xprofile/admin/js/admin.js
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/admin/js/admin.js	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/admin/js/admin.js	(revision 5057)
@@ -0,0 +1,1 @@
+function add_option(f){var h=document.getElementById(f+"_more");var j=document.getElementById(f+"_option_number").value;var a=document.createElement("p");a.setAttribute("id",f+"_div"+j);var g=document.createElement("input");g.setAttribute("type","text");g.setAttribute("name",f+"_option["+j+"]");g.setAttribute("id",f+"_option"+j);var i=document.createElement("label");i.setAttribute("for",f+"_option"+j);var e=document.createTextNode("Option "+j+": ");i.appendChild(e);var b=document.createElement("input");if(f=="checkbox"||f=="multiselectbox"){b.setAttribute("type","checkbox");b.setAttribute("name","isDefault_"+f+"_option["+j+"]")}else{b.setAttribute("type","radio");b.setAttribute("name","isDefault_"+f+"_option")}b.setAttribute("value",j);var d=document.createElement("label");var c=document.createTextNode(" Default Value ");d.appendChild(c);d.setAttribute("for","isDefault_"+f+"_option[]");toDelete=document.createElement("a");toDeleteText=document.createTextNode("[x]");toDelete.setAttribute("href","javascript:hide('"+f+"_div"+j+"')");toDelete.setAttribute("class","delete");toDelete.appendChild(toDeleteText);a.appendChild(i);a.appendChild(g);a.appendChild(document.createTextNode(" "));a.appendChild(b);a.appendChild(d);a.appendChild(toDelete);h.appendChild(a);j++;document.getElementById(f+"_option_number").value=j}function show_options(a){document.getElementById("radio").style.display="none";document.getElementById("selectbox").style.display="none";document.getElementById("multiselectbox").style.display="none";document.getElementById("checkbox").style.display="none";if(a=="radio"){document.getElementById("radio").style.display=""}if(a=="selectbox"){document.getElementById("selectbox").style.display=""}if(a=="multiselectbox"){document.getElementById("multiselectbox").style.display=""}if(a=="checkbox"){document.getElementById("checkbox").style.display=""}}function hide(a){if(!document.getElementById(a)){return false}document.getElementById(a).style.display="none";document.getElementById(a).value=""}jQuery(document).ready(function(){jQuery("a.ajax-option-delete").click(function(){var a=this.id.split("-");a=a[1];jQuery.post(ajaxurl,{action:"xprofile_delete_option",cookie:encodeURIComponent(document.cookie),_wpnonce:jQuery("input#_wpnonce").val(),option_id:a},function(b){})})});var fixHelper=function(b,a){a.children().each(function(){jQuery(this).width(jQuery(this).width())});return a};jQuery(document).ready(function(){jQuery("ul#field-group-tabs").show();jQuery("ul#field-group-tabs").sortable({cursor:"move",axis:"x",opacity:0.6,items:"li",tolerance:"pointer",update:function(){jQuery.post(ajaxurl,{action:"xprofile_reorder_groups",cookie:encodeURIComponent(document.cookie),_wpnonce_reorder_groups:jQuery("input#_wpnonce_reorder_groups").val(),group_order:jQuery(this).sortable("serialize")},function(d){})}}).disableSelection();jQuery("fieldset.field-group").sortable({cursor:"move",opacity:0.3,items:"fieldset",tolerance:"pointer",update:function(){jQuery.post(ajaxurl,{action:"xprofile_reorder_fields",cookie:encodeURIComponent(document.cookie),_wpnonce_reorder_fields:jQuery("input#_wpnonce_reorder_fields").val(),field_order:jQuery(this).sortable("serialize"),field_group_id:jQuery(this).attr("id")},function(d){})}}).disableSelection().css("cursor","move");var b;var a=jQuery("#tabs").tabs();c(a);function c(d){b=jQuery("ul:first li",d).droppable({accept:".connectedSortable fieldset",hoverClass:"ui-state-hover",activeClass:"ui-state-acceptable",touch:"pointer",tolerance:"pointer",drop:function(g,h){var e=jQuery(this);var f=jQuery(e.find("a").attr("href")).find(".connectedSortable");jQuery(e).removeClass("drop-candidate");h.draggable.hide("slow",function(){d.tabs("select",b.index(e));jQuery(this).appendTo(f).show("slow").animate({opacity:"1"},500);f=jQuery(e.find("a").attr("href")).find(".connectedSortable");jQuery(f).find("p.nofields").hide("slow");jQuery.post(ajaxurl,{action:"xprofile_reorder_fields",cookie:encodeURIComponent(document.cookie),_wpnonce_reorder_fields:jQuery("input#_wpnonce_reorder_fields").val(),field_order:jQuery(f).sortable("serialize"),field_group_id:jQuery(f).attr("id")},function(i){})})},over:function(e,f){jQuery(this).addClass("drop-candidate")},out:function(e,f){jQuery(this).removeClass("drop-candidate")}})}});
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-actions.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-actions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-actions.php	(revision 5057)
@@ -0,0 +1,44 @@
+<?php
+/*******************************************************************************
+ * Action functions are exactly the same as screen functions, however they do
+ * not have a template screen associated with them. Usually they will send the
+ * user back to the default screen after execution.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * This function runs when an action is set for a screen:
+ * example.com/members/andy/profile/change-avatar/ [delete-avatar]
+ *
+ * The function will delete the active avatar for a user.
+ *
+ * @package BuddyPress Xprofile
+ * @global object $bp Global BuddyPress settings object
+ * @uses bp_core_delete_avatar() Deletes the active avatar for the logged in user.
+ * @uses add_action() Runs a specific function for an action when it fires.
+ * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
+ */
+function xprofile_action_delete_avatar() {
+	global $bp;
+
+	if ( !bp_is_current_component( 'profile' ) || !bp_is_current_action( 'change-avatar' ) || !bp_is_action_variable( 'delete-avatar', 0 ) )
+		return false;
+
+	// Check the nonce
+	check_admin_referer( 'bp_delete_avatar_link' );
+
+	if ( !bp_is_my_profile() && !is_super_admin() )
+		return false;
+
+	if ( bp_core_delete_existing_avatar( array( 'item_id' => $bp->displayed_user->id ) ) )
+		bp_core_add_message( __( 'Your avatar was deleted successfully!', 'buddypress' ) );
+	else
+		bp_core_add_message( __( 'There was a problem deleting that avatar, please try again.', 'buddypress' ), 'error' );
+
+	bp_core_redirect( wp_get_referer() );
+}
+add_action( 'bp_actions', 'xprofile_action_delete_avatar' );
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-activity.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-activity.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-activity.php	(revision 5057)
@@ -0,0 +1,134 @@
+<?php
+/**
+ * BuddyPress XProfile Activity & Notification Functions
+ *
+ * These functions handle the recording, deleting and formatting of activity and
+ * notifications for the user and for this specific component.
+ *
+ * @package BuddyPress
+ * @subpackage XProfile
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function xprofile_register_activity_actions() {
+	global $bp;
+
+	if ( bp_is_active( 'activity' ) )
+		return false;
+
+	// Register the activity stream actions for this component
+	bp_activity_set_action( $bp->profile->id, 'new_member',      __( 'New member registered', 'buddypress' ) );
+	bp_activity_set_action( $bp->profile->id, 'updated_profile', __( 'Updated Profile',       'buddypress' ) );
+
+	do_action( 'xprofile_register_activity_actions' );
+}
+add_action( 'bp_register_activity_actions', 'xprofile_register_activity_actions' );
+
+/**
+ * Records activity for the logged in user within the profile component so that
+ * it will show in the users activity stream (if installed)
+ *
+ * @package BuddyPress XProfile
+ * @param $args Array containing all variables used after extract() call
+ * @global $bp The global BuddyPress settings variable created in bp_core_current_times()
+ * @uses bp_activity_record() Adds an entry to the activity component tables for a specific activity
+ */
+function xprofile_record_activity( $args = '' ) {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	$defaults = array (
+		'user_id'           => $bp->loggedin_user->id,
+		'action'            => '',
+		'content'           => '',
+		'primary_link'      => '',
+		'component'         => $bp->profile->id,
+		'type'              => false,
+		'item_id'           => false,
+		'secondary_item_id' => false,
+		'recorded_time'     => bp_core_current_time(),
+		'hide_sitewide'     => false
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	return bp_activity_add( array(
+		'user_id'           => $user_id,
+		'action'            => $action,
+		'content'           => $content,
+		'primary_link'      => $primary_link,
+		'component'         => $component,
+		'type'              => $type,
+		'item_id'           => $item_id,
+		'secondary_item_id' => $secondary_item_id,
+		'recorded_time'     => $recorded_time,
+		'hide_sitewide'     => $hide_sitewide
+	) );
+}
+
+/**
+ * Deletes activity for a user within the profile component so that
+ * it will be removed from the users activity stream and sitewide stream (if installed)
+ *
+ * @package BuddyPress XProfile
+ * @param $args Array containing all variables used after extract() call
+ * @global object $bp Global BuddyPress settings object
+ * @uses bp_activity_delete() Deletes an entry to the activity component tables for a specific activity
+ */
+function xprofile_delete_activity( $args = '' ) {
+	global $bp;
+
+	if ( bp_is_active( 'activity' ) ) {
+
+		extract( $args );
+
+		bp_activity_delete_by_item_id( array(
+			'item_id'           => $item_id,
+			'component'         => $bp->profile->id,
+			'type'              => $type,
+			'user_id'           => $user_id,
+			'secondary_item_id' => $secondary_item_id
+		) );
+	}
+}
+
+function xprofile_register_activity_action( $key, $value ) {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	return apply_filters( 'xprofile_register_activity_action', bp_activity_set_action( $bp->profile->id, $key, $value ), $key, $value );
+}
+
+/**
+ * Adds an activity stream item when a user has uploaded a new avatar.
+ *
+ * @package BuddyPress XProfile
+ * @global object $bp Global BuddyPress settings object
+ * @uses bp_activity_add() Adds an entry to the activity component tables for a specific activity
+ */
+function bp_xprofile_new_avatar_activity() {
+	global $bp;
+
+	if ( !bp_is_active( 'activity' ) )
+		return false;
+
+	$user_id = apply_filters( 'bp_xprofile_new_avatar_user_id', $bp->displayed_user->id );
+
+	$userlink = bp_core_get_userlink( $user_id );
+
+	bp_activity_add( array(
+		'user_id' => $user_id,
+		'action' => apply_filters( 'bp_xprofile_new_avatar_action', sprintf( __( '%s changed their profile picture', 'buddypress' ), $userlink ), $user_id ),
+		'component' => 'profile',
+		'type' => 'new_avatar'
+	) );
+}
+add_action( 'xprofile_avatar_uploaded', 'bp_xprofile_new_avatar_activity' );
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-admin.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-admin.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-admin.php	(revision 5057)
@@ -0,0 +1,477 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Creates the administration interface menus and checks to see if the DB
+ * tables are set up.
+ *
+ * @package BuddyPress XProfile
+ * @global object $bp Global BuddyPress settings object
+ * @global $wpdb WordPress DB access object.
+ * @uses is_super_admin() returns true if the current user is a site admin, false if not
+ * @uses bp_xprofile_install() runs the installation of DB tables for the xprofile component
+ * @uses wp_enqueue_script() Adds a JS file to the JS queue ready for output
+ * @uses add_submenu_page() Adds a submenu tab to a top level tab in the admin area
+ * @uses xprofile_install() Runs the DB table installation function
+ * @return
+ */
+function xprofile_add_admin_menu() {
+	global $wpdb, $bp;
+
+	if ( !is_super_admin() )
+		return false;
+
+	$hook = add_submenu_page( 'bp-general-settings', __( 'Profile Fields', 'buddypress' ), __( 'Profile Fields', 'buddypress' ), 'manage_options', 'bp-profile-setup', 'xprofile_admin' );
+
+	add_action( "admin_print_styles-$hook", 'bp_core_add_admin_menu_styles' );
+}
+add_action( bp_core_admin_hook(), 'xprofile_add_admin_menu' );
+
+/**
+ * Handles all actions for the admin area for creating, editing and deleting
+ * profile groups and fields.
+ */
+function xprofile_admin( $message = '', $type = 'error' ) {
+	global $bp;
+
+	$type = preg_replace( '|[^a-z]|i', '', $type );
+
+	$groups = BP_XProfile_Group::get( array(
+		'fetch_fields' => true
+	));
+
+	if ( isset( $_GET['mode'] ) && isset( $_GET['group_id'] ) && 'add_field' == $_GET['mode'] )
+		xprofile_admin_manage_field( $_GET['group_id'] );
+
+	else if ( isset( $_GET['mode'] ) && isset( $_GET['group_id'] ) && isset( $_GET['field_id'] ) && 'edit_field' == $_GET['mode'] )
+		xprofile_admin_manage_field( $_GET['group_id'], $_GET['field_id'] );
+
+	else if ( isset( $_GET['mode'] ) && isset( $_GET['field_id'] ) && 'delete_field' == $_GET['mode'] )
+		xprofile_admin_delete_field( $_GET['field_id'], 'field');
+
+	else if ( isset( $_GET['mode'] ) && isset( $_GET['option_id'] ) && 'delete_option' == $_GET['mode'] )
+		xprofile_admin_delete_field( $_GET['option_id'], 'option' );
+
+	else if ( isset( $_GET['mode'] ) && 'add_group' == $_GET['mode'] )
+		xprofile_admin_manage_group();
+
+	else if ( isset( $_GET['mode'] ) && isset( $_GET['group_id'] ) && 'delete_group' == $_GET['mode'] )
+		xprofile_admin_delete_group( $_GET['group_id'] );
+
+	else if ( isset( $_GET['mode'] ) && isset( $_GET['group_id'] ) && 'edit_group' == $_GET['mode'] )
+		xprofile_admin_manage_group( $_GET['group_id'] );
+
+	else {
+?>
+
+	<div class="wrap">
+
+		<?php screen_icon( 'buddypress' ); ?>
+
+		<h2>
+
+			<?php _e( 'Extended Profile Fields', 'buddypress'); ?>
+
+			<a id="add_group" class="button add-new-h2" href="admin.php?page=bp-profile-setup&amp;mode=add_group"><?php _e( 'Add New Group', 'buddypress' ); ?></a>
+		</h2>
+
+		<p><?php _e( 'Your users will distinguish themselves through their profile page. You must give them profile fields that allow them to describe themselves in a way that is relevant to the theme of your social network.', 'buddypress'); ?></p>
+		<p><?php echo sprintf( __( 'NOTE: Any fields in the "%s" group will appear on the signup page.', 'buddypress' ), esc_html( stripslashes( bp_get_option( 'bp-xprofile-base-group-name' ) ) ) ) ?></p>
+
+		<form action="" id="profile-field-form" method="post">
+
+			<?php wp_nonce_field( 'bp_reorder_fields', '_wpnonce_reorder_fields' ); ?>
+
+			<?php wp_nonce_field( 'bp_reorder_groups', '_wpnonce_reorder_groups', false );
+
+			if ( !empty( $message ) ) :
+				$type = ( $type == 'error' ) ? 'error' : 'updated'; ?>
+
+				<div id="message" class="<?php echo $type; ?> fade">
+					<p><?php echo esc_html( esc_attr( $message ) ); ?></p>
+				</div>
+
+<?php		endif; ?>
+
+			<div id="tabs">
+				<ul id="field-group-tabs">
+<?php
+			if ( !empty( $groups ) ) :
+				foreach ( $groups as $group ) { ?>
+
+					<li id="group_<?php echo $group->id; ?>"><a href="#tabs-<?php echo $group->id; ?>" class="ui-tab"><?php echo esc_attr( $group->name ); ?><?php if ( !$group->can_delete ) : ?> <?php _e( '(Primary)', 'buddypress'); endif; ?></a></li>
+
+<?php			}
+			endif; ?>
+
+				</ul>
+
+<?php		if ( !empty( $groups ) ) :
+				foreach ( $groups as $group ) { ?>
+
+					<noscript>
+						<h3><?php echo esc_attr( $group->name ); ?></h3>
+					</noscript>
+
+					<div id="tabs-<?php echo $group->id; ?>" class="tab-wrapper">
+						<div class="tab-toolbar">
+							<div class="tab-toolbar-left">
+								<a class="button" href="admin.php?page=bp-profile-setup&amp;group_id=<?php echo esc_attr( $group->id ); ?>&amp;mode=add_field"><?php _e( 'Add New Field', 'buddypress' ); ?></a>
+								<a class="button edit" href="admin.php?page=bp-profile-setup&amp;mode=edit_group&amp;group_id=<?php echo esc_attr( $group->id ); ?>"><?php _e( 'Edit Group', 'buddypress' ); ?></a>
+
+<?php				if ( $group->can_delete ) : ?>
+
+								<a class="submitdelete deletion" href="admin.php?page=bp-profile-setup&amp;mode=delete_group&amp;group_id=<?php echo esc_attr( $group->id ); ?>"><?php _e( 'Delete Group', 'buddypress' ); ?></a>
+
+<?php				endif; ?>
+
+							</div>
+						</div>
+
+						<fieldset id="<?php echo $group->id; ?>" class="connectedSortable field-group">
+
+<?php				if( $group->description ) : ?>
+
+							<legend><?php echo esc_attr( $group->description ) ?></legend>
+
+<?php				endif;
+
+					if ( !empty( $group->fields ) ) :
+						foreach ( $group->fields as $field ) {
+
+							// Load the field
+							$field = new BP_XProfile_Field( $field->id );
+
+							$class = '';
+							if ( !$field->can_delete )
+								$class = ' core nodrag';
+
+							/* This function handles the WYSIWYG profile field
+							 * display for the xprofile admin setup screen
+							 */
+							xprofile_admin_field( $field, $group, $class );
+
+						} // end for
+
+					else : // !$group->fields
+?>
+
+							<p class="nodrag nofields"><?php _e( 'There are no fields in this group.', 'buddypress' ); ?></p>
+
+<?php
+					endif; // end $group->fields
+?>
+
+						</fieldset>
+					</div>
+
+<?php
+					} // End For ?>
+
+				</div>
+<?php
+				else :
+?>
+
+				<div id="message" class="error"><p><?php _e( 'You have no groups.', 'buddypress' ); ?></p></div>
+				<p><a href="admin.php?page=bp-profile-setup&amp;mode=add_group"><?php _e( 'Add New Group', 'buddypress' ); ?></a></p>
+
+<?php
+				endif;
+?>
+				<div id="tabs-bottom">
+					&nbsp;
+				</div>
+			</form>
+		</div>
+<?php
+	}
+}
+
+/**************************************************************************
+ xprofile_admin_manage_group()
+
+ Handles the adding or editing of groups.
+ **************************************************************************/
+
+function xprofile_admin_manage_group( $group_id = null ) {
+	global $message, $type;
+
+	$group = new BP_XProfile_Group( $group_id );
+
+	if ( isset( $_POST['save_group'] ) ) {
+		if ( BP_XProfile_Group::admin_validate( $_POST ) ) {
+			$group->name		= wp_filter_kses( $_POST['group_name'] );
+			$group->description	= !empty( $_POST['group_description'] ) ? wp_filter_kses( $_POST['group_description'] ) : '';
+
+			if ( !$group->save() ) {
+				$message = __( 'There was an error saving the group. Please try again', 'buddypress' );
+				$type    = 'error';
+			} else {
+				$message = __( 'The group was saved successfully.', 'buddypress' );
+				$type    = 'success';
+
+				if ( 1 == $group_id )
+					bp_update_option( 'bp-xprofile-base-group-name', $group->name );
+
+				do_action( 'xprofile_groups_saved_group', $group );
+			}
+
+			unset( $_GET['mode'] );
+			xprofile_admin( $message, $type );
+
+		} else {
+			$group->render_admin_form( $message );
+		}
+	} else {
+		$group->render_admin_form();
+	}
+}
+
+/**************************************************************************
+ xprofile_admin_delete_group()
+
+ Handles the deletion of profile data groups.
+ **************************************************************************/
+
+function xprofile_admin_delete_group( $group_id ) {
+	global $message, $type;
+
+	$group = new BP_XProfile_Group( $group_id );
+
+	if ( !$group->delete() ) {
+		$message = __( 'There was an error deleting the group. Please try again', 'buddypress' );
+		$type    = 'error';
+	} else {
+		$message = __( 'The group was deleted successfully.', 'buddypress' );
+		$type    = 'success';
+
+		do_action( 'xprofile_groups_deleted_group', $group );
+	}
+
+	unset( $_GET['mode'] );
+	xprofile_admin( $message, $type );
+}
+
+
+/**************************************************************************
+ xprofile_admin_manage_field()
+
+ Handles the adding or editing of profile field data for a user.
+ **************************************************************************/
+
+function xprofile_admin_manage_field( $group_id, $field_id = null ) {
+	global $bp, $wpdb, $message, $groups;
+
+	$field = new BP_XProfile_Field( $field_id );
+	$field->group_id = $group_id;
+
+	if ( isset( $_POST['saveField'] ) ) {
+		if ( BP_XProfile_Field::admin_validate() ) {
+			$field->name        = wp_filter_kses( $_POST['title'] );
+			$field->description = !empty( $_POST['description'] ) ? wp_filter_kses( $_POST['description'] ) : '';
+			$field->is_required = wp_filter_kses( $_POST['required'] );
+			$field->type        = wp_filter_kses( $_POST['fieldtype'] );
+
+			if ( !empty( $_POST["sort_order_{$field->type}"] ) )
+				$field->order_by = wp_filter_kses( $_POST["sort_order_{$field->type}"] );
+
+			$field->field_order = $wpdb->get_var( $wpdb->prepare( "SELECT field_order FROM {$bp->profile->table_name_fields} WHERE id = %d", $field_id ) );
+
+			if ( !$field->field_order ) {
+				$field->field_order = (int) $wpdb->get_var( $wpdb->prepare( "SELECT max(field_order) FROM {$bp->profile->table_name_fields} WHERE group_id = %d", $group_id ) );
+				$field->field_order++;
+			}
+
+			if ( !$field->save() ) {
+				$message = __( 'There was an error saving the field. Please try again', 'buddypress' );
+				$type = 'error';
+
+				unset( $_GET['mode'] );
+				xprofile_admin( $message, $type );
+			} else {
+				$message = __( 'The field was saved successfully.', 'buddypress' );
+				$type = 'success';
+
+				if ( 1 == $field_id )
+					bp_update_option( 'bp-xprofile-fullname-field-name', $field->name );
+
+				unset( $_GET['mode'] );
+
+				do_action( 'xprofile_fields_saved_field', $field );
+
+				$groups = BP_XProfile_Group::get();
+				xprofile_admin( $message, $type );
+			}
+		} else {
+			$field->render_admin_form( $message );
+		}
+	} else {
+		$field->render_admin_form();
+	}
+}
+
+/**************************************************************************
+ xprofile_admin_delete_field()
+
+ Handles the deletion of a profile field [or option].
+**************************************************************************/
+function xprofile_admin_delete_field( $field_id, $type = 'field' ) {
+	global $message, $type;
+
+	if ( 'field' == $type )
+		$type = __('field', 'buddypress');
+	else
+		$type = __('option', 'buddypress');
+
+	$field = new BP_XProfile_Field( $field_id );
+
+	if ( !$field->delete() ) {
+		$message = sprintf( __('There was an error deleting the %s. Please try again', 'buddypress' ), $type );
+		$type = 'error';
+	} else {
+		$message = sprintf( __('The %s was deleted successfully!', 'buddypress' ), $type );
+		$type = 'success';
+
+		do_action( 'xprofile_fields_deleted_field', $field );
+	}
+
+	unset( $_GET['mode'] );
+	xprofile_admin( $message, $type );
+}
+
+/**************************************************************************
+ xprofile_ajax_reorder_fields()
+
+ Handles the ajax reordering of fields within a group
+**************************************************************************/
+function xprofile_ajax_reorder_fields() {
+	global $bp;
+
+	// Check the nonce
+	check_admin_referer( 'bp_reorder_fields', '_wpnonce_reorder_fields' );
+
+	if ( empty( $_POST['field_order'] ) )
+		return false;
+
+	parse_str( $_POST['field_order'], $order );
+	$field_group_id = $_POST['field_group_id'];
+
+	foreach ( (array) $order['field'] as $position => $field_id )
+		xprofile_update_field_position( (int) $field_id, (int) $position, (int) $field_group_id );
+
+}
+add_action( 'wp_ajax_xprofile_reorder_fields', 'xprofile_ajax_reorder_fields' );
+
+/**************************************************************************
+ xprofile_ajax_reorder_field_groups()
+
+ Handles the reordering of field groups
+**************************************************************************/
+function xprofile_ajax_reorder_field_groups() {
+	global $bp;
+
+	// Check the nonce
+	check_admin_referer( 'bp_reorder_groups', '_wpnonce_reorder_groups' );
+
+	if ( empty( $_POST['group_order'] ) )
+		return false;
+
+	parse_str( $_POST['group_order'], $order );
+
+	foreach ( (array) $order['group'] as $position => $field_group_id )
+		xprofile_update_field_group_position( (int) $field_group_id, (int) $position );
+
+}
+add_action( 'wp_ajax_xprofile_reorder_groups', 'xprofile_ajax_reorder_field_groups' );
+
+/**************************************************************************
+ xprofile_admin_field()
+
+ Handles the WYSIWYG display of each profile field on the edit screen
+**************************************************************************/
+function xprofile_admin_field( $admin_field, $admin_group, $class='' ) {
+	global $field;
+
+	$field = $admin_field; ?>
+						<fieldset id="field_<?php echo esc_attr( $field->id ); ?>" class="sortable<?php echo ' ' . $field->type; if ( $class ) echo ' ' . $class; ?>">
+							<legend><span><?php bp_the_profile_field_name(); ?> <?php if( !$field->can_delete ) : ?> <?php _e( '(Primary)', 'buddypress' ); endif; ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(Required)', 'buddypress' ) ?><?php endif; ?></span></legend>
+							<div class="field-wrapper">
+
+<?php
+	switch ( $field->type ) {
+		case 'textbox' : ?>
+
+								<input type="text" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" value="" />
+
+<?php		break; case 'textarea' : ?>
+
+								<textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>"></textarea>
+
+<?php		break; case 'selectbox' : ?>
+
+								<select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>">
+									<?php bp_the_profile_field_options() ?>
+
+								</select>
+
+<?php		break; case 'multiselectbox' : ?>
+
+								<select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" multiple="multiple">
+									<?php bp_the_profile_field_options() ?>
+
+								</select>
+
+<?php		break; case 'radio' : ?>
+
+								<?php bp_the_profile_field_options() ?>
+
+<?php			if ( !bp_get_the_profile_field_is_required() ) : ?>
+
+								<a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name() ?>' );"><?php _e( 'Clear', 'buddypress' ) ?></a>
+
+<?php			endif; ?>
+
+<?php		break; case 'checkbox' : ?>
+
+<?php bp_the_profile_field_options(); ?>
+
+<?php		break; case 'datebox' : ?>
+
+								<select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day">
+									<?php bp_the_profile_field_options( 'type=day' ); ?>
+
+								</select>
+
+								<select name="<?php bp_the_profile_field_input_name(); ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month">
+									<?php bp_the_profile_field_options( 'type=month' ); ?>
+
+								</select>
+
+								<select name="<?php bp_the_profile_field_input_name(); ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year">
+									<?php bp_the_profile_field_options( 'type=year' ); ?>
+
+								</select>
+
+<?php		break; default : ?>
+
+<?php	do_action( 'xprofile_admin_field', $field, 1 ); ?>
+
+<?php } ?>
+								<div class="actions">
+									<a class="button edit" href="admin.php?page=bp-profile-setup&amp;group_id=<?php echo esc_attr( $admin_group->id ); ?>&amp;field_id=<?php echo esc_attr( $field->id ); ?>&amp;mode=edit_field"><?php _e( 'Edit', 'buddypress' ); ?></a>
+									<?php if ( !$field->can_delete ) : ?>&nbsp;<?php else : ?><a class="button delete" href="admin.php?page=bp-profile-setup&amp;field_id=<?php echo esc_attr( $field->id ); ?>&amp;mode=delete_field"><?php _e( 'Delete', 'buddypress' ); ?></a><?php endif; ?>
+
+								</div>
+
+<?php if ( $field->description ) : ?>
+
+								<p class="description"><?php echo esc_attr( $field->description ); ?></p>
+
+<?php endif; ?>
+
+							</div>
+						</fieldset>
+<?php
+}
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-buddybar.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-buddybar.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-buddybar.php	(revision 5057)
@@ -0,0 +1,4 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cache.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cache.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cache.php	(revision 5057)
@@ -0,0 +1,30 @@
+<?php
+/********************************************************************************
+ * Caching
+ *
+ * Caching functions handle the clearing of cached objects and pages on specific
+ * actions throughout BuddyPress.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function xprofile_clear_profile_groups_object_cache( $group_obj ) {
+	wp_cache_delete( 'xprofile_groups_inc_empty', 'bp' );
+	wp_cache_delete( 'xprofile_group_' . $group_obj->id );
+}
+
+function xprofile_clear_profile_data_object_cache( $group_id ) {
+	global $bp;
+	wp_cache_delete( 'bp_user_fullname_' . $bp->loggedin_user->id, 'bp' );
+}
+
+// List actions to clear object caches on
+add_action( 'xprofile_groups_deleted_group', 'xprofile_clear_profile_groups_object_cache' );
+add_action( 'xprofile_groups_saved_group',   'xprofile_clear_profile_groups_object_cache' );
+add_action( 'xprofile_updated_profile',      'xprofile_clear_profile_data_object_cache'   );
+
+// List actions to clear super cached pages on, if super cache is installed
+add_action( 'xprofile_updated_profile', 'bp_core_clear_cache' );
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-classes.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-classes.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-classes.php	(revision 5057)
@@ -0,0 +1,1042 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_XProfile_Group {
+	var $id = null;
+	var $name;
+	var $description;
+	var $can_delete;
+	var $group_order;
+	var $fields;
+
+	function bp_xprofile_group( $id = null ) {
+		$this->__construct( $id );
+	}
+
+	function __construct( $id = null ) {
+		global $bp, $wpdb;
+
+		if ( $id )
+			$this->populate( $id );
+
+	}
+
+	function populate( $id ) {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_groups} WHERE id = %d", $id );
+
+		if ( !$group = $wpdb->get_row( $sql ) )
+			return false;
+
+		$this->id          = $group->id;
+		$this->name        = stripslashes( $group->name );
+		$this->description = stripslashes( $group->description );
+		$this->can_delete  = $group->can_delete;
+		$this->group_order = $group->group_order;
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->name			= apply_filters( 'xprofile_group_name_before_save',        $this->name,        $this->id );
+		$this->description	= apply_filters( 'xprofile_group_description_before_save', $this->description, $this->id );
+
+		do_action_ref_array( 'xprofile_group_before_save', array( &$this ) );
+
+		if ( $this->id )
+			$sql = $wpdb->prepare( "UPDATE {$bp->profile->table_name_groups} SET name = %s, description = %s WHERE id = %d", $this->name, $this->description, $this->id );
+		else
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_groups} (name, description, can_delete) VALUES (%s, %s, 1)", $this->name, $this->description );
+
+		if ( is_wp_error( $wpdb->query( $sql ) ) )
+			return false;
+
+		do_action_ref_array( 'xprofile_group_after_save', array( &$this ) );
+
+		if ( $this->id )
+			return $this->id;
+		else
+			return $wpdb->insert_id;
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		if ( !$this->can_delete )
+			return false;
+
+		/* Delete field group */
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_groups} WHERE id = %d", $this->id ) ) ) {
+			return false;
+		} else {
+			/* Remove the group's fields. */
+			if ( BP_XProfile_Field::delete_for_group( $this->id ) ) {
+				/* Remove profile data for the groups fields */
+				for ( $i = 0, $count = count( $this->fields ); $i < $count; ++$i ) {
+					BP_XProfile_ProfileData::delete_for_field( $this->fields[$i]->id );
+				}
+			}
+
+			return true;
+		}
+	}
+
+	/** Static Functions **/
+
+	/**
+	 * get()
+	 *
+	 * Populates the BP_XProfile_Group object with profile field groups, fields, and field data
+	 *
+	 * @package BuddyPress XProfile
+	 *
+	 * @global $wpdb WordPress DB access object.
+	 * @global object $bp Global BuddyPress settings object
+	 *
+	 * @param array $args Takes an array of parameters:
+	 *		'profile_group_id' - Limit results to a single profile group
+	 *		'user_id' - Required if you want to load a specific user's data
+	 *		'hide_empty_groups' - Hide groups without any fields
+	 *		'hide_empty_fields' - Hide fields where the user has not provided data
+	 *		'fetch_fields' - Load each group's fields
+	 *		'fetch_field_data' - Load each field's data. Requires a user_id
+	 *		'exclude_groups' - Comma-separated list of groups to exclude
+	 *		'exclude_fields' - Comma-separated list of fields to exclude
+	 *
+	 * @return array $groups
+	 */
+	function get( $args = '' ) {
+		global $wpdb, $bp;
+
+		$defaults = array(
+			'profile_group_id'  => false,
+			'user_id'           => $bp->displayed_user->id,
+			'hide_empty_groups' => false,
+			'hide_empty_fields' => false,
+			'fetch_fields'      => false,
+			'fetch_field_data'  => false,
+			'exclude_groups'    => false,
+			'exclude_fields'    => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+		$where_sql = '';
+
+		if ( $profile_group_id )
+			$where_sql = $wpdb->prepare( 'WHERE g.id = %d', $profile_group_id );
+		elseif ( $exclude_groups )
+			$where_sql = $wpdb->prepare( "WHERE g.id NOT IN ({$exclude_groups})");
+
+		if ( $hide_empty_groups )
+			$groups = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT g.* FROM {$bp->profile->table_name_groups} g INNER JOIN {$bp->profile->table_name_fields} f ON g.id = f.group_id {$where_sql} ORDER BY g.group_order ASC" ) );
+		else
+			$groups = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT g.* FROM {$bp->profile->table_name_groups} g {$where_sql} ORDER BY g.group_order ASC" ) );
+
+		if ( !$fetch_fields )
+			return $groups;
+
+		// Get the group ids
+		$group_ids = array();
+		foreach( (array)$groups as $group ) {
+			$group_ids[] = $group->id;
+		}
+
+		$group_ids = implode( ',', (array) $group_ids );
+
+		if ( empty( $group_ids ) )
+			return $groups;
+
+		$exclude_fields_sql = '';
+		if ( $exclude_fields )
+			$exclude_fields_sql = $wpdb->prepare( "AND id NOT IN ({$exclude_fields})" );
+
+		// Fetch the fields
+		$fields = $wpdb->get_results( $wpdb->prepare( "SELECT id, name, description, type, group_id, is_required FROM {$bp->profile->table_name_fields} WHERE group_id IN ( {$group_ids} ) AND parent_id = 0 {$exclude_fields_sql} ORDER BY field_order" ) );
+
+		if ( empty( $fields ) )
+			return $groups;
+
+		if ( $fetch_field_data ) {
+			// Fetch the field data for the user.
+			foreach( (array)$fields as $field )
+				$field_ids[] = $field->id;
+
+			$field_ids_sql = implode( ',', (array) $field_ids );
+
+			if ( !empty( $field_ids ) )
+				$field_data = $wpdb->get_results( $wpdb->prepare( "SELECT field_id, value FROM {$bp->profile->table_name_data} WHERE field_id IN ( {$field_ids_sql} ) AND user_id = %d", $user_id ) );
+
+			// Remove data-less fields, if necessary
+			if ( $hide_empty_fields ) {
+
+				// Loop through the results and find the fields that have data.
+				foreach( (array)$field_data as $data ) {
+					if ( false !== $key = array_search( $data->field_id, $field_ids ) ) {
+						// Fields that have data get removed from the list
+						unset( $field_ids[$key] );
+					}
+				}
+
+				// The remaining members of $field_ids are empty. Remove them.
+				foreach( $fields as $field_key => $field ) {
+					if ( in_array( $field->id, $field_ids ) ) {
+						unset( $fields[$field_key] );
+					}
+				}
+
+				// Reset indexes
+				$fields = array_values( $fields );
+
+			}
+
+			// Field data was found
+			if ( !empty( $field_data ) && !is_wp_error( $field_data ) ) {
+
+				// Loop through fields
+				foreach( (array)$fields as $field_key => $field ) {
+
+					// Loop throught the data in each field
+					foreach( (array)$field_data as $data ) {
+
+						// Assign correct data value to the field
+						if ( $field->id == $data->field_id )
+							$fields[$field_key]->data->value = $data->value;
+					}
+				}
+			}
+		}
+
+		// Merge the field array back in with the group array
+		foreach( (array) $groups as $group_key => $group ) {
+			foreach( (array) $fields as $field ) {
+				if ( $group->id == $field->group_id )
+					$groups[$group_key]->fields[] = $field;
+			}
+
+			// When we unset fields above, we may have created empty groups.
+			// Remove them, if necessary.
+			if ( empty( $group->fields ) && $hide_empty_groups ) {
+				unset( $groups[$group_key] );
+			}
+
+			// Reset indexes
+			$groups = array_values( $groups );
+		}
+
+		return $groups;
+	}
+
+	function admin_validate() {
+		global $message;
+
+		/* Validate Form */
+		if ( empty( $_POST['group_name'] ) ) {
+			$message = __( 'Please make sure you give the group a name.', 'buddypress' );
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	function update_position( $field_group_id, $position ) {
+		global $wpdb, $bp;
+
+		if ( !is_numeric( $position ) )
+			return false;
+
+		return $wpdb->query( $wpdb->prepare( "UPDATE {$bp->profile->table_name_groups} SET group_order = %d WHERE id = %d", $position, $field_group_id ) );
+	}
+
+	/* ADMIN AREA HTML.
+	* TODO: Get this out of here and replace with standard loops
+	*/
+
+	function render_admin_form() {
+		global $message;
+
+		if ( !$this->id ) {
+			$title	= __( 'Add New Field Group', 'buddypress' );
+			$action	= "admin.php?page=bp-profile-setup&amp;mode=add_group";
+			$button	= __( 'Create Field Group', 'buddypress' );
+		} else {
+			$title = __( 'Edit Field Group', 'buddypress' );
+			$action = "admin.php?page=bp-profile-setup&amp;mode=edit_group&amp;group_id=" . $this->id;
+			$button	= __( 'Save Changes', 'buddypress' );
+		}
+?>
+		<div class="wrap">
+
+			<?php screen_icon( 'buddypress' ); ?>
+
+			<h2><?php echo $title; ?></h2>
+			<p><?php _e( 'Fields marked * are required', 'buddypress' ) ?></p>
+
+<?php if ( $message != '' ) :
+		$type = ( 'error' == $type ) ? 'error' : 'updated'; ?>
+				<div id="message" class="<?php echo $type; ?> fade">
+					<p><?php echo $message; ?></p>
+				</div>
+<?php endif; ?>
+			<div id="poststuff">
+				<form action="<?php echo esc_attr( $action ); ?>" method="post">
+					<div id="titlediv">
+						<h3><label for="group_name"><?php _e( "Field Group Title", 'buddypress') ?> *</label></h3>
+						<div id="titlewrap">
+							<input type="text" name="group_name" id="title" value="<?php echo esc_attr( $this->name ); ?>" style="width:50%" />
+						</div>
+					</div>
+
+<?php if ( '0' != $this->can_delete ) : ?>
+					<div id="titlediv">
+						<h3><label for="description"><?php _e( "Group Description", 'buddypress' ); ?></label></h3>
+						<div id="titlewrap">
+							<textarea name="group_description" id="group_description" rows="8" cols="60"><?php echo htmlspecialchars( $this->description ); ?></textarea>
+						</div>
+					</div>
+<?php endif; ?>
+
+					<p class="submit">
+						<input type="hidden" name="group_order" id="group_order" value="<?php echo esc_attr( $this->group_order ); ?>" />
+						<input type="submit" name="save_group" value="<?php echo esc_attr( $button ); ?>" class="button-primary"/>
+						<?php _e( 'or', 'buddypress' ); ?> <a href="admin.php?page=bp-profile-setup" class="deletion"><?php _e( 'Cancel', 'buddypress' ); ?></a>
+					</p>
+				</form>
+			</div>
+		</div>
+<?php
+	}
+}
+
+
+Class BP_XProfile_Field {
+	var $id;
+	var $group_id;
+	var $parent_id;
+	var $type;
+	var $name;
+	var $description;
+	var $is_required;
+	var $can_delete;
+	var $field_order;
+	var $option_order;
+	var $order_by;
+	var $is_default_option;
+
+	var $data;
+	var $message = null;
+	var $message_type = 'err';
+
+	function bp_xprofile_field( $id = null, $user_id = null, $get_data = true ) {
+		$this->__construct( $id, $user_id, $get_data );
+	}
+
+	function __construct( $id = null, $user_id = null, $get_data = true ) {
+		if ( $id )
+			$this->populate( $id, $user_id, $get_data );
+	}
+
+	function populate( $id, $user_id, $get_data ) {
+		global $wpdb, $userdata, $bp;
+
+		$user_id = 0;
+		if ( is_null( $user_id ) )
+			$user_id = $userdata->ID;
+
+		$sql = $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_fields} WHERE id = %d", $id );
+
+		if ( $field = $wpdb->get_row( $sql ) ) {
+			$this->id               = $field->id;
+			$this->group_id          = $field->group_id;
+			$this->parent_id         = $field->parent_id;
+			$this->type              = $field->type;
+			$this->name              = stripslashes( $field->name );
+			$this->description       = stripslashes( $field->description );
+			$this->is_required       = $field->is_required;
+			$this->can_delete        = $field->can_delete;
+			$this->field_order       = $field->field_order;
+			$this->option_order      = $field->option_order;
+			$this->order_by          = $field->order_by;
+			$this->is_default_option = $field->is_default_option;
+
+			if ( $get_data && $user_id )
+				$this->data            = $this->get_field_data( $user_id );
+		}
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		if ( !$this->id ||
+			/* Prevent deletion by url when can_delete is false. */
+			!$this->can_delete ||
+			/* Prevent deletion of option 1 since this invalidates fields with options. */
+			( $this->parent_id && $this->option_order == 1 ) )
+			return false;
+
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE id = %d OR parent_id = %d", $this->id, $this->id ) ) )
+			return false;
+
+		/* delete the data in the DB for this field */
+		BP_XProfile_ProfileData::delete_for_field( $this->id );
+
+		return true;
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$error = false;
+
+		$this->group_id		= apply_filters( 'xprofile_field_group_id_before_save',    $this->group_id,    $this->id );
+		$this->parent_id	= apply_filters( 'xprofile_field_parent_id_before_save',   $this->parent_id,   $this->id );
+		$this->type			= apply_filters( 'xprofile_field_type_before_save',        $this->type,        $this->id );
+		$this->name			= apply_filters( 'xprofile_field_name_before_save',        $this->name,        $this->id );
+		$this->description	= apply_filters( 'xprofile_field_description_before_save', $this->description, $this->id );
+		$this->is_required	= apply_filters( 'xprofile_field_is_required_before_save', $this->is_required, $this->id );
+		$this->order_by		= apply_filters( 'xprofile_field_order_by_before_save',    $this->order_by,    $this->id );
+		$this->field_order	= apply_filters( 'xprofile_field_field_order_before_save', $this->field_order, $this->id );
+
+		do_action_ref_array( 'xprofile_field_before_save', array( &$this ) );
+
+		if ( $this->id != null )
+			$sql = $wpdb->prepare( "UPDATE {$bp->profile->table_name_fields} SET group_id = %d, parent_id = 0, type = %s, name = %s, description = %s, is_required = %d, order_by = %s, field_order = %d WHERE id = %d", $this->group_id, $this->type, $this->name, $this->description, $this->is_required, $this->order_by, $this->field_order, $this->id );
+		else
+			$sql = $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_fields} (group_id, parent_id, type, name, description, is_required, order_by, field_order ) VALUES (%d, %d, %s, %s, %s, %d, %s, %d )", $this->group_id, $this->parent_id, $this->type, $this->name, $this->description, $this->is_required, $this->order_by, $this->field_order );
+
+		/*
+		 * Check for null so field options can be changed without changing any other part of the field.
+		 * The described situation will return 0 here.
+		 */
+		if ( $wpdb->query( $sql ) !== null ) {
+			if ( $this->id )
+				$field_id = $this->id;
+			else
+				$field_id = $wpdb->insert_id;
+
+			/* Only do this if we are editing an existing field */
+			if ( $this->id != null ) {
+				/*
+				 * Remove any radio or dropdown options for this
+				 * field. They will be re-added if needed.
+				 * This stops orphan options if the user changes a
+				 * field from a radio button field to a text box.
+				 */
+				$this->delete_children();
+			}
+
+			/*
+			 * Check to see if this is a field with child options.
+			 * We need to add the options to the db, if it is.
+			 */
+			if ( 'radio' == $this->type || 'selectbox' == $this->type || 'checkbox' == $this->type || 'multiselectbox' == $this->type ) {
+				if ( $this->id )
+					$parent_id = $this->id;
+				else
+					$parent_id = $wpdb->insert_id;
+
+				if ( 'radio' == $this->type ) {
+					$post_option  = !empty( $_POST['radio_option']           ) ? $_POST['radio_option']           : '';
+					$post_default = !empty( $_POST['isDefault_radio_option'] ) ? $_POST['isDefault_radio_option'] : '';
+
+					$options	= apply_filters( 'xprofile_field_options_before_save', $post_option,  'radio' );
+					$defaults	= apply_filters( 'xprofile_field_default_before_save', $post_default, 'radio' );
+
+				} else if ( 'selectbox' == $this->type ) {
+					$post_option  = !empty( $_POST['selectbox_option']           ) ? $_POST['selectbox_option']           : '';
+					$post_default = !empty( $_POST['isDefault_selectbox_option'] ) ? $_POST['isDefault_selectbox_option'] : '';
+
+					$options	= apply_filters( 'xprofile_field_options_before_save', $post_option, 'selectbox' );
+					$defaults	= apply_filters( 'xprofile_field_default_before_save', $post_default, 'selectbox' );
+
+				} else if ( 'multiselectbox' == $this->type ) {
+					$post_option  = !empty( $_POST['multiselectbox_option']           ) ? $_POST['multiselectbox_option']           : '';
+					$post_default = !empty( $_POST['isDefault_multiselectbox_option'] ) ? $_POST['isDefault_multiselectbox_option'] : '';
+
+					$options	= apply_filters( 'xprofile_field_options_before_save', $post_option, 'multiselectbox' );
+					$defaults	= apply_filters( 'xprofile_field_default_before_save', $post_default, 'multiselectbox' );
+
+				} else if ( 'checkbox' == $this->type ) {
+					$post_option  = !empty( $_POST['checkbox_option']           ) ? $_POST['checkbox_option']           : '';
+					$post_default = !empty( $_POST['isDefault_checkbox_option'] ) ? $_POST['isDefault_checkbox_option'] : '';
+
+					$options	= apply_filters( 'xprofile_field_options_before_save', $post_option, 'checkbox' );
+					$defaults	= apply_filters( 'xprofile_field_default_before_save', $post_default, 'checkbox' );
+				}
+
+				$counter = 1;
+				if ( $options ) {
+					foreach ( (array)$options as $option_key => $option_value ) {
+						$is_default = 0;
+
+						if ( is_array( $defaults ) ) {
+							if ( isset( $defaults[$option_key] ) )
+								$is_default = 1;
+						} else {
+							if ( (int) $defaults == $option_key )
+								$is_default = 1;
+						}
+
+						if ( '' != $option_value ) {
+							if ( !$wpdb->query( $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_fields} (group_id, parent_id, type, name, description, is_required, option_order, is_default_option) VALUES (%d, %d, 'option', %s, '', 0, %d, %d)", $this->group_id, $parent_id, $option_value, $counter, $is_default ) ) )
+								return false;
+						}
+
+						$counter++;
+					}
+				}
+			}
+
+			do_action_ref_array( 'xprofile_field_after_save', array( &$this ) );
+
+			return $field_id;
+		} else {
+			return false;
+		}
+	}
+
+	function get_field_data( $user_id ) {
+		return new BP_XProfile_ProfileData( $this->id, $user_id );
+	}
+
+	function get_children( $for_editing = false ) {
+		global $wpdb, $bp;
+
+		/* This is done here so we don't have problems with sql injection */
+		if ( 'asc' == $this->order_by && !$for_editing )
+			$sort_sql = 'ORDER BY name ASC';
+		else if ( 'desc' == $this->order_by && !$for_editing )
+			$sort_sql = 'ORDER BY name DESC';
+		else
+			$sort_sql = 'ORDER BY option_order ASC';
+
+		/* This eliminates a problem with getting all fields when there is no id for the object */
+		if ( !$this->id )
+			$parent_id = -1;
+		else
+			$parent_id = $this->id;
+
+		$sql = $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_fields} WHERE parent_id = %d AND group_id = %d $sort_sql", $parent_id, $this->group_id );
+
+		if ( !$children = $wpdb->get_results( $sql ) )
+			return false;
+
+		return $children;
+	}
+
+	function delete_children() {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE parent_id = %d", $this->id );
+
+		$wpdb->query( $sql );
+	}
+
+	/* Static Functions */
+
+	function get_type( $field_id ) {
+		global $wpdb, $bp;
+
+		if ( $field_id ) {
+			$sql = $wpdb->prepare( "SELECT type FROM {$bp->profile->table_name_fields} WHERE id = %d", $field_id );
+
+			if ( !$field_type = $wpdb->get_var( $sql ) )
+				return false;
+
+			return $field_type;
+		}
+
+		return false;
+	}
+
+	function delete_for_group( $group_id ) {
+		global $wpdb, $bp;
+
+		if ( $group_id ) {
+			$sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE group_id = %d", $group_id );
+
+			if ( $wpdb->get_var( $sql ) === false )
+				return false;
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function get_id_from_name( $field_name ) {
+		global $wpdb, $bp;
+
+		if ( empty( $bp->profile->table_name_fields ) || !isset( $field_name ) )
+			return false;
+
+		return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", $field_name ) );
+	}
+
+	function update_position( $field_id, $position, $field_group_id ) {
+		global $wpdb, $bp;
+
+		if ( !is_numeric( $position ) || !is_numeric( $field_group_id ) )
+			return false;
+
+		/* Update $field_id with new $position and $field_group_id */
+		if ( $parent = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->profile->table_name_fields} SET field_order = %d, group_id = %d WHERE id = %d", $position, $field_group_id, $field_id ) ) ) {;
+			/* Update any children of this $field_id */
+			$children = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->profile->table_name_fields} SET group_id = %d WHERE parent_id = %d", $field_group_id, $field_id ) );
+
+			return $parent;
+		}
+
+		return false;
+	}
+
+	/* ADMIN AREA HTML.
+	* TODO: Get this out of here and replace with standard template loops
+	*/
+
+	/* This function populates the items for radio buttons checkboxes and drop down boxes */
+	function render_admin_form_children() {
+		$input_types = array( 'checkbox', 'selectbox', 'multiselectbox', 'radio' );
+
+		foreach ($input_types as $type) {
+			$default_name = '';
+
+			if ( 'multiselectbox' == $type || 'checkbox' == $type )
+				$default_input = 'checkbox';
+			else
+				$default_input = 'radio';
+?>
+			<div id="<?php echo $type; ?>" class="options-box" style="<?php if ( $this->type != $type ) { ?>display: none;<?php } ?> margin-left: 15px;">
+				<h4><?php _e( 'Please enter options for this Field:', 'buddypress' ); ?></h4>
+				<p><label for="sort_order_<?php echo $type; ?>"><?php _e( 'Order By:', 'buddypress' ); ?></label>
+					<select name="sort_order_<?php echo $type; ?>" id="sort_order_<?php echo $type; ?>" >
+						<option value="default" <?php if ( 'default' == $this->order_by ) {?> selected="selected"<?php } ?> ><?php _e( 'Order Entered', 'buddypress' ); ?></option>
+						<option value="asc" <?php if ( 'asc' == $this->order_by ) {?> selected="selected"<?php } ?>><?php _e( 'Name - Ascending', 'buddypress' ); ?></option>
+						<option value="desc" <?php if ( 'desc' == $this->order_by ) {?> selected="selected"<?php } ?>><?php _e( 'Name - Descending', 'buddypress' ); ?></option>
+					</select>
+<?php
+				if ( !$options = $this->get_children( true ) ) {
+					$i = 1;
+					while ( isset( $_POST[$type . '_option'][$i] ) ) {
+						(array) $options[] = (object) array(
+							'id' => -1,
+							'name' => $_POST[$type . '_option'][$i],
+							'is_default_option' => ( 'multiselectbox' != $type &&
+								'checkbox' != $type &&
+								$_POST["isDefault_{$type}_option"] == $i ) ?
+									1 :
+									$_POST["isDefault_{$type}_option"][$i]
+						);
+
+						++$i;
+					}
+				}
+
+				if ( !empty( $options ) ) {
+					for ( $i = 0, $count = count( $options ); $i < $count; ++$i ) {
+						$j = $i + 1;
+
+						if ( 'multiselectbox' == $type || 'checkbox' == $type )
+							$default_name = '[' . $j . ']';
+?>
+						<p><?php _e('Option', 'buddypress'); ?> <?php echo $j; ?>:
+						   <input type="text" name="<?php echo $type; ?>_option[<?php echo $j; ?>]" id="<?php echo $type; ?>_option<?php echo $j; ?>" value="<?php echo stripslashes( esc_attr( $options[$i]->name ) ); ?>" />
+						   <input type="<?php echo $default_input; ?>" name="isDefault_<?php echo $type; ?>_option<?php echo $default_name; ?>" <?php if ( (int) $options[$i]->is_default_option ) {?> checked="checked"<?php } ?> " value="<?php echo $j; ?>" /> <?php _e( 'Default Value', 'buddypress' ); ?>
+<?php
+					if ( $j != 1 && $options[$i]->id != -1 ) : ?>
+							<a href="admin.php?page=bp-profile-setup&amp;mode=delete_option&amp;option_id=<?php echo $options[$i]->id ?>" class="ajax-option-delete" id="delete-<?php echo $options[$i]->id; ?>">[x]</a>
+<?php				endif; ?>
+						</p>
+<?php				} /* end for */ ?>
+
+					<input type="hidden" name="<?php echo $type; ?>_option_number" id="<?php echo $type; ?>_option_number" value="<?php echo $j + 1; ?>" />
+<?php
+				} else {
+					if ( 'multiselectbox' == $type || 'checkbox' == $type )
+						$default_name = '[1]';
+?>
+
+					<p><?php _e( 'Option', 'buddypress' ); ?> 1: <input type="text" name="<?php echo $type; ?>_option[1]" id="<?php echo $type; ?>_option1" />
+					<input type="<?php echo $default_input; ?>" name="isDefault_<?php echo $type; ?>_option<?php echo $default_name; ?>" id="isDefault_<?php echo $type; ?>_option" value="1" /> <?php _e( 'Default Value', 'buddypress' ); ?>
+					<input type="hidden" name="<?php echo $type; ?>_option_number" id="<?php echo $type; ?>_option_number" value="2" />
+
+<?php			} /* end if */ ?>
+
+				<div id="<?php echo $type; ?>_more"></div>
+				<p><a href="javascript:add_option('<?php echo $type; ?>')"><?php _e('Add Another Option', 'buddypress'); ?></a></p>
+			</div>
+
+		<?php }
+	}
+
+	function render_admin_form( $message = '' ) {
+		if ( !$this->id ) {
+			$title				= __( 'Add Field', 'buddypress' );
+			$action				= "admin.php?page=bp-profile-setup&amp;group_id=" . $this->group_id . "&amp;mode=add_field#tabs-" . $this->group_id;
+
+			if ( !empty( $_POST['saveField'] ) ) {
+				$this->name		= $_POST['title'];
+				$this->description	= $_POST['description'];
+				$this->is_required	= $_POST['required'];
+				$this->type		= $_POST['fieldtype'];
+				$this->order_by		= $_POST["sort_order_{$this->type}"];
+				$this->field_order	= $_POST['field_order'];
+			}
+		} else {
+			$title				= __( 'Edit Field', 'buddypress' );
+			$action				= "admin.php?page=bp-profile-setup&amp;mode=edit_field&amp;group_id=" . $this->group_id . "&amp;field_id=" . $this->id . "#tabs-" . $this->group_id;
+		}
+?>
+	<div class="wrap">
+		<div id="icon-users" class="icon32"><br /></div>
+		<h2><?php echo $title; ?></h2>
+		<p><?php _e( 'Fields marked * are required', 'buddypress' ) ?></p>
+
+<?php
+		if ( $message != '' ) {
+?>
+			<div id="message" class="error fade">
+				<p><?php echo $message; ?></p>
+			</div>
+<?php	} ?>
+
+		<form action="<?php echo $action; ?>" method="post">
+			<div id="poststuff">
+				<div id="titlediv">
+					<h3><label for="title"><?php _e( 'Field Title', 'buddypress' ); ?> *</label></h3>
+					<div id="titlewrap">
+						<input type="text" name="title" id="title" value="<?php echo esc_attr( $this->name ); ?>" style="width:50%" />
+					</div>
+				</div>
+<?php
+		if ( '0' != $this->can_delete ) {
+?>
+				<div id="titlediv">
+					<h3><label for="description"><?php _e("Field Description", 'buddypress'); ?></label></h3>
+					<div id="titlewrap">
+						<textarea name="description" id="description" rows="8" cols="60"><?php echo htmlspecialchars( $this->description ); ?></textarea>
+					</div>
+				</div>
+
+				<div id="titlediv">
+					<h3><label for="required"><?php _e( "Is This Field Required?", 'buddypress' ); ?> *</label></h3>
+					<select name="required" id="required" style="width: 30%">
+						<option value="0"<?php if ( $this->is_required == '0' ) { ?> selected="selected"<?php } ?>><?php _e( 'Not Required', 'buddypress' ); ?></option>
+						<option value="1"<?php if ( $this->is_required == '1' ) { ?> selected="selected"<?php } ?>><?php _e( 'Required', 'buddypress' ); ?></option>
+					</select>
+				</div>
+
+				<div id="titlediv">
+					<h3><label for="fieldtype"><?php _e("Field Type", 'buddypress'); ?> *</label></h3>
+					<select name="fieldtype" id="fieldtype" onchange="show_options(this.value)" style="width: 30%">
+						<option value="textbox"<?php if ( $this->type == 'textbox' ) {?> selected="selected"<?php } ?>><?php _e( 'Text Box', 'buddypress' ); ?></option>
+						<option value="textarea"<?php if ( $this->type == 'textarea' ) {?> selected="selected"<?php } ?>><?php _e( 'Multi-line Text Box', 'buddypress' ); ?></option>
+						<option value="datebox"<?php if ( $this->type == 'datebox' ) {?> selected="selected"<?php } ?>><?php _e( 'Date Selector', 'buddypress' ); ?></option>
+						<option value="radio"<?php if ( $this->type == 'radio' ) {?> selected="selected"<?php } ?>><?php _e( 'Radio Buttons', 'buddypress' ); ?></option>
+						<option value="selectbox"<?php if ( $this->type == 'selectbox' ) {?> selected="selected"<?php } ?>><?php _e( 'Drop Down Select Box', 'buddypress' ); ?></option>
+						<option value="multiselectbox"<?php if ( $this->type == 'multiselectbox' ) {?> selected="selected"<?php } ?>><?php _e( 'Multi Select Box', 'buddypress' ); ?></option>
+						<option value="checkbox"<?php if ( $this->type == 'checkbox' ) {?> selected="selected"<?php } ?>><?php _e( 'Checkboxes', 'buddypress' ); ?></option>
+					</select>
+				</div>
+
+				<?php $this->render_admin_form_children(); ?>
+<?php	} else { ?>
+				<input type="hidden" name="required" id="required" value="1" />
+				<input type="hidden" name="fieldtype" id="fieldtype" value="textbox" />
+<?php	} ?>
+				<p class="submit">
+					<input type="hidden" name="field_order" id="field_order" value="<?php echo esc_attr( $this->field_order ); ?>" />
+					<input type="submit" value="<?php _e( 'Save', 'buddypress' ); ?>" name="saveField" id="saveField" style="font-weight: bold" class="button-primary" />
+					<?php _e( 'or', 'buddypress' ); ?> <a href="admin.php?page=bp-profile-setup" class="deletion"><?php _e( 'Cancel', 'buddypress' ); ?></a>
+				</p>
+
+			</div>
+
+			<?php wp_nonce_field( 'xprofile_delete_option' ); ?>
+
+		</form>
+	</div>
+
+<?php
+	}
+
+	function admin_validate() {
+		global $message;
+
+		/* Validate Form */
+		if ( '' == $_POST['title'] || '' == $_POST['required'] || '' == $_POST['fieldtype'] ) {
+			$message = __( 'Please make sure you fill out all required fields.', 'buddypress' );
+			return false;
+		} else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'radio' && empty( $_POST['radio_option'][1] ) ) {
+			$message = __( 'Radio button field types require at least one option. Please add options below.', 'buddypress' );
+			return false;
+		} else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'selectbox' && empty( $_POST['selectbox_option'][1] ) ) {
+			$message = __( 'Select box field types require at least one option. Please add options below.', 'buddypress' );
+			return false;
+		} else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'multiselectbox' && empty( $_POST['multiselectbox_option'][1] ) ) {
+			$message = __( 'Select box field types require at least one option. Please add options below.', 'buddypress' );
+			return false;
+		} else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'checkbox' && empty( $_POST['checkbox_option'][1] ) ) {
+			$message = __( 'Checkbox field types require at least one option. Please add options below.', 'buddypress' );
+			return false;
+		} else {
+			return true;
+		}
+	}
+}
+
+
+Class BP_XProfile_ProfileData {
+	var $id;
+	var $user_id;
+	var $field_id;
+	var $value;
+	var $last_updated;
+
+	function bp_xprofile_profiledata( $field_id = null, $user_id = null ) {
+		$this->__construct( $field_id, $user_id );
+	}
+
+	function __construct( $field_id = null, $user_id = null ) {
+		if ( $field_id )
+			$this->populate( $field_id, $user_id );
+	}
+
+	function populate( $field_id, $user_id )  {
+		global $wpdb, $bp;
+
+		$sql = $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_data} WHERE field_id = %d AND user_id = %d", $field_id, $user_id );
+
+		if ( $profiledata = $wpdb->get_row( $sql ) ) {
+			$this->id           = $profiledata->id;
+			$this->user_id      = $profiledata->user_id;
+			$this->field_id     = $profiledata->field_id;
+			$this->value        = stripslashes( $profiledata->value );
+			$this->last_updated = $profiledata->last_updated;
+		} else {
+			// When no row is found, we'll need to set these properties manually
+			$this->field_id	    = $field_id;
+			$this->user_id	    = $user_id;
+		}
+	}
+
+	/**
+	 * exists ()
+	 *
+	 * Check if there is data already for the user.
+	 *
+	 * @global object $wpdb
+	 * @global array $bp
+	 * @return bool
+	 */
+	function exists() {
+		global $wpdb, $bp;
+
+		$retval = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_data} WHERE user_id = %d AND field_id = %d", $this->user_id, $this->field_id ) );
+
+		return apply_filters_ref_array( 'xprofile_data_exists', array( (bool)$retval, &$this ) );
+	}
+
+	/**
+	 * is_valid_field()
+	 *
+	 * Check if this data is for a valid field.
+	 *
+	 * @global object $wpdb
+	 * @global array $bp
+	 * @return bool
+	 */
+	function is_valid_field() {
+		global $wpdb, $bp;
+
+		$retval = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE id = %d", $this->field_id ) );
+
+		return apply_filters_ref_array( 'xprofile_data_is_valid_field', array( (bool)$retval, &$this ) );
+	}
+
+	function save() {
+		global $wpdb, $bp;
+
+		$this->user_id      = apply_filters( 'xprofile_data_user_id_before_save',      $this->user_id,         $this->id );
+		$this->field_id     = apply_filters( 'xprofile_data_field_id_before_save',     $this->field_id,        $this->id );
+		$this->value        = apply_filters( 'xprofile_data_value_before_save',        $this->value,           $this->id );
+		$this->last_updated = apply_filters( 'xprofile_data_last_updated_before_save', bp_core_current_time(), $this->id );
+
+		do_action_ref_array( 'xprofile_data_before_save', array( &$this ) );
+
+		if ( $this->is_valid_field() ) {
+			if ( $this->exists() && !empty( $this->value ) && strlen( trim( $this->value ) ) ) {
+				$result   = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->profile->table_name_data} SET value = %s, last_updated = %s WHERE user_id = %d AND field_id = %d", $this->value, $this->last_updated, $this->user_id, $this->field_id ) );
+
+			} else if ( $this->exists() && empty( $this->value ) ) {
+				// Data removed, delete the entry.
+				$result   = $this->delete();
+
+			} else {
+				$result   = $wpdb->query( $wpdb->prepare("INSERT INTO {$bp->profile->table_name_data} (user_id, field_id, value, last_updated) VALUES (%d, %d, %s, %s)", $this->user_id, $this->field_id, $this->value, $this->last_updated ) );
+				$this->id = $wpdb->insert_id;
+			}
+
+			if ( !$result )
+				return false;
+
+			do_action_ref_array( 'xprofile_data_after_save', array( &$this ) );
+
+			return true;
+		}
+
+		return false;
+	}
+
+	function delete() {
+		global $wpdb, $bp;
+
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_data} WHERE field_id = %d AND user_id = %d", $this->field_id, $this->user_id ) ) )
+			return false;
+
+		return true;
+	}
+
+	/** Static Functions **/
+
+	/**
+	 * BP_XProfile_ProfileData::get_all_for_user()
+	 *
+	 * Get all of the profile information for a specific user.
+	 */
+	function get_all_for_user( $user_id ) {
+		global $wpdb, $bp;
+
+		$results = $wpdb->get_results( $wpdb->prepare( "SELECT g.id as field_group_id, g.name as field_group_name, f.id as field_id, f.name as field_name, f.type as field_type, d.value as field_data, u.user_login, u.user_nicename, u.user_email FROM {$bp->profile->table_name_groups} g LEFT JOIN {$bp->profile->table_name_fields} f ON g.id = f.group_id INNER JOIN {$bp->profile->table_name_data} d ON f.id = d.field_id LEFT JOIN {$wpdb->users} u ON d.user_id = u.ID WHERE d.user_id = %d AND d.value != ''", $user_id ) );
+
+		if ( $results ) {
+			$profile_data['user_login']		= $results[0]->user_login;
+			$profile_data['user_nicename']	= $results[0]->user_nicename;
+			$profile_data['user_email']		= $results[0]->user_email;
+
+			foreach( (array) $results as $field ) {
+				$profile_data[$field->field_name] = array(
+					'field_group_id'   => $field->field_group_id,
+					'field_group_name' => $field->field_group_name,
+					'field_id'         => $field->field_id,
+					'field_type'       => $field->field_type,
+					'field_data'       => $field->field_data
+				);
+			}
+		}
+
+		return $profile_data;
+	}
+
+	function get_value_byid( $field_id, $user_ids = null ) {
+		global $wpdb, $bp;
+
+		if ( !$user_ids )
+			$user_ids = $bp->displayed_user->id;
+
+		if ( is_array( $user_ids ) ) {
+			$user_ids = implode( ',', (array) $user_ids );
+			$data = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, value FROM {$bp->profile->table_name_data} WHERE field_id = %d AND user_id IN ({$user_ids})", $field_id ) );
+		} else {
+			$data = $wpdb->get_var( $wpdb->prepare( "SELECT value FROM {$bp->profile->table_name_data} WHERE field_id = %d AND user_id = %d", $field_id, $user_ids ) );
+		}
+
+		return $data;
+	}
+
+	function get_value_byfieldname( $fields, $user_id = null ) {
+		global $bp, $wpdb;
+
+		if ( !$fields )
+			return false;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		$field_sql = '';
+
+		if ( is_array( $fields ) ) {
+			for ( $i = 0, $count = count( $fields ); $i < $count; ++$i ) {
+				if ( $i == 0 )
+					$field_sql .= $wpdb->prepare( "AND ( f.name = %s ", $fields[$i] );
+				else
+					$field_sql .= $wpdb->prepare( "OR f.name = %s ", $fields[$i] );
+			}
+
+			$field_sql .= ')';
+		} else {
+			$field_sql .= $wpdb->prepare( "AND f.name = %s", $fields );
+		}
+
+		$sql = $wpdb->prepare( "SELECT d.value, f.name FROM {$bp->profile->table_name_data} d, {$bp->profile->table_name_fields} f WHERE d.field_id = f.id AND d.user_id = %d AND f.parent_id = 0 $field_sql", $user_id );
+
+		if ( !$values = $wpdb->get_results( $sql ) )
+			return false;
+
+		$new_values = array();
+
+		if ( is_array( $fields ) ) {
+			for ( $i = 0, $count = count( $values ); $i < $count; ++$i ) {
+				for ( $j = 0; $j < count( $fields ); $j++ ) {
+					if ( $values[$i]->name == $fields[$j] )
+						$new_values[$fields[$j]] = $values[$i]->value;
+					else if ( !array_key_exists( $fields[$j], $new_values ) )
+						$new_values[$fields[$j]] = NULL;
+				}
+			}
+		} else {
+			$new_values = $values[0]->value;
+		}
+
+		return $new_values;
+	}
+
+	function delete_for_field( $field_id ) {
+		global $wpdb, $userdata, $bp;
+
+		if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_data} WHERE field_id = %d", $field_id ) ) )
+			return false;
+
+		return true;
+	}
+
+	function get_last_updated( $user_id ) {
+		global $wpdb, $bp;
+
+		$last_updated = $wpdb->get_var( $wpdb->prepare( "SELECT last_updated FROM {$bp->profile->table_name_data} WHERE user_id = %d ORDER BY last_updated LIMIT 1", $user_id ) );
+
+		return $last_updated;
+	}
+
+	function delete_data_for_user( $user_id ) {
+		global $wpdb, $bp;
+
+		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_data} WHERE user_id = %d", $user_id ) );
+	}
+
+	function get_random( $user_id, $exclude_fullname ) {
+		global $wpdb, $bp;
+
+		if ( $exclude_fullname )
+			$exclude_sql = $wpdb->prepare( " AND pf.id != 1" );
+
+		return $wpdb->get_results( $wpdb->prepare( "SELECT pf.type, pf.name, pd.value FROM {$bp->profile->table_name_data} pd INNER JOIN {$bp->profile->table_name_fields} pf ON pd.field_id = pf.id AND pd.user_id = %d {$exclude_sql} ORDER BY RAND() LIMIT 1", $user_id ) );
+	}
+
+	function get_fullname( $user_id = 0 ) {
+		global $bp;
+
+		if ( !$user_id )
+			$user_id = $bp->displayed_user->id;
+
+		$field_name = bp_xprofile_fullname_field_name();
+
+		$data = xprofile_get_field_data( $field_name, $user_id );
+
+		return $data[$field_name];
+	}
+}
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cssjs.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cssjs.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-cssjs.php	(revision 5057)
@@ -0,0 +1,31 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+function xprofile_add_admin_css() {
+	if ( !empty( $_GET['page'] ) && strpos( $_GET['page'], 'bp-profile-setup' ) !== false ) {
+		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+			wp_enqueue_style( 'xprofile-admin-css', BP_PLUGIN_URL . '/bp-xprofile/admin/css/admin.dev.css', array(), '20110723' );
+		else
+			wp_enqueue_style( 'xprofile-admin-css', BP_PLUGIN_URL . '/bp-xprofile/admin/css/admin.css', array(), '20110723' );
+	}
+}
+add_action( bp_core_admin_hook(), 'xprofile_add_admin_css' );
+
+function xprofile_add_admin_js() {
+	if ( !empty( $_GET['page'] ) && strpos( $_GET['page'], 'bp-profile-setup' ) !== false ) {
+		wp_enqueue_script( 'jquery-ui-core' );
+		wp_enqueue_script( 'jquery-ui-tabs' );
+		wp_enqueue_script( 'jquery-ui-mouse' );
+		wp_enqueue_script( 'jquery-ui-draggable' );
+		wp_enqueue_script( 'jquery-ui-droppable' );
+		wp_enqueue_script( 'jquery-ui-sortable' );
+
+		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG )
+			wp_enqueue_script( 'xprofile-admin-js', BP_PLUGIN_URL . '/bp-xprofile/admin/js/admin.dev.js', array( 'jquery', 'jquery-ui-sortable' ), '20110723' );
+		else
+			wp_enqueue_script( 'xprofile-admin-js', BP_PLUGIN_URL . '/bp-xprofile/admin/js/admin.js', array( 'jquery', 'jquery-ui-sortable' ), '20110723' );
+	}
+}
+add_action( bp_core_admin_hook(), 'xprofile_add_admin_js', 1 );
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-filters.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-filters.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-filters.php	(revision 5057)
@@ -0,0 +1,183 @@
+<?php
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/* Apply WordPress defined filters */
+
+add_filter( 'bp_get_the_profile_group_name',          'wp_filter_kses',       1 );
+add_filter( 'bp_get_the_profile_group_description',   'wp_filter_kses',       1 );
+add_filter( 'bp_get_the_profile_field_value',         'xprofile_filter_kses', 1 );
+add_filter( 'bp_get_the_profile_field_name',          'wp_filter_kses',       1 );
+add_filter( 'bp_get_the_profile_field_edit_value',    'wp_filter_kses',       1 );
+add_filter( 'bp_get_the_profile_field_description',   'wp_filter_kses',       1 );
+
+add_filter( 'bp_get_the_profile_field_value',         'wptexturize'        );
+add_filter( 'bp_get_the_profile_field_value',         'convert_smilies', 2 );
+add_filter( 'bp_get_the_profile_field_value',         'convert_chars'      );
+add_filter( 'bp_get_the_profile_field_value',         'wpautop'            );
+add_filter( 'bp_get_the_profile_field_value',         'make_clickable', 8  );
+add_filter( 'bp_get_the_profile_field_value',         'force_balance_tags' );
+
+add_filter( 'bp_get_the_profile_field_edit_value',    'force_balance_tags' );
+add_filter( 'bp_get_the_profile_field_edit_value',    'esc_html'           );
+
+add_filter( 'bp_get_the_profile_group_name',          'stripslashes' );
+add_filter( 'bp_get_the_profile_group_description',   'stripslashes' );
+add_filter( 'bp_get_the_profile_field_value',         'stripslashes' );
+add_filter( 'bp_get_the_profile_field_edit_value',    'stripslashes' );
+add_filter( 'bp_get_the_profile_field_name',          'stripslashes' );
+add_filter( 'bp_get_the_profile_field_description',   'stripslashes' );
+
+add_filter( 'xprofile_get_field_data',                'wp_filter_kses', 1 );
+add_filter( 'xprofile_field_name_before_save',        'wp_filter_kses', 1 );
+add_filter( 'xprofile_field_description_before_save', 'wp_filter_kses', 1 );
+
+add_filter( 'xprofile_get_field_data',                'force_balance_tags' );
+add_filter( 'xprofile_field_name_before_save',        'force_balance_tags' );
+add_filter( 'xprofile_field_description_before_save', 'force_balance_tags' );
+
+add_filter( 'xprofile_get_field_data',                'stripslashes' );
+
+/* Custom BuddyPress filters */
+
+add_filter( 'bp_get_the_profile_field_value',         'xprofile_filter_format_field_value', 1, 2 );
+add_filter( 'bp_get_the_site_member_profile_data',    'xprofile_filter_format_field_value', 1, 2 );
+add_filter( 'bp_get_the_profile_field_value',         'xprofile_filter_link_profile_data', 9, 2 );
+
+add_filter( 'xprofile_data_value_before_save',        'xprofile_sanitize_data_value_before_save', 1, 2 );
+add_filter( 'xprofile_filtered_data_value_before_save', 'trim', 2 );
+
+/**
+ * xprofile_filter_kses ( $content )
+ *
+ * Run profile field values through kses with filterable allowed tags.
+ *
+ * @param string $content
+ * @return string $content
+ */
+function xprofile_filter_kses( $content ) {
+	global $allowedtags;
+
+	$xprofile_allowedtags             = $allowedtags;
+	$xprofile_allowedtags['a']['rel'] = array();
+
+	$xprofile_allowedtags = apply_filters( 'xprofile_allowed_tags', $xprofile_allowedtags );
+	return wp_kses( $content, $xprofile_allowedtags );
+}
+
+/**
+ * xprofile_sanitize_data_value_before_save ( $field_value, $field_id )
+ *
+ * Safely runs profile field data through kses and force_balance_tags.
+ *
+ * @param string $field_value
+ * @param int $field_id
+ * @param bool $reserialize Whether to reserialize arrays before returning. Defaults to true
+ * @return string
+ */
+function xprofile_sanitize_data_value_before_save ( $field_value, $field_id, $reserialize = true ) {
+
+	// Return if empty
+	if ( empty( $field_value ) )
+		return;
+
+	// Value might be serialized
+	$field_value = maybe_unserialize( $field_value );
+
+	// Filter single value
+	if ( !is_array( $field_value ) ) {
+		$kses_field_value     = xprofile_filter_kses( $field_value );
+		$filtered_field_value = wp_rel_nofollow( force_balance_tags( $kses_field_value ) );
+		$filtered_field_value = apply_filters( 'xprofile_filtered_data_value_before_save', $filtered_field_value, $field_value );
+
+	// Filter each array item independently
+	} else {
+		$filtered_values = array();
+		foreach ( (array)$field_value as $value ) {
+			$kses_field_value       = xprofile_filter_kses( $value );
+			$filtered_value 	= wp_rel_nofollow( force_balance_tags( $kses_field_value ) );
+			$filtered_values[] = apply_filters( 'xprofile_filtered_data_value_before_save', $filtered_value, $value );
+
+		}
+
+		if ( $reserialize )
+			$filtered_field_value = serialize( $filtered_values );
+		else
+			$filtered_field_value = $filtered_values;
+	}
+
+	return $filtered_field_value;
+}
+
+function xprofile_filter_format_field_value( $field_value, $field_type = '' ) {
+	if ( !isset( $field_value ) || empty( $field_value ) )
+		return false;
+
+	if ( 'datebox' == $field_type )
+		$field_value = bp_format_time( $field_value, true, false );
+	else
+		$field_value = str_replace(']]>', ']]&gt;', $field_value );
+
+	return stripslashes( $field_value );
+}
+
+function xprofile_filter_link_profile_data( $field_value, $field_type = 'textbox' ) {
+	if ( 'datebox' == $field_type )
+		return $field_value;
+
+	if ( !strpos( $field_value, ',' ) && ( count( explode( ' ', $field_value ) ) > 5 ) )
+		return $field_value;
+
+	$values = explode( ',', $field_value );
+
+	if ( $values ) {
+		foreach ( (array)$values as $value ) {
+			$value = trim( $value );
+
+			// If the value is a URL, skip it and just make it clickable.
+			if ( preg_match( '@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@', $value ) ) {
+				$new_values[] = make_clickable( $value );
+			} else {
+				if ( count( explode( ' ', $value ) ) > 5 ) {
+					$new_values[] = $value;
+				} else {
+					$new_values[] = '<a href="' . site_url( bp_get_members_root_slug() ) . '/?s=' . strip_tags( $value ) . '" rel="nofollow">' . $value . '</a>';
+				}
+			}
+		}
+
+		$values = implode( ', ', $new_values );
+	}
+
+	return $values;
+}
+
+function xprofile_filter_comments( $comments, $post_id ) {
+	foreach( (array)$comments as $comment ) {
+		if ( $comment->user_id ) {
+			$user_ids[] = $comment->user_id;
+		}
+	}
+
+	if ( empty( $user_ids ) )
+		return $comments;
+
+	if ( $fullnames = BP_XProfile_ProfileData::get_value_byid( 1, $user_ids ) ) {
+		foreach( (array)$fullnames as $user ) {
+			$users[$user->user_id] = trim($user->value);
+		}
+	}
+
+	foreach( (array)$comments as $i => $comment ) {
+		if ( !empty( $comment->user_id ) ) {
+			if ( !empty( $users[$comment->user_id] ) ) {
+				$comments[$i]->comment_author = $users[$comment->user_id];
+			}
+		}
+	}
+
+	return $comments;
+}
+add_filter( 'comments_array', 'xprofile_filter_comments', 10, 2 );
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-functions.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-functions.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-functions.php	(revision 5057)
@@ -0,0 +1,597 @@
+<?php
+/********************************************************************************
+ * Business Functions
+ *
+ * Business functions are where all the magic happens in BuddyPress. They will
+ * handle the actual saving or manipulation of information. Usually they will
+ * hand off to a database class for data access, then return
+ * true or false on success or failure.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/*** Field Group Management **************************************************/
+
+function xprofile_insert_field_group( $args = '' ) {
+	$defaults = array(
+		'field_group_id' => false,
+		'name'           => false,
+		'description'    => '',
+		'can_delete'     => true
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	if ( !$name )
+		return false;
+
+	$field_group              = new BP_XProfile_Group( $field_group_id );
+	$field_group->name        = $name;
+	$field_group->description = $description;
+	$field_group->can_delete  = $can_delete;
+
+	return $field_group->save();
+}
+
+function xprofile_get_field_group( $field_group_id ) {
+	$field_group = new BP_XProfile_Group( $field_group_id );
+
+	if ( empty( $field_group->id ) )
+		return false;
+
+	return $field_group;
+}
+
+function xprofile_delete_field_group( $field_group_id ) {
+	$field_group = new BP_XProfile_Group( $field_group_id );
+	return $field_group->delete();
+}
+
+function xprofile_update_field_group_position( $field_group_id, $position ) {
+	return BP_XProfile_Group::update_position( $field_group_id, $position );
+}
+
+
+/*** Field Management *********************************************************/
+
+function xprofile_insert_field( $args = '' ) {
+	global $bp;
+
+	extract( $args );
+
+	/**
+	 * Possible parameters (pass as assoc array):
+	 *	'field_id'
+	 *	'field_group_id'
+	 *	'parent_id'
+	 *	'type'
+	 *	'name'
+	 *	'description'
+	 *	'is_required'
+	 *	'can_delete'
+	 *	'field_order'
+	 *	'order_by'
+	 *	'is_default_option'
+	 *	'option_order'
+	 */
+
+	// Check we have the minimum details
+	if ( !$field_group_id )
+		return false;
+
+	// Check this is a valid field type
+	if ( !in_array( $type, (array) $bp->profile->field_types ) )
+		return false;
+
+	// Instantiate a new field object
+	if ( $field_id )
+		$field = new BP_XProfile_Field( $field_id );
+	else
+		$field = new BP_XProfile_Field;
+
+	$field->group_id = $field_group_id;
+
+	if ( !empty( $parent_id ) )
+		$field->parent_id = $parent_id;
+
+	if ( !empty( $type ) )
+		$field->type = $type;
+
+	if ( !empty( $name ) )
+		$field->name = $name;
+
+	if ( !empty( $description ) )
+		$field->description = $description;
+
+	if ( !empty( $is_required ) )
+		$field->is_required = $is_required;
+
+	if ( !empty( $can_delete ) )
+		$field->can_delete = $can_delete;
+
+	if ( !empty( $field_order ) )
+		$field->field_order = $field_order;
+
+	if ( !empty( $order_by ) )
+		$field->order_by = $order_by;
+
+	if ( !empty( $is_default_option ) )
+		$field->is_default_option = $is_default_option;
+
+	if ( !empty( $option_order ) )
+		$field->option_order = $option_order;
+
+	return $field->save();
+}
+
+function xprofile_get_field( $field_id ) {
+	return new BP_XProfile_Field( $field_id );
+}
+
+function xprofile_delete_field( $field_id ) {
+	$field = new BP_XProfile_Field( $field_id );
+	return $field->delete();
+}
+
+
+/*** Field Data Management *****************************************************/
+
+/**
+ * Fetches profile data for a specific field for the user.
+ *
+ * When the field value is serialized, this function unserializes and filters each item in the array
+ * that results.
+ *
+ * @package BuddyPress Core
+ * @param mixed $field The ID of the field, or the $name of the field.
+ * @param int $user_id The ID of the user
+ * @global object $bp Global BuddyPress settings object
+ * @uses BP_XProfile_ProfileData::get_value_byid() Fetches the value based on the params passed.
+ * @return mixed The profile field data.
+ */
+function xprofile_get_field_data( $field, $user_id = 0 ) {
+	global $bp;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->displayed_user->id;
+
+	if ( empty( $user_id ) )
+		return false;
+
+	if ( is_numeric( $field ) )
+		$field_id = $field;
+	else
+		$field_id = xprofile_get_field_id_from_name( $field );
+
+	if ( empty( $field_id ) )
+		return false;
+
+	$values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field_id, $user_id ) );
+
+	if ( is_array( $values ) ) {
+		$data = array();
+		foreach( (array)$values as $value ) {
+			$data[] = apply_filters( 'xprofile_get_field_data', $value, $field_id, $user_id );
+		}
+	} else {
+		$data = apply_filters( 'xprofile_get_field_data', $values, $field_id, $user_id );
+	}
+
+	return $data;
+}
+
+/**
+ * A simple function to set profile data for a specific field for a specific user.
+ *
+ * @package BuddyPress Core
+ * @param $field The ID of the field, or the $name of the field.
+ * @param $user_id The ID of the user
+ * @param $value The value for the field you want to set for the user.
+ * @global object $bp Global BuddyPress settings object
+ * @uses xprofile_get_field_id_from_name() Gets the ID for the field based on the name.
+ * @return true on success, false on failure.
+ */
+function xprofile_set_field_data( $field, $user_id, $value, $is_required = false ) {
+
+	if ( is_numeric( $field ) )
+		$field_id = $field;
+	else
+		$field_id = xprofile_get_field_id_from_name( $field );
+
+	if ( empty( $field_id ) )
+		return false;
+
+	if ( $is_required && ( empty( $value ) || !is_array( $value ) && !strlen( trim( $value ) ) ) )
+		return false;
+
+	$field = new BP_XProfile_Field( $field_id );
+
+	// If the value is empty, then delete any field data that exists, unless the field is of a
+	// type where null values are semantically meaningful
+	if ( empty( $value ) && 'checkbox' != $field->type && 'multiselectbox' != $field->type ) {
+		xprofile_delete_field_data( $field_id, $user_id );
+		return true;
+	}
+
+	// Check the value is an acceptable value
+	if ( 'checkbox' == $field->type || 'radio' == $field->type || 'selectbox' == $field->type || 'multiselectbox' == $field->type ) {
+		$options = $field->get_children();
+
+		foreach( $options as $option )
+			$possible_values[] = $option->name;
+
+		if ( is_array( $value ) ) {
+			foreach( $value as $i => $single ) {
+				if ( !in_array( $single, (array)$possible_values ) ) {
+					unset( $value[$i] );
+				}
+			}
+
+			// Reset the keys by merging with an empty array
+			$value = array_merge( array(), $value );
+		} else {
+			if ( !in_array( $value, (array)$possible_values ) ) {
+				return false;
+			}
+		}
+	}
+
+	$field           = new BP_XProfile_ProfileData();
+	$field->field_id = $field_id;
+	$field->user_id  = $user_id;
+	$field->value    = maybe_serialize( $value );
+
+	return $field->save();
+}
+
+function xprofile_delete_field_data( $field, $user_id ) {
+	if ( is_numeric( $field ) )
+		$field_id = $field;
+	else
+		$field_id = xprofile_get_field_id_from_name( $field );
+
+	if ( empty( $field_id ) || empty( $user_id ) )
+		return false;
+
+	$field = new BP_XProfile_ProfileData( $field_id, $user_id );
+	return $field->delete();
+}
+
+function xprofile_check_is_required_field( $field_id ) {
+	$field = new BP_Xprofile_Field( $field_id );
+
+	if ( (int)$field->is_required )
+		return true;
+
+	return false;
+}
+
+/**
+ * Returns the ID for the field based on the field name.
+ *
+ * @package BuddyPress Core
+ * @param $field_name The name of the field to get the ID for.
+ * @return int $field_id on success, false on failure.
+ */
+function xprofile_get_field_id_from_name( $field_name ) {
+	return BP_Xprofile_Field::get_id_from_name( $field_name );
+}
+
+/**
+ * Fetches a random piece of profile data for the user.
+ *
+ * @package BuddyPress Core
+ * @param $user_id User ID of the user to get random data for
+ * @param $exclude_fullname whether or not to exclude the full name field as random data.
+ * @global object $bp Global BuddyPress settings object
+ * @global $wpdb WordPress DB access object.
+ * @global $current_user WordPress global variable containing current logged in user information
+ * @uses xprofile_format_profile_field() Formats profile field data so it is suitable for display.
+ * @return $field_data The fetched random data for the user.
+ */
+function xprofile_get_random_profile_data( $user_id, $exclude_fullname = true ) {
+	$field_data           = BP_XProfile_ProfileData::get_random( $user_id, $exclude_fullname );
+
+	if ( !$field_data )
+		return false;
+
+	$field_data[0]->value = xprofile_format_profile_field( $field_data[0]->type, $field_data[0]->value );
+
+	if ( !$field_data[0]->value || empty( $field_data[0]->value ) )
+		return false;
+
+	return apply_filters( 'xprofile_get_random_profile_data', $field_data );
+}
+
+/**
+ * Formats a profile field according to its type. [ TODO: Should really be moved to filters ]
+ *
+ * @package BuddyPress Core
+ * @param $field_type The type of field: datebox, selectbox, textbox etc
+ * @param $field_value The actual value
+ * @uses bp_format_time() Formats a time value based on the WordPress date format setting
+ * @return $field_value The formatted value
+ */
+function xprofile_format_profile_field( $field_type, $field_value ) {
+	if ( !isset( $field_value ) || empty( $field_value ) )
+		return false;
+
+	$field_value = bp_unserialize_profile_field( $field_value );
+
+	if ( 'datebox' == $field_type ) {
+		$field_value = bp_format_time( $field_value, true );
+	} else {
+		$content = $field_value;
+		$field_value = str_replace( ']]>', ']]&gt;', $content );
+	}
+
+	return stripslashes_deep( $field_value );
+}
+
+function xprofile_update_field_position( $field_id, $position, $field_group_id ) {
+	return BP_XProfile_Field::update_position( $field_id, $position, $field_group_id );
+}
+
+/**
+ * Setup the avatar upload directory for a user.
+ *
+ * @package BuddyPress Core
+ * @param $directory The root directory name
+ * @param $user_id The user ID.
+ * @return array() containing the path and URL plus some other settings.
+ */
+function xprofile_avatar_upload_dir( $directory = false, $user_id = 0 ) {
+	global $bp;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->displayed_user->id;
+
+	if ( empty( $directory ) )
+		$directory = 'avatars';
+
+	$path    = bp_core_avatar_upload_path() . '/avatars/' . $user_id;
+	$newbdir = $path;
+
+	if ( !file_exists( $path ) )
+		@wp_mkdir_p( $path );
+
+	$newurl    = bp_core_avatar_url() . '/avatars/' . $user_id;
+	$newburl   = $newurl;
+	$newsubdir = '/avatars/' . $user_id;
+
+	return apply_filters( 'xprofile_avatar_upload_dir', array(
+		'path'    => $path,
+		'url'     => $newurl,
+		'subdir'  => $newsubdir,
+		'basedir' => $newbdir,
+		'baseurl' => $newburl,
+		'error'   => false
+	) );
+}
+
+/**
+ * Syncs Xprofile data to the standard built in WordPress profile data.
+ *
+ * @package BuddyPress Core
+ */
+function xprofile_sync_wp_profile( $user_id = 0 ) {
+	global $bp, $wpdb;
+
+	if ( !empty( $bp->site_options['bp-disable-profile-sync'] ) && (int)$bp->site_options['bp-disable-profile-sync'] )
+		return true;
+
+	if ( empty( $user_id ) )
+		$user_id = $bp->loggedin_user->id;
+
+	if ( empty( $user_id ) )
+		return false;
+
+	$fullname = xprofile_get_field_data( bp_xprofile_fullname_field_name(), $user_id );
+	$space    = strpos( $fullname, ' ' );
+
+	if ( false === $space ) {
+		$firstname = $fullname;
+		$lastname = '';
+	} else {
+		$firstname = substr( $fullname, 0, $space );
+		$lastname = trim( substr( $fullname, $space, strlen( $fullname ) ) );
+	}
+
+	update_user_meta( $user_id, 'nickname',   $fullname  );
+	update_user_meta( $user_id, 'first_name', $firstname );
+	update_user_meta( $user_id, 'last_name',  $lastname  );
+
+	$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->users} SET display_name = %s WHERE ID = %d", $fullname, $user_id ) );
+}
+add_action( 'xprofile_updated_profile', 'xprofile_sync_wp_profile' );
+add_action( 'bp_core_signup_user', 'xprofile_sync_wp_profile' );
+
+
+/**
+ * Syncs the standard built in WordPress profile data to XProfile.
+ *
+ * @since 1.2.4
+ * @package BuddyPress Core
+ */
+function xprofile_sync_bp_profile( &$errors, $update, &$user ) {
+	global $bp;
+
+	if ( ( !empty( $bp->site_options['bp-disable-profile-sync'] ) && (int)$bp->site_options['bp-disable-profile-sync'] ) || !$update || $errors->get_error_codes() )
+		return;
+
+	xprofile_set_field_data( bp_xprofile_fullname_field_name(), $user->ID, $user->display_name );
+}
+add_action( 'user_profile_update_errors', 'xprofile_sync_bp_profile', 10, 3 );
+
+
+/**
+ * When a user is deleted, we need to clean up the database and remove all the
+ * profile data from each table. Also we need to clean anything up in the usermeta table
+ * that this component uses.
+ *
+ * @package BuddyPress XProfile
+ * @param $user_id The ID of the deleted user
+ * @uses get_user_meta() Get a user meta value based on meta key from wp_usermeta
+ * @uses delete_user_meta() Delete user meta value based on meta key from wp_usermeta
+ * @uses delete_data_for_user() Removes all profile data from the xprofile tables for the user
+ */
+function xprofile_remove_data( $user_id ) {
+	BP_XProfile_ProfileData::delete_data_for_user( $user_id );
+
+	// delete any avatar files.
+	@unlink( get_user_meta( $user_id, 'bp_core_avatar_v1_path', true ) );
+	@unlink( get_user_meta( $user_id, 'bp_core_avatar_v2_path', true ) );
+
+	// unset the usermeta for avatars from the usermeta table.
+	delete_user_meta( $user_id, 'bp_core_avatar_v1'      );
+	delete_user_meta( $user_id, 'bp_core_avatar_v1_path' );
+	delete_user_meta( $user_id, 'bp_core_avatar_v2'      );
+	delete_user_meta( $user_id, 'bp_core_avatar_v2_path' );
+}
+add_action( 'wpmu_delete_user',  'xprofile_remove_data' );
+add_action( 'delete_user',       'xprofile_remove_data' );
+add_action( 'bp_make_spam_user', 'xprofile_remove_data' );
+
+/*** XProfile Meta ****************************************************/
+
+function bp_xprofile_delete_meta( $object_id, $object_type, $meta_key = false, $meta_value = false ) {
+	global $wpdb, $bp;
+
+	$object_id = (int) $object_id;
+
+	if ( !$object_id )
+		return false;
+
+	if ( !isset( $object_type ) )
+		return false;
+
+	if ( !in_array( $object_type, array( 'group', 'field', 'data' ) ) )
+		return false;
+
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_array( $meta_value ) || is_object( $meta_value ) )
+		$meta_value = serialize( $meta_value );
+
+	$meta_value = trim( $meta_value );
+
+	if ( !$meta_key )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s", $object_id, $object_type ) );
+	else if ( $meta_value )
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s AND meta_key = %s AND meta_value = %s", $object_id, $object_type, $meta_key, $meta_value ) );
+	else
+		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s AND meta_key = %s", $object_id, $object_type, $meta_key ) );
+
+	// Delete the cached object
+	wp_cache_delete( 'bp_xprofile_meta_' . $object_type . '_' . $object_id . '_' . $meta_key, 'bp' );
+
+	return true;
+}
+
+function bp_xprofile_get_meta( $object_id, $object_type, $meta_key = '') {
+	global $wpdb, $bp;
+
+	$object_id = (int) $object_id;
+
+	if ( !$object_id )
+		return false;
+
+	if ( !isset( $object_type ) )
+		return false;
+
+	if ( !in_array( $object_type, array( 'group', 'field', 'data' ) ) )
+		return false;
+
+	if ( !empty( $meta_key ) ) {
+		$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+		if ( !$metas = wp_cache_get( 'bp_xprofile_meta_' . $object_type . '_' . $object_id . '_' . $meta_key, 'bp' ) ) {
+			$metas = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s AND meta_key = %s", $object_id, $object_type, $meta_key ) );
+			wp_cache_set( 'bp_xprofile_meta_' . $object_type . '_' . $object_id . '_' . $meta_key, $metas, 'bp' );
+		}
+	} else {
+		$metas = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s", $object_id, $object_type ) );
+	}
+
+	if ( empty( $metas ) ) {
+		if ( empty( $meta_key ) ) {
+			return array();
+		} else {
+			return '';
+		}
+	}
+
+	$metas = array_map( 'maybe_unserialize', (array)$metas );
+
+	if ( 1 == count( $metas ) )
+		return $metas[0];
+	else
+		return $metas;
+}
+
+function bp_xprofile_update_meta( $object_id, $object_type, $meta_key, $meta_value ) {
+	global $wpdb, $bp;
+
+	$object_id = (int) $object_id;
+
+	if ( empty( $object_id ) )
+		return false;
+
+	if ( !isset( $object_type ) )
+		return false;
+
+	if ( !in_array( $object_type, array( 'group', 'field', 'data' ) ) )
+		return false;
+
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
+
+	if ( is_string( $meta_value ) )
+		$meta_value = stripslashes( $wpdb->escape( $meta_value ) );
+
+	$meta_value = maybe_serialize( $meta_value );
+
+	if ( empty( $meta_value ) )
+		return bp_xprofile_delete_meta( $object_id, $object_type, $meta_key );
+
+	$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM " . $bp->profile->table_name_meta . " WHERE object_id = %d AND object_type = %s AND meta_key = %s", $object_id, $object_type, $meta_key ) );
+
+	if ( empty( $cur ) )
+		$wpdb->query( $wpdb->prepare( "INSERT INTO " . $bp->profile->table_name_meta . " ( object_id, object_type, meta_key, meta_value ) VALUES ( %d, %s, %s, %s )", $object_id, $object_type,  $meta_key, $meta_value ) );
+	else if ( $cur->meta_value != $meta_value )
+		$wpdb->query( $wpdb->prepare( "UPDATE " . $bp->profile->table_name_meta . " SET meta_value = %s WHERE object_id = %d AND object_type = %s AND meta_key = %s", $meta_value, $object_id, $object_type, $meta_key ) );
+	else
+		return false;
+
+	// Update the cached object and recache
+	wp_cache_set( 'bp_xprofile_meta_' . $object_type . '_' . $object_id . '_' . $meta_key, $meta_value, 'bp' );
+
+	return true;
+}
+
+function bp_xprofile_update_fieldgroup_meta( $field_group_id, $meta_key, $meta_value ) {
+	return bp_xprofile_update_meta( $field_group_id, 'group', $meta_key, $meta_value );
+}
+
+function bp_xprofile_update_field_meta( $field_id, $meta_key, $meta_value ) {
+	return bp_xprofile_update_meta( $field_id, 'field', $meta_key, $meta_value );
+}
+
+function bp_xprofile_update_fielddata_meta( $field_data_id, $meta_key, $meta_value ) {
+	return bp_xprofile_update_meta( $field_data_id, 'data', $meta_key, $meta_value );
+}
+
+/**
+ * Return the field name for the Full Name xprofile field
+ *
+ * @package BuddyPress
+ * @since 1.5
+ *
+ * @return str The field name
+ */
+function bp_xprofile_fullname_field_name() {
+	return apply_filters( 'bp_xprofile_fullname_field_name', BP_XPROFILE_FULLNAME_FIELD_NAME );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-loader.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-loader.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-loader.php	(revision 5057)
@@ -0,0 +1,234 @@
+<?php
+/**
+ * BuddyPress XProfile Loader
+ *
+ * An extended profile component for users. This allows site admins to create
+ * groups of fields for users to enter information about themselves.
+ *
+ * @package BuddyPress
+ * @subpackage XProfile Core
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+class BP_XProfile_Component extends BP_Component {
+
+	/**
+	 * Start the xprofile component creation process
+	 *
+	 * @since 1.5
+	 */
+	function __construct() {
+		parent::start(
+			'xprofile',
+			__( 'Extended Profiles', 'buddypress' ),
+			BP_PLUGIN_DIR
+		);
+	}
+
+	/**
+	 * Include files
+	 */
+	function includes() {
+		$includes = array(
+			'cssjs',
+			'cache',
+			'actions',
+			'activity',
+			'screens',
+			'classes',
+			'filters',
+			'template',
+			'buddybar',
+			'functions',
+		);
+
+		if ( is_admin() )
+			$includes[] = 'admin';
+
+		parent::includes( $includes );
+	}
+
+	/**
+	 * Setup globals
+	 *
+	 * The BP_XPROFILE_SLUG constant is deprecated, and only used here for
+	 * backwards compatibility.
+	 *
+	 * @since 1.5
+	 * @global obj $bp
+	 */
+	function setup_globals() {
+		global $bp;
+
+		// Define a slug, if necessary
+		if ( !defined( 'BP_XPROFILE_SLUG' ) )
+			define( 'BP_XPROFILE_SLUG', 'profile' );
+
+		// Assign the base group and fullname field names to constants to use
+		// in SQL statements
+		define ( 'BP_XPROFILE_BASE_GROUP_NAME',     stripslashes( $bp->site_options['bp-xprofile-base-group-name']     ) );
+		define ( 'BP_XPROFILE_FULLNAME_FIELD_NAME', stripslashes( $bp->site_options['bp-xprofile-fullname-field-name'] ) );
+
+		// Set the support field type ids
+		$this->field_types = apply_filters( 'xprofile_field_types', array(
+			'textbox',
+			'textarea',
+			'radio',
+			'checkbox',
+			'selectbox',
+			'multiselectbox',
+			'datebox'
+		) );
+
+		// Tables
+		$global_tables = array(
+			'table_name_data'   => $bp->table_prefix . 'bp_xprofile_data',
+			'table_name_groups' => $bp->table_prefix . 'bp_xprofile_groups',
+			'table_name_fields' => $bp->table_prefix . 'bp_xprofile_fields',
+			'table_name_meta'   => $bp->table_prefix . 'bp_xprofile_meta',
+		);
+
+		$globals = array(
+			'slug'                  => BP_XPROFILE_SLUG,
+			'has_directory'         => false,
+			'notification_callback' => 'xprofile_format_notifications',
+			'global_tables'         => $global_tables
+		);
+
+		parent::setup_globals( $globals );
+	}
+
+	/**
+	 * Setup BuddyBar navigation
+	 *
+	 * @global obj $bp
+	 */
+	function setup_nav() {
+		global $bp;
+
+		// Add 'Profile' to the main navigation
+		$main_nav = array(
+			'name'                => __( 'Profile', 'buddypress' ),
+			'slug'                => $this->slug,
+			'position'            => 20,
+			'screen_function'     => 'xprofile_screen_display_profile',
+			'default_subnav_slug' => 'public',
+			'item_css_id'         => $this->id
+		);
+
+		$profile_link = trailingslashit( $bp->loggedin_user->domain . $this->slug );
+
+		// Add the subnav items to the profile
+		$sub_nav[] = array(
+			'name'            => __( 'Public', 'buddypress' ),
+			'slug'            => 'public',
+			'parent_url'      => $profile_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'xprofile_screen_display_profile',
+			'position'        => 10
+		);
+
+		// Edit Profile
+		$sub_nav[] = array(
+			'name'            => __( 'Edit', 'buddypress' ),
+			'slug'            => 'edit',
+			'parent_url'      => $profile_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'xprofile_screen_edit_profile',
+			'position'        => 20
+		);
+
+		// Change Avatar
+		$sub_nav[] = array(
+			'name'            => __( 'Change Avatar', 'buddypress' ),
+			'slug'            => 'change-avatar',
+			'parent_url'      => $profile_link,
+			'parent_slug'     => $this->slug,
+			'screen_function' => 'xprofile_screen_change_avatar',
+			'position'        => 30
+		);
+
+		parent::setup_nav( $main_nav, $sub_nav );
+	}
+
+	/**
+	 * Set up the admin bar
+	 *
+	 * @global obj $bp
+	 */
+	function setup_admin_bar() {
+		global $bp;
+
+		// Prevent debug notices
+		$wp_admin_nav = array();
+
+		// Menus for logged in user
+		if ( is_user_logged_in() ) {
+
+			// Profile link
+			$profile_link = trailingslashit( $bp->loggedin_user->domain . $this->slug );
+
+			// Add the "Profile" sub menu
+			$wp_admin_nav[] = array(
+				'parent' => $bp->my_account_menu_id,
+				'id'     => 'my-account-' . $this->id,
+				'title'  => __( 'Profile', 'buddypress' ),
+				'href'   => trailingslashit( $profile_link )
+			);
+
+			// View Profile
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'View', 'buddypress' ),
+				'href'   => trailingslashit( $profile_link . 'public' )
+			);
+
+			// Edit Profile
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Edit', 'buddypress' ),
+				'href'   => trailingslashit( $profile_link . 'edit' )
+			);
+
+			// Edit Profile
+			$wp_admin_nav[] = array(
+				'parent' => 'my-account-' . $this->id,
+				'title'  => __( 'Change Avatar', 'buddypress' ),
+				'href'   => trailingslashit( $profile_link . 'change-avatar' )
+			);
+
+		}
+
+		parent::setup_admin_bar( $wp_admin_nav );
+	}
+
+	/**
+	 * Sets up the title for pages and <title>
+	 *
+	 * @global obj $bp
+	 */
+	function setup_title() {
+		global $bp;
+
+		if ( bp_is_profile_component() ) {
+			if ( bp_is_my_profile() ) {
+				$bp->bp_options_title = __( 'My Profile', 'buddypress' );
+			} else {
+				$bp->bp_options_avatar = bp_core_fetch_avatar( array(
+					'item_id' => $bp->displayed_user->id,
+					'type'    => 'thumb'
+				) );
+				$bp->bp_options_title = $bp->displayed_user->fullname;
+			}
+		}
+
+		parent::setup_title();
+	}
+}
+// Create the xprofile component
+if ( !isset( $bp->profile->id ) )
+	$bp->profile = new BP_XProfile_Component();
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-screens.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-screens.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-screens.php	(revision 5057)
@@ -0,0 +1,177 @@
+<?php
+/*******************************************************************************
+ * Screen functions are the controllers of BuddyPress. They will execute when
+ * their specific URL is caught. They will first save or manipulate data using
+ * business functions, then pass on the user to a template file.
+ */
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Handles the display of the profile page by loading the correct template file.
+ *
+ * @package BuddyPress XProfile
+ * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
+ */
+function xprofile_screen_display_profile() {
+	$new = isset( $_GET['new'] ) ? $_GET['new'] : '';
+
+	do_action( 'xprofile_screen_display_profile', $new );
+	bp_core_load_template( apply_filters( 'xprofile_template_display_profile', 'members/single/home' ) );
+}
+
+/**
+ * Handles the display of the profile edit page by loading the correct template file.
+ * Also checks to make sure this can only be accessed for the logged in users profile.
+ *
+ * @package BuddyPress XProfile
+ * @uses bp_is_my_profile() Checks to make sure the current user being viewed equals the logged in user
+ * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
+ */
+function xprofile_screen_edit_profile() {
+	global $bp;
+
+	if ( !bp_is_my_profile() && !is_super_admin() )
+		return false;
+
+	// Make sure a group is set.
+	if ( !bp_action_variable( 1 ) )
+		bp_core_redirect( bp_displayed_user_domain() . $bp->profile->slug . '/edit/group/1' );
+
+	// Check the field group exists
+	if ( !bp_is_action_variable( 'group' ) || !xprofile_get_field_group( bp_action_variable( 1 ) ) ) {
+		bp_do_404();
+		return;
+	}
+
+	// Check to see if any new information has been submitted
+	if ( isset( $_POST['field_ids'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'bp_xprofile_edit' );
+
+		// Check we have field ID's
+		if ( empty( $_POST['field_ids'] ) )
+			bp_core_redirect( trailingslashit( $bp->displayed_user->domain . $bp->profile->slug . '/edit/group/' . bp_action_variable( 1 ) ) );
+
+		// Explode the posted field IDs into an array so we know which
+		// fields have been submitted
+		$posted_field_ids = explode( ',', $_POST['field_ids'] );
+		$is_required      = array();
+
+		// Loop through the posted fields formatting any datebox values
+		// then validate the field
+		foreach ( (array)$posted_field_ids as $field_id ) {
+			if ( !isset( $_POST['field_' . $field_id] ) ) {
+
+				if ( !empty( $_POST['field_' . $field_id . '_day'] ) && is_numeric( $_POST['field_' . $field_id . '_day'] ) ) {
+					// Concatenate the values
+					$date_value =   $_POST['field_' . $field_id . '_day'] . ' ' . $_POST['field_' . $field_id . '_month'] . ' ' . $_POST['field_' . $field_id . '_year'];
+
+					// Turn the concatenated value into a timestamp
+					$_POST['field_' . $field_id] = strtotime( $date_value );
+				}
+
+			}
+
+			$is_required[$field_id] = xprofile_check_is_required_field( $field_id );
+			if ( $is_required[$field_id] && empty( $_POST['field_' . $field_id] ) )
+				$errors = true;
+		}
+
+		// There are errors
+		if ( !empty( $errors ) ) {
+			bp_core_add_message( __( 'Please make sure you fill in all required fields in this profile field group before saving.', 'buddypress' ), 'error' );
+
+		// No errors
+		} else {
+			// Reset the errors var
+			$errors = false;
+
+			// Now we've checked for required fields, lets save the values.
+			foreach ( (array)$posted_field_ids as $field_id ) {
+
+				// Certain types of fields (checkboxes, multiselects) may come through empty. Save them as an empty array so that they don't get overwritten by the default on the next edit.
+				if ( empty( $_POST['field_' . $field_id] ) )
+					$value = array();
+				else
+					$value = $_POST['field_' . $field_id];
+
+				if ( !xprofile_set_field_data( $field_id, $bp->displayed_user->id, $value, $is_required[$field_id] ) )
+					$errors = true;
+				else
+					do_action( 'xprofile_profile_field_data_updated', $field_id, $value );
+			}
+
+			do_action( 'xprofile_updated_profile', $bp->displayed_user->id, $posted_field_ids, $errors );
+
+			// Set the feedback messages
+			if ( $errors )
+				bp_core_add_message( __( 'There was a problem updating some of your profile information, please try again.', 'buddypress' ), 'error' );
+			else
+				bp_core_add_message( __( 'Changes saved.', 'buddypress' ) );
+
+			// Redirect back to the edit screen to display the updates and message
+			bp_core_redirect( trailingslashit( bp_displayed_user_domain() . $bp->profile->slug . '/edit/group/' . bp_action_variable( 1 ) ) );
+		}
+	}
+
+	do_action( 'xprofile_screen_edit_profile' );
+	bp_core_load_template( apply_filters( 'xprofile_template_edit_profile', 'members/single/home' ) );
+}
+
+/**
+ * Handles the uploading and cropping of a user avatar. Displays the change avatar page.
+ *
+ * @package BuddyPress XProfile
+ * @uses bp_is_my_profile() Checks to make sure the current user being viewed equals the logged in user
+ * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
+ */
+function xprofile_screen_change_avatar() {
+	global $bp;
+
+	if ( !bp_is_my_profile() && !is_super_admin() )
+		return false;
+
+	if ( bp_action_variables() ) {
+		bp_do_404();
+		return;
+	}
+
+	$bp->avatar_admin->step = 'upload-image';
+
+	if ( !empty( $_FILES ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'bp_avatar_upload' );
+
+		// Pass the file to the avatar upload handler
+		if ( bp_core_avatar_handle_upload( $_FILES, 'xprofile_avatar_upload_dir' ) ) {
+			$bp->avatar_admin->step = 'crop-image';
+
+			// Make sure we include the jQuery jCrop file for image cropping
+			add_action( 'wp_print_scripts', 'bp_core_add_jquery_cropper' );
+		}
+	}
+
+	// If the image cropping is done, crop the image and save a full/thumb version
+	if ( isset( $_POST['avatar-crop-submit'] ) ) {
+
+		// Check the nonce
+		check_admin_referer( 'bp_avatar_cropstore' );
+
+		if ( !bp_core_avatar_handle_crop( array( 'item_id' => $bp->displayed_user->id, 'original_file' => $_POST['image_src'], 'crop_x' => $_POST['x'], 'crop_y' => $_POST['y'], 'crop_w' => $_POST['w'], 'crop_h' => $_POST['h'] ) ) )
+			bp_core_add_message( __( 'There was a problem cropping your avatar, please try uploading it again', 'buddypress' ), 'error' );
+		else {
+			bp_core_add_message( __( 'Your new avatar was uploaded successfully!', 'buddypress' ) );
+			do_action( 'xprofile_avatar_uploaded' );
+		}
+	}
+
+	do_action( 'xprofile_screen_change_avatar' );
+
+	bp_core_load_template( apply_filters( 'xprofile_template_change_avatar', 'members/single/home' ) );
+}
+
+?>
Index: /tags/1.5-beta-3/bp-xprofile/bp-xprofile-template.php
===================================================================
--- /tags/1.5-beta-3/bp-xprofile/bp-xprofile-template.php	(revision 5057)
+++ /tags/1.5-beta-3/bp-xprofile/bp-xprofile-template.php	(revision 5057)
@@ -0,0 +1,797 @@
+<?php
+/***************************************************************************
+ * XProfile Data Display Template Tags
+ **/
+
+// Exit if accessed directly
+if ( !defined( 'ABSPATH' ) ) exit;
+
+Class BP_XProfile_Data_Template {
+	var $current_group = -1;
+	var $group_count;
+	var $groups;
+	var $group;
+
+	var $current_field = -1;
+	var $field_count;
+	var $field_has_data;
+	var $field;
+
+	var $in_the_loop;
+	var $user_id;
+
+	function bp_xprofile_data_template( $user_id, $profile_group_id, $hide_empty_groups = false, $fetch_fields = false, $fetch_field_data = false, $exclude_groups = false, $exclude_fields = false, $hide_empty_fields = false ) {
+		$this->__construct( $user_id, $profile_group_id, $hide_empty_groups, $fetch_fields, $fetch_field_data, $exclude_groups, $exclude_fields, $hide_empty_fields );
+	}
+
+	function __construct( $user_id, $profile_group_id, $hide_empty_groups = false, $fetch_fields = false, $fetch_field_data = false, $exclude_groups = false, $exclude_fields = false, $hide_empty_fields = false ) {
+		$this->groups = BP_XProfile_Group::get( array(
+			'profile_group_id'  => $profile_group_id,
+			'user_id'           => $user_id,
+			'hide_empty_groups' => $hide_empty_groups,
+			'hide_empty_fields' => $hide_empty_fields,
+			'fetch_fields'      => $fetch_fields,
+			'fetch_field_data'  => $fetch_field_data,
+			'exclude_groups'    => $exclude_groups,
+			'exclude_fields'    => $exclude_fields
+		) );
+
+		$this->group_count = count($this->groups);
+		$this->user_id = $user_id;
+	}
+
+	function has_groups() {
+		if ( $this->group_count )
+			return true;
+
+		return false;
+	}
+
+	function next_group() {
+		$this->current_group++;
+
+		$this->group         = $this->groups[$this->current_group];
+		$this->group->fields = apply_filters( 'xprofile_group_fields', $this->group->fields, $this->group->id );
+		$this->field_count   = count( $this->group->fields );
+
+		return $this->group;
+	}
+
+	function rewind_groups() {
+		$this->current_group = -1;
+		if ( $this->group_count > 0 ) {
+			$this->group = $this->groups[0];
+		}
+	}
+
+	function profile_groups() {
+		if ( $this->current_group + 1 < $this->group_count ) {
+			return true;
+		} elseif ( $this->current_group + 1 == $this->group_count ) {
+			do_action('xprofile_template_loop_end');
+			// Do some cleaning up after the loop
+			$this->rewind_groups();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	function the_profile_group() {
+		global $group;
+
+		$this->in_the_loop = true;
+		$group = $this->next_group();
+
+		if ( 0 == $this->current_group ) // loop has just started
+			do_action('xprofile_template_loop_start');
+	}
+
+	/**** FIELDS ****/
+
+	function next_field() {
+		$this->current_field++;
+
+		$this->field = $this->group->fields[$this->current_field];
+		return $this->field;
+	}
+
+	function rewind_fields() {
+		$this->current_field = -1;
+		if ( $this->field_count > 0 ) {
+			$this->field = $this->group->fields[0];
+		}
+	}
+
+	function has_fields() {
+		$has_data = false;
+
+		for ( $i = 0, $count = count( $this->group->fields ); $i < $count; ++$i ) {
+			$field = &$this->group->fields[$i];
+
+			if ( !empty( $field->data ) && $field->data->value != null ) {
+				$has_data = true;
+			}
+		}
+
+		if ( $has_data )
+			return true;
+
+		return false;
+	}
+
+	function profile_fields() {
+		if ( $this->current_field + 1 < $this->field_count ) {
+			return true;
+		} elseif ( $this->current_field + 1 == $this->field_count ) {
+			// Do some cleaning up after the loop
+			$this->rewind_fields();
+		}
+
+		return false;
+	}
+
+	function the_profile_field() {
+		global $field;
+
+		$field = $this->next_field();
+
+		$value = !empty( $field->data ) && !empty( $field->data->value ) ? maybe_unserialize( $field->data->value ) : false;
+
+		if ( !empty( $value ) ) {
+			$this->field_has_data = true;
+		} else {
+			$this->field_has_data = false;
+		}
+	}
+}
+
+function xprofile_get_profile() {
+	locate_template( array( 'profile/profile-loop.php'), true );
+}
+
+function bp_has_profile( $args = '' ) {
+	global $bp, $profile_template;
+
+	// Only show empty fields if we're on the Dashboard, or we're on a user's profile edit page,
+	// or this is a registration page
+	$hide_empty_fields_default = ( !is_network_admin() && !is_admin() && !bp_is_user_profile_edit() && !bp_is_register_page() );
+
+	$defaults = array(
+		'user_id'           => $bp->displayed_user->id,
+		'profile_group_id'  => false,
+		'hide_empty_groups' => true,
+		'hide_empty_fields' => $hide_empty_fields_default,
+		'fetch_fields'      => true,
+		'fetch_field_data'  => true,
+		'exclude_groups'    => false, // Comma-separated list of profile field group IDs to exclude
+		'exclude_fields'    => false  // Comma-separated list of profile field IDs to exclude
+	);
+
+	$r = wp_parse_args( $args, $defaults );
+	extract( $r, EXTR_SKIP );
+
+	$profile_template = new BP_XProfile_Data_Template( $user_id, $profile_group_id, $hide_empty_groups, $fetch_fields, $fetch_field_data, $exclude_groups, $exclude_fields, $hide_empty_fields );
+	return apply_filters( 'bp_has_profile', $profile_template->has_groups(), $profile_template );
+}
+
+function bp_profile_groups() {
+	global $profile_template;
+	return $profile_template->profile_groups();
+}
+
+function bp_the_profile_group() {
+	global $profile_template;
+	return $profile_template->the_profile_group();
+}
+
+function bp_profile_group_has_fields() {
+	global $profile_template;
+	return $profile_template->has_fields();
+}
+
+function bp_field_css_class( $class = false ) {
+	echo bp_get_field_css_class( $class );
+}
+	function bp_get_field_css_class( $class = false ) {
+		global $profile_template;
+
+		$css_classes = array();
+
+		if ( $class )
+			$css_classes[] = sanitize_title( esc_attr( $class ) );
+
+		// Set a class with the field ID
+		$css_classes[] = 'field_' . $profile_template->field->id;
+
+		// Set a class with the field name (sanitized)
+		$css_classes[] = 'field_' . sanitize_title( $profile_template->field->name );
+
+		if ( $profile_template->current_field % 2 == 1 )
+			$css_classes[] = 'alt';
+
+		$css_classes = apply_filters_ref_array( 'bp_field_css_classes', array( &$css_classes ) );
+
+		return apply_filters( 'bp_get_field_css_class', ' class="' . implode( ' ', $css_classes ) . '"' );
+	}
+
+function bp_field_has_data() {
+	global $profile_template;
+	return $profile_template->field_has_data;
+}
+
+function bp_field_has_public_data() {
+	global $profile_template;
+
+	if ( $profile_template->field_has_data )
+		return true;
+
+	return false;
+}
+
+function bp_the_profile_group_id() {
+	echo bp_get_the_profile_group_id();
+}
+	function bp_get_the_profile_group_id() {
+		global $group;
+		return apply_filters( 'bp_get_the_profile_group_id', $group->id );
+	}
+
+function bp_the_profile_group_name() {
+	echo bp_get_the_profile_group_name();
+}
+	function bp_get_the_profile_group_name() {
+		global $group;
+		return apply_filters( 'bp_get_the_profile_group_name', $group->name );
+	}
+
+function bp_the_profile_group_slug() {
+	echo bp_get_the_profile_group_slug();
+}
+	function bp_get_the_profile_group_slug() {
+		global $group;
+		return apply_filters( 'bp_get_the_profile_group_slug', sanitize_title( $group->name ) );
+	}
+
+function bp_the_profile_group_description() {
+	echo bp_get_the_profile_group_description();
+}
+	function bp_get_the_profile_group_description() {
+		global $group;
+		echo apply_filters( 'bp_get_the_profile_group_description', $group->description );
+	}
+
+function bp_the_profile_group_edit_form_action() {
+	echo bp_get_the_profile_group_edit_form_action();
+}
+	function bp_get_the_profile_group_edit_form_action() {
+		global $bp, $group;
+
+		return apply_filters( 'bp_get_the_profile_group_edit_form_action', trailingslashit( $bp->displayed_user->domain . $bp->profile->slug . '/edit/group/' . $group->id ) );
+	}
+
+function bp_the_profile_group_field_ids() {
+	echo bp_get_the_profile_group_field_ids();
+}
+	function bp_get_the_profile_group_field_ids() {
+		global $group;
+
+		$field_ids = '';
+		foreach ( (array) $group->fields as $field )
+			$field_ids .= $field->id . ',';
+
+		return substr( $field_ids, 0, -1 );
+	}
+
+function bp_profile_fields() {
+	global $profile_template;
+	return $profile_template->profile_fields();
+}
+
+function bp_the_profile_field() {
+	global $profile_template;
+	return $profile_template->the_profile_field();
+}
+
+function bp_the_profile_field_id() {
+	echo bp_get_the_profile_field_id();
+}
+	function bp_get_the_profile_field_id() {
+		global $field;
+		return apply_filters( 'bp_get_the_profile_field_id', $field->id );
+	}
+
+function bp_the_profile_field_name() {
+	echo bp_get_the_profile_field_name();
+}
+	function bp_get_the_profile_field_name() {
+		global $field;
+
+		return apply_filters( 'bp_get_the_profile_field_name', $field->name );
+	}
+
+function bp_the_profile_field_value() {
+	echo bp_get_the_profile_field_value();
+}
+	function bp_get_the_profile_field_value() {
+		global $field;
+
+		$field->data->value = bp_unserialize_profile_field( $field->data->value );
+
+		return apply_filters( 'bp_get_the_profile_field_value', $field->data->value, $field->type, $field->id );
+	}
+
+function bp_the_profile_field_edit_value() {
+	echo bp_get_the_profile_field_edit_value();
+}
+	function bp_get_the_profile_field_edit_value() {
+		global $field;
+
+		/**
+		 * Check to see if the posted value is different, if it is re-display this
+		 * value as long as it's not empty and a required field.
+		 */
+		if ( !isset( $field->data->value ) )
+			$field->data->value = '';
+
+		if ( isset( $_POST['field_' . $field->id] ) && $field->data->value != $_POST['field_' . $field->id] ) {
+			if ( !empty( $_POST['field_' . $field->id] ) )
+				$field->data->value = $_POST['field_' . $field->id];
+			else
+				$field->data->value = '';
+		}
+
+		$field_value = isset( $field->data->value ) ? bp_unserialize_profile_field( $field->data->value ) : '';
+
+		return apply_filters( 'bp_get_the_profile_field_edit_value', $field_value, $field->type, $field->id );
+	}
+
+function bp_the_profile_field_type() {
+	echo bp_get_the_profile_field_type();
+}
+	function bp_get_the_profile_field_type() {
+		global $field;
+
+		return apply_filters( 'bp_the_profile_field_type', $field->type );
+	}
+
+function bp_the_profile_field_description() {
+	echo bp_get_the_profile_field_description();
+}
+	function bp_get_the_profile_field_description() {
+		global $field;
+
+		return apply_filters( 'bp_get_the_profile_field_description', $field->description );
+	}
+
+function bp_the_profile_field_input_name() {
+	echo bp_get_the_profile_field_input_name();
+}
+	function bp_get_the_profile_field_input_name() {
+		global $field;
+
+		$array_box = false;
+		if ( 'multiselectbox' == $field->type )
+			$array_box = '[]';
+
+		return apply_filters( 'bp_get_the_profile_field_input_name', 'field_' . $field->id . $array_box );
+	}
+
+/**
+ * bp_the_profile_field_options()
+ *
+ * Displays field options HTML for field types of 'selectbox', 'multiselectbox',
+ * 'radio', 'checkbox', and 'datebox'.
+ *
+ * @package BuddyPress Xprofile
+ * @since 1.1
+ *
+ * @uses bp_get_the_profile_field_options()
+ *
+ * @param array $args Specify type for datebox. Allowed 'day', 'month', 'year'.
+ */
+function bp_the_profile_field_options( $args = '' ) {
+	echo bp_get_the_profile_field_options( $args );
+}
+	/**
+	 * bp_get_the_profile_field_options()
+	 *
+	 * Retrieves field options HTML for field types of 'selectbox', 'multiselectbox',
+	 * 'radio', 'checkbox', and 'datebox'.
+	 *
+	 * @package BuddyPress Xprofile
+	 * @since 1.1
+	 *
+	 * @uses BP_XProfile_Field::get_children()
+	 * @uses BP_XProfile_ProfileData::get_value_byid()
+	 *
+	 * @param array $args Specify type for datebox. Allowed 'day', 'month', 'year'.
+	 */
+	function bp_get_the_profile_field_options( $args = '' ) {
+		global $field;
+
+		$defaults = array(
+			'type' => false
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		if ( !method_exists( $field, 'get_children' ) )
+			$field = new BP_XProfile_Field( $field->id );
+
+		$options = $field->get_children();
+
+		// Setup some defaults
+		$html     = '';
+		$selected = '';
+
+		switch ( $field->type ) {
+			case 'selectbox':
+				if ( !$field->is_required )
+					$html .= '<option value="">' . /* translators: no option picked in select box */ __( '----', 'buddypress' ) . '</option>';
+
+				$original_option_values = '';
+				$original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field->id ) );
+
+				if ( empty( $original_option_values ) && !empty( $_POST['field_' . $field->id] ) )
+					$original_option_values = $_POST['field_' . $field->id];
+
+				$option_values = (array) $original_option_values;
+
+				for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
+					// Check for updated posted values, but errors preventing them from being saved first time
+					foreach( $option_values as $i => $option_value ) {
+						if ( isset( $_POST['field_' . $field->id] ) && $_POST['field_' . $field->id] != $option_value ) {
+							if ( !empty( $_POST['field_' . $field->id] ) )
+								$option_values[$i] = $_POST['field_' . $field->id];
+						}
+					}
+					$selected = '';
+
+					// Run the allowed option name through the before_save filter, so we'll be sure to get a match
+					$allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
+
+					// First, check to see whether the user-entered value matches
+					if ( in_array( $allowed_options, (array) $option_values ) )
+						$selected = ' selected="selected"';
+
+					// Then, if the user has not provided a value, check for defaults
+					if ( !is_array( $original_option_values ) && empty( $option_values ) && $options[$k]->is_default_option )
+						$selected = ' selected="selected"';
+
+					$html .= apply_filters( 'bp_get_the_profile_field_options_select', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_attr( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $field->id, $selected, $k );
+				}
+				break;
+
+			case 'multiselectbox':
+				$original_option_values = '';
+				$original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field->id ) );
+
+				if ( empty( $original_option_values ) && !empty( $_POST['field_' . $field->id] ) )
+					$original_option_values = $_POST['field_' . $field->id];
+
+				$option_values = (array) $original_option_values;
+
+				for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
+					// Check for updated posted values, but errors preventing them from being saved first time
+					foreach( $option_values as $i => $option_value ) {
+						if ( isset( $_POST['field_' . $field->id] ) && $_POST['field_' . $field->id][$i] != $option_value ) {
+							if ( !empty( $_POST['field_' . $field->id][$i] ) )
+								$option_values[] = $_POST['field_' . $field->id][$i];
+						}
+					}
+					$selected = '';
+
+					// Run the allowed option name through the before_save filter, so we'll be sure to get a match
+					$allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
+
+					// First, check to see whether the user-entered value matches
+					if ( in_array( $allowed_options, (array) $option_values ) )
+						$selected = ' selected="selected"';
+
+					// Then, if the user has not provided a value, check for defaults
+					if ( !is_array( $original_option_values ) && empty( $option_values ) && $options[$k]->is_default_option )
+						$selected = ' selected="selected"';
+
+					$html .= apply_filters( 'bp_get_the_profile_field_options_multiselect', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_attr( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $field->id, $selected, $k );
+				}
+				break;
+
+			case 'radio':
+				$html .= '<div id="field_' . $field->id . '">';
+				$option_value = BP_XProfile_ProfileData::get_value_byid( $field->id );
+
+				for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
+					// Check for updated posted values, but errors preventing them from being saved first time
+					if ( isset( $_POST['field_' . $field->id] ) && $option_value != $_POST['field_' . $field->id] ) {
+						if ( !empty( $_POST['field_' . $field->id] ) )
+							$option_value = $_POST['field_' . $field->id];
+					}
+
+					// Run the allowed option name through the before_save
+					// filter, so we'll be sure to get a match
+					$allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
+
+					$selected = '';
+					if ( $option_value == $allowed_options || !empty( $value ) && $value == $allowed_options || ( empty( $option_value ) && $options[$k]->is_default_option ) )
+						$selected = ' checked="checked"';
+
+					$html .= apply_filters( 'bp_get_the_profile_field_options_radio', '<label><input' . $selected . ' type="radio" name="field_' . $field->id . '" id="option_' . $options[$k]->id . '" value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '"> ' . esc_attr( stripslashes( $options[$k]->name ) ) . '</label>', $options[$k], $field->id, $selected, $k );
+				}
+
+				$html .= '</div>';
+				break;
+
+			case 'checkbox':
+				$option_values = BP_XProfile_ProfileData::get_value_byid( $field->id );
+				$option_values = maybe_unserialize( $option_values );
+
+				// Check for updated posted values, but errors preventing them from being saved first time
+				if ( isset( $_POST['field_' . $field->id] ) && $option_values != maybe_serialize( $_POST['field_' . $field->id] ) ) {
+					if ( !empty( $_POST['field_' . $field->id] ) )
+						$option_values = $_POST['field_' . $field->id];
+				}
+
+				for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
+					$selected = '';
+
+					// First, check to see whether the user's saved values
+					// match the option
+					for ( $j = 0, $count_values = count( $option_values ); $j < $count_values; ++$j ) {
+
+						// Run the allowed option name through the
+						// before_save filter, so we'll be sure to get a match
+						$allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
+
+						if ( $option_values[$j] == $allowed_options || @in_array( $allowed_options, $value ) ) {
+							$selected = ' checked="checked"';
+							break;
+						}
+					}
+
+					// If the user has not yet supplied a value for this field,
+					// check to see whether there is a default value available
+					if ( !is_array( $option_values ) && empty( $option_values ) && !$selected && $options[$k]->is_default_option) {
+						$selected = ' checked="checked"';
+					}
+
+					$html .= apply_filters( 'bp_get_the_profile_field_options_checkbox', '<label><input' . $selected . ' type="checkbox" name="field_' . $field->id . '[]" id="field_' . $options[$k]->id . '_' . $k . '" value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '"> ' . esc_attr( stripslashes( $options[$k]->name ) ) . '</label>', $options[$k], $field->id, $selected, $k );
+				}
+				break;
+
+			case 'datebox':
+				$date = BP_XProfile_ProfileData::get_value_byid( $field->id );
+
+				// Set day, month, year defaults
+				$day   = '';
+				$month = '';
+				$year  = '';
+
+				if ( !empty( $date ) ) {
+					$day   = date( 'j', $date );
+					$month = date( 'F', $date );
+					$year  = date( 'Y', $date );
+				}
+
+				// Check for updated posted values, but errors preventing them from being saved first time
+				if ( !empty( $_POST['field_' . $field->id . '_day'] ) ) {
+					if ( $day != $_POST['field_' . $field->id . '_day'] )
+						$day = $_POST['field_' . $field->id . '_day'];
+				}
+
+				if ( !empty( $_POST['field_' . $field->id . '_month'] ) ) {
+					if ( $month != $_POST['field_' . $field->id . '_month'] )
+						$month = $_POST['field_' . $field->id . '_month'];
+				}
+
+				if ( !empty( $_POST['field_' . $field->id . '_year'] ) ) {
+					if ( $year != date( "j", $_POST['field_' . $field->id . '_year'] ) )
+						$year = $_POST['field_' . $field->id . '_year'];
+				}
+
+				switch ( $type ) {
+					case 'day':
+						$html .= '<option value=""' . selected( $day, '', false ) . '>--</option>';
+
+						for ( $i = 1; $i < 32; ++$i ) {
+							$html .= '<option value="' . $i .'"' . selected( $day, $i, false ) . '>' . $i . '</option>';
+						}
+						break;
+
+					case 'month':
+						$eng_months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
+
+						$months = array( __( 'January', 'buddypress' ), __( 'February', 'buddypress' ), __( 'March', 'buddypress' ),
+								 __( 'April', 'buddypress' ), __( 'May', 'buddypress' ), __( 'June', 'buddypress' ),
+								 __( 'July', 'buddypress' ), __( 'August', 'buddypress' ), __( 'September', 'buddypress' ),
+								 __( 'October', 'buddypress' ), __( 'November', 'buddypress' ), __( 'December', 'buddypress' )
+								);
+
+						$html .= '<option value=""' . selected( $month, '', false ) . '>------</option>';
+
+						for ( $i = 0; $i < 12; ++$i ) {
+							$html .= '<option value="' . $eng_months[$i] . '"' . selected( $month, $eng_months[$i], false ) . '>' . $months[$i] . '</option>';
+						}
+						break;
+
+					case 'year':
+						$html .= '<option value=""' . selected( $year, '', false ) . '>----</option>';
+
+						for ( $i = 2037; $i > 1901; $i-- ) {
+							$html .= '<option value="' . $i .'"' . selected( $year, $i, false ) . '>' . $i . '</option>';
+						}
+						break;
+				}
+
+				$html = apply_filters( 'bp_get_the_profile_field_datebox', $html, $type, $day, $month, $year, $field->id, $date );
+
+				break;
+		}
+
+		return $html;
+	}
+
+function bp_the_profile_field_is_required() {
+	echo bp_get_the_profile_field_is_required();
+}
+	function bp_get_the_profile_field_is_required() {
+		global $field;
+
+		return apply_filters( 'bp_get_the_profile_field_is_required', (int)$field->is_required );
+	}
+
+function bp_unserialize_profile_field( $value ) {
+	if ( is_serialized($value) ) {
+		$field_value = maybe_unserialize($value);
+		$field_value = implode( ', ', $field_value );
+		return $field_value;
+	}
+
+	return $value;
+}
+
+function bp_profile_field_data( $args = '' ) {
+	echo bp_get_profile_field_data( $args );
+}
+	function bp_get_profile_field_data( $args = '' ) {
+		global $bp;
+
+		$defaults = array(
+			'field'   => false, // Field name or ID.
+			'user_id' => $bp->displayed_user->id
+		);
+
+		$r = wp_parse_args( $args, $defaults );
+		extract( $r, EXTR_SKIP );
+
+		return apply_filters( 'bp_get_profile_field_data', xprofile_get_field_data( $field, $user_id ) );
+	}
+
+function bp_profile_group_tabs() {
+	global $bp, $group_name;
+
+	if ( !$groups = wp_cache_get( 'xprofile_groups_inc_empty', 'bp' ) ) {
+		$groups = BP_XProfile_Group::get( array( 'fetch_fields' => true ) );
+		wp_cache_set( 'xprofile_groups_inc_empty', $groups, 'bp' );
+	}
+
+	if ( empty( $group_name ) )
+		$group_name = bp_profile_group_name(false);
+
+	$tabs = array();
+	for ( $i = 0, $count = count( $groups ); $i < $count; ++$i ) {
+		if ( $group_name == $groups[$i]->name )
+			$selected = ' class="current"';
+		else
+			$selected = '';
+
+		if ( !empty( $groups[$i]->fields ) ) {
+			$link = $bp->displayed_user->domain . $bp->profile->slug . '/edit/group/' . $groups[$i]->id;
+			$tabs[] = sprintf( '<li %1$s><a href="%2$s">%3$s</a></li>', $selected, $link, esc_html( $groups[$i]->name ) );
+		}
+	}
+
+	$tabs = apply_filters( 'xprofile_filter_profile_group_tabs', $tabs, $groups, $group_name );
+	foreach ( (array) $tabs as $tab )
+		echo $tab;
+
+	do_action( 'xprofile_profile_group_tabs' );
+}
+
+function bp_profile_group_name( $deprecated = true ) {
+	if ( !$deprecated ) {
+		return bp_get_profile_group_name();
+	} else {
+		echo bp_get_profile_group_name();
+	}
+}
+	function bp_get_profile_group_name() {
+		if ( !$group_id = bp_action_variable( 1 ) )
+			$group_id = 1;
+
+		if ( !is_numeric( $group_id ) )
+			$group_id = 1;
+
+		if ( !$group = wp_cache_get( 'xprofile_group_' . $group_id, 'bp' ) ) {
+			$group = new BP_XProfile_Group($group_id);
+			wp_cache_set( 'xprofile_group_' . $group_id, $group, 'bp' );
+		}
+
+		return apply_filters( 'bp_get_profile_group_name', $group->name );
+	}
+
+function bp_avatar_upload_form() {
+	global $bp;
+
+	if ( !(int)$bp->site_options['bp-disable-avatar-uploads'] )
+		bp_core_avatar_admin( null, $bp->loggedin_user->domain . $bp->profile->slug . '/change-avatar/', $bp->loggedin_user->domain . $bp->profile->slug . '/delete-avatar/' );
+	else
+		_e( 'Avatar uploads are currently disabled. Why not use a <a href="http://gravatar.com" target="_blank">gravatar</a> instead?', 'buddypress' );
+}
+
+function bp_profile_last_updated() {
+	global $bp;
+
+	$last_updated = bp_get_profile_last_updated();
+
+	if ( !$last_updated ) {
+		_e( 'Profile not recently updated', 'buddypress' ) . '.';
+	} else {
+		echo $last_updated;
+	}
+}
+	function bp_get_profile_last_updated() {
+		global $bp;
+
+		$last_updated = bp_get_user_meta( $bp->displayed_user->id, 'profile_last_updated', true );
+
+		if ( $last_updated )
+			return apply_filters( 'bp_get_profile_last_updated', sprintf( __('Profile updated %s', 'buddypress'), bp_core_time_since( strtotime( $last_updated ) ) ) );
+
+		return false;
+	}
+
+function bp_current_profile_group_id() {
+	echo bp_get_current_profile_group_id();
+}
+	function bp_get_current_profile_group_id() {
+		if ( !$profile_group_id = bp_action_variable( 1 ) )
+			$profile_group_id = 1;
+
+		return apply_filters( 'bp_get_current_profile_group_id', $profile_group_id ); // admin/profile/edit/[group-id]
+	}
+
+function bp_avatar_delete_link() {
+	echo bp_get_avatar_delete_link();
+}
+	function bp_get_avatar_delete_link() {
+		global $bp;
+
+		return apply_filters( 'bp_get_avatar_delete_link', wp_nonce_url( $bp->displayed_user->domain . $bp->profile->slug . '/change-avatar/delete-avatar/', 'bp_delete_avatar_link' ) );
+	}
+
+function bp_get_user_has_avatar() {
+	global $bp;
+
+	if ( !bp_core_fetch_avatar( array( 'item_id' => $bp->displayed_user->id, 'no_grav' => true ) ) )
+		return false;
+
+	return true;
+}
+
+function bp_edit_profile_button() {
+	global $bp;
+
+	bp_button( array (
+		'id'                => 'edit_profile',
+		'component'         => 'xprofile',
+		'must_be_logged_in' => true,
+		'block_self'        => true,
+		'link_href'         => trailingslashit( $bp->displayed_user->domain . $bp->profile->slug . '/edit' ),
+		'link_class'        => 'edit',
+		'link_text'         => __( 'Edit Profile', 'buddypress' ),
+		'link_title'        => __( 'Edit Profile', 'buddypress' ),
+	) );
+}
+?>
Index: /tags/1.5-beta-3/humans.txt
===================================================================
--- /tags/1.5-beta-3/humans.txt	(revision 5057)
+++ /tags/1.5-beta-3/humans.txt	(revision 5057)
@@ -0,0 +1,54 @@
+               .$$$$$$$Z$.
+          .~$$7...........$$$.
+        .$$...?$$$$$$$$$$Z....$Z.
+      .Z$..Z$$$$$$$$$$$$$$$$Z:..$$.
+     ,$..$$$$$$$$$$$$$7...$$$$$?..$.
+   .$Z .$$$$$$$$$$..?$$.....$$$$$..Z:
+  .$$.$$$$$$$$$........$$....Z$$$$..Z,
+  :$.7$$$$$$$$..........$$....$$$$$..$.
+ .$. $$$$$$$$$...........$....$$$$$$..$.
+.$$.$$$$$$$$$$...........$...$$$$$$$$.$.
+.$. $$$$$$$$$$ ..........Z..$$$$$$$$$.$$.
+.$.,$$$$$$$$$$$.........$$$$Z$$$$$$$$..$.
+.Z $$$$$$$$$$$$$$....+$$$$$,,,,~$$$$$..$.
+.$.=$$$$$$Z,,,,,?$$$$$,,,,,Z$,,,,$$$$..$.
+.$..$$$$$$,,,,,,,,Z$$,,,,,,,,$:,,~$$$.:$.
+.$$.$$$$$,,,,,,,,,,,,,,,,,,,,:$,,:$$$.Z.
+ .$..$$$$,,,,,,,,,,,,,,,,,,,,:$,,,$$..$.
+ .$$.$$$:,,,,,,,,,,,,,,,,,,,,,Z$$$$+.$.
+  .$= $$~,,,,,,:::,:,,:,,::,:,$$$$Z.$Z.
+   .$+.$$$$$$$$$$$$$$$$$$$$$$$$$$:.$$.
+    .$$..Z$$$$$$$$$$$$$$$$$$$$$Z..$7.
+      .$...$$$$$$$$$$$$$$$$$$$..Z$.
+        .Z$=..?Z$$$$$$$$$$$Z...$$.
+          .$$7.............Z$$.
+             .7Z$$$$$$$$$$~
+
+   We are the humans behind BuddyPress
+
+/* TEAM */
+Name: Andy Peatling
+Title: Founding Developer
+Twitter: apeatling
+
+Name: John James Jacoby
+Title: Lead Developer
+Twitter: johnjamesjacoby
+Favorite Food: Pizza
+
+Name: Boone B. Gorges
+Title: Developer
+Twitter: boone
+Favorite Pudgy Pie Filling: Cherry
+
+Name: Paul Gibbs
+Title:
+Twitter: pgibbs
+Favorite Food:
+
+/* THANKS */
+r-a-y, hnla, mercime, modemlooper, cnorris23
+
+/* META */
+Updated: 2011/08/29
+See: http://humanstxt.org/
Index: /tags/1.5-beta-3/license.txt
===================================================================
--- /tags/1.5-beta-3/license.txt	(revision 5057)
+++ /tags/1.5-beta-3/license.txt	(revision 5057)
@@ -0,0 +1,280 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
Index: /tags/1.5-beta-3/readme.txt
===================================================================
--- /tags/1.5-beta-3/readme.txt	(revision 5057)
+++ /tags/1.5-beta-3/readme.txt	(revision 5057)
@@ -0,0 +1,123 @@
+=== Plugin Name ===
+Contributors: apeatling, johnjamesjacoby, MrMaz, DJPaul, boonebgorges
+Tags: buddypress, social networking, activity, profiles, messaging, friends, groups, forums, microblogging, twitter, facebook, social, community, networks, networking, cms
+Requires at least: 3.2
+Tested up to: 3.2.1
+Stable tag: 1.5-beta-3
+
+Social networking in a box. Build a social network for your company, school, sports team or niche community.
+
+== Description ==
+
+BuddyPress lets users register on your site and start creating profiles, posting messages, making connections, creating and interacting in groups and much more. A social network in a box, BuddyPress lets you build a social network for your company, school, sports team or niche community.
+
+<h4>Try the Demo</h4>
+
+If you're interested in seeing what a default installation of BuddyPress has to offer, try out the BuddyPress Test Drive! This site is a community of BuddyPress users looking to try out and discuss the latest features of BuddyPress.
+
+<a href="http://testbp.org/">BuddyPress Test Drive</a>
+
+<h4>Who's Using BuddyPress?</h4>
+
+More and more BuddyPress powered sites are popping up. You can take a look at some of the best sites on the <a href="http://buddypress.org/showcase/">BuddyPress Showcase</a>.
+
+<h4>Plugins: Adding So Much More</h4>
+
+BuddyPress boasts an ever growing array of new features developed by an awesome plugin development community. There are more than 330 BuddyPress plugins available, and the list is growing every day. For a full list of plugins, please visit the <a href="http://buddypress.org/extend/plugins/">BuddyPress.org plugins page</a>. You can install any of these plugins automatically, using the plugin installer on your WordPress Dashboard.
+
+<h4>More Information</h4>
+
+Visit the <a href="http://buddypress.org/">BuddyPress website</a> for more information about BuddyPress.
+
+== Installation ==
+
+You can download and install BuddyPress using the built in WordPress plugin installer. If you download BuddyPress manually, make sure it is uploaded to "/wp-content/plugins/buddypress/".
+
+Activate BuddyPress in the "Plugins" admin panel using the "Activate" link. You'll then see a message asking you to complete the BuddyPress Installation Wizard, which will guide you through configuring your site for BuddyPress.
+
+--- Discussion Forums ---
+
+BuddyPress includes full support for discussion forums. Each group created on your site can have its own forum. If you'd like to enable this feature, after completing the Installation Wizard, visit the "Forums Setup" item under the "BuddyPress" menu in your WordPress admin area, and follow the on-screen instructions.
+
+== Frequently Asked Questions ==
+
+= Can I use my existing WordPress theme? =
+
+Of course! Once you've installed and activated BuddyPress, the Installation Wizard will guide you through the available theme options. You'll be given the option of installing the <a href="http://wordpress.org/extend/plugins/bp-template-pack/">BuddyPress Template Pack</a>, which will help you add BuddyPress compatibility to your existing theme.
+
+Be sure to also try out the default theme bundled with BuddyPress. It provides all the awesome features of a standard WordPress blog, but also integrates BuddyPress's features in a seamless and beautiful way. The BuddyPress Default theme is a snap to customize, with full support for custom headers and backgrounds and multiple widget areas. It also makes a great starting point for your own  <a href="http://codex.buddypress.org/how-to-guides/building-a-buddypress-child-theme/">child theme</a>.
+
+= Will this work on WordPress multisite? =
+
+Yes! If your WordPress site has multisite enabled, BuddyPress will support the global tracking of blogs, posts and comments.
+
+= Where can I get support? =
+
+The support forums can be found at <a href="http://buddypress.org/forums/">http://buddypress.org/forums/</a>.
+
+= Where can I find documentation? =
+
+The documentation codex can be found at <a href="http://codex.buddypress.org/">http://codex.buddypress.org/</a>.
+
+= Where can I report a bug? =
+
+Report bugs and participate in development at <a href="http://buddypress.trac.wordpress.org/">http://buddypress.trac.wordpress.org</a>.
+
+= Where can I get the bleeding edge version of BuddyPress? =
+
+Check out the development trunk of BuddyPress via Subversion, from <a href="http://buddypress.svn.wordpress.org/trunk/">http://buddypress.svn.wordpress.org/trunk/</a>
+
+== Screenshots ==
+
+1. **Activity Streams** - Global, personal and group activity streams with threaded commenting, direct posting, favoriting and @mentions. All with full RSS feeds and email notification support.
+2. **Extended Profiles** - Fully editable rofile fields allow you to define the fields users can fill in to describe themselves. Tailor profile fields to suit your audience.
+3. **Extensible Groups** - Powerful public, private or hidden groups allow your users to break the discussion down into specific topics. Extend groups with your own custom features using the group extension API.
+4. **Friend Connections** - Let your users make connections so they can track the activity of others, or filter on only those users they care about the most.
+5. **Private Messaging** - Private messaging will allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, your users can send messages to multiple recipients.
+6. **Discussion Forums** - Full powered discussion forums built directly into groups allow for more conventional in-depth conversations.
+7. **WordPress Blogging** - Allow your users to start their own WordPress sites (using WordPress's Multisite feature), and track posts and comments from across your blog network in the activity stream.
+8. **User Settings** - Give your users complete control over profile and notification settings. Settings are fully integrated into your theme, and can be disabled by the administrator.
+
+== Languages ==
+
+BuddyPress is available in more than 20 languages. For more information, check out the <a href="http://codex.buddypress.org/getting-started/translations/">translation page</a> on the BuddyPress Codex.
+
+== Upgrade Notice ==
+
+= 1.5-beta-3 =
+Fixed debug notices
+Fixed user spam/unspam issues
+Fixed single-site activity stream actions
+Added responsive CSS to bp-default
+Refreshed screenshots
+Updated readme.txt
+RTL improvements
+
+= 1.5-beta-2 =
+Fixed debug notices
+Fixed bp-pages issues
+Fixed group extension messages
+Fixed login redirect handling
+Fixed incorrect BP_Component action names
+Fixed references to 1.3 in PHPDoc
+RTL improvements
+Removed sidebar gradient from bp-default
+
+= 1.5-beta-1 =
+792 tickets closed
+494 confirmed bug fixes
+2,161 total code changes since
+484 pizzas consumed
+
+= 1.2.9 =
+Compatibility with WordPress 3.2
+
+= 1.2.8 =
+Compatibility with WordPress 3.1
+
+= 1.2.7 =
+Fixes over 10 bugs.
+
+== Changelog ==
+
+See http://buddypress.org/about/release-history/ for a list of changes.
