Skip to:
Content

Changeset 6503


Ignore:
Timestamp:
11/09/12 16:28:30 (18 months ago)
Author:
djpaul
Message:

Introduces bp_nav_menu(), which displays a merged navigation list from registered components. Fixes #4629.

BP has historically used bp_get_displayed_user_nav() and bp_get_options_nav() to render the navigation, and these menus have always been visually separate in the Default theme; either in columns before 1.2, and in rows since 1.2.

To provide more customisation for theme authors, and have both parts of the navigation displayed in the same block, this change introduces a merged menu function, with the parts from bp_get_options_nav nested appropriately under bp_get_displayed_user_nav.

Location:
trunk/bp-core
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/bp-core/bp-core-classes.php

    r6493 r6503  
    17971797    } 
    17981798} 
     1799 
     1800/** 
     1801 * Create HTML list of BP nav items 
     1802 * 
     1803 * @since BuddyPress (1.7) 
     1804 */ 
     1805class BP_Walker_Nav_Menu extends Walker_Nav_Menu { 
     1806    /** 
     1807     * @since BuddyPress (1.7) 
     1808     * @var array 
     1809     */ 
     1810    var $db_fields = array( 'id' => 'css_id', 'parent' => 'parent' ); 
     1811 
     1812    /** 
     1813     * @since BuddyPress (1.7) 
     1814     * @var string 
     1815     */ 
     1816    var $tree_type = array(); 
     1817 
     1818    /** 
     1819     * Display array of elements hierarchically. 
     1820     * 
     1821     * This method is almost identical to the version in {@link Walker::walk()}. The only change is on one line 
     1822     * which has been commented. An IF was comparing 0 to a non-empty string which was preventing child elements 
     1823     * being grouped under their parent menu element. 
     1824     * 
     1825     * This caused a problem for BuddyPress because our primary/secondary navigations doesn't have a unique numerical 
     1826     * ID that describes a hierarchy (we use a slug). Obviously, WordPress Menus use Posts, and those have ID/post_parent. 
     1827     * 
     1828     * @param array $elements 
     1829     * @param int $max_depth 
     1830     * @return string 
     1831     * @see Walker::walk() 
     1832     * @since BuddyPress (1.7) 
     1833     */ 
     1834    function walk( $elements, $max_depth ) { 
     1835        $args   = array_slice( func_get_args(), 2 ); 
     1836        $output = ''; 
     1837 
     1838        if ( $max_depth < -1 ) // invalid parameter 
     1839            return $output; 
     1840 
     1841        if ( empty( $elements ) ) // nothing to walk 
     1842            return $output; 
     1843 
     1844        $id_field     = $this->db_fields['id']; 
     1845        $parent_field = $this->db_fields['parent']; 
     1846 
     1847        // flat display 
     1848        if ( -1 == $max_depth ) { 
     1849 
     1850            $empty_array = array(); 
     1851            foreach ( $elements as $e ) 
     1852                $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 
     1853 
     1854            return $output; 
     1855        } 
     1856 
     1857        /* 
     1858         * need to display in hierarchical order 
     1859         * separate elements into two buckets: top level and children elements 
     1860         * children_elements is two dimensional array, eg. 
     1861         * children_elements[10][] contains all sub-elements whose parent is 10. 
     1862         */ 
     1863        $top_level_elements = array(); 
     1864        $children_elements  = array(); 
     1865 
     1866        foreach ( $elements as $e ) { 
     1867            // BuddyPress: changed '==' to '==='. This is the only change from version in Walker::walk(). 
     1868            if ( 0 === $e->$parent_field ) 
     1869                $top_level_elements[] = $e; 
     1870            else 
     1871                $children_elements[$e->$parent_field][] = $e; 
     1872        } 
     1873 
     1874        /* 
     1875         * when none of the elements is top level 
     1876         * assume the first one must be root of the sub elements 
     1877         */ 
     1878        if ( empty( $top_level_elements ) ) { 
     1879 
     1880            $first              = array_slice( $elements, 0, 1 ); 
     1881            $root               = $first[0]; 
     1882            $top_level_elements = array(); 
     1883            $children_elements  = array(); 
     1884 
     1885            foreach ( $elements as $e ) { 
     1886                if ( $root->$parent_field == $e->$parent_field ) 
     1887                    $top_level_elements[] = $e; 
     1888                else 
     1889                    $children_elements[$e->$parent_field][] = $e; 
     1890            } 
     1891        } 
     1892 
     1893        foreach ( $top_level_elements as $e ) 
     1894            $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 
     1895 
     1896        /* 
     1897         * if we are displaying all levels, and remaining children_elements is not empty, 
     1898         * then we got orphans, which should be displayed regardless 
     1899         */ 
     1900        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) { 
     1901            $empty_array = array(); 
     1902 
     1903            foreach ( $children_elements as $orphans ) 
     1904                foreach ( $orphans as $op ) 
     1905                    $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 
     1906         } 
     1907 
     1908         return $output; 
     1909    } 
     1910 
     1911    /** 
     1912     * Displays the current <li> that we are on. 
     1913     * 
     1914     * @param string $output Passed by reference. Used to append additional content. 
     1915     * @param object $item Menu item data object. 
     1916     * @param int $depth Depth of menu item. Used for padding. Optional, defaults to 0. 
     1917     * @param array $args Optional 
     1918     * @param int $current_page Menu item ID. Optional. 
     1919     * @since BuddyPress (1.7) 
     1920     */ 
     1921    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { 
     1922        // If we're someway down the tree, indent the HTML with the appropriate number of tabs 
     1923        $indent = $depth ? str_repeat( "\t", $depth ) : ''; 
     1924 
     1925        // Add HTML classes 
     1926        $class_names = join( ' ', apply_filters( 'bp_nav_menu_css_class', array_filter( $item->class ), $item, $args ) ); 
     1927        $class_names = ! empty( $class_names ) ? ' class="' . esc_attr( $class_names ) . '"' : ''; 
     1928 
     1929        // Add HTML ID 
     1930        $id = sanitize_html_class( $item->css_id . '-personal-li' );  // Backpat with BP pre-1.7 
     1931        $id = apply_filters( 'bp_nav_menu_item_id', $id, $item, $args ); 
     1932        $id = ! empty( $id ) ? ' id="' . esc_attr( $id ) . '"' : ''; 
     1933 
     1934        // Opening tag; closing tag is handled in Walker_Nav_Menu::end_el(). 
     1935        $output .= $indent . '<li' . $id . $class_names . '>'; 
     1936 
     1937        // Add href attribute 
     1938        $attributes = ! empty( $item->link ) ? ' href="' . esc_attr( esc_url( $item->link ) ) . '"' : ''; 
     1939 
     1940        // Construct the link 
     1941        $item_output = $args->before; 
     1942        $item_output .= '<a' . $attributes . '>'; 
     1943        $item_output .= $args->link_before . apply_filters( 'the_title', $item->name, 0 ) . $args->link_after; 
     1944        $item_output .= '</a>'; 
     1945        $item_output .= $args->after; 
     1946 
     1947        // $output is byref 
     1948        $output .= apply_filters( 'bp_walker_nav_menu_start_el', $item_output, $item, $depth, $args ); 
     1949    } 
     1950} 
  • trunk/bp-core/bp-core-template.php

    r6484 r6503  
    17431743    } 
    17441744    add_filter( 'body_class', 'bp_get_the_body_class', 10, 2 ); 
     1745 
     1746/** 
     1747 * Sort BuddyPress nav menu items by their position property. 
     1748 * 
     1749 * This is an internal convenience function and it will probably be removed in a later release. Do not use. 
     1750 * 
     1751 * @access private 
     1752 * @param array $a First item 
     1753 * @param array $b Second item 
     1754 * @return int Returns an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. 
     1755 * @since BuddyPress (1.7) 
     1756 */ 
     1757function _bp_nav_menu_sort( $a, $b ) { 
     1758    if ( $a["position"] == $b["position"] ) 
     1759        return 0; 
     1760 
     1761    else if ( $a["position"] < $b["position"] ) 
     1762        return -1; 
     1763 
     1764    else 
     1765        return 1; 
     1766} 
     1767 
     1768/** 
     1769 * Get an array of all the items registered in the primary and secondary BuddyPress navigation menus 
     1770 * 
     1771 * @return array 
     1772 * @since BuddyPress (1.7) 
     1773 */ 
     1774function bp_get_nav_menu_items() { 
     1775    $menus = $selected_menus = array(); 
     1776 
     1777    // Get the second level menus 
     1778    foreach ( (array) buddypress()->bp_options_nav as $parent_menu => $sub_menus ) { 
     1779 
     1780        // The root menu's ID is "xprofile", but the Profile submenus are using "profile". See BP_Core::setup_nav(). 
     1781        if ( 'profile' == $parent_menu ) 
     1782            $parent_menu = 'xprofile'; 
     1783 
     1784        // Sort the items in this menu's navigation by their position property 
     1785        $second_level_menus = (array) $sub_menus; 
     1786        usort( $second_level_menus, '_bp_nav_menu_sort' ); 
     1787 
     1788        // Iterate through the second level menus 
     1789        foreach( $second_level_menus as $sub_nav ) { 
     1790 
     1791            // Skip items we don't have access to 
     1792            if ( ! $sub_nav['user_has_access'] ) 
     1793                continue; 
     1794 
     1795            // Add this menu 
     1796            $menu         = new stdClass; 
     1797            $menu->class  = array(); 
     1798            $menu->css_id = $sub_nav['css_id']; 
     1799            $menu->link   = $sub_nav['link']; 
     1800            $menu->name   = $sub_nav['name']; 
     1801            $menu->parent = $parent_menu;  // Associate this sub nav with a top-level menu 
     1802 
     1803            // If we're viewing this item's screen, record that we need to mark its parent menu to be selected 
     1804            if ( $sub_nav['slug'] == bp_current_action() ) { 
     1805                $menu->class      = array( 'current-menu-item' ); 
     1806                $selected_menus[] = $parent_menu; 
     1807            } 
     1808 
     1809            $menus[] = $menu; 
     1810        } 
     1811    } 
     1812 
     1813    // Get the top-level menu parts (Friends, Groups, etc) and sort by their position property 
     1814    $top_level_menus = (array) buddypress()->bp_nav; 
     1815    usort( $top_level_menus, '_bp_nav_menu_sort' ); 
     1816 
     1817    // Iterate through the top-level menus 
     1818    foreach ( $top_level_menus as $nav ) { 
     1819 
     1820        // Skip items marked as user-specific if you're not on your own profile 
     1821        if ( ! $nav['show_for_displayed_user'] && ! bp_is_my_profile() ) 
     1822            continue; 
     1823 
     1824        // Get the correct menu link. See http://buddypress.trac.wordpress.org/ticket/4624 
     1825        $link = bp_loggedin_user_domain() ? str_replace( bp_loggedin_user_domain(), bp_displayed_user_domain(), $nav['link'] ) : trailingslashit( bp_displayed_user_domain() . $nav['link'] ); 
     1826 
     1827        // Add this menu 
     1828        $menu         = new stdClass; 
     1829        $menu->class  = array(); 
     1830        $menu->css_id = $nav['css_id']; 
     1831        $menu->link   = $link; 
     1832        $menu->name   = $nav['name']; 
     1833        $menu->parent = 0; 
     1834 
     1835        // Check if we need to mark this menu as selected 
     1836        if ( in_array( $nav['css_id'], $selected_menus ) ) 
     1837            $menu->class = array( 'current-menu-parent' ); 
     1838 
     1839        $menus[] = $menu; 
     1840    } 
     1841 
     1842    return apply_filters( 'bp_get_nav_menu_items', $menus ); 
     1843} 
     1844 
     1845/** 
     1846 * Displays a navigation menu. 
     1847 * 
     1848 * @param string|array $args Optional arguments: 
     1849 *  before - Text before the link text. 
     1850 *  container - Whether to wrap the ul, and what to wrap it with. Defaults to div. 
     1851 *  container_class - The class that is applied to the container. Defaults to 'menu-bp-container'. 
     1852 *  container_id - The ID that is applied to the container. Defaults to blank. 
     1853 *  depth - How many levels of the hierarchy are to be included. 0 means all. Defaults to 0. 
     1854 *  echo - Whether to echo the menu or return it. Defaults to echo. 
     1855 *  fallback_cb - If the menu doesn't exists, a callback function will fire. Defaults to false (no fallback). 
     1856 *  items_wrap - How the list items should be wrapped. Defaults to a ul with an id and class. Uses printf() format with numbered placeholders. 
     1857 *  link_after - Text after the link. 
     1858 *  link_before - Text before the link. 
     1859 *  menu_class - CSS class to use for the ul element which forms the menu. Defaults to 'menu'. 
     1860 *  menu_id - The ID that is applied to the ul element which forms the menu. Defaults to 'menu-bp', incremented. 
     1861 *  walker - Allows a custom walker to be specified. Defaults to 'BP_Walker_Nav_Menu'. 
     1862 * @since BuddyPress (1.7) 
     1863 */ 
     1864function bp_nav_menu( $args = array() ) { 
     1865    static $menu_id_slugs = array(); 
     1866 
     1867    $defaults = array( 
     1868        'after'           => '', 
     1869        'before'          => '', 
     1870        'container'       => 'div', 
     1871        'container_class' => '', 
     1872        'container_id'    => '', 
     1873        'depth'           => 0, 
     1874        'echo'            => true, 
     1875        'fallback_cb'     => false, 
     1876        'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>', 
     1877        'link_after'      => '', 
     1878        'link_before'     => '', 
     1879        'menu_class'      => 'menu', 
     1880        'menu_id'         => '', 
     1881        'walker'          => '', 
     1882    ); 
     1883    $args = wp_parse_args( $args, $defaults ); 
     1884    $args = apply_filters( 'bp_nav_menu_args', $args ); 
     1885    $args = (object) $args; 
     1886 
     1887    $items = $nav_menu = ''; 
     1888    $show_container = false; 
     1889 
     1890    // Create custom walker if one wasn't set 
     1891    if ( empty( $args->walker ) ) 
     1892        $args->walker = new BP_Walker_Nav_Menu; 
     1893 
     1894    // Sanitise values for class and ID 
     1895    $args->container_class = sanitize_html_class( $args->container_class ); 
     1896    $args->container_id    = sanitize_html_class( $args->container_id ); 
     1897 
     1898    // Whether to wrap the ul, and what to wrap it with 
     1899    if ( $args->container ) { 
     1900        $allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav', ) ); 
     1901 
     1902        if ( in_array( $args->container, $allowed_tags ) ) { 
     1903            $show_container = true; 
     1904 
     1905            $class     = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-bp-container"'; 
     1906            $id        = $args->container_id    ? ' id="' . esc_attr( $args->container_id ) . '"'       : ''; 
     1907            $nav_menu .= '<' . $args->container . $id . $class . '>'; 
     1908        } 
     1909    } 
     1910 
     1911    // Get the BuddyPress menu items 
     1912    $menu_items = apply_filters( 'bp_nav_menu_objects', bp_get_nav_menu_items(), $args ); 
     1913    $items      = walk_nav_menu_tree( $menu_items, $args->depth, $args ); 
     1914    unset( $menu_items ); 
     1915 
     1916    // Set the ID that is applied to the ul element which forms the menu. 
     1917    if ( ! empty( $args->menu_id ) ) { 
     1918        $wrap_id = $args->menu_id; 
     1919 
     1920    } else { 
     1921        $wrap_id = 'menu-bp'; 
     1922 
     1923        // If a specific ID wasn't requested, and there are multiple menus on the same screen, make sure the autogenerated ID is unique 
     1924        while ( in_array( $wrap_id, $menu_id_slugs ) ) { 
     1925            if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) ) 
     1926                $wrap_id = preg_replace('#-(\d+)$#', '-' . ++$matches[1], $wrap_id ); 
     1927            else 
     1928                $wrap_id = $wrap_id . '-1'; 
     1929        } 
     1930    } 
     1931    $menu_id_slugs[] = $wrap_id; 
     1932 
     1933    // Allow plugins to hook into the menu to add their own <li>'s 
     1934    $items = apply_filters( 'bp_nav_menu_items', $items, $args ); 
     1935 
     1936    // Build the output 
     1937    $wrap_class  = $args->menu_class ? $args->menu_class : ''; 
     1938    $nav_menu   .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items ); 
     1939    unset( $items ); 
     1940 
     1941    // If we've wrapped the ul, close it 
     1942    if ( $show_container ) 
     1943        $nav_menu .= '</' . $args->container . '>'; 
     1944 
     1945    // Final chance to modify output 
     1946    $nav_menu = apply_filters( 'bp_nav_menu', $nav_menu, $args ); 
     1947 
     1948    if ( $args->echo ) 
     1949        echo $nav_menu; 
     1950    else 
     1951        return $nav_menu; 
     1952} 
Note: See TracChangeset for help on using the changeset viewer.