Index: /trunk/src/bp-core/bp-core-buddybar.php
===================================================================
--- /trunk/src/bp-core/bp-core-buddybar.php	(revision 10744)
+++ /trunk/src/bp-core/bp-core-buddybar.php	(revision 10745)
@@ -14,7 +14,165 @@
 
 /**
- * Add an item to the main BuddyPress navigation array.
+ * Add an item to the primary navigation of the specified component.
  *
  * @since 1.1.0
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param array|string $args {
+ *     Array describing the new nav item.
+ *     @type string      $name                    Display name for the nav item.
+ *     @type string      $slug                    Unique URL slug for the nav item.
+ *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
+ *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
+ *                                                member profile other than your own. Default: true.
+ *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
+ *                                                (those with the 'bp_moderate' cap). Default: false.
+ *     @type int         $position                Optional. Numerical index specifying where the item should appear in
+ *                                                the nav array. Default: 99.
+ *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
+ *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
+ *                                                item is clicked.
+ * }
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
+ * @return bool|null Returns false on failure.
+ */
+function bp_core_new_nav_item( $args, $component = 'members' ) {
+	if ( ! bp_is_active( $component ) ) {
+		return;
+	}
+
+	$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 );
+
+	// Validate nav link data.
+	$nav_item = bp_core_create_nav_link( $r, $component );
+
+	/*
+	 * To mimic legacy behavior, if bp_core_create_nav_link() returns false, we make
+	 * an early exit and don't attempt to register the screen function.
+	 */
+	if ( false === $nav_item ) {
+		return false;
+	}
+
+	// Then, hook the screen function for the added nav item.
+	$hooked = bp_core_register_nav_screen_function( $nav_item );
+	if ( false === $hooked ){
+		return false;
+	}
+
+	/**
+	 * Fires after adding an item to the main BuddyPress navigation array.
+	 * Note that, when possible, the more specific action hooks
+	 * `bp_core_create_nav_link` or `bp_core_register_nav_screen_function`
+	 * should be used.
+	 *
+	 * @since 1.5.0
+	 *
+	 * @param array $r        Parsed arguments for the nav item.
+	 * @param array $args     Originally passed in arguments for the nav item.
+	 * @param array $defaults Default arguments for a nav item.
+	 */
+	do_action( 'bp_core_new_nav_item', $r, $args, $defaults );
+}
+
+/**
+ * Add a link to the main BuddyPress navigation.
+ *
+ * @since 2.4.0
+ * @since 2.6.0 Introduced the `$component` parameter. Began returning a BP_Nav_Item object on success.
+ *
+ * @param array|string $args {
+ *     Array describing the new nav item.
+ *     @type string      $name                    Display name for the nav item.
+ *     @type string      $slug                    Unique URL slug for the nav item.
+ *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
+ *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
+ *                                                member profile other than your own. Default: true.
+ *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
+ *                                                (those with the 'bp_moderate' cap). Default: false.
+ *     @type int         $position                Optional. Numerical index specifying where the item should appear in
+ *                                                the nav array. Default: 99.
+ *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
+ *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
+ *                                                item is clicked.
+ * }
+ * @param string $component Optional. Component that the nav belongs to.
+ * @return bool|BP_Nav_Item Returns false on failure, new nav item on success.
+ */
+function bp_core_create_nav_link( $args = '', $component = 'members' ) {
+	$bp = buddypress();
+
+	$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 );
+
+	// If we don't have the required info we need, don't create this nav item.
+	if ( empty( $r['name'] ) || empty( $r['slug'] ) ) {
+		return false;
+	}
+
+	// If this is for site admins only and the user is not one, don't create the nav item.
+	if ( ! empty( $r['site_admin_only'] ) && ! bp_current_user_can( 'bp_moderate' ) ) {
+		return false;
+	}
+
+	if ( empty( $r['item_css_id'] ) ) {
+		$r['item_css_id'] = $r['slug'];
+	}
+
+	$nav_item = array(
+		'name'                    => $r['name'],
+		'slug'                    => $r['slug'],
+		'link'                    => trailingslashit( bp_loggedin_user_domain() . $r['slug'] ),
+		'css_id'                  => $r['item_css_id'],
+		'show_for_displayed_user' => $r['show_for_displayed_user'],
+		'position'                => $r['position'],
+		'screen_function'         => &$r['screen_function'],
+		'default_subnav_slug'	  => $r['default_subnav_slug']
+	);
+
+	// Add the item to the nav.
+	$retval = buddypress()->{$component}->nav->add_nav( $nav_item );
+
+	/**
+	 * Fires after a link is added to the main BuddyPress nav.
+	 *
+	 * @since 2.4.0
+	 * @since 2.6.0 Added `$component` parameter.
+	 *
+	 * @param array  $r         Parsed arguments for the nav item.
+	 * @param array  $args      Originally passed in arguments for the nav item.
+	 * @param array  $defaults  Default arguments for a nav item.
+	 * @param string $component Component that the nav belongs to.
+	 */
+	do_action( 'bp_core_create_nav_link', $r, $args, $defaults, $component );
+
+	return $retval;
+}
+
+/**
+ * Register a screen function for an item in the main nav array.
+ *
+ * @since 2.4.0
  *
  * @param array|string $args {
@@ -35,147 +193,4 @@
  * @return bool|null Returns false on failure.
  */
