Skip to:
Content

BuddyPress.org

Changeset 10745


Ignore:
Timestamp:
05/12/2016 05:19:06 PM (8 years ago)
Author:
boonebgorges
Message:

Introduce new API for BuddyPress navigation.

The new BP_Core_Nav overhauls the way that BuddyPress registers, stores, and
renders navigation items. Navigations are now component-specific, eliminating
the potential for confusion and conflict between navigation items with similar
names in different components, and opening the possibility of generating navs
for separate objects of the same type on a single pageload.

The new nav API replaces the old bp_nav and bp_options_nav system, which
dates from the earliest days of BuddyPress. These global properties were
responsible for handling nav and subnav across all of BP's components. The data
structure of bp_nav and bp_options_nav was simultaneously too opaque (in the
sense of being difficult to approach for developers and not having a complete
interface for programmatic modification) and too transparent (forcing devs to
manipulate the global arrays directly in order to customize navigation). The
new system eliminates most of these problems, by removing direct access to the
underlying navigation data, while providing a full-fledged API for accessing
and modifying that data.

An abstraction layer provides backward compatibility for most legacy uses of
bp_nav and bp_options_nav. Plugins that read data from the globals, modify
the data (eg $bp->bp_nav['foo']['name'] = 'Bar'), and unset nav items via
bp_nav and bp_options_nav should all continue to work as before. Anyone
accessing the globals in this way will see a _doing_it_wrong() notice. This
backward compatibility layer requires SPL (Standard PHP Library), which means
that it will not be enabled on certain configurations running PHP 5.2.x. (SPL
cannot be disabled in PHP 5.3+, and is on by default for earlier versions.)

The new system breaks backward compatibility in a number of small ways. Our
research suggests that these breaks will affect very few customizations, but
we list them here for posterity:

  • Some array functions, such as sort(), will no longer work to modify the nav globals.
  • Subnav items added to nonexistent parents can no longer be successfully registered. Previously, they could be loaded into the global, but were never displayed on the front end.
  • Manual management of group navigation items previously worked by passing the group slug as the parent_slug parameter to the bp_core_*_subnav_item() functions. The new API requires specifying a $component ('members', 'groups') when accessing nav items. To provide compatibility with legacy use - where $component was not required - we make some educated guesses about whether a nav item is "meant" to be attached to a group. This could result in unpredictable behavior in cases where a group slug clashes with a Members navigation item. This has always been broken - see #5103 - but may break in different ways after this changeset.

Props imath, boonebgorges, r-a-y.
Fixes #5103. Fixes #6534.

