diff --git bp-core/admin/bp-core-functions.php bp-core/admin/bp-core-functions.php
index 7e145fa..0c2f14d 100644
--- bp-core/admin/bp-core-functions.php
+++ bp-core/admin/bp-core-functions.php
@@ -642,3 +642,126 @@ function bp_admin_list_table_current_bulk_action() {
return $action;
}
+
+/** Menus *********************************************************************/
+
+/**
+ * Registers a meta box for BuddyPress WP Nav Menu and a js at the bottom of
+ * the page
+ *
+ * @since BuddyPress (1.9)
+ *
+ * @global integer the current blog id
+ * @uses bp_is_root_blog() to check whether this is the root blog
+ * @uses add_meta_box() to register the BuddyPress WP Nav accordeon
+ * @uses add_action() to hook to admin footer scripts and load a piece of
+ * javascript
+ */
+function bp_admin_wp_nav_menu_meta_box() {
+ if ( ! bp_is_root_blog() ) {
+ return;
+ }
+
+ add_meta_box( 'add-buddypress-nav-menu', __( 'BuddyPress', 'buddypress' ), 'bp_admin_do_wp_nav_menu_meta_box', 'nav-menus', 'side', 'default' );
+
+ add_action( 'admin_print_footer_scripts', 'bp_admin_wp_nav_menu_restrict_items' );
+}
+
+/**
+ * Builds and populates the BuddyPress accordion on Appearance > Menus
+ *
+ * @since BuddyPress (1.9)
+ *
+ * @global $nav_menu_selected_id
+ * @uses BP_Walker_Nav_Menu_Checklist to use our adapted walker
+ * @uses bp_nav_menu_get_loggedin_pages() to get the logged-in menu items
+ * @uses bp_nav_menu_get_loggedout_pages() to get the logged-out menu items
+ */
+function bp_admin_do_wp_nav_menu_meta_box() {
+ global $nav_menu_selected_id;
+
+ $walker = new BP_Walker_Nav_Menu_Checklist( false );
+ $args = array( 'walker' => $walker );
+
+ $post_type_name = 'buddypress';
+ $current_tab = 'loggedin';
+
+ $tabs = array();
+
+ $tabs['loggedin']['label'] = __( 'Logged-In', 'buddypress' );
+ $tabs['loggedin']['pages'] = bp_nav_menu_get_loggedin_pages();
+
+ $tabs['loggedout']['label'] = __( 'Logged-Out', 'buddypress' );
+ $tabs['loggedout']['pages'] = bp_nav_menu_get_loggedout_pages();
+
+ ?>
+
+
+
+
+
+ db_fields = $fields;
+ }
+ }
+
+ public function start_lvl( &$output, $depth = 0, $args = array() ) {
+ $indent = str_repeat( "\t", $depth );
+ $output .= "\n$indent\n";
+ }
+
+ public function end_lvl( &$output, $depth = 0, $args = array() ) {
+ $indent = str_repeat( "\t", $depth );
+ $output .= "\n$indent
";
+ }
+
+ /**
+ * @see Walker::start_el()
+ *
+ * @param string $output Passed by reference. Used to append additional content.
+ * @param object $item Menu item data object.
+ * @param int $depth Depth of menu item. Used for padding.
+ * @param object $args
+ */
+ function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
+ global $_nav_menu_placeholder;
+
+ $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1;
+ $possible_object_id = isset( $item->post_type ) && 'nav_menu_item' == $item->post_type ? $item->object_id : $_nav_menu_placeholder;
+ $possible_db_id = ( ! empty( $item->ID ) ) && ( 0 < $possible_object_id ) ? (int) $item->ID : 0;
+
+ $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
+
+ $output .= $indent . '';
+ $output .= '';
+
+ if ( empty( $item->url ) ) {
+ $item->url = $item->guid;
+ }
+
+ if ( ! in_array( array( 'bp-menu', 'bp-'. $item->post_excerpt .'-nav' ), $item->classes ) ) {
+ $item->classes[] = 'bp-menu';
+ $item->classes[] = 'bp-'. $item->post_excerpt .'-nav';
+ }
+
+ // Menu item hidden fields
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ }
+}
diff --git bp-core/bp-core-filters.php bp-core/bp-core-filters.php
index f8f05d0..e59a0e3 100644
--- bp-core/bp-core-filters.php
+++ bp-core/bp-core-filters.php
@@ -386,3 +386,84 @@ add_filter( 'wp_title', 'bp_modify_page_title', 10, 3 );
add_filter( 'bp_modify_page_title', 'wptexturize' );
add_filter( 'bp_modify_page_title', 'convert_chars' );
add_filter( 'bp_modify_page_title', 'esc_html' );
+
+/**
+ * Add BuddyPress-specific items to the wp_nav_menu
+ *
+ * If user is not logged in regiser menu and login menu item must show
+ * If user is logged in register menu must be unset and logout must replace login
+ *
+ * @param WP_Post $menu_item the menu item
+ * @uses is_admin() to check we're in backend
+ * @uses is_user_logged_in() to check if we have a user logged in
+ * @uses wp_logout_url() to build the log out url
+ * @uses bp_get_root_domain() to be redirected to root domain url once logged out
+ * @uses wp_login_url() to build log in url
+ * @uses wp_guess_url() to be redirected to be redirected at the best place once logged in
+ * @uses bp_get_nav_item_pages() to get the url for the user's nav
+ * @uses bp_get_requested_url() to get the current url
+ * @return obj The modified WP_Post object
+ */
+function bp_setup_nav_menu_item( $menu_item ) {
+ if ( is_admin() ) {
+ return $menu_item;
+ }
+
+ // We use information stored in the CSS class to determine what kind of
+ // menu item this is, and how it should be treated
+ $css_target = preg_match( '/\sbp-(.*)-nav/', implode( ' ', $menu_item->classes), $matches );
+
+ // If this isn't a BP menu item, we can stop here
+ if ( empty( $matches[1] ) ) {
+ return $menu_item;
+ }
+
+ switch ( $matches[1] ) {
+ case 'login' :
+ if ( is_user_logged_in() ) {
+ $menu_item->_invalid = true;
+ } else {
+ $menu_item->url = wp_login_url( wp_guess_url() );
+ }
+
+ break;
+
+ case 'logout' :
+ if ( ! is_user_logged_in() ) {
+ $menu_item->_invalid = true;
+ } else {
+ $menu_item->url = wp_logout_url( wp_guess_url() );
+ }
+
+ break;
+
+ // Don't show the Register link to logged-in users
+ case 'register' :
+ if ( is_user_logged_in() ) {
+ $menu_item->_invalid = true;
+ }
+
+ break;
+
+ // All other BP nav items are specific to the logged-in user,
+ // and so are not relevant to logged-out users
+ default:
+ if ( is_user_logged_in() ) {
+ $menu_item->url = bp_nav_menu_get_item_url( $matches[1] );
+ } else {
+ $menu_item->_invalid = true;
+ }
+
+ break;
+ }
+
+ // Highlight the current page
+ $current = bp_get_requested_url();
+ if ( strpos( $current, $menu_item->url ) !== false ) {
+ $menu_item->classes[] = 'current_page_item';
+ }
+
+ return $menu_item;
+}
+
+add_filter( 'wp_setup_nav_menu_item', 'bp_setup_nav_menu_item', 10, 1 );
diff --git bp-core/bp-core-functions.php bp-core/bp-core-functions.php
index 80d7889..3237f22 100644
--- bp-core/bp-core-functions.php
+++ bp-core/bp-core-functions.php
@@ -1411,3 +1411,183 @@ function bp_core_print_generation_time() {
Appearance > Menu as well as the menu as rendered on the front
+ * end. Most of the items in the BuddyPress set of nav items are neither posts
+ * nor tax terms, so we fake a post-like object so as to be compatible with the
+ * menu.
+ *
+ * This technique also allows us to generate links dynamically, so that, for
+ * example, "My Profile" will always point to the URL of the profile of the
+ * logged-in user.
+ *
+ * @since BuddyPress (1.9)
+ *
+ * @uses buddypress() to get the bp_nav items
+ * @return mixed A URL or an array of dummy pages
+ */
+function bp_nav_menu_get_loggedin_pages() {
+
+ // Try to catch the cached version first
+ if ( ! empty( buddypress()->wp_nav_menu_items ) ) {
+ return buddypress()->wp_nav_menu_items;
+ }
+
+ // Pull up a list of items registered in BP's top-level nav array
+ $bp_menu_items = buddypress()->bp_nav;
+
+ // Some BP nav menu items will not be represented in bp_nav, because
+ // they are not real BP components. We add them manually here.
+ $bp_menu_items[] = array(
+ 'name' => __( 'Log Out', 'buddypress' ),
+ 'slug' => 'logout',
+ 'link' => wp_logout_url(),
+ );
+
+ // If there's nothing to show, we're done
+ if ( count( $bp_menu_items ) < 1 ) {
+ return false;
+ }
+
+ $page_args = array();
+
+ foreach ( $bp_menu_items as $bp_item ) {
+ $item_name = '';
+
+ // Remove number
+ $item_name = preg_replace( '/([.0-9]+)/', '', $bp_item['name'] );
+ $item_name = trim( strip_tags( $item_name ) );
+
+ $page_args[ $bp_item['slug'] ] = (object) array(
+ 'ID' => -1,
+ 'post_title' => $item_name,
+ 'post_author' => 0,
+ 'post_date' => 0,
+ 'post_excerpt' => $bp_item['slug'],
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'comment_status' => 'closed',
+ 'guid' => $bp_item['link']
+ );
+ }
+
+ if ( empty( buddypress()->wp_nav_menu_items ) ) {
+ buddypress()->wp_nav_menu_items = new stdClass;
+ }
+
+ buddypress()->wp_nav_menu_items->loggedin = $page_args;
+
+ return $page_args;
+}
+
+/**
+ * Create fake "post" objects for BP's logged-out nav menu for use in
+ * the WordPress "Menus" settings page.
+ *
+ * WordPress nav menus work by representing post or tax term data as a custom
+ * post type, which is then used to populate the checkboxes that appear on
+ * Dashboard > Appearance > Menu as well as the menu as rendered on the front
+ * end. Most of the items in the BuddyPress set of nav items are neither posts
+ * nor tax terms, so we fake a post-like object so as to be compatible with the
+ * menu.
+ *
+ * @since BuddyPress (1.9)
+ *
+ * @uses bp_core_get_directory_page_ids() to get all the directory pages, and
+ * check for the proper location of the Register page
+ * @uses get_the_title() to get the title for the registration page
+ * @uses get_permalink() to get the link to the registration page
+ * @uses buddypress() to get the bp_nav items
+ * @return mixed A URL or an array of dummy pages
+ */
+function bp_nav_menu_get_loggedout_pages() {
+
+ // Try to catch the cached version first
+ if ( ! empty( buddypress()->wp_nav_menu_items->loggedout ) ) {
+ return buddypress()->wp_nav_menu_items->loggedout;
+ }
+
+ $bp_menu_items = array();
+
+ // Some BP nav menu items will not be represented in bp_nav, because
+ // they are not real BP components. We add them manually here.
+ $bp_menu_items[] = array(
+ 'name' => __( 'Log In', 'buddypress' ),
+ 'slug' => 'login',
+ 'link' => wp_login_url(),
+ );
+
+ // The Register page will not always be available (ie, when
+ // registration is disabled)
+ $bp_directory_page_ids = bp_core_get_directory_page_ids();
+
+ if( ! empty( $bp_directory_page_ids['register'] ) ) {
+ $register_page = get_post( $bp_directory_page_ids['register'] );
+ $bp_menu_items[] = array(
+ 'name' => $register_page->post_title,
+ 'slug' => $register_page->post_name,
+ 'link' => get_permalink( $register_page->ID ),
+ );
+ }
+
+ // If there's nothing to show, we're done
+ if ( count( $bp_menu_items ) < 1 ) {
+ return false;
+ }
+
+ $page_args = array();
+
+ foreach ( $bp_menu_items as $bp_item ) {
+ $page_args[ $bp_item['slug'] ] = (object) array(
+ 'ID' => -1,
+ 'post_title' => $bp_item['name'],
+ 'post_author' => 0,
+ 'post_date' => 0,
+ 'post_excerpt' => $bp_item['slug'],
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'comment_status' => 'closed',
+ 'guid' => $bp_item['link']
+ );
+ }
+
+ if ( empty( buddypress()->wp_nav_menu_items ) ) {
+ buddypress()->wp_nav_menu_items = new stdClass;
+ }
+
+ buddypress()->wp_nav_menu_items->loggedout = $page_args;
+
+ return $page_args;
+}
+
+/**
+ * Get the URL for a BuddyPress WP nav menu item, based on slug
+ *
+ * BuddyPress-specific WP nav menu items have dynamically generated URLs,
+ * based on the identity of the current user. This function lets you fetch the
+ * proper URL for a given nav item slug (such as 'login' or 'messages').
+ *
+ * @since BuddyPress (1.9)
+ *
+ * @param string $slug The slug of the nav item: login, register, or one of the
+ * slugs from buddypress()->bp_nav
+ * @return string $nav_item_url The URL generated for the current user
+ */
+function bp_nav_menu_get_item_url( $slug ) {
+ $nav_item_url = '';
+ $nav_menu_items = bp_nav_menu_get_loggedin_pages();
+
+ if ( isset( $nav_menu_items[ $slug ] ) ) {
+ $nav_item_url = $nav_menu_items[ $slug ]->guid;
+ }
+
+ return $nav_item_url;
+}