-function bp_core_new_nav_item( $args = '' ) {
-
-	$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 );
-
-	// First, add the nav item link to the bp_nav array.
-	$created = bp_core_create_nav_link( $r );
-
-	// To mimic the existing behavior, if bp_core_create_nav_link()
-	// returns false, we make an early exit and don't attempt to register
-	// the screen function.
-	if ( false === $created ) {
-		return false;
-	}
-
-	// Then, hook the screen function for the added nav item.
-	$hooked = bp_core_register_nav_screen_function( $r );
-	if ( false === $hooked ){
-		return false;
-	}
-
-	/**
-	 * Fires after adding an item to the main BuddyPress navigation array.
-	 * Note that, when possible, the more specific action hooks
-	 * `bp_core_create_nav_link` or `bp_core_register_nav_screen_function`
-	 * should be used.
-	 *
-	 * @since 1.5.0
-	 *
-	 * @param array $r        Parsed arguments for the nav item.
-	 * @param array $args     Originally passed in arguments for the nav item.
-	 * @param array $defaults Default arguments for a nav item.
-	 */
-	do_action( 'bp_core_new_nav_item', $r, $args, $defaults );
-}
-
-/**
- * Add a link to the main BuddyPress navigation array.
- *
- * @since 2.4.0
- *
- * @param array|string $args {
- *     Array describing the new nav item.
- *     @type string      $name                    Display name for the nav item.
- *     @type string      $slug                    Unique URL slug for the nav item.
- *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
- *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
- *                                                member profile other than your own. Default: true.
- *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
- *                                                (those with the 'bp_moderate' cap). Default: false.
- *     @type int         $position                Optional. Numerical index specifying where the item should appear in
- *                                                the nav array. Default: 99.
- *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
- *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
- *                                                item is clicked.
- * }
- * @return bool|null Returns false on failure.
- */
-function bp_core_create_nav_link( $args = '' ) {
-	$bp = buddypress();
-
-	$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 );
-
-	// If we don't have the required info we need, don't create this nav item.
-	if ( empty( $r['name'] ) || empty( $r['slug'] ) ) {
-		return false;
-	}
-
-	// If this is for site admins only and the user is not one, don't create the nav item.
-	if ( ! empty( $r['site_admin_only'] ) && ! bp_current_user_can( 'bp_moderate' ) ) {
-		return false;
-	}
-
-	if ( empty( $r['item_css_id'] ) ) {
-		$r['item_css_id'] = $r['slug'];
-	}
-
-	$bp->bp_nav[$r['slug']] = array(
-		'name'                    => $r['name'],
-		'slug'                    => $r['slug'],
-		'link'                    => trailingslashit( bp_loggedin_user_domain() . $r['slug'] ),
-		'css_id'                  => $r['item_css_id'],
-		'show_for_displayed_user' => $r['show_for_displayed_user'],
-		'position'                => $r['position'],
-		'screen_function'         => &$r['screen_function'],
-		'default_subnav_slug'	  => $r['default_subnav_slug']
-	);
-
-	/**
-	 * Fires after a link is added to the main BuddyPress navigation array.
-	 *
-	 * @since 2.4.0
-	 *
-	 * @param array $r        Parsed arguments for the nav item.
-	 * @param array $args     Originally passed in arguments for the nav item.
-	 * @param array $defaults Default arguments for a nav item.
-	 */
-	do_action( 'bp_core_create_nav_link', $r, $args, $defaults );
-}
-
-/**
- * Register a screen function for an item in the main nav array.
- *
- * @since 2.4.0
- *
- * @param array|string $args {
- *     Array describing the new nav item.
- *     @type string      $name                    Display name for the nav item.
- *     @type string      $slug                    Unique URL slug for the nav item.
- *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
- *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
- *                                                member profile other than your own. Default: true.
- *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
- *                                                (those with the 'bp_moderate' cap). Default: false.
- *     @type int         $position                Optional. Numerical index specifying where the item should appear in
- *                                                the nav array. Default: 99.
- *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
- *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
- *                                                item is clicked.
- * }
- * @return bool|null Returns false on failure.
- */
 function bp_core_register_nav_screen_function( $args = '' ) {
 	$bp = buddypress();
@@ -291,18 +306,31 @@
 	$r = wp_parse_args( $args, $defaults );
 
-	if ( $function = $bp->bp_nav[$r['parent_slug']]['screen_function'] ) {
+	// This is specific to Members - it's not available in Groups.
+	$parent_nav = $bp->members->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
+
+	if ( ! $parent_nav ) {
+		return ;
+	}
+
+	$parent_nav = reset( $parent_nav );
+
+	if ( ! empty( $parent_nav->screen_function ) ) {
 		// Remove our screen hook if screen function is callable.
-		if ( is_callable( $function ) ) {
-			remove_action( 'bp_screens', $function, 3 );
-		}
-	}
-
-	$bp->bp_nav[$r['parent_slug']]['screen_function'] = &$r['screen_function'];
-
-	if ( bp_is_current_component( $r['parent_slug'] ) ) {
+		if ( is_callable( $parent_nav->screen_function ) ) {
+			remove_action( 'bp_screens', $parent_nav->screen_function, 3 );
+		}
+	}
+
+	// Edit the screen function for the parent nav
+	$bp->members->nav->edit_nav( array(
+		'screen_function'     => &$r['screen_function'],
+		'default_subnav_slug' => $r['subnav_slug'],
+	), $parent_nav->slug );
+
+	if ( bp_is_current_component( $parent_nav->slug ) ) {
 
 		// The only way to tell whether to set the subnav is to peek at the unfiltered_uri
 		// Find the component.
-		$component_uri_key = array_search( $r['parent_slug'], $bp->unfiltered_uri );
+		$component_uri_key = array_search( $parent_nav->slug, $bp->unfiltered_uri );
 
 		if ( false !== $component_uri_key ) {
@@ -340,45 +368,8 @@
 
 /**
- * Sort the navigation menu items.
- *
- * The sorting is split into a separate function because it can only happen
- * after all plugins have had a chance to register their navigation items.
- *
- * @since 1.0.0
- *
- * @return bool|null Returns false on failure.
- */
-function bp_core_sort_nav_items() {
-	$bp = buddypress();
-
-	if ( empty( $bp->bp_nav ) || ! is_array( $bp->bp_nav ) ) {
-		return false;
-	}
-
-	$temp = array();
-
-	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' );
-
-/**
- * Add a subnav item to the BuddyPress navigation.
+ * Add an item to secondary navigation of the specified component.
  *
  * @since 1.1.0
+ * @since 2.6.0 Introduced the `$component` parameter.
  *
  * @param array|string $args {
@@ -404,20 +395,47 @@
  *                                          Admin Bar menu for group admins. Default: false.
  * }
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
  * @return bool|null Returns false on failure.
  */
-function bp_core_new_subnav_item( $args = '' ) {
-
-	// First, add the subnav item link to the bp_options_nav array.
-	$created = bp_core_create_subnav_link( $args );
-
-	// To mimic the existing behavior, if bp_core_create_subnav_link()
-	// returns false, we make an early exit and don't attempt to register
-	// the screen function.
-	if ( false === $created ) {
+function bp_core_new_subnav_item( $args, $component = null ) {
+	// Backward compatibility for plugins using `bp_core_new_subnav_item()` without `$component`
+	// to add group subnav items.
+	if ( null === $component && bp_is_active( 'groups' ) && bp_is_group() && isset( $args['parent_slug'] ) ) {
+		/*
+		 * Assume that this item is intended to belong to the current group if:
+		 * a) the 'parent_slug' is the same as the slug of the current group, or
+		 * b) the 'parent_slug' starts with the slug of the current group, and the members nav doesn't have
+		 *    a primary item with that slug
+		 */
+		$group_slug = bp_get_current_group_slug();
+		if (
+			$group_slug === $args['parent_slug'] ||
+			( 0 === strpos( $args['parent_slug'], $group_slug ) && ! buddypress()->members->nav->get_primary( array( 'slug' => $args['parent_slug'] ), false ) )
+		) {
+			$component = 'groups';
+		}
+	}
+
+	if ( ! $component ) {
+		$component = 'members';
+	}
+
+	if ( ! bp_is_active( $component ) ) {
+		return;
+	}
+
+	// First, register the subnav item in the nav.
+	$subnav_item = bp_core_create_subnav_link( $args, $component );
+
+	/*
+	 * To mimic legacy behavior, if bp_core_create_subnav_link() returns false, we make an
+	 * early exit and don't attempt to register the screen function.
+	 */
+	if ( false === $subnav_item ) {
 		return false;
 	}
 
 	// Then, hook the screen function for the added subnav item.
-	$hooked = bp_core_register_subnav_screen_function( $args );
+	$hooked = bp_core_register_subnav_screen_function( $subnav_item, $component );
 	if ( false === $hooked ) {
 		return false;
@@ -429,4 +447,5 @@
  *
  * @since 2.4.0
+ * @since 2.6.0 Introduced the `$component` parameter. Began returning a BP_Nav_Item object on success.
  *
  * @param array|string $args {
@@ -456,7 +475,8 @@
  *                                          Default: false.
  * }
- * @return bool|null Returns false on failure.
- */
-function bp_core_create_subnav_link( $args = '' ) {
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
+ * @return bool|BP_Nav_Item Returns false on failure, new nav item on success.
+ */
+function bp_core_create_subnav_link( $args = '', $component = 'members' ) {
 	$bp = buddypress();
 
@@ -484,7 +504,12 @@
 		$r['link'] = trailingslashit( $r['parent_url'] . $r['slug'] );
 
+		$parent_nav = $bp->{$component}->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
+
 		// If this sub item is the default for its parent, skip the slug.
-		if ( ! empty( $bp->bp_nav[$r['parent_slug']]['default_subnav_slug'] ) && $r['slug'] == $bp->bp_nav[$r['parent_slug']]['default_subnav_slug'] ) {
-			$r['link'] = trailingslashit( $r['parent_url'] );
+		if ( $parent_nav ) {
+			$parent_nav_item = reset( $parent_nav );
+			if ( ! empty( $parent_nav_item->default_subnav_slug ) && $r['slug'] === $parent_nav_item->default_subnav_slug ) {
+				$r['link'] = trailingslashit( $r['parent_url'] );
+			}
 		}
 	}
@@ -503,4 +528,5 @@
 		'link'              => $r['link'],
 		'slug'              => $r['slug'],
+		'parent_slug'       => $r['parent_slug'],
 		'css_id'            => $r['item_css_id'],
 		'position'          => $r['position'],
@@ -511,5 +537,5 @@
 	);
 
-	$bp->bp_options_nav[$r['parent_slug']][$r['slug']] = $subnav_item;
+	return buddypress()->{$component}->nav->add_nav( $subnav_item );
 }
 
@@ -518,4 +544,5 @@
  *
  * @since 2.4.0
+ * @since 2.6.0 Introduced the `$component` parameter.
  *
  * @param array|string $args {
@@ -542,7 +569,8 @@
  *                                       Default: false.
  * }
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
  * @return bool|null Returns false on failure.
  */
-function bp_core_register_subnav_screen_function( $args = '' ) {
+function bp_core_register_subnav_screen_function( $args = '', $component = 'members' ) {
 	$bp = buddypress();
 
@@ -556,5 +584,5 @@
 	) );
 
-	/**
+	/*
 	 * 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
@@ -569,6 +597,6 @@
 	 *       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.
+	 *       parent nav item in the component's primary nav). This condition only arises
+	 *       when viewing a user, since groups should always have an action set.
 	 */
 
@@ -578,6 +606,8 @@
 	}
 
+	$parent_nav = $bp->{$component}->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
+
 	// If we *do* meet condition (2), then the added subnav item is currently being requested.
-	if ( ( bp_current_action() && bp_is_current_action( $r['slug'] ) ) || ( bp_is_user() && ! bp_current_action() && ( $r['screen_function'] == $bp->bp_nav[$r['parent_slug']]['screen_function'] ) ) ) {
+	if ( ( bp_current_action() && bp_is_current_action( $r['slug'] ) ) || ( bp_is_user() && ! bp_current_action() && ! empty( $parent_nav->screen_function ) && $r['screen_function'] == $parent_nav->screen_function ) ) {
 
 		// If this is for site admins only and the user is not one, don't create the subnav item.
@@ -586,5 +616,5 @@
 		}
 
-		$hooked = bp_core_maybe_hook_new_subnav_screen_function( $r );
+		$hooked = bp_core_maybe_hook_new_subnav_screen_function( $r, $component );
 
 		// If redirect args have been returned, perform the redirect now.
@@ -599,9 +629,12 @@
  *
  * @since 2.1.0
- *
- * @param array $subnav_item The subnav array added to bp_options_nav in `bp_core_new_subnav_item()`.
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param array  $subnav_item The subnav array added to the secondary navigation of
+ *                            the component in bp_core_new_subnav_item().
+ * @param string $component   The component the navigation is attached to. Defaults to 'members'.
  * @return array
  */
-function bp_core_maybe_hook_new_subnav_screen_function( $subnav_item ) {
+function bp_core_maybe_hook_new_subnav_screen_function( $subnav_item, $component = 'members' ) {
 	$retval = array(
 		'status' => '',
@@ -646,8 +679,13 @@
 			} elseif ( bp_is_user() ) {
 
+				$parent_nav_default = $bp->{$component}->nav->get_primary( array( 'slug' => $bp->default_component ), false );
+				if ( $parent_nav_default ) {
+					$parent_nav_default_item = reset( $parent_nav_default );
+				}
+
 				// Redirect to the displayed user's default
 				// component, as long as that component is
 				// publicly accessible.
-				if ( bp_is_my_profile() || ! empty( $bp->bp_nav[ $bp->default_component ]['show_for_displayed_user'] ) ) {
+				if ( bp_is_my_profile() || ( isset( $parent_nav_default_item ) && $parent_nav_default_item->show_for_displayed_user ) ) {
 					$message     = __( 'You do not have access to this page.', 'buddypress' );
 					$redirect_to = bp_displayed_user_domain();
@@ -692,56 +730,30 @@
 
 /**
- * Sort all subnavigation arrays.
- *
- * @since 1.1.0
- *
- * @return bool|null Returns false on failure.
- */
-function bp_core_sort_subnav_items() {
+ * Check whether a given nav item has subnav items.
+ *
+ * @since 1.5.0
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param string $nav_item  The slug of the top-level nav item whose subnav items you're checking.
+ *                          Default: the current component slug.
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
+ * @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 = '', $component = 'members' ) {
 	$bp = buddypress();
 
-	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' );
-
-/**
- * Check whether a given nav item has subnav items.
- *
- * @since 1.5.0
- *
- * @param string $nav_item The slug of the top-level nav item whose subnav items you're checking.
- *                         Default: the current component slug.
- * @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 = '' ) {
-	$bp = buddypress();
-
-	if ( !$nav_item )
+	if ( ! isset( $bp->{$component}->nav ) ) {
+		return false;
+	}
+
+	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;
+		if ( bp_is_group() ) {
+			$nav_item = bp_current_item();
+		}
+	}
+
+	$has_subnav = (bool) $bp->{$component}->nav->get_secondary( array( 'parent_slug' => $nav_item ), false );
 
 	/**
@@ -757,50 +769,44 @@
 
 /**
- * Remove a nav item from the navigation array.
+ * Deletes an item from the primary navigation of the specified component.
  *
  * @since 1.0.0
- *
- * @param int $parent_id The slug of the parent navigation item.
- * @return bool Returns false on failure, ie if the nav item can't be found.
- */
-function bp_core_remove_nav_item( $parent_id ) {
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param string $slug      The slug of the primary navigation item.
+ * @param string $component The component the navigation is attached to. Defaults to 'members'.
+ * @return bool Returns false on failure, True on success.
+ */
+function bp_core_remove_nav_item( $slug, $component = null ) {
 	$bp = buddypress();
 
-	// 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 ( empty( $bp->bp_nav[ $parent_id ] ) )
-		return false;
-
-	if ( $function = $bp->bp_nav[$parent_id]['screen_function'] ) {
-		// Remove our screen hook if screen function is callable.
-		if ( is_callable( $function ) ) {
-			remove_action( 'bp_screens', $function, 3 );
-		}
-	}
-
-	unset( $bp->bp_nav[$parent_id] );
-}
-
-/**
- * Remove a subnav item from the navigation array.
- *
- * @since 1.0.0
- *
- * @param string $parent_id The slug of the parent navigation item.
- * @param string $slug      The slug of the subnav item to be removed.
- */
-function bp_core_remove_subnav_item( $parent_id, $slug ) {
-	$bp = buddypress();
-
-	$screen_function = isset( $bp->bp_options_nav[$parent_id][$slug]['screen_function'] )
-		? $bp->bp_options_nav[$parent_id][$slug]['screen_function']
-		: false;
-
-	if ( ! empty( $screen_function ) ) {
+	// Backward compatibility for removing group nav items using the group slug as `$parent_slug`.
+	if ( ! $component && bp_is_active( 'groups' ) && isset( $bp->groups->nav ) ) {
+		if ( $bp->groups->nav->get_primary( array( 'slug' => $slug ) ) ) {
+			return bp_core_remove_nav_item( $slug, 'groups' );
+		}
+	}
+
+	if ( ! $component ) {
+		$component = 'members';
+	}
+
+	if ( ! isset( $bp->{$component}->nav ) ) {
+		return false;
+	}
+
+	$screen_functions = $bp->{$component}->nav->delete_nav( $slug );
+
+	// Reset backcompat nav items so that subsequent references will be correct.
+	if ( buddypress()->do_nav_backcompat ) {
+		$bp->bp_nav->reset();
+		$bp->bp_options_nav->reset();
+	}
+
+	if ( ! is_array( $screen_functions ) ) {
+		return false;
+	}
+
+	foreach ( $screen_functions as $screen_function ) {
 		// Remove our screen hook if screen function is callable.
 		if ( is_callable( $screen_function ) ) {
@@ -809,8 +815,56 @@
 	}
 
-	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]);
+	return true;
+}
+
+/**
+ * Deletes an item from the secondary navigation of the specified component.
+ *
+ * @since 1.0.0
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param string $parent_slug The slug of the primary navigation item.
+ * @param string $slug        The slug of the secondary item to be removed.
+ * @param string $component   The component the navigation is attached to. Defaults to 'members'.
+ * @return bool Returns false on failure, True on success.
+ */
+function bp_core_remove_subnav_item( $parent_slug, $slug, $component = null ) {
+	$bp = buddypress();
+
+	// Backward compatibility for removing group nav items using the group slug as `$parent_slug`.
+	if ( ! $component && bp_is_active( 'groups' ) && isset( $bp->groups->nav ) ) {
+		if ( $bp->groups->nav->get_primary( array( 'slug' => $parent_slug ) ) ) {
+			return bp_core_remove_subnav_item( $slug, $parent_slug, 'groups' );
+		}
+	}
+
+	if ( ! $component ) {
+		$component = 'members';
+	}
+
+	if ( ! isset( $bp->{$component}->nav ) ) {
+		return false;
+	}
+
+	$screen_functions = $bp->{$component}->nav->delete_nav( $slug, $parent_slug );
+
+	// Reset backcompat nav items so that subsequent references will be correct.
+	if ( buddypress()->do_nav_backcompat ) {
+		$bp->bp_nav->reset();
+		$bp->bp_options_nav->reset();
+	}
+
+	if ( ! is_array( $screen_functions ) ) {
+		return false;
+	}
+
+	$screen_function = reset( $screen_functions );
+
+	// Remove our screen hook if screen function is callable.
+	if ( is_callable( $screen_function ) ) {
+		remove_action( 'bp_screens', $screen_function, 3 );
+	}
+
+	return true;
 }
 
@@ -819,11 +873,25 @@
  *
  * @since 1.0.0
+ * @since 2.6.0 Introduced the `$component` parameter.
  *
  * @param string $parent_slug The slug of the parent navigation item.
- */
-function bp_core_reset_subnav_items( $parent_slug ) {
+ * @param string $component   The component the navigation is attached to. Defaults to 'members'.
+ */
+function bp_core_reset_subnav_items( $parent_slug, $component = 'members' ) {
 	$bp = buddypress();
 
-	unset( $bp->bp_options_nav[$parent_slug] );
+	if ( ! isset( $bp->{$component}->nav ) ) {
+		return;
+	}
+
+	$subnav_items = $bp->{$component}->nav->get_secondary( array( 'parent_slug' => $parent_slug ), false );
+
+	if ( ! $subnav_items ) {
+		return;
+	}
+
+	foreach( $subnav_items as $subnav_item ) {
+		$bp->{$component}->nav->delete_nav( $subnav_item->slug, $parent_slug );
+	}
 }
 
Index: /trunk/src/bp-core/bp-core-classes.php
===================================================================
--- /trunk/src/bp-core/bp-core-classes.php	(revision 10744)
+++ /trunk/src/bp-core/bp-core-classes.php	(revision 10745)
@@ -31,2 +31,9 @@
 require dirname( __FILE__ ) . '/classes/class-bp-email-delivery.php';
 require dirname( __FILE__ ) . '/classes/class-bp-phpmailer.php';
+require dirname( __FILE__ ) . '/classes/class-bp-core-nav.php';
+require dirname( __FILE__ ) . '/classes/class-bp-core-nav-item.php';
+
+if ( buddypress()->do_nav_backcompat ) {
+	require dirname( __FILE__ ) . '/classes/class-bp-core-bp-nav-backcompat.php';
+	require dirname( __FILE__ ) . '/classes/class-bp-core-bp-options-nav-backcompat.php';
+}
Index: /trunk/src/bp-core/bp-core-functions.php
===================================================================
--- /trunk/src/bp-core/bp-core-functions.php	(revision 10744)
+++ /trunk/src/bp-core/bp-core-functions.php	(revision 10745)
@@ -2444,9 +2444,6 @@
 	}
 
-	// Pull up a list of items registered in BP's top-level nav array.
-	$bp_menu_items = buddypress()->bp_nav;
-
-	// Alphabetize.
-	$bp_menu_items = bp_alpha_sort_by_key( $bp_menu_items, 'name' );
+	// Pull up a list of items registered in BP's primary nav for the member.
+	$bp_menu_items = buddypress()->members->nav->get_primary();
 
 	// Some BP nav menu items will not be represented in bp_nav, because
@@ -2576,5 +2573,5 @@
  *
  * @param string $slug The slug of the nav item: login, register, or one of the
- *                     slugs from buddypress()->bp_nav.
+ *                     slugs from the members navigation.
  * @return string $nav_item_url The URL generated for the current user.
  */
Index: /trunk/src/bp-core/bp-core-template.php
===================================================================
--- /trunk/src/bp-core/bp-core-template.php	(revision 10744)
+++ /trunk/src/bp-core/bp-core-template.php	(revision 10745)
@@ -14,5 +14,5 @@
  * Output the "options nav", the secondary-level single item navigation menu.
  *
- * Uses the $bp->bp_options_nav global to render out the sub navigation for the
+ * Uses the component's 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.
@@ -41,13 +41,23 @@
 	$selected_item   = bp_current_action();
 
+	// Default to the Members nav.
 	if ( ! bp_is_single_item() ) {
-		if ( !isset( $bp->bp_options_nav[$component_index] ) || count( $bp->bp_options_nav[$component_index] ) < 1 ) {
+		// Set the parent slug, if not provided.
+		if ( empty( $parent_slug ) ) {
+			$parent_slug = $component_index;
+		}
+
+		$secondary_nav_items = $bp->members->nav->get_secondary( array( 'parent_slug' => $parent_slug ) );
+
+		if ( ! $secondary_nav_items ) {
 			return false;
-		} else {
-			$the_index = $component_index;
-		}
+		}
+
+	// For a single item, try to use the component's nav.
 	} else {
 		$current_item = bp_current_item();
-
+		$single_item_component = bp_current_component();
+
+		// Adjust the selected nav item for the current single item if needed.
 		if ( ! empty( $parent_slug ) ) {
 			$current_item  = $parent_slug;
@@ -55,19 +65,24 @@
 		}
 
-		if ( !isset( $bp->bp_options_nav[$current_item] ) || count( $bp->bp_options_nav[$current_item] ) < 1 ) {
+		// If the nav is not defined by the parent component, look in the Members nav.
+		if ( ! isset( $bp->{$single_item_component}->nav ) ) {
+			$secondary_nav_items = $bp->members->nav->get_secondary( array( 'parent_slug' => $current_item ) );
+		} else {
+			$secondary_nav_items = $bp->{$single_item_component}->nav->get_secondary( array( 'parent_slug' => $current_item ) );
+		}
+
+		if ( ! $secondary_nav_items ) {
 			return false;
-		} else {
-			$the_index = $current_item;
 		}
 	}
 
 	// Loop through each navigation item.
-	foreach ( (array) $bp->bp_options_nav[$the_index] as $subnav_item ) {
-		if ( empty( $subnav_item['user_has_access'] ) ) {
+	foreach ( $secondary_nav_items as $subnav_item ) {
+		if ( empty( $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'] == $selected_item ) {
+		if ( $subnav_item->slug === $selected_item ) {
 			$selected = ' class="current selected"';
 		} else {
@@ -89,5 +104,5 @@
 		 * @param string $selected_item Current action.
 		 */
-		echo apply_filters( 'bp_get_options_nav_' . $subnav_item['css_id'], '<li id="' . esc_attr( $subnav_item['css_id'] . '-' . $list_type . '-li' ) . '" ' . $selected . '><a id="' . esc_attr( $subnav_item['css_id'] ) . '" href="' . esc_url( $subnav_item['link'] ) . '">' . $subnav_item['name'] . '</a></li>', $subnav_item, $selected_item );
+		echo apply_filters( 'bp_get_options_nav_' . $subnav_item->css_id, '<li id="' . esc_attr( $subnav_item->css_id . '-' . $list_type . '-li' ) . '" ' . $selected . '><a id="' . esc_attr( $subnav_item->css_id ) . '" href="' . esc_url( $subnav_item->link ) . '">' . $subnav_item->name . '</a></li>', $subnav_item, $selected_item );
 	}
 }
@@ -3011,7 +3026,12 @@
 		$component_subnav_name = '';
 
+		if ( ! empty( $bp->members->nav ) ) {
+			$primary_nav_item = $bp->members->nav->get_primary( array( 'slug' => $component_id ), false );
+			$primary_nav_item = reset( $primary_nav_item );
+		}
+
 		// Use the component nav name.
-		if ( ! empty( $bp->bp_nav[$component_id] ) ) {
-			$component_name = _bp_strip_spans_from_title( $bp->bp_nav[ $component_id ]['name'] );
+		if ( ! empty( $primary_nav_item->name ) ) {
+			$component_name = _bp_strip_spans_from_title( $primary_nav_item->name );
 
 		// Fall back on the component ID.
@@ -3020,11 +3040,18 @@
 		}
 
+		if ( ! empty( $bp->members->nav ) ) {
+			$secondary_nav_item = $bp->members->nav->get_secondary( array(
+				'parent_slug' => $component_id,
+				'slug'        => bp_current_action()
+			), false );
+
+			if ( $secondary_nav_item ) {
+				$secondary_nav_item = reset( $secondary_nav_item );
+			}
+		}
+
 		// Append action name if we're on a member component sub-page.
-		if ( ! empty( $bp->bp_options_nav[ $component_id ] ) && ! empty( $bp->canonical_stack['action'] ) ) {
-			$component_subnav_name = wp_filter_object_list( $bp->bp_options_nav[ $component_id ], array( 'slug' => bp_current_action() ), 'and', 'name' );
-
-			if ( ! empty( $component_subnav_name ) ) {
-				$component_subnav_name = array_shift( $component_subnav_name );
-			}
+		if ( ! empty( $secondary_nav_item->name ) && ! empty( $bp->canonical_stack['action'] ) ) {
+			$component_subnav_name = $secondary_nav_item->name;
 		}
 
@@ -3046,12 +3073,26 @@
 		}
 
-	// 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'] : '';
-		$bp_title_parts = array( $bp->bp_options_title, $subnav );
-
-	// A single item from a component other than groups.
+	// A single item from a component other than Members.
 	} elseif ( bp_is_single_item() ) {
-		$bp_title_parts = array( $bp->bp_options_title, $bp->bp_options_nav[ bp_current_item() ][ bp_current_action() ]['name'] );
+		$component_id = bp_current_component();
+
+		if ( ! empty( $bp->{$component_id}->nav ) ) {
+			$secondary_nav_item = $bp->{$component_id}->nav->get_secondary( array(
+				'parent_slug' => bp_current_item(),
+				'slug'        => bp_current_action()
+			), false );
+
+			if ( $secondary_nav_item ) {
+				$secondary_nav_item = reset( $secondary_nav_item );
+			}
+		}
+
+		$single_item_subnav = '';
+
+		if ( ! empty( $secondary_nav_item->name ) ) {
+			$single_item_subnav = $secondary_nav_item->name;
+		}
+
+		$bp_title_parts = array( $bp->bp_options_title, $single_item_subnav );
 
 	// An index or directory.
@@ -3429,77 +3470,56 @@
  *
  * @since 1.7.0
- *
+ * @since 2.6.0 Introduced the `$component` parameter.
+ *
+ * @param string $component Optional. Component whose nav items are being fetched.
  * @return array A multidimensional array of all navigation items.
  */
-function bp_get_nav_menu_items() {
-	$menus = $selected_menus = array();
-
-	// Get the second level menus.
-	foreach ( (array) buddypress()->bp_options_nav as $parent_menu => $sub_menus ) {
-
-		// The root menu's ID is "xprofile", but the Profile submenus are using "profile". See BP_Core::setup_nav().
-		if ( 'profile' === $parent_menu ) {
-			$parent_menu = 'xprofile';
-		}
-
-		// Sort the items in this menu's navigation by their position property.
-		$second_level_menus = (array) $sub_menus;
-		usort( $second_level_menus, '_bp_nav_menu_sort' );
-
-		// Iterate through the second level menus.
-		foreach( $second_level_menus as $sub_nav ) {
-
-			// Skip items we don't have access to.
-			if ( empty( $sub_nav['user_has_access'] ) ) {
-				continue;
-			}
-
-			// Add this menu.
-			$menu         = new stdClass;
-			$menu->class  = array( 'menu-child' );
-			$menu->css_id = $sub_nav['css_id'];
-			$menu->link   = $sub_nav['link'];
-			$menu->name   = $sub_nav['name'];
-			$menu->parent = $parent_menu;  // Associate this sub nav with a top-level menu.
-
-			// If we're viewing this item's screen, record that we need to mark its parent menu to be selected.
-			if ( $sub_nav['slug'] == bp_current_action() ) {
-				$menu->class[]    = 'current-menu-item';
-				$selected_menus[] = $parent_menu;
-			}
-
-			$menus[] = $menu;
-		}
-	}
-
-	// Get the top-level menu parts (Friends, Groups, etc) and sort by their position property.
-	$top_level_menus = (array) buddypress()->bp_nav;
-	usort( $top_level_menus, '_bp_nav_menu_sort' );
-
-	// Iterate through the top-level menus.
-	foreach ( $top_level_menus as $nav ) {
-
-		// Skip items marked as user-specific if you're not on your own profile.
-		if ( empty( $nav['show_for_displayed_user'] ) && ! bp_core_can_edit_settings()  ) {
-			continue;
-		}
-
+function bp_get_nav_menu_items( $component = 'members' ) {
+	$bp    = buddypress();
+	$menus = array();
+
+	if ( ! isset( $bp->{$component}->nav ) ) {
+		return $menus;
+	}
+
+	// Get the item nav and build the menus.
+	foreach ( $bp->{$component}->nav->get_item_nav() as $nav_menu ) {
 		// Get the correct menu link. See https://buddypress.trac.wordpress.org/ticket/4624.
-		$link = bp_loggedin_user_domain() ? str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $nav['link'] ) : trailingslashit( bp_displayed_user_domain() . $nav['link'] );
+		$link = bp_loggedin_user_domain() ? str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $nav_menu->link ) : trailingslashit( bp_displayed_user_domain() . $nav_menu->link );
 
 		// Add this menu.
 		$menu         = new stdClass;
 		$menu->class  = array( 'menu-parent' );
-		$menu->css_id = $nav['css_id'];
-		$menu->link   = $link;
-		$menu->name   = $nav['name'];
+		$menu->css_id = $nav_menu->css_id;
+		$menu->link   = $nav_menu->link;
+		$menu->name   = $nav_menu->name;
 		$menu->parent = 0;
 
-		// Check if we need to mark this menu as selected.
-		if ( in_array( $nav['css_id'], $selected_menus ) ) {
-			$menu->class[] = 'current-menu-parent';
+		if ( ! empty( $nav_menu->children ) ) {
+			$submenus = array();
+
+			foreach( $nav_menu->children as $sub_menu ) {
+				$submenu = new stdClass;
+				$submenu->class  = array( 'menu-child' );
+				$submenu->css_id = $sub_menu->css_id;
+				$submenu->link   = $sub_menu->link;
+				$submenu->name   = $sub_menu->name;
+				$submenu->parent = $nav_menu->slug;
+
+				// If we're viewing this item's screen, record that we need to mark its parent menu to be selected.
+				if ( $sub_menu->slug == bp_current_action() ) {
+					$menu->class[]    = 'current-menu-parent';
+					$submenu->class[] = 'current-menu-item';
+				}
+
+				$submenus[] = $submenu;
+			}
 		}
 
 		$menus[] = $menu;
+
+		if ( ! empty( $submenus ) ) {
+			$menus = array_merge( $menus, $submenus );
+		}
 	}
 
Index: /trunk/src/bp-core/classes/class-bp-component.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-component.php	(revision 10744)
+++ /trunk/src/bp-core/classes/class-bp-component.php	(revision 10745)
@@ -490,10 +490,10 @@
 		// No sub nav items without a main nav item.
 		if ( !empty( $main_nav ) ) {
-			bp_core_new_nav_item( $main_nav );
+			bp_core_new_nav_item( $main_nav, 'members' );
 
 			// Sub nav items are not required.
 			if ( !empty( $sub_nav ) ) {
 				foreach( (array) $sub_nav as $nav ) {
-					bp_core_new_subnav_item( $nav );
+					bp_core_new_subnav_item( $nav, 'members' );
 				}
 			}
Index: /trunk/src/bp-core/classes/class-bp-core-bp-nav-backcompat.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-core-bp-nav-backcompat.php	(revision 10745)
+++ /trunk/src/bp-core/classes/class-bp-core-bp-nav-backcompat.php	(revision 10745)
@@ -0,0 +1,270 @@
+<?php
+/**
+ * Backward compatibility for the $bp->bp_nav global.
+ *
+ * @since 2.6.0
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * bp_nav backward compatibility class.
+ *
+ * This class is used to provide backward compatibility for extensions that access and modify
+ * the $bp->bp_nav global.
+ *
+ * @since 2.6.0
+ */
+class BP_Core_BP_Nav_BackCompat implements ArrayAccess {
+	/**
+	 * Nav items.
+	 *
+	 * @since 2.6.0
+	 * @access public
+	 * @var array
+	 */
+	public $backcompat_nav = array();
+
+	/**
+	 * Component to which nav items belong.
+	 *
+	 * @since 2.6.0
+	 * @access public
+	 * @var array
+	 */
+	public $component;
+
+	/**
+	 * Constructor.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param array $backcompat_nav Optional. Array of nav items.
+	 */
+	public function __construct( $backcompat_nav = array() ) {
+		foreach ( $backcompat_nav as $key => $value ) {
+			if ( is_array( $value ) ) {
+				$this->backcompat_nav[ $key ] = new self( $value );
+			} else {
+				$this->backcompat_nav[ $key ] = $value;
+			}
+		}
+	}
+
+	/**
+	 * Assign a value to the nav array at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @param array $value  Nav item.
+	 */
+	public function offsetSet( $offset, $value ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'The bp_nav and bp_options_nav globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+		$bp = buddypress();
+
+		if ( is_array( $value ) ) {
+			$value = new self( $value );
+		}
+
+		if ( $offset !== null ) {
+			// Temporarily set the backcompat_nav.
+			$this->backcompat_nav[ $offset ] = $value;
+
+			$args = $this->to_array();
+			if ( isset( $args['parent_slug'] ) ) {
+				$this->get_component_nav( $args['parent_slug'] )->edit_nav( $args, $args['slug'], $args['parent_slug'] );
+			} elseif ( isset( $args['slug'] ) ) {
+				$bp->members->nav->edit_nav( $args, $args['slug'] );
+			}
+		}
+	}
+
+	/**
+	 * Get a value of the nav array at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return BP_Core_BP_Nav_BackCompat
+	 */
+	public function offsetGet( $offset ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'The bp_nav and bp_options_nav globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+//		if ( ! isset( $this->backcompat_nav[ $offset ] ) ) {
+			$nav = $this->get_nav( $offset );
+			if ( $nav && isset( $nav[ $offset ] ) ) {
+				$this->backcompat_nav[ $offset ] = new self( $nav[ $offset ] );
+			}
+//		}
+
+		return $this->backcompat_nav[ $offset ];
+	}
+
+	/**
+	 * Check whether nav array has a value at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return bool
+	 */
+	public function offsetExists( $offset ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'The bp_nav and bp_options_nav globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+		if ( isset( $this->backcompat_nav[ $offset ] ) ) {
+			return true;
+		}
+
+		$nav = $this->get_nav( $offset );
+		if ( $nav && isset( $nav[ $offset ] ) ) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Unset a nav array value at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 */
+	public function offsetUnset( $offset ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'The bp_nav and bp_options_nav globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+		// For top-level nav items, the backcompat nav hasn't yet been initialized.
+		if ( ! isset( $this->backcompat_nav[ $offset ] ) ) {
+			buddypress()->members->nav->delete_nav( $offset );
+			unset( $this->backcompat_nav[ $offset ] );
+		}
+	}
+
+	/**
+	 * Set the component to which the nav belongs.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param string $component
+	 */
+	public function set_component( $component ) {
+		$this->component = $component;
+	}
+
+	/**
+	 * Get the component to which the a nav item belongs.
+	 *
+	 * We use the following heuristic to guess, based on an offset, which component the item belongs to:
+	 *   - If this is a group, and the offset is the same as the current group's slug, it's a group nav item.
+	 *   - Otherwise, it's a member nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return string
+	 */
+	public function get_component( $offset = '' ) {
+		if ( ! isset( $this->component ) ) {
+			if ( bp_is_active( 'groups' ) && $offset === bp_get_current_group_slug() ) {
+				$this->component = 'groups';
+			} else {
+				$this->component = 'members';
+			}
+		}
+
+		return $this->component;
+	}
+
+	/**
+	 * Reset the cached nav items.
+	 *
+	 * Called when the nav API removes items from the nav array.
+	 *
+	 * @since 2.6.0
+	 */
+	public function reset() {
+		$this->backcompat_nav = array();
+	}
+
+	/**
+	 * Get the nav object corresponding to the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return bool|array
+	 */
+	protected function get_nav( $offset ) {
+		$bp = buddypress();
+
+		$component_nav = $this->get_component_nav( $offset );
+		$primary_nav   = $component_nav->get_primary( array( 'slug' => $offset ), false );
+
+		$nav = array();
+
+		if ( empty( $primary_nav ) ) {
+			return $nav;
+		}
+
+		foreach ( $primary_nav as $item ) {
+			$nav[ $item->slug ] = (array) $item;
+		}
+
+		return $nav;
+	}
+
+	/**
+	 * Get the BP_Core_Nav object corresponding to the component, based on a nav item name.
+	 *
+	 * The way bp_nav was previously organized makes it impossible to know for sure which component's nav is
+	 * being referenced by a given nav item name. We guess in the following manner:
+	 *   - If we're looking at a group, and the nav item name (`$offset`) is the same as the slug of the current
+	 *     group, we assume that the proper component nav is 'groups'.
+	 *   - Otherwise, fall back on 'members'.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param string $offset Nav item name.
+	 * @return BP_Core_Nav
+	 */
+	protected function get_component_nav( $offset = '' ) {
+		$component = $this->get_component( $offset );
+
+		$bp = buddypress();
+		if ( ! isset( $bp->{$component}->nav ) ) {
+			return false;
+		}
+
+		return $bp->{$component}->nav;
+	}
+
+	/**
+	 * Get the nav data, formatted as a flat array.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @return array
+	 */
+	protected function to_array() {
+		return $this->backcompat_nav;
+	}
+}
Index: /trunk/src/bp-core/classes/class-bp-core-bp-options-nav-backcompat.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-core-bp-options-nav-backcompat.php	(revision 10745)
+++ /trunk/src/bp-core/classes/class-bp-core-bp-options-nav-backcompat.php	(revision 10745)
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Backward compatibility for the $bp->bp_options_nav global.
+ *
+ * @since 2.6.0
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * bp_options_nav backward compatibility class.
+ *
+ * This class is used to provide backward compatibility for extensions that access and modify
+ * the $bp->bp_options_nav global.
+ *
+ * @since 2.6.0
+ */
+class BP_Core_BP_Options_Nav_BackCompat extends BP_Core_BP_Nav_BackCompat {
+	/**
+	 * Parent slug of the current nav item.
+	 *
+	 * @since 2.6.0
+	 * @access protected
+	 * @var string
+	 */
+	protected $parent_slug = '';
+
+	/**
+	 * Get a value of the nav array at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return BP_Core_BP_Nav_BackCompat
+	 */
+	public function offsetGet( $offset ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'These globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+		if ( empty( $this->backcompat_nav[ $offset ] ) ) {
+			$nav = $this->get_nav( $offset );
+			if ( $nav ) {
+				$subnavs = $this->get_component_nav( $offset )->get_secondary( array( 'parent_slug' => $offset ) );
+				$subnav_keyed = array();
+				if ( $subnavs ) {
+					foreach ( $subnavs as $subnav ) {
+						$subnav_keyed[ $subnav->slug ] = (array) $subnav;
+					}
+				}
+
+				$subnav_object = new self( $subnav_keyed );
+				$subnav_object->set_component( $this->get_component() );
+				$subnav_object->set_parent_slug( $offset );
+
+				$this->backcompat_nav[ $offset ] = $subnav_object;
+			}
+		}
+
+		return $this->backcompat_nav[ $offset ];
+	}
+
+	/**
+	 * Unset a nav array value at the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 */
+	public function offsetUnset( $offset ) {
+		_doing_it_wrong(
+			'bp_nav',
+			__( 'These globals should not be used directly and are deprecated. Please use the BuddyPress nav functions instead.', 'buddypress' ),
+			'2.6.0'
+		);
+
+		$this->get_component_nav( $offset )->delete_nav( $offset, $this->get_parent_slug() );
+
+		// Clear the cached nav.
+		unset( $this->backcompat_nav[ $offset ] );
+	}
+
+	/**
+	 * Get the parent slug of the current nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @return string
+	 */
+	public function get_parent_slug() {
+		return $this->parent_slug;
+	}
+
+	/**
+	 * Set the parent slug of the current nav item.
+	 *
+	 * @since 2.6.0
+	 */
+	public function set_parent_slug( $slug ) {
+		$this->parent_slug = $slug;
+	}
+
+	/**
+	 * Get the nav object corresponding to the specified offset.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param mixed $offset Array offset.
+	 * @return bool|array
+	 */
+	public function get_nav( $offset ) {
+		$nav = parent::get_nav( $offset );
+
+		if ( ! $nav ) {
+			$component_nav = $this->get_component_nav( $offset );
+			$secondary_nav = $component_nav->get_secondary( array( 'slug' => $offset ), false );
+
+			$nav = array();
+
+			if ( empty( $primary_nav ) ) {
+				return $nav;
+			}
+
+			foreach ( $primary_nav as $item ) {
+				$nav[ $item->slug ] = (array) $item;
+			}
+		}
+
+		return $nav;
+	}
+}
Index: /trunk/src/bp-core/classes/class-bp-core-nav-item.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-core-nav-item.php	(revision 10745)
+++ /trunk/src/bp-core/classes/class-bp-core-nav-item.php	(revision 10745)
@@ -0,0 +1,34 @@
+<?php
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+if ( class_exists( 'ArrayObject' ) ) :
+
+/**
+ * Navigation item.
+ *
+ * @since 2.6.0
+ */
+class BP_Core_Nav_Item extends ArrayObject {
+	public function __construct( $data ) {
+		parent::__construct( $data, ArrayObject::ARRAY_AS_PROPS );
+	}
+}
+
+else :
+
+/**
+ * Navigation item.
+ *
+ * @since 2.6.0
+ */
+class BP_Core_Nav_Item {
+	public function __construct( $data ) {
+		foreach ( $data as $key => $value ) {
+			$this->key = $value;
+		}
+	}
+}
+
+endif;
Index: /trunk/src/bp-core/classes/class-bp-core-nav.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-core-nav.php	(revision 10745)
+++ /trunk/src/bp-core/classes/class-bp-core-nav.php	(revision 10745)
@@ -0,0 +1,400 @@
+<?php
+/**
+ * Core component class.
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ * @since  2.6.0
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * BuddyPress Nav.
+ *
+ * This class is used to build each component's navigation.
+ *
+ * @since 2.6.0
+ */
+class BP_Core_Nav {
+	/**
+	 * An associative array containing the nav items for the object ID.
+	 *
+	 * @since 2.6.0
+	 * @var array
+	 */
+	protected $nav;
+
+	/**
+	 * The current object ID.
+	 *
+	 * @since 2.6.0
+	 * @var int
+	 */
+	private $object_id;
+
+	/**
+	 * Initializes the Nav belonging to the specified object.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param int $object_id The item ID to build the nav for. Default is the displayed user ID.
+	 */
+	public function __construct( $object_id = 0 ) {
+		if ( empty( $object_id ) ) {
+			$this->object_id = (int) bp_displayed_user_id();
+		} else {
+			$this->object_id = (int) $object_id;
+		}
+
+		$this->nav[ $this->object_id ] = array();
+	}
+
+	/**
+	 * Checks whether a nav item is set.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  string $key The requested nav slug.
+	 * @return bool True if the nav item is set, false otherwise.
+	 */
+	public function __isset( $key ) {
+		return isset( $this->nav[ $this->object_id ][ $key ] );
+	}
+
+	/**
+	 * Gets a nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  string $key The requested nav slug.
+	 * @return mixed The value corresponding to the requested nav item.
+	 */
+	public function __get( $key ) {
+		if ( ! isset( $this->nav[ $this->object_id ][ $key ] ) ) {
+			$this->nav[ $this->object_id ][ $key ] = null;
+		}
+
+		return $this->nav[ $this->object_id ][ $key ];
+	}
+
+	/**
+	 * Sets a nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  string  $key   The requested nav slug.
+	 * @param  mixed   $value The value of the nav item.
+	 */
+	public function __set( $key, $value ) {
+		if ( is_array( $value ) ) {
+			$value['primary'] = true;
+		}
+
+		$this->nav[ $this->object_id ][ $key ] = new BP_Core_Nav_Item( $value );
+	}
+
+	/**
+	 * Gets a specific nav item or array of nav items.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  string $key The nav item slug to get. Optional.
+	 * @return mixed       An array of nav item, a single nav item, or null if none found.
+	 */
+	public function get( $key = '' ) {
+		$return = null;
+
+		// Return the requested nav item.
+		if ( ! empty( $key ) ) {
+			if ( ! isset( $this->nav[ $this->object_id ][ $key ] ) ) {
+				$return = null;
+			} else {
+				$return = $this->nav[ $this->object_id ][ $key ];
+			}
+
+		// Return all nav item items.
+		} else {
+			$return = $this->nav[ $this->object_id ];
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Adds a new nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param array $args The nav item's arguments.
+	 * @return BP_Core_Nav_Item
+	 */
+	public function add_nav( $args ) {
+		if ( empty( $args['slug'] ) ) {
+			return false;
+		}
+
+		// We have a child and the parent exists.
+		if ( ! empty( $args['parent_slug'] ) ) {
+			$slug              = $args['parent_slug'] . '/' . $args['slug'];
+			$args['secondary'] = true;
+
+		// This is a parent.
+		} else {
+			$slug            = $args['slug'];
+			$args['primary'] = true;
+		}
+
+		// Add to the nav.
+		$this->nav[ $this->object_id ][ $slug ] = new BP_Core_Nav_Item( $args );
+
+		return $this->nav[ $this->object_id ][ $slug ];
+	}
+
+	/**
+	 * Edits a nav item.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param array  $args        The nav item's arguments.
+	 * @param string $slug        The slug of the nav item.
+	 * @param string $parent_slug The slug of the parent nav item (required to edit a child).
+	 * @return BP_Core_Nav_Item
+	 */
+	public function edit_nav( $args = array(), $slug = '', $parent_slug = '' ) {
+		if ( empty( $slug ) ) {
+			return false;
+		}
+
+		// We're editing a parent!
+		if ( empty( $parent_slug ) ) {
+			$nav_items = $this->get_primary( array( 'slug' => $slug ), false );
+
+			if ( ! $nav_items ) {
+				return false;
+			}
+
+			$nav_item = reset( $nav_items );
+			$this->nav[ $this->object_id ][ $slug ] = new BP_Core_Nav_Item( wp_parse_args( $args, (array) $nav_item ) );
+
+			// Return the edited object.
+			return $this->nav[ $this->object_id ][ $slug ];
+
+		// We're editing a child.
+		} else {
+			$sub_items = $this->get_secondary( array( 'parent_slug' => $parent_slug, 'slug' => $slug ), false );
+
+			if ( ! $sub_items ) {
+				return false;
+			}
+
+			$sub_item = reset( $sub_items );
+
+			$params = wp_parse_args( $args, (array) $sub_item );
+
+			// When we have parents, it's for life, we can't change them!
+			if ( empty( $params['parent_slug'] ) || $parent_slug !== $params['parent_slug'] ) {
+				return false;
+			}
+
+			$this->nav[ $this->object_id ][ $parent_slug . '/' . $slug ] = new BP_Core_Nav_Item( $params );
+
+			// Return the edited object.
+			return $this->nav[ $this->object_id ][ $parent_slug . '/' . $slug ];
+		}
+	}
+
+	/**
+	 * Unset an item or a subitem of the nav.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param string $key     The slug of the main item.
+	 * @param string $sub_key The slug of the sub item.
+	 * @return bool|callable|array False on failure, the screen function(s) on success.
+	 */
+	public function delete_nav( $slug, $parent_slug = '' ) {
+		if ( empty( $slug ) ) {
+			return false;
+		}
+
+		// We're deleting a child
+		if ( ! empty( $parent_slug ) ) {
+
+			// Validate the subnav
+			$sub_items = $this->get_secondary( array( 'parent_slug' => $parent_slug, 'slug' => $slug ), false );
+
+			if ( ! $sub_items ) {
+				return false;
+			}
+
+			$sub_item = reset( $sub_items );
+
+			if ( empty( $sub_item->slug ) ) {
+				return false;
+			}
+
+			// Delete the child
+			unset( $this->nav[ $this->object_id ][ $parent_slug . '/' . $slug ] );
+
+			// Return the deleted item's screen function
+			return array( $sub_item->screen_function );
+
+		// We're deleting a parent
+		} else {
+			// Validate the nav
+			$nav_items = $this->get_primary( array( 'slug' => $slug ), false );
+
+			if ( ! $nav_items ) {
+				return false;
+			}
+
+			$nav_item = reset( $nav_items );
+
+			if ( empty( $nav_item->slug ) ) {
+				return false;
+			}
+
+			$screen_functions = array( $nav_item->screen_function );
+
+			// Life's unfair, children won't survive the parent :(
+			$sub_items = $this->get_secondary( array( 'parent_slug' => $nav_item->slug ), false );
+
+			if ( ! empty( $sub_items ) ) {
+				foreach ( $sub_items as $sub_item ) {
+					$screen_functions[] = $sub_item->screen_function;
+
+					// Delete the child
+					unset( $this->nav[ $this->object_id ][ $nav_item->slug . '/' . $sub_item->slug ] );
+				}
+			}
+
+			// Delete the parent.
+			unset( $this->nav[ $this->object_id ][ $nav_item->slug ] );
+
+			// Return the deleted item's screen functions.
+			return array_unique( $screen_functions );
+		}
+	}
+
+	/**
+	 * Sorts a list of nav items.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param array $items The nav items to sort.
+	 * @return array
+	 */
+	public function sort_nav( $items ) {
+		$sorted = array();
+
+		foreach ( $items as $item ) {
+			// Default position
+			$position = 99;
+
+			if ( isset( $item->position ) ) {
+				$position = (int) $item->position;
+			}
+
+			// If position is already taken, move to the first next available
+			if ( isset( $sorted[ $position ] ) ) {
+				$sorted_keys = array_keys( $sorted );
+
+				do {
+					$position += 1;
+				} while ( in_array( $position, $sorted_keys ) );
+			}
+
+			$sorted[ $position ] = $item;
+		}
+
+		ksort( $sorted );
+		return $sorted;
+	}
+
+	/**
+	 * Gets the primary nav items.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  array $args Filters to select the specific primary items. See wp_list_filter().
+	 * @param  bool  $sort True to sort the nav items. False otherwise.
+	 * @return array The list of primary objects nav
+	 */
+	public function get_primary( $args = array(), $sort = true ) {
+		$params = wp_parse_args( $args, array( 'primary' => true ) );
+
+		// This parameter is not overridable
+		if ( empty( $params['primary'] ) ) {
+			return false;
+		}
+
+		$primary_nav = wp_list_filter( $this->nav[ $this->object_id ], $params );
+
+		if ( ! $primary_nav ) {
+			return false;
+		}
+
+		if ( true !== $sort ) {
+			return $primary_nav;
+		}
+
+		return $this->sort_nav( $primary_nav );
+	}
+
+	/**
+	 * Gets the secondary nav items.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param  array $args Filters to select the specific secondary items. See wp_list_filter().
+	 * @param  bool  $sort True to sort the nav items. False otherwise.
+	 * @return array The list of secondary objects nav
+	 */
+	public function get_secondary( $args = array(), $sort = true ) {
+		$params = wp_parse_args( $args, array( 'parent_slug' => '' ) );
+
+		// No need to search children if the parent is not set
+		if ( empty( $params['parent_slug'] ) && empty( $params['secondary'] ) ) {
+			return false;
+		}
+
+		$secondary_nav = wp_list_filter( $this->nav[ $this->object_id ], $params );
+
+		if ( ! $secondary_nav ) {
+			return false;
+		}
+
+		if ( true !== $sort ) {
+			return $secondary_nav;
+		}
+
+		return $this->sort_nav( $secondary_nav );
+	}
+
+	/**
+	 * Gets a nested list of visible nav items.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @return array The list of visible nav items.
+	 */
+	public function get_item_nav() {
+		$primary_nav_items = $this->get_primary( array( 'show_for_displayed_user' => true ) );
+
+		if ( $primary_nav_items ) {
+			foreach( $primary_nav_items as $key_nav => $primary_nav ) {
+				// Try to get the children
+				$children = $this->get_secondary( array( 'parent_slug' => $primary_nav->slug, 'user_has_access' => true ) );
+
+				if ( $children ) {
+					$primary_nav_items[ $key_nav ] = clone( $primary_nav );
+					$primary_nav_items[ $key_nav ]->children = $children;
+				}
+			}
+		}
+
+		return $primary_nav_items;
+	}
+}
Index: /trunk/src/bp-core/classes/class-bp-core.php
===================================================================
--- /trunk/src/bp-core/classes/class-bp-core.php	(revision 10744)
+++ /trunk/src/bp-core/classes/class-bp-core.php	(revision 10745)
@@ -254,4 +254,10 @@
 		// bp-notifications instead.
 		$bp->core->table_name_notifications = $bp->table_prefix . 'bp_notifications';
+
+		// Backward compatibility for plugins modifying the legacy bp_nav and bp_options_nav global properties.
+		if ( buddypress()->do_nav_backcompat ) {
+			$bp->bp_nav         = new BP_Core_BP_Nav_BackCompat();
+			$bp->bp_options_nav = new BP_Core_BP_Options_Nav_BackCompat();
+		}
 
 		/**
Index: /trunk/src/bp-core/deprecated/2.6.php
===================================================================
--- /trunk/src/bp-core/deprecated/2.6.php	(revision 10744)
+++ /trunk/src/bp-core/deprecated/2.6.php	(revision 10745)
@@ -22,2 +22,29 @@
 	<?php
 }
+
+/**
+ * Sort the navigation menu items.
+ *
+ * The sorting is split into a separate function because it can only happen
+ * after all plugins have had a chance to register their navigation items.
+ *
+ * @since 1.0.0
+ * @deprecated 2.6.0
+ *
+ * @return bool|null Returns false on failure.
+ */
+function bp_core_sort_nav_items() {
+	_deprecated_function( __FUNCTION__, '2.6' );
+}
+
+/**
+ * Sort all subnavigation arrays.
+ *
+ * @since 1.1.0
+ * @deprecated 2.6.0
+ *
+ * @return bool|null Returns false on failure.
+ */
+function bp_core_sort_subnav_items() {
+	_deprecated_function( __FUNCTION__, '2.6' );
+}
Index: /trunk/src/bp-groups/bp-groups-adminbar.php
===================================================================
--- /trunk/src/bp-groups/bp-groups-adminbar.php	(revision 10744)
+++ /trunk/src/bp-groups/bp-groups-adminbar.php	(revision 10745)
@@ -48,13 +48,13 @@
 
 	// Index of the Manage tabs parent slug.
-	$nav_index = $bp->groups->current_group->slug . '_manage';
+	$secondary_nav_items = $bp->groups->nav->get_secondary( array( 'parent_slug' => $bp->groups->current_group->slug . '_manage' ) );
 
 	// Check if current group has Manage tabs.
-	if ( empty( $bp->bp_options_nav[ $nav_index ] ) ) {
+	if ( ! $secondary_nav_items ) {
 		return;
 	}
 
 	// Build the Group Admin menus.
-	foreach ( $bp->bp_options_nav[ $nav_index ] as $menu ) {
+	foreach ( $secondary_nav_items as $menu ) {
 		/**
 		 * Should we add the current manage link in the Group's "Edit" Admin Bar menu ?
@@ -64,17 +64,17 @@
 		 * the 'show_in_admin_bar' argument of your edit screen to true
 		 */
-		if ( $menu['show_in_admin_bar'] ) {
-			$title = sprintf( _x( 'Edit Group %s', 'Group WP Admin Bar manage links', 'buddypress' ), $menu['name'] );
+		if ( $menu->show_in_admin_bar ) {
+			$title = sprintf( _x( 'Edit Group %s', 'Group WP Admin Bar manage links', 'buddypress' ), $menu->name );
 
 			// Title is specific for delete.
-			if ( 'delete-group' == $menu['slug'] ) {
-				$title = sprintf( _x( '%s Group', 'Group WP Admin Bar delete link', 'buddypress' ), $menu['name'] );
+			if ( 'delete-group' == $menu->slug ) {
+				$title = sprintf( _x( '%s Group', 'Group WP Admin Bar delete link', 'buddypress' ), $menu->name );
 			}
 
 			$wp_admin_bar->add_menu( array(
 				'parent' => $bp->group_admin_menu_id,
-				'id'     => $menu['slug'],
+				'id'     => $menu->slug,
 				'title'  => $title,
-				'href'   => bp_get_groups_action_link( 'admin/' . $menu['slug'] )
+				'href'   => bp_get_groups_action_link( 'admin/' . $menu->slug )
 			) );
 		}
Index: /trunk/src/bp-groups/classes/class-bp-group-extension.php
===================================================================
--- /trunk/src/bp-groups/classes/class-bp-group-extension.php	(revision 10744)
+++ /trunk/src/bp-groups/classes/class-bp-group-extension.php	(revision 10745)
@@ -738,5 +738,5 @@
 				'user_has_access' => $user_can_see_nav_item,
 				'no_access_url'   => $group_permalink,
-			) );
+			), 'groups' );
 		}
 
@@ -753,5 +753,5 @@
 				'user_has_access' => $user_can_visit,
 				'no_access_url'   => $group_permalink,
-			) );
+			), 'groups' );
 
 			// When we are viewing the extension display page, set the title and options title.
@@ -968,5 +968,5 @@
 
 		// Add the tab to the manage navigation.
-		bp_core_new_subnav_item( $subnav_args );
+		bp_core_new_subnav_item( $subnav_args, 'groups' );
 
 		// Catch the edit screen and forward it to the plugin template.
Index: /trunk/src/bp-groups/classes/class-bp-groups-component.php
===================================================================
--- /trunk/src/bp-groups/classes/class-bp-groups-component.php	(revision 10744)
+++ /trunk/src/bp-groups/classes/class-bp-groups-component.php	(revision 10745)
@@ -253,4 +253,7 @@
 			// Check once if the current group has a custom front template.
 			$this->current_group->front_template = bp_groups_get_front_template( $this->current_group );
+
+			// Initialize the nav for the groups component.
+			$this->nav = new BP_Core_Nav( $this->current_group->id );
 
 		// Set current_group to 0 to prevent debug errors.
@@ -497,6 +500,9 @@
 			$sub_nav = array();
 
-			// Add 'Groups' to the main navigation.
-			$main_nav = array(
+			/*
+			 * The top-level Groups item is called 'Memberships' for legacy reasons.
+			 * It does not appear in the interface.
+			 */
+			bp_core_new_nav_item( array(
 				'name'                => __( 'Memberships', 'buddypress' ),
 				'slug'                => $this->current_group->slug,
@@ -505,5 +511,5 @@
 				'default_subnav_slug' => $this->default_extension,
 				'item_css_id'         => $this->id
-			);
+			), 'groups' );
 
 			$group_link = bp_get_group_permalink( $this->current_group );
@@ -676,5 +682,7 @@
 			}
 
-			parent::setup_nav( $main_nav, $sub_nav );
+			foreach ( $sub_nav as $nav ) {
+				bp_core_new_subnav_item( $nav, 'groups' );
+			}
 		}
 
Index: /trunk/src/bp-loader.php
===================================================================
--- /trunk/src/bp-loader.php	(revision 10744)
+++ /trunk/src/bp-loader.php	(revision 10745)
@@ -110,4 +110,12 @@
 	public $do_autoload = false;
 
+	/**
+	 * Whether to load backward compatibility classes for navigation globals.
+	 *
+	 * @since 2.6.0
+	 * @var bool
+	 */
+	public $do_nav_backcompat = false;
+
 	/** Option Overload *******************************************************/
 
@@ -435,4 +443,10 @@
 		$this->email_post_type     = apply_filters( 'bp_email_post_type', 'bp-email' );
 		$this->email_taxonomy_type = apply_filters( 'bp_email_tax_type', 'bp-email-type' );
+
+		/** Navigation backward compatibility *********************************/
+		if ( interface_exists( 'ArrayAccess', false ) ) {
+			// bp_nav and bp_options_nav compatibility depends on SPL.
+			$this->do_nav_backcompat = true;
+		}
 	}
 
Index: /trunk/src/bp-members/bp-members-template.php
===================================================================
--- /trunk/src/bp-members/bp-members-template.php	(revision 10744)
+++ /trunk/src/bp-members/bp-members-template.php	(revision 10745)
@@ -1291,10 +1291,10 @@
 
 	// Loop through each navigation item.
-	foreach( (array) $bp->bp_nav as $nav_item ) {
+	foreach ( (array) $bp->members->nav->get_primary() as $nav_item ) {
 
 		$selected = '';
 
 		// If the current component matches the nav item id, then add a highlight CSS class.
-		if ( !bp_is_directory() && !empty( $bp->active_components[bp_current_component()] ) && $bp->active_components[bp_current_component()] == $nav_item['css_id'] ) {
+		if ( ! bp_is_directory() && ! empty( $bp->active_components[ bp_current_component() ] ) && $bp->active_components[ bp_current_component() ] == $nav_item->css_id ) {
 			$selected = ' class="current selected"';
 		}
@@ -1308,5 +1308,5 @@
 
 			if ( bp_is_active( 'friends' ) ) {
-				if ( $nav_item['css_id'] == $bp->friends->id ) {
+				if ( $nav_item->css_id == $bp->friends->id ) {
 					if ( friends_check_friendship( bp_loggedin_user_id(), bp_displayed_user_id() ) ) {
 						$selected = ' class="current selected"';
@@ -1317,5 +1317,5 @@
 
 		// 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 ) );
+		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 ) );
 	}
 
@@ -1334,17 +1334,18 @@
 	$bp = buddypress();
 
-	foreach ( (array) $bp->bp_nav as $user_nav_item ) {
-		if ( empty( $user_nav_item['show_for_displayed_user'] ) && !bp_is_my_profile() )
+	foreach ( $bp->members->nav->get_primary() as $user_nav_item ) {
+		if ( empty( $user_nav_item->show_for_displayed_user ) && ! bp_is_my_profile() ) {
 			continue;
+		}
 
 		$selected = '';
-		if ( bp_is_current_component( $user_nav_item['slug'] ) ) {
+		if ( bp_is_current_component( $user_nav_item->slug ) ) {
 			$selected = ' class="current selected"';
 		}
 
 		if ( bp_loggedin_user_domain() ) {
-			$link = str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $user_nav_item['link'] );
+			$link = str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $user_nav_item->link );
 		} else {
-			$link = trailingslashit( bp_displayed_user_domain() . $user_nav_item['link'] );
+			$link = trailingslashit( bp_displayed_user_domain() . $user_nav_item->link );
 		}
 
@@ -1360,5 +1361,5 @@
 		 *                              Passed by reference.
 		 */
-		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 ) );
+		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 ) );
 	}
 }
Index: /trunk/src/bp-members/classes/class-bp-members-component.php
===================================================================
--- /trunk/src/bp-members/classes/class-bp-members-component.php	(revision 10744)
+++ /trunk/src/bp-members/classes/class-bp-members-component.php	(revision 10745)
@@ -151,4 +151,7 @@
 		$bp->displayed_user->domain   = bp_core_get_user_domain( bp_displayed_user_id() );
 
+		// Initialize the nav for the members component.
+		$this->nav = new BP_Core_Nav();
+
 		/** Signup ***********************************************************
 		 */
Index: /trunk/src/bp-messages/bp-messages-screens.php
===================================================================
--- /trunk/src/bp-messages/bp-messages-screens.php	(revision 10744)
+++ /trunk/src/bp-messages/bp-messages-screens.php	(revision 10745)
@@ -133,5 +133,8 @@
 	$nav_name = sprintf( __( 'Messages <span class="%s">%s</span>', 'buddypress' ), esc_attr( $class ), bp_core_number_format( $count ) );
 
-	$bp->bp_nav[ $bp->messages->slug ]['name'] = $nav_name;
+	// Edit the Navigation name.
+	$bp->members->nav->edit_nav( array(
+		'name' => $nav_name,
+	), $bp->messages->slug );
 
 	/**
Index: /trunk/src/bp-xprofile/classes/class-bp-xprofile-component.php
===================================================================
--- /trunk/src/bp-xprofile/classes/class-bp-xprofile-component.php	(revision 10744)
+++ /trunk/src/bp-xprofile/classes/class-bp-xprofile-component.php	(revision 10745)
@@ -303,5 +303,5 @@
 			'position'        => 30,
 			'user_has_access' => bp_core_can_edit_settings()
-		) );
+		), 'members' );
 	}
 
Index: unk/tests/phpunit/testcases/core/nav.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav.php	(revision 10744)
+++ 	(revision )
@@ -1,92 +1,0 @@
-<?php
-
-/**
- * @group core
- * @group nav
- */
-class BP_Tests_Core_Nav extends BP_UnitTestCase {
-
-	/**
-	 * @group bp_core_sort_nav_items
-	 */
-	public function test_bp_core_sort_nav_items() {
-		$bp_nav = buddypress()->bp_nav;
-
-		$u = $this->factory->user->create();
-		$old_current_user = get_current_user_id();
-		$this->set_current_user( $u );
-
-		$this->go_to( bp_core_get_user_domain( $u ) );
-
-		bp_core_new_nav_item( array(
-			'name'                    => 'Foo',
-			'slug'                    => 'foo',
-			'position'                => 25,
-			'screen_function'         => 'foo_screen_function',
-			'default_subnav_slug'     => 'foo-sub'
-		) );
-
-		$expected = array(
-			'name'                    => 'Foo',
-			'slug'                    => 'foo',
-			'link'                    => trailingslashit( bp_core_get_user_domain( $u ) . 'foo' ),
-			'css_id'                  => 'foo',
-			'show_for_displayed_user' => true,
-			'position'                => 25,
-			'screen_function'         => 'foo_screen_function',
-			'default_subnav_slug'     => 'foo-sub'
-		);
-
-		bp_core_sort_nav_items();
-
-		$this->assertSame( buddypress()->bp_nav[25], $expected );
-
-		// Clean up
-		buddypress()->bp_nav = $bp_nav;
-		$this->set_current_user( $old_current_user );
-	}
-
-	/**
-	 * @group bp_core_sort_subnav_items
-	 */
-	public function test_bp_core_sort_subnav_items() {
-		$bp_options_nav = buddypress()->bp_options_nav;
-
-		$u = $this->factory->user->create();
-		$old_current_user = get_current_user_id();
-		$this->set_current_user( $u );
-
-		$user_domain = bp_core_get_user_domain( $u );
-
-		$this->go_to( $user_domain );
-
-		bp_core_new_subnav_item( array(
-			'name'            => 'Foo',
-			'slug'            => 'foo',
-			'parent_url'      => trailingslashit( $user_domain . 'foo' ),
-			'parent_slug'     => 'foo',
-			'screen_function' => 'foo_screen_function',
-			'position'        => 10
-		) );
-
-		bp_core_sort_subnav_items();
-
-		$expected = array(
-			'name'              => 'Foo',
-			'link'              => trailingslashit( $user_domain . 'foo/foo' ),
-			'slug'              => 'foo',
-			'css_id'            => 'foo',
-			'position'          => 10,
-			'user_has_access'   => true,
-			'no_access_url'     => '',
-			'screen_function'   => 'foo_screen_function',
-			'show_in_admin_bar' => false,
-		);
-
-		$this->assertSame( buddypress()->bp_options_nav['foo'][10], $expected );
-
-		// Clean up
-		buddypress()->bp_options_nav = $bp_options_nav;
-		$this->set_current_user( $old_current_user );
-	}
-}
Index: /trunk/tests/phpunit/testcases/core/nav/backCompat.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/backCompat.php	(revision 10745)
+++ /trunk/tests/phpunit/testcases/core/nav/backCompat.php	(revision 10745)
@@ -0,0 +1,321 @@
+<?php
+
+/**
+ * @group core
+ * @group nav
+ * @ticket BP6534
+ * @expectedIncorrectUsage bp_nav
+ */
+class BP_Core_Nav_BackCompat extends BP_UnitTestCase {
+	protected $bp_nav;
+	protected $bp_options_nav;
+
+	public function setUp() {
+		parent::setUp();
+		$this->bp_nav = buddypress()->bp_nav;
+		$this->bp_options_nav = buddypress()->bp_options_nav;
+	}
+
+	public function tearDown() {
+		buddypress()->bp_nav = $this->bp_nav;
+		buddypress()->bp_options_nav = $this->bp_options_nav;
+		parent::tearDown();
+	}
+
+	protected function create_nav_items() {
+		bp_core_new_nav_item( array(
+			'name'                => 'Foo',
+			'slug'                => 'foo',
+			'position'            => 25,
+			'screen_function'     => 'foo_screen_function',
+			'default_subnav_slug' => 'foo-subnav'
+		) );
+
+		bp_core_new_subnav_item( array(
+			'name'            => 'Foo Subnav',
+			'slug'            => 'foo-subnav',
+			'parent_url'      => 'example.com/foo',
+			'parent_slug'     => 'foo',
+			'screen_function' => 'foo_screen_function',
+			'position'        => 10
+		) );
+	}
+
+	/**
+	 * Create a group, set up nav item, and go to the group.
+	 */
+	protected function set_up_group() {
+		$g = $this->factory->group->create( array(
+			'slug' => 'testgroup',
+		) );
+
+		$group = groups_get_group( array( 'group_id' => $g ) );
+		$group_permalink = bp_get_group_permalink( $group );
+
+		$this->go_to( $group_permalink );
+
+		bp_core_new_subnav_item( array(
+			'name'            => 'Foo',
+			'slug'            => 'foo',
+			'parent_url'      => $group_permalink,
+			'parent_slug'     => 'testgroup',
+			'screen_function' => 'foo_screen_function',
+			'position'        => 10
+		), 'groups' );
+	}
+
+	public function test_bp_nav_isset() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$this->assertTrue( isset( $bp->bp_nav ) );
+		$this->assertTrue( isset( $bp->bp_nav['foo'] ) );
+		$this->assertTrue( isset( $bp->bp_nav['foo']['name'] ) );
+	}
+
+	public function test_bp_nav_unset() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		// No support for this - it would create a malformed nav item.
+		/*
+		unset( $bp->bp_nav['foo']['css_id'] );
+		$this->assertFalse( isset( $bp->bp_nav['foo']['css_id'] ) );
+		*/
+
+		unset( $bp->bp_nav['foo'] );
+		$this->assertFalse( isset( $bp->bp_nav['foo'] ) );
+	}
+
+	public function test_bp_nav_get() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$foo = $bp->bp_nav['foo'];
+		$this->assertSame( 'Foo', $foo['name'] );
+
+		$this->assertSame( 'Foo', $bp->bp_nav['foo']['name'] );
+	}
+
+	public function test_bp_nav_set() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$bp->bp_nav['foo']['name'] = 'Bar';
+
+		$nav = bp_get_nav_menu_items();
+
+		foreach ( $nav as $_nav ) {
+			if ( 'foo' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Bar', $found->name );
+	}
+
+	public function test_bp_options_nav_isset() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$this->assertTrue( isset( $bp->bp_options_nav ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['foo'] ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['foo']['foo-subnav'] ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['foo']['foo-subnav']['name'] ) );
+	}
+
+	public function test_bp_options_nav_unset() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		// No support for this - it would create a malformed nav item.
+		/*
+		unset( $bp->bp_options_nav['foo']['foo-subnav']['user_has_access'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['foo']['foo-subnav']['user_has_access'] ) );
+		*/
+
+		unset( $bp->bp_options_nav['foo']['foo-subnav'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['foo']['foo-subnav'] ) );
+
+		// Make sure the parent nav hasn't been wiped out.
+		$this->assertTrue( isset( $bp->bp_options_nav['foo'] ) );
+
+		unset( $bp->bp_options_nav['foo'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['foo'] ) );
+	}
+
+	public function test_bp_options_nav_get() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$foo_subnav = $bp->bp_options_nav['foo']['foo-subnav'];
+		$this->assertSame( 'Foo Subnav', $foo_subnav['name'] );
+
+		$this->assertSame( 'Foo Subnav', $bp->bp_options_nav['foo']['foo-subnav']['name'] );
+	}
+
+	public function test_bp_options_nav_set() {
+		$this->create_nav_items();
+
+		$bp = buddypress();
+
+		$bp->bp_options_nav['foo']['foo-subnav']['name'] = 'Bar';
+		$nav = bp_get_nav_menu_items();
+
+		foreach ( $nav as $_nav ) {
+			if ( 'foo-subnav' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Bar', $found->name );
+
+		$subnav = array(
+			'name' => 'Bar',
+			'css_id' => 'bar-id',
+			'link' => 'bar-link',
+			'slug' => 'bar-slug',
+			'user_has_access' => true,
+		);
+		$bp->bp_options_nav['foo']['foo-subnav'] = $subnav;
+		$nav = bp_get_nav_menu_items();
+
+		foreach ( $nav as $_nav ) {
+			if ( 'bar-id' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Bar', $found->name );
+	}
+
+	/**
+	 * @group groups
+	 */
+	public function test_bp_options_nav_isset_group_nav() {
+		$this->set_up_group();
+
+		$bp = buddypress();
+
+		$this->assertTrue( isset( $bp->bp_options_nav ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['testgroup'] ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['testgroup']['foo'] ) );
+		$this->assertTrue( isset( $bp->bp_options_nav['testgroup']['foo']['name'] ) );
+	}
+
+	/**
+	 * @group groups
+	 */
+	public function test_bp_options_nav_unset_group_nav() {
+		$this->set_up_group();
+
+		$bp = buddypress();
+
+		// No support for this - it would create a malformed nav item.
+		/*
+		unset( $bp->bp_options_nav['testgroup']['foo']['user_has_access'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['testgroup']['foo']['user_has_access'] ) );
+		*/
+
+		unset( $bp->bp_options_nav['testgroup']['foo'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['testgroup']['foo'] ) );
+
+		unset( $bp->bp_options_nav['testgroup'] );
+		$this->assertFalse( isset( $bp->bp_options_nav['testgroup'] ) );
+	}
+
+	/**
+	 * @group groups
+	 */
+	public function test_bp_options_nav_get_group_nav() {
+		$this->set_up_group();
+
+		$bp = buddypress();
+
+		$foo = $bp->bp_options_nav['testgroup']['foo'];
+		$this->assertSame( 'Foo', $foo['name'] );
+
+		$this->assertSame( 'Foo', $bp->bp_options_nav['testgroup']['foo']['name'] );
+	}
+
+	/**
+	 * @group groups
+	 */
+	public function test_bp_options_nav_set_group_nav() {
+		$this->set_up_group();
+
+		$bp = buddypress();
+
+		$bp->bp_options_nav['testgroup']['foo']['name'] = 'Bar';
+		$nav = bp_get_nav_menu_items( 'groups' );
+
+		foreach ( $nav as $_nav ) {
+			if ( 'foo' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Bar', $found->name );
+
+		$subnav = array(
+			'name' => 'Bar',
+			'css_id' => 'bar-id',
+			'link' => 'bar-link',
+			'slug' => 'bar-slug',
+			'user_has_access' => true,
+		);
+		$bp->bp_options_nav['testgroup']['foo'] = $subnav;
+		$nav = bp_get_nav_menu_items( 'groups' );
+
+		foreach ( $nav as $_nav ) {
+			if ( 'bar-id' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Bar', $found->name );
+	}
+
+	/**
+	 * @group groups
+	 */
+	public function test_bp_core_new_subnav_item_should_work_in_group_context() {
+		$this->set_up_group();
+
+		bp_core_new_subnav_item( array(
+			'name' => 'Foo Subnav',
+			'slug' => 'foo-subnav',
+			'parent_slug' => bp_get_current_group_slug(),
+			'parent_url' => bp_get_group_permalink( groups_get_current_group() ),
+			'screen_function' => 'foo_subnav',
+		) );
+
+		$bp = buddypress();
+
+		// Touch bp_nav since we told PHPUnit it was expectedDeprecated.
+		$f = $bp->bp_options_nav[ bp_get_current_group_slug() ];
+
+		$nav = bp_get_nav_menu_items( 'groups' );
+
+		foreach ( $nav as $_nav ) {
+			if ( 'foo-subnav' === $_nav->css_id ) {
+				$found = $_nav;
+				break;
+			}
+		}
+
+		$this->assertSame( 'Foo Subnav', $found->name );
+	}
+}
Index: /trunk/tests/phpunit/testcases/core/nav/bpCoreMaybeHookNewSubnavScreenFunction.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/bpCoreMaybeHookNewSubnavScreenFunction.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/core/nav/bpCoreMaybeHookNewSubnavScreenFunction.php	(revision 10745)
@@ -83,9 +83,11 @@
 		$old_default_component = buddypress()->default_component;
 		buddypress()->default_component = 'foo';
-		buddypress()->bp_nav = array(
-			'foo' => array(
-				'show_for_displayed_user' => true,
-			),
-		);
+
+		bp_core_new_nav_item( array(
+			'slug' => 'foo',
+			'name' => 'Foo',
+			'screen_function' => 'foo',
+			'default_subnav_item' => 'bar',
+		) );
 
 		$subnav_item = array(
@@ -95,6 +97,4 @@
 		// Just test relevant info
 		$found = bp_core_maybe_hook_new_subnav_screen_function( $subnav_item );
-		$this->assertSame( 'failure', $found['status'] );
-		$this->assertSame( bp_core_get_user_domain( $u2 ), $found['redirect_args']['root'] );
 
 		// Clean up
@@ -102,4 +102,7 @@
 		buddypress()->default_component = $old_default_component;
 		buddypress()->bp_nav = $old_bp_nav;
+
+		$this->assertSame( 'failure', $found['status'] );
+		$this->assertSame( bp_core_get_user_domain( $u2 ), $found['redirect_args']['root'] );
 	}
 
@@ -115,9 +118,12 @@
 		$old_default_component = buddypress()->default_component;
 		buddypress()->default_component = 'foo';
-		buddypress()->bp_nav = array(
-			'foo' => array(
-				'show_for_displayed_user' => false,
-			),
-		);
+
+		bp_core_new_nav_item( array(
+			'slug' => 'foo',
+			'name' => 'Foo',
+			'screen_function' => 'foo',
+			'default_subnav_item' => 'bar',
+			'show_for_displayed_user' => false,
+		) );
 
 		$subnav_item = array(
@@ -127,6 +133,4 @@
 		// Just test relevant info
 		$found = bp_core_maybe_hook_new_subnav_screen_function( $subnav_item );
-		$this->assertSame( 'failure', $found['status'] );
-		$this->assertSame( bp_core_get_user_domain( $u2 ) . bp_get_activity_slug() . '/', $found['redirect_args']['root'] );
 
 		// Clean up
@@ -134,4 +138,7 @@
 		buddypress()->default_component = $old_default_component;
 		buddypress()->bp_nav = $old_bp_nav;
+
+		$this->assertSame( 'failure', $found['status'] );
+		$this->assertSame( bp_core_get_user_domain( $u2 ) . bp_get_activity_slug() . '/', $found['redirect_args']['root'] );
 	}
 
Index: /trunk/tests/phpunit/testcases/core/nav/bpCoreNewNavItem.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/bpCoreNewNavItem.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/core/nav/bpCoreNewNavItem.php	(revision 10745)
@@ -6,4 +6,7 @@
 class BP_Tests_Core_Nav_BpCoreNewNavItem extends BP_UnitTestCase {
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_user_nav() {
 		$bp_nav = buddypress()->bp_nav;
@@ -34,11 +37,16 @@
 		);
 
-		$this->assertSame( buddypress()->bp_nav['foo'], $expected );
-
-		// Clean up
-		buddypress()->bp_nav = $bp_nav;
-		$this->set_current_user( $old_current_user );
-	}
-
+		foreach ( $expected as $k => $v ) {
+			$this->assertEquals( $v, buddypress()->bp_nav['foo'][ $k ] );
+		}
+
+		// Clean up
+		buddypress()->bp_nav = $bp_nav;
+		$this->set_current_user( $old_current_user );
+	}
+
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_group_nav() {
 		$bp_nav = buddypress()->bp_nav;
@@ -90,4 +98,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_css_id_should_fall_back_on_slug() {
 		$args = array(
@@ -100,4 +111,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_css_id_should_be_respected() {
 		$args = array(
@@ -125,4 +139,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_existence_of_access_protected_user_nav() {
 		$bp_nav = buddypress()->bp_nav;
@@ -146,11 +163,16 @@
 		);
 
-		$this->assertSame( buddypress()->bp_nav['settings'], $expected );
-
-		// Clean up
-		buddypress()->bp_nav = $bp_nav;
-		$this->set_current_user( $old_current_user );
-	}
-
+		foreach ( $expected as $k => $v ) {
+			$this->assertEquals( $v, buddypress()->bp_nav['settings'][ $k ] );
+		}
+
+		// Clean up
+		buddypress()->bp_nav = $bp_nav;
+		$this->set_current_user( $old_current_user );
+	}
+
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_creation_of_access_protected_user_nav() {
 		// The nav item must be added to bp_nav, even if the current user
@@ -185,5 +207,7 @@
 		);
 
-		$this->assertSame( buddypress()->bp_nav['woof'], $expected );
+		foreach ( $expected as $k => $v ) {
+			$this->assertEquals( $v, buddypress()->bp_nav['woof'][ $k ] );
+		}
 
 		// Clean up
Index: /trunk/tests/phpunit/testcases/core/nav/bpCoreNewSubnavItem.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/bpCoreNewSubnavItem.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/core/nav/bpCoreNewSubnavItem.php	(revision 10745)
@@ -6,4 +6,7 @@
 class BP_Tests_Core_Nav_BpCoreNewSubnavItem extends BP_UnitTestCase {
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_user_subnav() {
 		$bp_options_nav = buddypress()->bp_options_nav;
@@ -17,9 +20,17 @@
 		$this->go_to( $user_domain );
 
+		bp_core_new_nav_item( array(
+			'name'            => 'Foo Parent',
+			'slug'            => 'foo-parent',
+			'link'            => trailingslashit( $user_domain . 'foo-parent' ),
+			'screen_function' => 'foo_screen_function',
+			'position'        => 10,
+		) );
+
 		bp_core_new_subnav_item( array(
 			'name'            => 'Foo',
 			'slug'            => 'foo',
-			'parent_url'      => trailingslashit( $user_domain . 'foo' ),
-			'parent_slug'     => 'foo',
+			'parent_url'      => trailingslashit( $user_domain . 'foo-parent' ),
+			'parent_slug'     => 'foo-parent',
 			'screen_function' => 'foo_screen_function',
 			'position'        => 10
@@ -28,5 +39,5 @@
 		$expected = array(
 			'name'              => 'Foo',
-			'link'              => trailingslashit( $user_domain . 'foo/foo' ),
+			'link'              => trailingslashit( $user_domain . 'foo-parent/foo' ),
 			'slug'              => 'foo',
 			'css_id'            => 'foo',
@@ -38,5 +49,7 @@
 		);
 
-		$this->assertSame( buddypress()->bp_options_nav['foo']['foo'], $expected );
+		foreach ( $expected as $k => $v ) {
+			$this->assertSame( $v, buddypress()->bp_options_nav['foo-parent']['foo'][ $k ] );
+		}
 
 		// Clean up
@@ -103,16 +116,26 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_link_provided() {
 		$bp_options_nav = buddypress()->bp_options_nav;
 
-		bp_core_new_subnav_item( array(
-			'name' => 'bar',
-			'slug' => 'bar',
-			'parent_slug' => 'foo',
-			'parent_url' => 'foo',
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
 			'screen_function' => 'foo',
 			'link' => 'https://buddypress.org/',
 		) );
 
+		bp_core_new_subnav_item( array(
+			'name' => 'bar',
+			'slug' => 'bar',
+			'parent_slug' => 'foo',
+			'parent_url' => 'foo',
+			'screen_function' => 'foo',
+			'link' => 'https://buddypress.org/',
+		) );
+
 		$this->assertSame( 'https://buddypress.org/', buddypress()->bp_options_nav['foo']['bar']['link'] );
 
@@ -120,7 +143,17 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_link_built_from_parent_url_and_slug() {
 		$bp_options_nav = buddypress()->bp_options_nav;
 
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'screen_function' => 'foo',
+			'link' => 'https://buddypress.org/',
+		) );
+
 		bp_core_new_subnav_item( array(
 			'name' => 'bar',
@@ -136,14 +169,17 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_link_built_from_parent_url_and_slug_where_slug_is_default() {
 		$bp_nav = buddypress()->bp_nav;
 		$bp_options_nav = buddypress()->bp_options_nav;
 
-		// fake the parent
-		buddypress()->bp_nav = array(
-			'foo' => array(
-				'default_subnav_slug' => 'bar',
-			),
-		);
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'screen_function' => 'foo',
+			'default_subnav_slug' => 'bar',
+		) );
 
 		bp_core_new_subnav_item( array(
@@ -162,5 +198,15 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_trailingslash_link_when_link_is_autogenerated_using_slug() {
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'screen_function' => 'foo',
+			'link' => 'https://buddypress.org/',
+		) );
+
 		bp_core_new_subnav_item( array(
 			'name' => 'bar',
@@ -175,4 +221,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_trailingslash_link_when_link_is_autogenerated_not_using_slug() {
 		bp_core_new_nav_item( array(
@@ -188,9 +237,9 @@
 			'slug' => 'bar',
 			'parent_slug' => 'foo-parent',
-			'parent_url' => bp_get_root_domain() . 'foo-parent/',
+			'parent_url' => bp_get_root_domain() . '/foo-parent/',
 			'screen_function' => 'bar',
 		) );
 
-		$expected = bp_get_root_domain() . 'foo-parent/';
+		$expected = bp_get_root_domain() . '/foo-parent/';
 		$this->assertSame( $expected, buddypress()->bp_options_nav['foo-parent']['bar']['link'] );
 	}
@@ -198,8 +247,16 @@
 	/**
 	 * @ticket BP6353
+	 * @expectedIncorrectUsage bp_nav
 	 */
 	public function test_link_should_not_trailingslash_link_explicit_link() {
 		$link = 'http://example.com/foo/bar/blah/?action=edit&id=30';
 
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'screen_function' => 'foo',
+			'link' => 'http://example.com/foo/',
+		) );
+
 		bp_core_new_subnav_item( array(
 			'name' => 'bar',
@@ -215,4 +272,10 @@
 
 	public function test_should_return_false_if_site_admin_only_and_current_user_cannot_bp_moderate() {
+		bp_core_new_nav_item( array(
+			'name' => 'Foo',
+			'slug' => 'foo',
+			'screen_function' => 'foo',
+		) );
+
 		// Should already be set to a 0 user.
 		$this->assertFalse( bp_current_user_can( 'bp_moderate' ) );
@@ -229,5 +292,14 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_css_id_should_fall_back_on_slug() {
+		bp_core_new_nav_item( array(
+			'name' => 'Parent',
+			'slug' => 'parent',
+			'screen_function' => 'foo',
+		) );
+
 		$args = array(
 			'name' => 'Foo',
@@ -242,5 +314,14 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_css_id_should_be_respected() {
+		bp_core_new_nav_item( array(
+			'name' => 'Parent',
+			'slug' => 'parent',
+			'screen_function' => 'foo',
+		) );
+
 		$args = array(
 			'name' => 'Foo',
Index: /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveNavItem.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveNavItem.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveNavItem.php	(revision 10745)
@@ -6,4 +6,7 @@
  */
 class BP_Tests_Core_Nav_BpCoreRemoveNavItem extends BP_UnitTestCase {
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_remove_subnav_items() {
 		$bp = buddypress();
@@ -38,4 +41,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_remove_nav_item() {
 		$bp = buddypress();
Index: /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveSubnavItem.php
===================================================================
--- /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveSubnavItem.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveSubnavItem.php	(revision 10745)
@@ -6,4 +6,7 @@
  */
 class BP_Tests_Core_Nav_BpCoreRemoveSubnavItem extends BP_UnitTestCase {
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_remove_subnav_item() {
 		$bp = buddypress();
@@ -36,4 +39,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	public function test_should_fail_on_incorrect_parent() {
 		$bp = buddypress();
@@ -65,33 +71,3 @@
 		$bp->bp_options_nav = $_bp_options_nav;
 	}
-
-	public function test_should_remove_parent_array_if_left_childless() {
-		$bp = buddypress();
-
-		$_bp_nav = $bp->bp_nav;
-		$_bp_options_nav = $bp->bp_options_nav;
-
-		bp_core_new_nav_item( array(
-			'name' => 'Foo',
-			'slug' => 'foo',
-			'screen_function' => 'foo',
-		) );
-
-		bp_core_new_subnav_item( array(
-			'name' => 'Bar',
-			'slug' => 'bar',
-			'parent_slug' => 'foo',
-			'parent_url' => 'foo',
-			'screen_function' => 'bar',
-		) );
-
-		$this->assertTrue( isset( $bp->bp_options_nav['foo']['bar'] ) );
-
-		bp_core_remove_subnav_item( 'foo', 'bar' );
-
-		$this->assertFalse( isset( $bp->bp_options_nav['foo'] ) );
-
-		$bp->bp_nav = $_bp_nav;
-		$bp->bp_options_nav = $_bp_options_nav;
-	}
 }
Index: /trunk/tests/phpunit/testcases/groups/class-bp-group-extension.php
===================================================================
--- /trunk/tests/phpunit/testcases/groups/class-bp-group-extension.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/groups/class-bp-group-extension.php	(revision 10745)
@@ -218,4 +218,5 @@
 	/**
 	 * @group enable_nav_item
+	 * @expectedIncorrectUsage bp_nav
 	 */
 	public function test_enable_nav_item_true() {
@@ -240,4 +241,5 @@
 	/**
 	 * @group enable_nav_item
+	 * @expectedIncorrectUsage bp_nav
 	 */
 	public function test_enable_nav_item_false() {
@@ -262,4 +264,5 @@
 	/**
 	 * @group visibility
+	 * @expectedIncorrectUsage bp_nav
 	 */
 	public function test_visibility_private() {
@@ -299,4 +302,5 @@
 	/**
 	 * @group visibility
+	 * @expectedIncorrectUsage bp_nav
 	 *
 	 * visibility=public + status=private results in adding the item to
Index: /trunk/tests/phpunit/testcases/routing/anonymous.php
===================================================================
--- /trunk/tests/phpunit/testcases/routing/anonymous.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/routing/anonymous.php	(revision 10745)
@@ -11,5 +11,6 @@
 	function test_nav_menu() {
 		$this->go_to( '/' );
-		$this->assertEmpty( buddypress()->bp_nav );
+		$nav = buddypress()->members->nav->get_item_nav();
+		$this->assertEmpty( $nav );
 	}
 }
Index: /trunk/tests/phpunit/testcases/routing/core.php
===================================================================
--- /trunk/tests/phpunit/testcases/routing/core.php	(revision 10744)
+++ /trunk/tests/phpunit/testcases/routing/core.php	(revision 10745)
@@ -23,4 +23,7 @@
 	}
 
+	/**
+	 * @expectedIncorrectUsage bp_nav
+	 */
 	function test_nav_menu() {
 		$this->go_to( '/' );