Location:
trunk
Files:
5 added
1 deleted
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-core/bp-core-buddybar.php

    r10497 r10745  
    1414
    1515/**
    16  * Add an item to the main BuddyPress navigation array.
     16 * Add an item to the primary navigation of the specified component.
    1717 *
    1818 * @since 1.1.0
     19 * @since 2.6.0 Introduced the `$component` parameter.
     20 *
     21 * @param array|string $args {
     22 *     Array describing the new nav item.
     23 *     @type string      $name                    Display name for the nav item.
     24 *     @type string      $slug                    Unique URL slug for the nav item.
     25 *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
     26 *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
     27 *                                                member profile other than your own. Default: true.
     28 *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
     29 *                                                (those with the 'bp_moderate' cap). Default: false.
     30 *     @type int         $position                Optional. Numerical index specifying where the item should appear in
     31 *                                                the nav array. Default: 99.
     32 *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
     33 *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
     34 *                                                item is clicked.
     35 * }
     36 * @param string $component The component the navigation is attached to. Defaults to 'members'.
     37 * @return bool|null Returns false on failure.
     38 */
     39function bp_core_new_nav_item( $args, $component = 'members' ) {
     40    if ( ! bp_is_active( $component ) ) {
     41        return;
     42    }
     43
     44    $defaults = array(
     45        'name'                    => false, // Display name for the nav item.
     46        'slug'                    => false, // URL slug for the nav item.
     47        'item_css_id'             => false, // The CSS ID to apply to the HTML of the nav item.
     48        'show_for_displayed_user' => true,  // When viewing another user does this nav item show up?
     49        'site_admin_only'         => false, // Can only site admins see this nav item?
     50        'position'                => 99,    // Index of where this nav item should be positioned.
     51        'screen_function'         => false, // The name of the function to run when clicked.
     52        'default_subnav_slug'     => false  // The slug of the default subnav item to select when clicked.
     53    );
     54
     55    $r = wp_parse_args( $args, $defaults );
     56
     57    // Validate nav link data.
     58    $nav_item = bp_core_create_nav_link( $r, $component );
     59
     60    /*
     61     * To mimic legacy behavior, if bp_core_create_nav_link() returns false, we make
     62     * an early exit and don't attempt to register the screen function.
     63     */
     64    if ( false === $nav_item ) {
     65        return false;
     66    }
     67
     68    // Then, hook the screen function for the added nav item.
     69    $hooked = bp_core_register_nav_screen_function( $nav_item );
     70    if ( false === $hooked ){
     71        return false;
     72    }
     73
     74    /**
     75     * Fires after adding an item to the main BuddyPress navigation array.
     76     * Note that, when possible, the more specific action hooks
     77     * `bp_core_create_nav_link` or `bp_core_register_nav_screen_function`
     78     * should be used.
     79     *
     80     * @since 1.5.0
     81     *
     82     * @param array $r        Parsed arguments for the nav item.
     83     * @param array $args     Originally passed in arguments for the nav item.
     84     * @param array $defaults Default arguments for a nav item.
     85     */
     86    do_action( 'bp_core_new_nav_item', $r, $args, $defaults );
     87}
     88
     89/**
     90 * Add a link to the main BuddyPress navigation.
     91 *
     92 * @since 2.4.0
     93 * @since 2.6.0 Introduced the `$component` parameter. Began returning a BP_Nav_Item object on success.
     94 *
     95 * @param array|string $args {
     96 *     Array describing the new nav item.
     97 *     @type string      $name                    Display name for the nav item.
     98 *     @type string      $slug                    Unique URL slug for the nav item.
     99 *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
     100 *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
     101 *                                                member profile other than your own. Default: true.
     102 *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
     103 *                                                (those with the 'bp_moderate' cap). Default: false.
     104 *     @type int         $position                Optional. Numerical index specifying where the item should appear in
     105 *                                                the nav array. Default: 99.
     106 *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
     107 *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
     108 *                                                item is clicked.
     109 * }
     110 * @param string $component Optional. Component that the nav belongs to.
     111 * @return bool|BP_Nav_Item Returns false on failure, new nav item on success.
     112 */
     113function bp_core_create_nav_link( $args = '', $component = 'members' ) {
     114    $bp = buddypress();
     115
     116    $defaults = array(
     117        'name'                    => false, // Display name for the nav item.
     118        'slug'                    => false, // URL slug for the nav item.
     119        'item_css_id'             => false, // The CSS ID to apply to the HTML of the nav item.
     120        'show_for_displayed_user' => true,  // When viewing another user does this nav item show up?
     121        'site_admin_only'         => false, // Can only site admins see this nav item?
     122        'position'                => 99,    // Index of where this nav item should be positioned.
     123        'screen_function'         => false, // The name of the function to run when clicked.
     124        'default_subnav_slug'     => false  // The slug of the default subnav item to select when clicked.
     125    );
     126
     127    $r = wp_parse_args( $args, $defaults );
     128
     129    // If we don't have the required info we need, don't create this nav item.
     130    if ( empty( $r['name'] ) || empty( $r['slug'] ) ) {
     131        return false;
     132    }
     133
     134    // If this is for site admins only and the user is not one, don't create the nav item.
     135    if ( ! empty( $r['site_admin_only'] ) && ! bp_current_user_can( 'bp_moderate' ) ) {
     136        return false;
     137    }
     138
     139    if ( empty( $r['item_css_id'] ) ) {
     140        $r['item_css_id'] = $r['slug'];
     141    }
     142
     143    $nav_item = array(
     144        'name'                    => $r['name'],
     145        'slug'                    => $r['slug'],
     146        'link'                    => trailingslashit( bp_loggedin_user_domain() . $r['slug'] ),
     147        'css_id'                  => $r['item_css_id'],
     148        'show_for_displayed_user' => $r['show_for_displayed_user'],
     149        'position'                => $r['position'],
     150        'screen_function'         => &$r['screen_function'],
     151        'default_subnav_slug'     => $r['default_subnav_slug']
     152    );
     153
     154    // Add the item to the nav.
     155    $retval = buddypress()->{$component}->nav->add_nav( $nav_item );
     156
     157    /**
     158     * Fires after a link is added to the main BuddyPress nav.
     159     *
     160     * @since 2.4.0
     161     * @since 2.6.0 Added `$component` parameter.
     162     *
     163     * @param array  $r         Parsed arguments for the nav item.
     164     * @param array  $args      Originally passed in arguments for the nav item.
     165     * @param array  $defaults  Default arguments for a nav item.
     166     * @param string $component Component that the nav belongs to.
     167     */
     168    do_action( 'bp_core_create_nav_link', $r, $args, $defaults, $component );
     169
     170    return $retval;
     171}
     172
     173/**
     174 * Register a screen function for an item in the main nav array.
     175 *
     176 * @since 2.4.0
    19177 *
    20178 * @param array|string $args {
     
    35193 * @return bool|null Returns false on failure.
    36194 */
    37 function bp_core_new_nav_item( $args = '' ) {
    38 
    39     $defaults = array(
    40         'name'                    => false, // Display name for the nav item.
    41         'slug'                    => false, // URL slug for the nav item.
    42         'item_css_id'             => false, // The CSS ID to apply to the HTML of the nav item.
    43         'show_for_displayed_user' => true,  // When viewing another user does this nav item show up?
    44         'site_admin_only'         => false, // Can only site admins see this nav item?
    45         'position'                => 99,    // Index of where this nav item should be positioned.
    46         'screen_function'         => false, // The name of the function to run when clicked.
    47         'default_subnav_slug'     => false  // The slug of the default subnav item to select when clicked.
    48     );
    49 
    50     $r = wp_parse_args( $args, $defaults );
    51 
    52     // First, add the nav item link to the bp_nav array.
    53     $created = bp_core_create_nav_link( $r );
    54 
    55     // To mimic the existing behavior, if bp_core_create_nav_link()
    56     // returns false, we make an early exit and don't attempt to register
    57     // the screen function.
    58     if ( false === $created ) {
    59         return false;
    60     }
    61 
    62     // Then, hook the screen function for the added nav item.
    63     $hooked = bp_core_register_nav_screen_function( $r );
    64     if ( false === $hooked ){
    65         return false;
    66     }
    67 
    68     /**
    69      * Fires after adding an item to the main BuddyPress navigation array.
    70      * Note that, when possible, the more specific action hooks
    71      * `bp_core_create_nav_link` or `bp_core_register_nav_screen_function`
    72      * should be used.
    73      *
    74      * @since 1.5.0
    75      *
    76      * @param array $r        Parsed arguments for the nav item.
    77      * @param array $args     Originally passed in arguments for the nav item.
    78      * @param array $defaults Default arguments for a nav item.
    79      */
    80     do_action( 'bp_core_new_nav_item', $r, $args, $defaults );
    81 }
    82 
    83 /**
    84  * Add a link to the main BuddyPress navigation array.
    85  *
    86  * @since 2.4.0
    87  *
    88  * @param array|string $args {
    89  *     Array describing the new nav item.
    90  *     @type string      $name                    Display name for the nav item.
    91  *     @type string      $slug                    Unique URL slug for the nav item.
    92  *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
    93  *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
    94  *                                                member profile other than your own. Default: true.
    95  *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
    96  *                                                (those with the 'bp_moderate' cap). Default: false.
    97  *     @type int         $position                Optional. Numerical index specifying where the item should appear in
    98  *                                                the nav array. Default: 99.
    99  *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
    100  *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
    101  *                                                item is clicked.
    102  * }
    103  * @return bool|null Returns false on failure.
    104  */
    105 function bp_core_create_nav_link( $args = '' ) {
    106     $bp = buddypress();
    107 
    108     $defaults = array(
    109         'name'                    => false, // Display name for the nav item.
    110         'slug'                    => false, // URL slug for the nav item.
    111         'item_css_id'             => false, // The CSS ID to apply to the HTML of the nav item.
    112         'show_for_displayed_user' => true,  // When viewing another user does this nav item show up?
    113         'site_admin_only'         => false, // Can only site admins see this nav item?
    114         'position'                => 99,    // Index of where this nav item should be positioned.
    115         'screen_function'         => false, // The name of the function to run when clicked.
    116         'default_subnav_slug'     => false  // The slug of the default subnav item to select when clicked.
    117     );
    118 
    119     $r = wp_parse_args( $args, $defaults );
    120 
    121     // If we don't have the required info we need, don't create this nav item.
    122     if ( empty( $r['name'] ) || empty( $r['slug'] ) ) {
    123         return false;
    124     }
    125 
    126     // If this is for site admins only and the user is not one, don't create the nav item.
    127     if ( ! empty( $r['site_admin_only'] ) && ! bp_current_user_can( 'bp_moderate' ) ) {
    128         return false;
    129     }
    130 
    131     if ( empty( $r['item_css_id'] ) ) {
    132         $r['item_css_id'] = $r['slug'];
    133     }
    134 
    135     $bp->bp_nav[$r['slug']] = array(
    136         'name'                    => $r['name'],
    137         'slug'                    => $r['slug'],
    138         'link'                    => trailingslashit( bp_loggedin_user_domain() . $r['slug'] ),
    139         'css_id'                  => $r['item_css_id'],
    140         'show_for_displayed_user' => $r['show_for_displayed_user'],
    141         'position'                => $r['position'],
    142         'screen_function'         => &$r['screen_function'],
    143         'default_subnav_slug'     => $r['default_subnav_slug']
    144     );
    145 
    146     /**
    147      * Fires after a link is added to the main BuddyPress navigation array.
    148      *
    149      * @since 2.4.0
    150      *
    151      * @param array $r        Parsed arguments for the nav item.
    152      * @param array $args     Originally passed in arguments for the nav item.
    153      * @param array $defaults Default arguments for a nav item.
    154      */
    155     do_action( 'bp_core_create_nav_link', $r, $args, $defaults );
    156 }
    157 
    158 /**
    159  * Register a screen function for an item in the main nav array.
    160  *
    161  * @since 2.4.0
    162  *
    163  * @param array|string $args {
    164  *     Array describing the new nav item.
    165  *     @type string      $name                    Display name for the nav item.
    166  *     @type string      $slug                    Unique URL slug for the nav item.
    167  *     @type bool|string $item_css_id             Optional. 'id' attribute for the nav item. Default: the value of `$slug`.
    168  *     @type bool        $show_for_displayed_user Optional. Whether the nav item should be visible when viewing a
    169  *                                                member profile other than your own. Default: true.
    170  *     @type bool        $site_admin_only         Optional. Whether the nav item should be visible only to site admins
    171  *                                                (those with the 'bp_moderate' cap). Default: false.
    172  *     @type int         $position                Optional. Numerical index specifying where the item should appear in
    173  *                                                the nav array. Default: 99.
    174  *     @type callable    $screen_function         The callback function that will run when the nav item is clicked.
    175  *     @type bool|string $default_subnav_slug     Optional. The slug of the default subnav item to select when the nav
    176  *                                                item is clicked.
    177  * }
    178  * @return bool|null Returns false on failure.
    179  */
    180195function bp_core_register_nav_screen_function( $args = '' ) {
    181196    $bp = buddypress();
     
    291306    $r = wp_parse_args( $args, $defaults );
    292307
    293     if ( $function = $bp->bp_nav[$r['parent_slug']]['screen_function'] ) {
     308    // This is specific to Members - it's not available in Groups.
     309    $parent_nav = $bp->members->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
     310
     311    if ( ! $parent_nav ) {
     312        return ;
     313    }
     314
     315    $parent_nav = reset( $parent_nav );
     316
     317    if ( ! empty( $parent_nav->screen_function ) ) {
    294318        // Remove our screen hook if screen function is callable.
    295         if ( is_callable( $function ) ) {
    296             remove_action( 'bp_screens', $function, 3 );
    297         }
    298     }
    299 
    300     $bp->bp_nav[$r['parent_slug']]['screen_function'] = &$r['screen_function'];
    301 
    302     if ( bp_is_current_component( $r['parent_slug'] ) ) {
     319        if ( is_callable( $parent_nav->screen_function ) ) {
     320            remove_action( 'bp_screens', $parent_nav->screen_function, 3 );
     321        }
     322    }
     323
     324    // Edit the screen function for the parent nav
     325    $bp->members->nav->edit_nav( array(
     326        'screen_function'     => &$r['screen_function'],
     327        'default_subnav_slug' => $r['subnav_slug'],
     328    ), $parent_nav->slug );
     329
     330    if ( bp_is_current_component( $parent_nav->slug ) ) {
    303331
    304332        // The only way to tell whether to set the subnav is to peek at the unfiltered_uri
    305333        // Find the component.
    306         $component_uri_key = array_search( $r['parent_slug'], $bp->unfiltered_uri );
     334        $component_uri_key = array_search( $parent_nav->slug, $bp->unfiltered_uri );
    307335
    308336        if ( false !== $component_uri_key ) {
     
    340368
    341369/**
    342  * Sort the navigation menu items.
    343  *
    344  * The sorting is split into a separate function because it can only happen
    345  * after all plugins have had a chance to register their navigation items.
    346  *
    347  * @since 1.0.0
    348  *
    349  * @return bool|null Returns false on failure.
    350  */
    351 function bp_core_sort_nav_items() {
    352     $bp = buddypress();
    353 
    354     if ( empty( $bp->bp_nav ) || ! is_array( $bp->bp_nav ) ) {
    355         return false;
    356     }
    357 
    358     $temp = array();
    359 
    360     foreach ( (array) $bp->bp_nav as $slug => $nav_item ) {
    361         if ( empty( $temp[$nav_item['position']] ) ) {
    362             $temp[$nav_item['position']] = $nav_item;
    363         } else {
    364             // Increase numbers here to fit new items in.
    365             do {
    366                 $nav_item['position']++;
    367             } while ( ! empty( $temp[$nav_item['position']] ) );
    368 
    369             $temp[$nav_item['position']] = $nav_item;
    370         }
    371     }
    372 
    373     ksort( $temp );
    374     $bp->bp_nav = &$temp;
    375 }
    376 add_action( 'wp_head',    'bp_core_sort_nav_items' );
    377 add_action( 'admin_head', 'bp_core_sort_nav_items' );
    378 
    379 /**
    380  * Add a subnav item to the BuddyPress navigation.
     370 * Add an item to secondary navigation of the specified component.
    381371 *
    382372 * @since 1.1.0
     373 * @since 2.6.0 Introduced the `$component` parameter.
    383374 *
    384375 * @param array|string $args {
     
    404395 *                                          Admin Bar menu for group admins. Default: false.
    405396 * }
     397 * @param string $component The component the navigation is attached to. Defaults to 'members'.
    406398 * @return bool|null Returns false on failure.
    407399 */
    408 function bp_core_new_subnav_item( $args = '' ) {
    409 
    410     // First, add the subnav item link to the bp_options_nav array.
    411     $created = bp_core_create_subnav_link( $args );
    412 
    413     // To mimic the existing behavior, if bp_core_create_subnav_link()
    414     // returns false, we make an early exit and don't attempt to register
    415     // the screen function.
    416     if ( false === $created ) {
     400function bp_core_new_subnav_item( $args, $component = null ) {
     401    // Backward compatibility for plugins using `bp_core_new_subnav_item()` without `$component`
     402    // to add group subnav items.
     403    if ( null === $component && bp_is_active( 'groups' ) && bp_is_group() && isset( $args['parent_slug'] ) ) {
     404        /*
     405         * Assume that this item is intended to belong to the current group if:
     406         * a) the 'parent_slug' is the same as the slug of the current group, or
     407         * b) the 'parent_slug' starts with the slug of the current group, and the members nav doesn't have
     408         *    a primary item with that slug
     409         */
     410        $group_slug = bp_get_current_group_slug();
     411        if (
     412            $group_slug === $args['parent_slug'] ||
     413            ( 0 === strpos( $args['parent_slug'], $group_slug ) && ! buddypress()->members->nav->get_primary( array( 'slug' => $args['parent_slug'] ), false ) )
     414        ) {
     415            $component = 'groups';
     416        }
     417    }
     418
     419    if ( ! $component ) {
     420        $component = 'members';
     421    }
     422
     423    if ( ! bp_is_active( $component ) ) {
     424        return;
     425    }
     426
     427    // First, register the subnav item in the nav.
     428    $subnav_item = bp_core_create_subnav_link( $args, $component );
     429
     430    /*
     431     * To mimic legacy behavior, if bp_core_create_subnav_link() returns false, we make an
     432     * early exit and don't attempt to register the screen function.
     433     */
     434    if ( false === $subnav_item ) {
    417435        return false;
    418436    }
    419437
    420438    // Then, hook the screen function for the added subnav item.
    421     $hooked = bp_core_register_subnav_screen_function( $args );
     439    $hooked = bp_core_register_subnav_screen_function( $subnav_item, $component );
    422440    if ( false === $hooked ) {
    423441        return false;
     
    429447 *
    430448 * @since 2.4.0
     449 * @since 2.6.0 Introduced the `$component` parameter. Began returning a BP_Nav_Item object on success.
    431450 *
    432451 * @param array|string $args {
     
    456475 *                                          Default: false.
    457476 * }
    458  * @return bool|null Returns false on failure.
    459  */
    460 function bp_core_create_subnav_link( $args = '' ) {
     477 * @param string $component The component the navigation is attached to. Defaults to 'members'.
     478 * @return bool|BP_Nav_Item Returns false on failure, new nav item on success.
     479 */
     480function bp_core_create_subnav_link( $args = '', $component = 'members' ) {
    461481    $bp = buddypress();
    462482
     
    484504        $r['link'] = trailingslashit( $r['parent_url'] . $r['slug'] );
    485505
     506        $parent_nav = $bp->{$component}->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
     507
    486508        // If this sub item is the default for its parent, skip the slug.
    487         if ( ! empty( $bp->bp_nav[$r['parent_slug']]['default_subnav_slug'] ) && $r['slug'] == $bp->bp_nav[$r['parent_slug']]['default_subnav_slug'] ) {
    488             $r['link'] = trailingslashit( $r['parent_url'] );
     509        if ( $parent_nav ) {
     510            $parent_nav_item = reset( $parent_nav );
     511            if ( ! empty( $parent_nav_item->default_subnav_slug ) && $r['slug'] === $parent_nav_item->default_subnav_slug ) {
     512                $r['link'] = trailingslashit( $r['parent_url'] );
     513            }
    489514        }
    490515    }
     
    503528        'link'              => $r['link'],
    504529        'slug'              => $r['slug'],
     530        'parent_slug'       => $r['parent_slug'],
    505531        'css_id'            => $r['item_css_id'],
    506532        'position'          => $r['position'],
     
    511537    );
    512538
    513     $bp->bp_options_nav[$r['parent_slug']][$r['slug']] = $subnav_item;
     539    return buddypress()->{$component}->nav->add_nav( $subnav_item );
    514540}
    515541
     
    518544 *
    519545 * @since 2.4.0
     546 * @since 2.6.0 Introduced the `$component` parameter.
    520547 *
    521548 * @param array|string $args {
     
    542569 *                                       Default: false.
    543570 * }
     571 * @param string $component The component the navigation is attached to. Defaults to 'members'.
    544572 * @return bool|null Returns false on failure.
    545573 */
    546 function bp_core_register_subnav_screen_function( $args = '' ) {
     574function bp_core_register_subnav_screen_function( $args = '', $component = 'members' ) {
    547575    $bp = buddypress();
    548576
     
    556584    ) );
    557585
    558     /**
     586    /*
    559587     * Hook the screen function for the added subnav item. But this only needs to
    560588     * be done if this subnav item is the current view, and the user has access to the
     
    569597     *       and this subnav item is the default for the parent item (which we check by
    570598     *       comparing this subnav item's screen function with the screen function of the
    571      *       parent nav item in $bp->bp_nav). This condition only arises when viewing a
    572      *       user, since groups should always have an action set.
     599     *       parent nav item in the component's primary nav). This condition only arises
     600     *       when viewing a user, since groups should always have an action set.
    573601     */
    574602
     
    578606    }
    579607
     608    $parent_nav = $bp->{$component}->nav->get_primary( array( 'slug' => $r['parent_slug'] ), false );
     609
    580610    // If we *do* meet condition (2), then the added subnav item is currently being requested.
    581     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'] ) ) ) {
     611    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 ) ) {
    582612
    583613        // If this is for site admins only and the user is not one, don't create the subnav item.
     
    586616        }
    587617
    588         $hooked = bp_core_maybe_hook_new_subnav_screen_function( $r );
     618        $hooked = bp_core_maybe_hook_new_subnav_screen_function( $r, $component );
    589619
    590620        // If redirect args have been returned, perform the redirect now.
     
    599629 *
    600630 * @since 2.1.0
    601  *
    602  * @param array $subnav_item The subnav array added to bp_options_nav in `bp_core_new_subnav_item()`.
     631 * @since 2.6.0 Introduced the `$component` parameter.
     632 *
     633 * @param array  $subnav_item The subnav array added to the secondary navigation of
     634 *                            the component in bp_core_new_subnav_item().
     635 * @param string $component   The component the navigation is attached to. Defaults to 'members'.
    603636 * @return array
    604637 */
    605 function bp_core_maybe_hook_new_subnav_screen_function( $subnav_item ) {
     638function bp_core_maybe_hook_new_subnav_screen_function( $subnav_item, $component = 'members' ) {
    606639    $retval = array(
    607640        'status' => '',
     
    646679            } elseif ( bp_is_user() ) {
    647680
     681                $parent_nav_default = $bp->{$component}->nav->get_primary( array( 'slug' => $bp->default_component ), false );
     682                if ( $parent_nav_default ) {
     683                    $parent_nav_default_item = reset( $parent_nav_default );
     684                }
     685
    648686                // Redirect to the displayed user's default
    649687                // component, as long as that component is
    650688                // publicly accessible.
    651                 if ( bp_is_my_profile() || ! empty( $bp->bp_nav[ $bp->default_component ]['show_for_displayed_user'] ) ) {
     689                if ( bp_is_my_profile() || ( isset( $parent_nav_default_item ) && $parent_nav_default_item->show_for_displayed_user ) ) {
    652690                    $message     = __( 'You do not have access to this page.', 'buddypress' );
    653691                    $redirect_to = bp_displayed_user_domain();
     
    692730
    693731/**
    694  * Sort all subnavigation arrays.
    695  *
    696  * @since 1.1.0
    697  *
    698  * @return bool|null Returns false on failure.
    699  */
    700 function bp_core_sort_subnav_items() {
     732 * Check whether a given nav item has subnav items.
     733 *
     734 * @since 1.5.0
     735 * @since 2.6.0 Introduced the `$component` parameter.
     736 *
     737 * @param string $nav_item  The slug of the top-level nav item whose subnav items you're checking.
     738 *                          Default: the current component slug.
     739 * @param string $component The component the navigation is attached to. Defaults to 'members'.
     740 * @return bool $has_subnav True if the nav item is found and has subnav items; false otherwise.
     741 */
     742function bp_nav_item_has_subnav( $nav_item = '', $component = 'members' ) {
    701743    $bp = buddypress();
    702744
    703     if ( empty( $bp->bp_options_nav ) || !is_array( $bp->bp_options_nav ) )
    704         return false;
    705 
    706     foreach ( (array) $bp->bp_options_nav as $parent_slug => $subnav_items ) {
    707         if ( !is_array( $subnav_items ) )
    708             continue;
    709 
    710         foreach ( (array) $subnav_items as $subnav_item ) {
    711             if ( empty( $temp[$subnav_item['position']]) )
    712                 $temp[$subnav_item['position']] = $subnav_item;
    713             else {
    714                 // Increase numbers here to fit new items in.
    715                 do {
    716                     $subnav_item['position']++;
    717                 } while ( !empty( $temp[$subnav_item['position']] ) );
    718 
    719                 $temp[$subnav_item['position']] = $subnav_item;
    720             }
    721         }
    722         ksort( $temp );
    723         $bp->bp_options_nav[$parent_slug] = &$temp;
    724         unset( $temp );
    725     }
    726 }
    727 add_action( 'wp_head',    'bp_core_sort_subnav_items' );
    728 add_action( 'admin_head', 'bp_core_sort_subnav_items' );
    729 
    730 /**
    731  * Check whether a given nav item has subnav items.
    732  *
    733  * @since 1.5.0
    734  *
    735  * @param string $nav_item The slug of the top-level nav item whose subnav items you're checking.
    736  *                         Default: the current component slug.
    737  * @return bool $has_subnav True if the nav item is found and has subnav items; false otherwise.
    738  */
    739 function bp_nav_item_has_subnav( $nav_item = '' ) {
    740     $bp = buddypress();
    741 
    742     if ( !$nav_item )
     745    if ( ! isset( $bp->{$component}->nav ) ) {
     746        return false;
     747    }
     748
     749    if ( ! $nav_item ) {
    743750        $nav_item = bp_current_component();
    744751
    745     $has_subnav = isset( $bp->bp_options_nav[$nav_item] ) && count( $bp->bp_options_nav[$nav_item] ) > 0;
     752        if ( bp_is_group() ) {
     753            $nav_item = bp_current_item();
     754        }
     755    }
     756
     757    $has_subnav = (bool) $bp->{$component}->nav->get_secondary( array( 'parent_slug' => $nav_item ), false );
    746758
    747759    /**
     
    757769
    758770/**
    759  * Remove a nav item from the navigation array.
     771 * Deletes an item from the primary navigation of the specified component.
    760772 *
    761773 * @since 1.0.0
    762  *
    763  * @param int $parent_id The slug of the parent navigation item.
    764  * @return bool Returns false on failure, ie if the nav item can't be found.
    765  */
    766 function bp_core_remove_nav_item( $parent_id ) {
     774 * @since 2.6.0 Introduced the `$component` parameter.
     775 *
     776 * @param string $slug      The slug of the primary navigation item.
     777 * @param string $component The component the navigation is attached to. Defaults to 'members'.
     778 * @return bool Returns false on failure, True on success.
     779 */
     780function bp_core_remove_nav_item( $slug, $component = null ) {
    767781    $bp = buddypress();
    768782
    769     // Unset subnav items for this nav item.
    770     if ( isset( $bp->bp_options_nav[$parent_id] ) && is_array( $bp->bp_options_nav[$parent_id] ) ) {
    771         foreach( (array) $bp->bp_options_nav[$parent_id] as $subnav_item ) {
    772             bp_core_remove_subnav_item( $parent_id, $subnav_item['slug'] );
    773         }
    774     }
    775 
    776     if ( empty( $bp->bp_nav[ $parent_id ] ) )
    777         return false;
    778 
    779     if ( $function = $bp->bp_nav[$parent_id]['screen_function'] ) {
    780         // Remove our screen hook if screen function is callable.
    781         if ( is_callable( $function ) ) {
    782             remove_action( 'bp_screens', $function, 3 );
    783         }
    784     }
    785 
    786     unset( $bp->bp_nav[$parent_id] );
    787 }
    788 
    789 /**
    790  * Remove a subnav item from the navigation array.
    791  *
    792  * @since 1.0.0
    793  *
    794  * @param string $parent_id The slug of the parent navigation item.
    795  * @param string $slug      The slug of the subnav item to be removed.
    796  */
    797 function bp_core_remove_subnav_item( $parent_id, $slug ) {
    798     $bp = buddypress();
    799 
    800     $screen_function = isset( $bp->bp_options_nav[$parent_id][$slug]['screen_function'] )
    801         ? $bp->bp_options_nav[$parent_id][$slug]['screen_function']
    802         : false;
    803 
    804     if ( ! empty( $screen_function ) ) {
     783    // Backward compatibility for removing group nav items using the group slug as `$parent_slug`.
     784    if ( ! $component && bp_is_active( 'groups' ) && isset( $bp->groups->nav ) ) {
     785        if ( $bp->groups->nav->get_primary( array( 'slug' => $slug ) ) ) {
     786            return bp_core_remove_nav_item( $slug, 'groups' );
     787        }
     788    }
     789
     790    if ( ! $component ) {
     791        $component = 'members';
     792    }
     793
     794    if ( ! isset( $bp->{$component}->nav ) ) {
     795        return false;
     796    }
     797
     798    $screen_functions = $bp->{$component}->nav->delete_nav( $slug );
     799
     800    // Reset backcompat nav items so that subsequent references will be correct.
     801    if ( buddypress()->do_nav_backcompat ) {
     802        $bp->bp_nav->reset();
     803        $bp->bp_options_nav->reset();
     804    }
     805
     806    if ( ! is_array( $screen_functions ) ) {
     807        return false;
     808    }
     809
     810    foreach ( $screen_functions as $screen_function ) {
    805811        // Remove our screen hook if screen function is callable.
    806812        if ( is_callable( $screen_function ) ) {
     
    809815    }
    810816
    811     unset( $bp->bp_options_nav[$parent_id][$slug] );
    812 
    813     if ( isset( $bp->bp_options_nav[$parent_id] ) && !count( $bp->bp_options_nav[$parent_id] ) )
    814         unset($bp->bp_options_nav[$parent_id]);
     817    return true;
     818}
     819
     820/**
     821 * Deletes an item from the secondary navigation of the specified component.
     822 *
     823 * @since 1.0.0
     824 * @since 2.6.0 Introduced the `$component` parameter.
     825 *
     826 * @param string $parent_slug The slug of the primary navigation item.
     827 * @param string $slug        The slug of the secondary item to be removed.
     828 * @param string $component   The component the navigation is attached to. Defaults to 'members'.
     829 * @return bool Returns false on failure, True on success.
     830 */
     831function bp_core_remove_subnav_item( $parent_slug, $slug, $component = null ) {
     832    $bp = buddypress();
     833
     834    // Backward compatibility for removing group nav items using the group slug as `$parent_slug`.
     835    if ( ! $component && bp_is_active( 'groups' ) && isset( $bp->groups->nav ) ) {
     836        if ( $bp->groups->nav->get_primary( array( 'slug' => $parent_slug ) ) ) {
     837            return bp_core_remove_subnav_item( $slug, $parent_slug, 'groups' );
     838        }
     839    }
     840
     841    if ( ! $component ) {
     842        $component = 'members';
     843    }
     844
     845    if ( ! isset( $bp->{$component}->nav ) ) {
     846        return false;
     847    }
     848
     849    $screen_functions = $bp->{$component}->nav->delete_nav( $slug, $parent_slug );
     850
     851    // Reset backcompat nav items so that subsequent references will be correct.
     852    if ( buddypress()->do_nav_backcompat ) {
     853        $bp->bp_nav->reset();
     854        $bp->bp_options_nav->reset();
     855    }
     856
     857    if ( ! is_array( $screen_functions ) ) {
     858        return false;
     859    }
     860
     861    $screen_function = reset( $screen_functions );
     862
     863    // Remove our screen hook if screen function is callable.
     864    if ( is_callable( $screen_function ) ) {
     865        remove_action( 'bp_screens', $screen_function, 3 );
     866    }
     867
     868    return true;
    815869}
    816870
     
    819873 *
    820874 * @since 1.0.0
     875 * @since 2.6.0 Introduced the `$component` parameter.
    821876 *
    822877 * @param string $parent_slug The slug of the parent navigation item.
    823  */
    824 function bp_core_reset_subnav_items( $parent_slug ) {
     878 * @param string $component   The component the navigation is attached to. Defaults to 'members'.
     879 */
     880function bp_core_reset_subnav_items( $parent_slug, $component = 'members' ) {
    825881    $bp = buddypress();
    826882
    827     unset( $bp->bp_options_nav[$parent_slug] );
     883    if ( ! isset( $bp->{$component}->nav ) ) {
     884        return;
     885    }
     886
     887    $subnav_items = $bp->{$component}->nav->get_secondary( array( 'parent_slug' => $parent_slug ), false );
     888
     889    if ( ! $subnav_items ) {
     890        return;
     891    }
     892
     893    foreach( $subnav_items as $subnav_item ) {
     894        $bp->{$component}->nav->delete_nav( $subnav_item->slug, $parent_slug );
     895    }
    828896}
    829897
  • trunk/src/bp-core/bp-core-classes.php

    r10514 r10745  
    3131require dirname( __FILE__ ) . '/classes/class-bp-email-delivery.php';
    3232require dirname( __FILE__ ) . '/classes/class-bp-phpmailer.php';
     33require dirname( __FILE__ ) . '/classes/class-bp-core-nav.php';
     34require dirname( __FILE__ ) . '/classes/class-bp-core-nav-item.php';
     35
     36if ( buddypress()->do_nav_backcompat ) {
     37    require dirname( __FILE__ ) . '/classes/class-bp-core-bp-nav-backcompat.php';
     38    require dirname( __FILE__ ) . '/classes/class-bp-core-bp-options-nav-backcompat.php';
     39}
  • trunk/src/bp-core/bp-core-functions.php

    r10726 r10745  
    24442444    }
    24452445
    2446     // Pull up a list of items registered in BP's top-level nav array.
    2447     $bp_menu_items = buddypress()->bp_nav;
    2448 
    2449     // Alphabetize.
    2450     $bp_menu_items = bp_alpha_sort_by_key( $bp_menu_items, 'name' );
     2446    // Pull up a list of items registered in BP's primary nav for the member.
     2447    $bp_menu_items = buddypress()->members->nav->get_primary();
    24512448
    24522449    // Some BP nav menu items will not be represented in bp_nav, because
     
    25762573 *
    25772574 * @param string $slug The slug of the nav item: login, register, or one of the
    2578  *                     slugs from buddypress()->bp_nav.
     2575 *                     slugs from the members navigation.
    25792576 * @return string $nav_item_url The URL generated for the current user.
    25802577 */
  • trunk/src/bp-core/bp-core-template.php

    r10693 r10745  
    1414 * Output the "options nav", the secondary-level single item navigation menu.
    1515 *
    16  * Uses the $bp->bp_options_nav global to render out the sub navigation for the
     16 * Uses the component's nav global to render out the sub navigation for the
    1717 * current component. Each component adds to its sub navigation array within
    1818 * its own setup_nav() function.
     
    4141    $selected_item   = bp_current_action();
    4242
     43    // Default to the Members nav.
    4344    if ( ! bp_is_single_item() ) {
    44         if ( !isset( $bp->bp_options_nav[$component_index] ) || count( $bp->bp_options_nav[$component_index] ) < 1 ) {
     45        // Set the parent slug, if not provided.
     46        if ( empty( $parent_slug ) ) {
     47            $parent_slug = $component_index;
     48        }
     49
     50        $secondary_nav_items = $bp->members->nav->get_secondary( array( 'parent_slug' => $parent_slug ) );
     51
     52        if ( ! $secondary_nav_items ) {
    4553            return false;
    46         } else {
    47             $the_index = $component_index;
    48         }
     54        }
     55
     56    // For a single item, try to use the component's nav.
    4957    } else {
    5058        $current_item = bp_current_item();
    51 
     59        $single_item_component = bp_current_component();
     60
     61        // Adjust the selected nav item for the current single item if needed.
    5262        if ( ! empty( $parent_slug ) ) {
    5363            $current_item  = $parent_slug;
     
    5565        }
    5666
    57         if ( !isset( $bp->bp_options_nav[$current_item] ) || count( $bp->bp_options_nav[$current_item] ) < 1 ) {
     67        // If the nav is not defined by the parent component, look in the Members nav.
     68        if ( ! isset( $bp->{$single_item_component}->nav ) ) {
     69            $secondary_nav_items = $bp->members->nav->get_secondary( array( 'parent_slug' => $current_item ) );
     70        } else {
     71            $secondary_nav_items = $bp->{$single_item_component}->nav->get_secondary( array( 'parent_slug' => $current_item ) );
     72        }
     73
     74        if ( ! $secondary_nav_items ) {
    5875            return false;
    59         } else {
    60             $the_index = $current_item;
    6176        }
    6277    }
    6378
    6479    // Loop through each navigation item.
    65     foreach ( (array) $bp->bp_options_nav[$the_index] as $subnav_item ) {
    66         if ( empty( $subnav_item['user_has_access'] ) ) {
     80    foreach ( $secondary_nav_items as $subnav_item ) {
     81        if ( empty( $subnav_item->user_has_access ) ) {
    6782            continue;
    6883        }
    6984
    7085        // If the current action or an action variable matches the nav item id, then add a highlight CSS class.
    71         if ( $subnav_item['slug'] == $selected_item ) {
     86        if ( $subnav_item->slug === $selected_item ) {
    7287            $selected = ' class="current selected"';
    7388        } else {
     
    89104         * @param string $selected_item Current action.
    90105         */
    91         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 );
     106        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 );
    92107    }
    93108}
     
    30113026        $component_subnav_name = '';
    30123027
     3028        if ( ! empty( $bp->members->nav ) ) {
     3029            $primary_nav_item = $bp->members->nav->get_primary( array( 'slug' => $component_id ), false );
     3030            $primary_nav_item = reset( $primary_nav_item );
     3031        }
     3032
    30133033        // Use the component nav name.
    3014         if ( ! empty( $bp->bp_nav[$component_id] ) ) {
    3015             $component_name = _bp_strip_spans_from_title( $bp->bp_nav[ $component_id ]['name'] );
     3034        if ( ! empty( $primary_nav_item->name ) ) {
     3035            $component_name = _bp_strip_spans_from_title( $primary_nav_item->name );
    30163036
    30173037        // Fall back on the component ID.
     
    30203040        }
    30213041
     3042        if ( ! empty( $bp->members->nav ) ) {
     3043            $secondary_nav_item = $bp->members->nav->get_secondary( array(
     3044                'parent_slug' => $component_id,
     3045                'slug'        => bp_current_action()
     3046            ), false );
     3047
     3048            if ( $secondary_nav_item ) {
     3049                $secondary_nav_item = reset( $secondary_nav_item );
     3050            }
     3051        }
     3052
    30223053        // Append action name if we're on a member component sub-page.
    3023         if ( ! empty( $bp->bp_options_nav[ $component_id ] ) && ! empty( $bp->canonical_stack['action'] ) ) {
    3024             $component_subnav_name = wp_filter_object_list( $bp->bp_options_nav[ $component_id ], array( 'slug' => bp_current_action() ), 'and', 'name' );
    3025 
    3026             if ( ! empty( $component_subnav_name ) ) {
    3027                 $component_subnav_name = array_shift( $component_subnav_name );
    3028             }
     3054        if ( ! empty( $secondary_nav_item->name ) && ! empty( $bp->canonical_stack['action'] ) ) {
     3055            $component_subnav_name = $secondary_nav_item->name;
    30293056        }
    30303057
     
    30463073        }
    30473074
    3048     // A single group.
    3049     } elseif ( bp_is_active( 'groups' ) && ! empty( $bp->groups->current_group ) && ! empty( $bp->bp_options_nav[ $bp->groups->current_group->slug ] ) ) {
    3050         $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'] : '';
    3051         $bp_title_parts = array( $bp->bp_options_title, $subnav );
    3052 
    3053     // A single item from a component other than groups.
     3075    // A single item from a component other than Members.
    30543076    } elseif ( bp_is_single_item() ) {
    3055         $bp_title_parts = array( $bp->bp_options_title, $bp->bp_options_nav[ bp_current_item() ][ bp_current_action() ]['name'] );
     3077        $component_id = bp_current_component();
     3078
     3079        if ( ! empty( $bp->{$component_id}->nav ) ) {
     3080            $secondary_nav_item = $bp->{$component_id}->nav->get_secondary( array(
     3081                'parent_slug' => bp_current_item(),
     3082                'slug'        => bp_current_action()
     3083            ), false );
     3084
     3085            if ( $secondary_nav_item ) {
     3086                $secondary_nav_item = reset( $secondary_nav_item );
     3087            }
     3088        }
     3089
     3090        $single_item_subnav = '';
     3091
     3092        if ( ! empty( $secondary_nav_item->name ) ) {
     3093            $single_item_subnav = $secondary_nav_item->name;
     3094        }
     3095
     3096        $bp_title_parts = array( $bp->bp_options_title, $single_item_subnav );
    30563097
    30573098    // An index or directory.
     
    34293470 *
    34303471 * @since 1.7.0
    3431  *
     3472 * @since 2.6.0 Introduced the `$component` parameter.
     3473 *
     3474 * @param string $component Optional. Component whose nav items are being fetched.
    34323475 * @return array A multidimensional array of all navigation items.
    34333476 */
    3434 function bp_get_nav_menu_items() {
    3435     $menus = $selected_menus = array();
    3436 
    3437     // Get the second level menus.
    3438     foreach ( (array) buddypress()->bp_options_nav as $parent_menu => $sub_menus ) {
    3439 
    3440         // The root menu's ID is "xprofile", but the Profile submenus are using "profile". See BP_Core::setup_nav().
    3441         if ( 'profile' === $parent_menu ) {
    3442             $parent_menu = 'xprofile';
    3443         }
    3444 
    3445         // Sort the items in this menu's navigation by their position property.
    3446         $second_level_menus = (array) $sub_menus;
    3447         usort( $second_level_menus, '_bp_nav_menu_sort' );
    3448 
    3449         // Iterate through the second level menus.
    3450         foreach( $second_level_menus as $sub_nav ) {
    3451 
    3452             // Skip items we don't have access to.
    3453             if ( empty( $sub_nav['user_has_access'] ) ) {
    3454                 continue;
    3455             }
    3456 
    3457             // Add this menu.
    3458             $menu         = new stdClass;
    3459             $menu->class  = array( 'menu-child' );
    3460             $menu->css_id = $sub_nav['css_id'];
    3461             $menu->link   = $sub_nav['link'];
    3462             $menu->name   = $sub_nav['name'];
    3463             $menu->parent = $parent_menu;  // Associate this sub nav with a top-level menu.
    3464 
    3465             // If we're viewing this item's screen, record that we need to mark its parent menu to be selected.
    3466             if ( $sub_nav['slug'] == bp_current_action() ) {
    3467                 $menu->class[]    = 'current-menu-item';
    3468                 $selected_menus[] = $parent_menu;
    3469             }
    3470 
    3471             $menus[] = $menu;
    3472         }
    3473     }
    3474 
    3475     // Get the top-level menu parts (Friends, Groups, etc) and sort by their position property.
    3476     $top_level_menus = (array) buddypress()->bp_nav;
    3477     usort( $top_level_menus, '_bp_nav_menu_sort' );
    3478 
    3479     // Iterate through the top-level menus.
    3480     foreach ( $top_level_menus as $nav ) {
    3481 
    3482         // Skip items marked as user-specific if you're not on your own profile.
    3483         if ( empty( $nav['show_for_displayed_user'] ) && ! bp_core_can_edit_settings()  ) {
    3484             continue;
    3485         }
    3486 
     3477function bp_get_nav_menu_items( $component = 'members' ) {
     3478    $bp    = buddypress();
     3479    $menus = array();
     3480
     3481    if ( ! isset( $bp->{$component}->nav ) ) {
     3482        return $menus;
     3483    }
     3484
     3485    // Get the item nav and build the menus.
     3486    foreach ( $bp->{$component}->nav->get_item_nav() as $nav_menu ) {
    34873487        // Get the correct menu link. See https://buddypress.trac.wordpress.org/ticket/4624.
    3488         $link = bp_loggedin_user_domain() ? str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $nav['link'] ) : trailingslashit( bp_displayed_user_domain() . $nav['link'] );
     3488        $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 );
    34893489
    34903490        // Add this menu.
    34913491        $menu         = new stdClass;
    34923492        $menu->class  = array( 'menu-parent' );
    3493         $menu->css_id = $nav['css_id'];
    3494         $menu->link   = $link;
    3495         $menu->name   = $nav['name'];
     3493        $menu->css_id = $nav_menu->css_id;
     3494        $menu->link   = $nav_menu->link;
     3495        $menu->name   = $nav_menu->name;
    34963496        $menu->parent = 0;
    34973497
    3498         // Check if we need to mark this menu as selected.
    3499         if ( in_array( $nav['css_id'], $selected_menus ) ) {
    3500             $menu->class[] = 'current-menu-parent';
     3498        if ( ! empty( $nav_menu->children ) ) {
     3499            $submenus = array();
     3500
     3501            foreach( $nav_menu->children as $sub_menu ) {
     3502                $submenu = new stdClass;
     3503                $submenu->class  = array( 'menu-child' );
     3504                $submenu->css_id = $sub_menu->css_id;
     3505                $submenu->link   = $sub_menu->link;
     3506                $submenu->name   = $sub_menu->name;
     3507                $submenu->parent = $nav_menu->slug;
     3508
     3509                // If we're viewing this item's screen, record that we need to mark its parent menu to be selected.
     3510                if ( $sub_menu->slug == bp_current_action() ) {
     3511                    $menu->class[]    = 'current-menu-parent';
     3512                    $submenu->class[] = 'current-menu-item';
     3513                }
     3514
     3515                $submenus[] = $submenu;
     3516            }
    35013517        }
    35023518
    35033519        $menus[] = $menu;
     3520
     3521        if ( ! empty( $submenus ) ) {
     3522            $menus = array_merge( $menus, $submenus );
     3523        }
    35043524    }
    35053525
  • trunk/src/bp-core/classes/class-bp-component.php

    r10555 r10745  
    490490        // No sub nav items without a main nav item.
    491491        if ( !empty( $main_nav ) ) {
    492             bp_core_new_nav_item( $main_nav );
     492            bp_core_new_nav_item( $main_nav, 'members' );
    493493
    494494            // Sub nav items are not required.
    495495            if ( !empty( $sub_nav ) ) {
    496496                foreach( (array) $sub_nav as $nav ) {
    497                     bp_core_new_subnav_item( $nav );
     497                    bp_core_new_subnav_item( $nav, 'members' );
    498498                }
    499499            }
  • trunk/src/bp-core/classes/class-bp-core.php

    r10536 r10745  
    254254        // bp-notifications instead.
    255255        $bp->core->table_name_notifications = $bp->table_prefix . 'bp_notifications';
     256
     257        // Backward compatibility for plugins modifying the legacy bp_nav and bp_options_nav global properties.
     258        if ( buddypress()->do_nav_backcompat ) {
     259            $bp->bp_nav         = new BP_Core_BP_Nav_BackCompat();
     260            $bp->bp_options_nav = new BP_Core_BP_Options_Nav_BackCompat();
     261        }
    256262
    257263        /**
  • trunk/src/bp-core/deprecated/2.6.php

    r10723 r10745  
    2222    <?php
    2323}
     24
     25/**
     26 * Sort the navigation menu items.
     27 *
     28 * The sorting is split into a separate function because it can only happen
     29 * after all plugins have had a chance to register their navigation items.
     30 *
     31 * @since 1.0.0
     32 * @deprecated 2.6.0
     33 *
     34 * @return bool|null Returns false on failure.
     35 */
     36function bp_core_sort_nav_items() {
     37    _deprecated_function( __FUNCTION__, '2.6' );
     38}
     39
     40/**
     41 * Sort all subnavigation arrays.
     42 *
     43 * @since 1.1.0
     44 * @deprecated 2.6.0
     45 *
     46 * @return bool|null Returns false on failure.
     47 */
     48function bp_core_sort_subnav_items() {
     49    _deprecated_function( __FUNCTION__, '2.6' );
     50}
  • trunk/src/bp-groups/bp-groups-adminbar.php

    r10417 r10745  
    4848
    4949    // Index of the Manage tabs parent slug.
    50     $nav_index = $bp->groups->current_group->slug . '_manage';
     50    $secondary_nav_items = $bp->groups->nav->get_secondary( array( 'parent_slug' => $bp->groups->current_group->slug . '_manage' ) );
    5151
    5252    // Check if current group has Manage tabs.
    53     if ( empty( $bp->bp_options_nav[ $nav_index ] ) ) {
     53    if ( ! $secondary_nav_items ) {
    5454        return;
    5555    }
    5656
    5757    // Build the Group Admin menus.
    58     foreach ( $bp->bp_options_nav[ $nav_index ] as $menu ) {
     58    foreach ( $secondary_nav_items as $menu ) {
    5959        /**
    6060         * Should we add the current manage link in the Group's "Edit" Admin Bar menu ?
     
    6464         * the 'show_in_admin_bar' argument of your edit screen to true
    6565         */
    66         if ( $menu['show_in_admin_bar'] ) {
    67             $title = sprintf( _x( 'Edit Group %s', 'Group WP Admin Bar manage links', 'buddypress' ), $menu['name'] );
     66        if ( $menu->show_in_admin_bar ) {
     67            $title = sprintf( _x( 'Edit Group %s', 'Group WP Admin Bar manage links', 'buddypress' ), $menu->name );
    6868
    6969            // Title is specific for delete.
    70             if ( 'delete-group' == $menu['slug'] ) {
    71                 $title = sprintf( _x( '%s Group', 'Group WP Admin Bar delete link', 'buddypress' ), $menu['name'] );
     70            if ( 'delete-group' == $menu->slug ) {
     71                $title = sprintf( _x( '%s Group', 'Group WP Admin Bar delete link', 'buddypress' ), $menu->name );
    7272            }
    7373
    7474            $wp_admin_bar->add_menu( array(
    7575                'parent' => $bp->group_admin_menu_id,
    76                 'id'     => $menu['slug'],
     76                'id'     => $menu->slug,
    7777                'title'  => $title,
    78                 'href'   => bp_get_groups_action_link( 'admin/' . $menu['slug'] )
     78                'href'   => bp_get_groups_action_link( 'admin/' . $menu->slug )
    7979            ) );
    8080        }
  • trunk/src/bp-groups/classes/class-bp-group-extension.php

    r10454 r10745  
    738738                'user_has_access' => $user_can_see_nav_item,
    739739                'no_access_url'   => $group_permalink,
    740             ) );
     740            ), 'groups' );
    741741        }
    742742
     
    753753                'user_has_access' => $user_can_visit,
    754754                'no_access_url'   => $group_permalink,
    755             ) );
     755            ), 'groups' );
    756756
    757757            // When we are viewing the extension display page, set the title and options title.
     
    968968
    969969        // Add the tab to the manage navigation.
    970         bp_core_new_subnav_item( $subnav_args );
     970        bp_core_new_subnav_item( $subnav_args, 'groups' );
    971971
    972972        // Catch the edit screen and forward it to the plugin template.
  • trunk/src/bp-groups/classes/class-bp-groups-component.php

    r10679 r10745  
    253253            // Check once if the current group has a custom front template.
    254254            $this->current_group->front_template = bp_groups_get_front_template( $this->current_group );
     255
     256            // Initialize the nav for the groups component.
     257            $this->nav = new BP_Core_Nav( $this->current_group->id );
    255258
    256259        // Set current_group to 0 to prevent debug errors.
     
    497500            $sub_nav = array();
    498501
    499             // Add 'Groups' to the main navigation.
    500             $main_nav = array(
     502            /*
     503             * The top-level Groups item is called 'Memberships' for legacy reasons.
     504             * It does not appear in the interface.
     505             */
     506            bp_core_new_nav_item( array(
    501507                'name'                => __( 'Memberships', 'buddypress' ),
    502508                'slug'                => $this->current_group->slug,
     
    505511                'default_subnav_slug' => $this->default_extension,
    506512                'item_css_id'         => $this->id
    507             );
     513            ), 'groups' );
    508514
    509515            $group_link = bp_get_group_permalink( $this->current_group );
     
    676682            }
    677683
    678             parent::setup_nav( $main_nav, $sub_nav );
     684            foreach ( $sub_nav as $nav ) {
     685                bp_core_new_subnav_item( $nav, 'groups' );
     686            }
    679687        }
    680688
  • trunk/src/bp-loader.php

    r10723 r10745  
    110110    public $do_autoload = false;
    111111
     112    /**
     113     * Whether to load backward compatibility classes for navigation globals.
     114     *
     115     * @since 2.6.0
     116     * @var bool
     117     */
     118    public $do_nav_backcompat = false;
     119
    112120    /** Option Overload *******************************************************/
    113121
     
    435443        $this->email_post_type     = apply_filters( 'bp_email_post_type', 'bp-email' );
    436444        $this->email_taxonomy_type = apply_filters( 'bp_email_tax_type', 'bp-email-type' );
     445
     446        /** Navigation backward compatibility *********************************/
     447        if ( interface_exists( 'ArrayAccess', false ) ) {
     448            // bp_nav and bp_options_nav compatibility depends on SPL.
     449            $this->do_nav_backcompat = true;
     450        }
    437451    }
    438452
  • trunk/src/bp-members/bp-members-template.php

    r10711 r10745  
    12911291
    12921292    // Loop through each navigation item.
    1293     foreach( (array) $bp->bp_nav as $nav_item ) {
     1293    foreach ( (array) $bp->members->nav->get_primary() as $nav_item ) {
    12941294
    12951295        $selected = '';
    12961296
    12971297        // If the current component matches the nav item id, then add a highlight CSS class.
    1298         if ( !bp_is_directory() && !empty( $bp->active_components[bp_current_component()] ) && $bp->active_components[bp_current_component()] == $nav_item['css_id'] ) {
     1298        if ( ! bp_is_directory() && ! empty( $bp->active_components[ bp_current_component() ] ) && $bp->active_components[ bp_current_component() ] == $nav_item->css_id ) {
    12991299            $selected = ' class="current selected"';
    13001300        }
     
    13081308
    13091309            if ( bp_is_active( 'friends' ) ) {
    1310                 if ( $nav_item['css_id'] == $bp->friends->id ) {
     1310                if ( $nav_item->css_id == $bp->friends->id ) {
    13111311                    if ( friends_check_friendship( bp_loggedin_user_id(), bp_displayed_user_id() ) ) {
    13121312                        $selected = ' class="current selected"';
     
    13171317
    13181318        // Echo out the final list item.
    1319         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 ) );
     1319        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 ) );
    13201320    }
    13211321
     
    13341334    $bp = buddypress();
    13351335
    1336     foreach ( (array) $bp->bp_nav as $user_nav_item ) {
    1337         if ( empty( $user_nav_item['show_for_displayed_user'] ) && !bp_is_my_profile() )
     1336    foreach ( $bp->members->nav->get_primary() as $user_nav_item ) {
     1337        if ( empty( $user_nav_item->show_for_displayed_user ) && ! bp_is_my_profile() ) {
    13381338            continue;
     1339        }
    13391340
    13401341        $selected = '';
    1341         if ( bp_is_current_component( $user_nav_item['slug'] ) ) {
     1342        if ( bp_is_current_component( $user_nav_item->slug ) ) {
    13421343            $selected = ' class="current selected"';
    13431344        }
    13441345
    13451346        if ( bp_loggedin_user_domain() ) {
    1346             $link = str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $user_nav_item['link'] );
     1347            $link = str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $user_nav_item->link );
    13471348        } else {
    1348             $link = trailingslashit( bp_displayed_user_domain() . $user_nav_item['link'] );
     1349            $link = trailingslashit( bp_displayed_user_domain() . $user_nav_item->link );
    13491350        }
    13501351
     
    13601361         *                              Passed by reference.
    13611362         */
    1362         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 ) );
     1363        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 ) );
    13631364    }
    13641365}
  • trunk/src/bp-members/classes/class-bp-members-component.php

    r10711 r10745  
    151151        $bp->displayed_user->domain   = bp_core_get_user_domain( bp_displayed_user_id() );
    152152
     153        // Initialize the nav for the members component.
     154        $this->nav = new BP_Core_Nav();
     155
    153156        /** Signup ***********************************************************
    154157         */
  • trunk/src/bp-messages/bp-messages-screens.php

    r10417 r10745  
    133133    $nav_name = sprintf( __( 'Messages <span class="%s">%s</span>', 'buddypress' ), esc_attr( $class ), bp_core_number_format( $count ) );
    134134
    135     $bp->bp_nav[ $bp->messages->slug ]['name'] = $nav_name;
     135    // Edit the Navigation name.
     136    $bp->members->nav->edit_nav( array(
     137        'name' => $nav_name,
     138    ), $bp->messages->slug );
    136139
    137140    /**
  • trunk/src/bp-xprofile/classes/class-bp-xprofile-component.php

    r10740 r10745  
    303303            'position'        => 30,
    304304            'user_has_access' => bp_core_can_edit_settings()
    305         ) );
     305        ), 'members' );
    306306    }
    307307
  • trunk/tests/phpunit/testcases/core/nav/bpCoreMaybeHookNewSubnavScreenFunction.php

    r9988 r10745  
    8383        $old_default_component = buddypress()->default_component;
    8484        buddypress()->default_component = 'foo';
    85         buddypress()->bp_nav = array(
    86             'foo' => array(
    87                 'show_for_displayed_user' => true,
    88             ),
    89         );
     85
     86        bp_core_new_nav_item( array(
     87            'slug' => 'foo',
     88            'name' => 'Foo',
     89            'screen_function' => 'foo',
     90            'default_subnav_item' => 'bar',
     91        ) );
    9092
    9193        $subnav_item = array(
     
    9597        // Just test relevant info
    9698        $found = bp_core_maybe_hook_new_subnav_screen_function( $subnav_item );
    97         $this->assertSame( 'failure', $found['status'] );
    98         $this->assertSame( bp_core_get_user_domain( $u2 ), $found['redirect_args']['root'] );
    9999
    100100        // Clean up
     
    102102        buddypress()->default_component = $old_default_component;
    103103        buddypress()->bp_nav = $old_bp_nav;
     104
     105        $this->assertSame( 'failure', $found['status'] );
     106        $this->assertSame( bp_core_get_user_domain( $u2 ), $found['redirect_args']['root'] );
    104107    }
    105108
     
    115118        $old_default_component = buddypress()->default_component;
    116119        buddypress()->default_component = 'foo';
    117         buddypress()->bp_nav = array(
    118             'foo' => array(
    119                 'show_for_displayed_user' => false,
    120             ),
    121         );
     120
     121        bp_core_new_nav_item( array(
     122            'slug' => 'foo',
     123            'name' => 'Foo',
     124            'screen_function' => 'foo',
     125            'default_subnav_item' => 'bar',
     126            'show_for_displayed_user' => false,
     127        ) );
    122128
    123129        $subnav_item = array(
     
    127133        // Just test relevant info
    128134        $found = bp_core_maybe_hook_new_subnav_screen_function( $subnav_item );
    129         $this->assertSame( 'failure', $found['status'] );
    130         $this->assertSame( bp_core_get_user_domain( $u2 ) . bp_get_activity_slug() . '/', $found['redirect_args']['root'] );
    131135
    132136        // Clean up
     
    134138        buddypress()->default_component = $old_default_component;
    135139        buddypress()->bp_nav = $old_bp_nav;
     140
     141        $this->assertSame( 'failure', $found['status'] );
     142        $this->assertSame( bp_core_get_user_domain( $u2 ) . bp_get_activity_slug() . '/', $found['redirect_args']['root'] );
    136143    }
    137144
  • trunk/tests/phpunit/testcases/core/nav/bpCoreNewNavItem.php

    r10326 r10745  
    66class BP_Tests_Core_Nav_BpCoreNewNavItem extends BP_UnitTestCase {
    77
     8    /**
     9     * @expectedIncorrectUsage bp_nav
     10     */
    811    public function test_user_nav() {
    912        $bp_nav = buddypress()->bp_nav;
     
    3437        );
    3538
    36         $this->assertSame( buddypress()->bp_nav['foo'], $expected );
    37 
    38         // Clean up
    39         buddypress()->bp_nav = $bp_nav;
    40         $this->set_current_user( $old_current_user );
    41     }
    42 
     39        foreach ( $expected as $k => $v ) {
     40            $this->assertEquals( $v, buddypress()->bp_nav['foo'][ $k ] );
     41        }
     42
     43        // Clean up
     44        buddypress()->bp_nav = $bp_nav;
     45        $this->set_current_user( $old_current_user );
     46    }
     47
     48    /**
     49     * @expectedIncorrectUsage bp_nav
     50     */
    4351    public function test_group_nav() {
    4452        $bp_nav = buddypress()->bp_nav;
     
    9098    }
    9199
     100    /**
     101     * @expectedIncorrectUsage bp_nav
     102     */
    92103    public function test_css_id_should_fall_back_on_slug() {
    93104        $args = array(
     
    100111    }
    101112
     113    /**
     114     * @expectedIncorrectUsage bp_nav
     115     */
    102116    public function test_css_id_should_be_respected() {
    103117        $args = array(
     
    125139    }
    126140
     141    /**
     142     * @expectedIncorrectUsage bp_nav
     143     */
    127144    public function test_existence_of_access_protected_user_nav() {
    128145        $bp_nav = buddypress()->bp_nav;
     
    146163        );
    147164
    148         $this->assertSame( buddypress()->bp_nav['settings'], $expected );
    149 
    150         // Clean up
    151         buddypress()->bp_nav = $bp_nav;
    152         $this->set_current_user( $old_current_user );
    153     }
    154 
     165        foreach ( $expected as $k => $v ) {
     166            $this->assertEquals( $v, buddypress()->bp_nav['settings'][ $k ] );
     167        }
     168
     169        // Clean up
     170        buddypress()->bp_nav = $bp_nav;
     171        $this->set_current_user( $old_current_user );
     172    }
     173
     174    /**
     175     * @expectedIncorrectUsage bp_nav
     176     */
    155177    public function test_creation_of_access_protected_user_nav() {
    156178        // The nav item must be added to bp_nav, even if the current user
     
    185207        );
    186208
    187         $this->assertSame( buddypress()->bp_nav['woof'], $expected );
     209        foreach ( $expected as $k => $v ) {
     210            $this->assertEquals( $v, buddypress()->bp_nav['woof'][ $k ] );
     211        }
    188212
    189213        // Clean up
  • trunk/tests/phpunit/testcases/core/nav/bpCoreNewSubnavItem.php

    r9991 r10745  
    66class BP_Tests_Core_Nav_BpCoreNewSubnavItem extends BP_UnitTestCase {
    77
     8    /**
     9     * @expectedIncorrectUsage bp_nav
     10     */
    811    public function test_user_subnav() {
    912        $bp_options_nav = buddypress()->bp_options_nav;
     
    1720        $this->go_to( $user_domain );
    1821
     22        bp_core_new_nav_item( array(
     23            'name'            => 'Foo Parent',
     24            'slug'            => 'foo-parent',
     25            'link'            => trailingslashit( $user_domain . 'foo-parent' ),
     26            'screen_function' => 'foo_screen_function',
     27            'position'        => 10,
     28        ) );
     29
    1930        bp_core_new_subnav_item( array(
    2031            'name'            => 'Foo',
    2132            'slug'            => 'foo',
    22             'parent_url'      => trailingslashit( $user_domain . 'foo' ),
    23             'parent_slug'     => 'foo',
     33            'parent_url'      => trailingslashit( $user_domain . 'foo-parent' ),
     34            'parent_slug'     => 'foo-parent',
    2435            'screen_function' => 'foo_screen_function',
    2536            'position'        => 10
     
    2839        $expected = array(
    2940            'name'              => 'Foo',
    30             'link'              => trailingslashit( $user_domain . 'foo/foo' ),
     41            'link'              => trailingslashit( $user_domain . 'foo-parent/foo' ),
    3142            'slug'              => 'foo',
    3243            'css_id'            => 'foo',
     
    3849        );
    3950
    40         $this->assertSame( buddypress()->bp_options_nav['foo']['foo'], $expected );
     51        foreach ( $expected as $k => $v ) {
     52            $this->assertSame( $v, buddypress()->bp_options_nav['foo-parent']['foo'][ $k ] );
     53        }
    4154
    4255        // Clean up
     
    103116    }
    104117
     118    /**
     119     * @expectedIncorrectUsage bp_nav
     120     */
    105121    public function test_link_provided() {
    106122        $bp_options_nav = buddypress()->bp_options_nav;
    107123
    108         bp_core_new_subnav_item( array(
    109             'name' => 'bar',
    110             'slug' => 'bar',
    111             'parent_slug' => 'foo',
    112             'parent_url' => 'foo',
     124        bp_core_new_nav_item( array(
     125            'name' => 'Foo',
     126            'slug' => 'foo',
    113127            'screen_function' => 'foo',
    114128            'link' => 'https://buddypress.org/',
    115129        ) );
    116130
     131        bp_core_new_subnav_item( array(
     132            'name' => 'bar',
     133            'slug' => 'bar',
     134            'parent_slug' => 'foo',
     135            'parent_url' => 'foo',
     136            'screen_function' => 'foo',
     137            'link' => 'https://buddypress.org/',
     138        ) );
     139
    117140        $this->assertSame( 'https://buddypress.org/', buddypress()->bp_options_nav['foo']['bar']['link'] );
    118141
     
    120143    }
    121144
     145    /**
     146     * @expectedIncorrectUsage bp_nav
     147     */
    122148    public function test_link_built_from_parent_url_and_slug() {
    123149        $bp_options_nav = buddypress()->bp_options_nav;
    124150
     151        bp_core_new_nav_item( array(
     152            'name' => 'Foo',
     153            'slug' => 'foo',
     154            'screen_function' => 'foo',
     155            'link' => 'https://buddypress.org/',
     156        ) );
     157
    125158        bp_core_new_subnav_item( array(
    126159            'name' => 'bar',
     
    136169    }
    137170
     171    /**
     172     * @expectedIncorrectUsage bp_nav
     173     */
    138174    public function test_link_built_from_parent_url_and_slug_where_slug_is_default() {
    139175        $bp_nav = buddypress()->bp_nav;
    140176        $bp_options_nav = buddypress()->bp_options_nav;
    141177
    142         // fake the parent
    143         buddypress()->bp_nav = array(
    144             'foo' => array(
    145                 'default_subnav_slug' => 'bar',
    146             ),
    147         );
     178        bp_core_new_nav_item( array(
     179            'name' => 'Foo',
     180            'slug' => 'foo',
     181            'screen_function' => 'foo',
     182            'default_subnav_slug' => 'bar',
     183        ) );
    148184
    149185        bp_core_new_subnav_item( array(
     
    162198    }
    163199
     200    /**
     201     * @expectedIncorrectUsage bp_nav
     202     */
    164203    public function test_should_trailingslash_link_when_link_is_autogenerated_using_slug() {
     204        bp_core_new_nav_item( array(
     205            'name' => 'Foo',
     206            'slug' => 'foo',
     207            'screen_function' => 'foo',
     208            'link' => 'https://buddypress.org/',
     209        ) );
     210
    165211        bp_core_new_subnav_item( array(
    166212            'name' => 'bar',
     
    175221    }
    176222
     223    /**
     224     * @expectedIncorrectUsage bp_nav
     225     */
    177226    public function test_should_trailingslash_link_when_link_is_autogenerated_not_using_slug() {
    178227        bp_core_new_nav_item( array(
     
    188237            'slug' => 'bar',
    189238            'parent_slug' => 'foo-parent',
    190             'parent_url' => bp_get_root_domain() . 'foo-parent/',
     239            'parent_url' => bp_get_root_domain() . '/foo-parent/',
    191240            'screen_function' => 'bar',
    192241        ) );
    193242
    194         $expected = bp_get_root_domain() . 'foo-parent/';
     243        $expected = bp_get_root_domain() . '/foo-parent/';
    195244        $this->assertSame( $expected, buddypress()->bp_options_nav['foo-parent']['bar']['link'] );
    196245    }
     
    198247    /**
    199248     * @ticket BP6353
     249     * @expectedIncorrectUsage bp_nav
    200250     */
    201251    public function test_link_should_not_trailingslash_link_explicit_link() {
    202252        $link = 'http://example.com/foo/bar/blah/?action=edit&id=30';
    203253
     254        bp_core_new_nav_item( array(
     255            'name' => 'Foo',
     256            'slug' => 'foo',
     257            'screen_function' => 'foo',
     258            'link' => 'http://example.com/foo/',
     259        ) );
     260
    204261        bp_core_new_subnav_item( array(
    205262            'name' => 'bar',
     
    215272
    216273    public function test_should_return_false_if_site_admin_only_and_current_user_cannot_bp_moderate() {
     274        bp_core_new_nav_item( array(
     275            'name' => 'Foo',
     276            'slug' => 'foo',
     277            'screen_function' => 'foo',
     278        ) );
     279
    217280        // Should already be set to a 0 user.
    218281        $this->assertFalse( bp_current_user_can( 'bp_moderate' ) );
     
    229292    }
    230293
     294    /**
     295     * @expectedIncorrectUsage bp_nav
     296     */
    231297    public function test_css_id_should_fall_back_on_slug() {
     298        bp_core_new_nav_item( array(
     299            'name' => 'Parent',
     300            'slug' => 'parent',
     301            'screen_function' => 'foo',
     302        ) );
     303
    232304        $args = array(
    233305            'name' => 'Foo',
     
    242314    }
    243315
     316    /**
     317     * @expectedIncorrectUsage bp_nav
     318     */
    244319    public function test_css_id_should_be_respected() {
     320        bp_core_new_nav_item( array(
     321            'name' => 'Parent',
     322            'slug' => 'parent',
     323            'screen_function' => 'foo',
     324        ) );
     325
    245326        $args = array(
    246327            'name' => 'Foo',
  • trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveNavItem.php

    r10743 r10745  
    66 */
    77class BP_Tests_Core_Nav_BpCoreRemoveNavItem extends BP_UnitTestCase {
     8    /**
     9     * @expectedIncorrectUsage bp_nav
     10     */
    811    public function test_should_remove_subnav_items() {
    912        $bp = buddypress();
     
    3841    }
    3942
     43    /**
     44     * @expectedIncorrectUsage bp_nav
     45     */
    4046    public function test_should_remove_nav_item() {
    4147        $bp = buddypress();
  • trunk/tests/phpunit/testcases/core/nav/bpCoreRemoveSubnavItem.php

    r10744 r10745  
    66 */
    77class BP_Tests_Core_Nav_BpCoreRemoveSubnavItem extends BP_UnitTestCase {
     8    /**
     9     * @expectedIncorrectUsage bp_nav
     10     */
    811    public function test_should_remove_subnav_item() {
    912        $bp = buddypress();
     
    3639    }
    3740
     41    /**
     42     * @expectedIncorrectUsage bp_nav
     43     */
    3844    public function test_should_fail_on_incorrect_parent() {
    3945        $bp = buddypress();
     
    6571        $bp->bp_options_nav = $_bp_options_nav;
    6672    }
    67 
    68     public function test_should_remove_parent_array_if_left_childless() {
    69         $bp = buddypress();
    70 
    71         $_bp_nav = $bp->bp_nav;
    72         $_bp_options_nav = $bp->bp_options_nav;
    73 
    74         bp_core_new_nav_item( array(
    75             'name' => 'Foo',
    76             'slug' => 'foo',
    77             'screen_function' => 'foo',
    78         ) );
    79 
    80         bp_core_new_subnav_item( array(
    81             'name' => 'Bar',
    82             'slug' => 'bar',
    83             'parent_slug' => 'foo',
    84             'parent_url' => 'foo',
    85             'screen_function' => 'bar',
    86         ) );
    87 
    88         $this->assertTrue( isset( $bp->bp_options_nav['foo']['bar'] ) );
    89 
    90         bp_core_remove_subnav_item( 'foo', 'bar' );
    91 
    92         $this->assertFalse( isset( $bp->bp_options_nav['foo'] ) );
    93 
    94         $bp->bp_nav = $_bp_nav;
    95         $bp->bp_options_nav = $_bp_options_nav;
    96     }
    9773}
  • trunk/tests/phpunit/testcases/groups/class-bp-group-extension.php

    r9819 r10745  
    218218    /**
    219219     * @group enable_nav_item
     220     * @expectedIncorrectUsage bp_nav
    220221     */
    221222    public function test_enable_nav_item_true() {
     
    240241    /**
    241242     * @group enable_nav_item
     243     * @expectedIncorrectUsage bp_nav
    242244     */
    243245    public function test_enable_nav_item_false() {
     
    262264    /**
    263265     * @group visibility
     266     * @expectedIncorrectUsage bp_nav
    264267     */
    265268    public function test_visibility_private() {
     
    299302    /**
    300303     * @group visibility
     304     * @expectedIncorrectUsage bp_nav
    301305     *
    302306     * visibility=public + status=private results in adding the item to
  • trunk/tests/phpunit/testcases/routing/anonymous.php

    r9819 r10745  
    1111    function test_nav_menu() {
    1212        $this->go_to( '/' );
    13         $this->assertEmpty( buddypress()->bp_nav );
     13        $nav = buddypress()->members->nav->get_item_nav();
     14        $this->assertEmpty( $nav );
    1415    }
    1516}
  • trunk/tests/phpunit/testcases/routing/core.php

    r9819 r10745  
    2323    }
    2424
     25    /**
     26     * @expectedIncorrectUsage bp_nav
     27     */
    2528    function test_nav_menu() {
    2629        $this->go_to( '/' );
Note: See TracChangeset for help on using the changeset viewer.