Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm looking for a custom permalink plugin that is able to interact with menu classes.

For instance, I have a menu that includes the parent website.com/shop as one of its list items. If a page, say website.com/shop/product, includes that menu, the list item for website.com/shop will get a class, something like current-page-ancestor. This is useful for styling that link so users know what section of the website they are on.

But I will have pages whose parent will not be website.com/shop (a feature I can't change), but I want it to appear that website.com/shop is the parent. For instance, the page is website.com/foo/product-2. I'm currently using the Custom Permalinks plugin to change the permalink to website.com/shop/product-2. But in doing this, the menu does not recognize that website.com/shop is the parent, and no class is added to the menu's list item.

Is there any workaround for this, or does anyone know of a custom permalinks plugin that would support menu ancestry?


Edit

I feel like I'm right there, but missing one thing...

function getUrl() {
      $url  = @( $_SERVER["HTTPS"] != 'on' ) ? 'http://'.$_SERVER["SERVER_NAME"] :  'https://'.$_SERVER["SERVER_NAME"];
      $url .= ( $_SERVER["SERVER_PORT"] !== 80 ) ? ":".$_SERVER["SERVER_PORT"] : "";
      $url .= $_SERVER["REQUEST_URI"];
      return $url;
    }
    function getDir($foo) {
        $url = $foo;
        $info = parse_url($url);
        $dir = dirname($info["path"]);
        return $dir;
    }
    function wpa83498_nav_class( $classes, $item ){
        // if Shop is the current menu item being output
        // and we're viewing a single product post type
        $currentDirPath = getDir(getUrl());
        if( ('Sales' == $item->title && is_singular( 'product' )) || (parse_url($item->url, PHP_URL_PATH) == $currentDirPath && is_singular( 'product' )) )
            $classes[] = 'current-page-ancestor';
        return $classes;
    }
    add_filter( 'nav_menu_css_class', 'wpa83498_nav_class', 10, 2 );
share|improve this question
add comment

2 Answers

You could add classes to wp_nav_menu via the nav_menu_css_class filter:

function wpa83498_nav_class( $classes, $item ){
    // if Shop is the current menu item being output
    // and we're viewing a single product post type
    if( 'Shop' == $item->title && is_singular( 'product' ) )
        $classes[] = 'current-page-ancestor';
    return $classes;
}
add_filter( 'nav_menu_css_class', 'wpa83498_nav_class', 10, 2 );

EDIT-

Try this to see how the objects that make up a menu are constructed. Perhaps it will shed some light on how you can manipulate menu output. Look at things like menu_item_parent and object_id to see how menu items are related to each other and the posts / pages they represent. Warning- do not do this on a live site!

function wpa_inspect_menu_objects( $menu ) {    
    echo '<pre>';
    print_r( $menu );
    echo '</pre>';  
    die();  
}
add_filter( 'wp_nav_menu_objects' , 'wpa_inspect_menu_objects' );
share|improve this answer
    
Okay, so that worked well (although I'm not 100% clear how). But I'd like to add another step if I may. This may be another part of the function. Am I able to add current-page-ancestor to a menu list item based upon different sub-parent pages. e.g. website.com/shop/new/product and website.com/shop/used/product. Where the menu would have Shop -> New | Used and the individual product page would determine whether New or Used got the current-page-ancestor class? Hope that's clear! –  Timothy D Jan 28 '13 at 19:46
    
for that you'd probably want either the nav_menu_objects filter, a custom walker, or to build the menu manually via wp_get_nav_menu_items. See my edit above for some code that will maybe help you understand how WordPress stores menu items and builds the menu. –  Milo Jan 28 '13 at 20:22
    
another thought - if you're adding items to a nav menu, WP should be inserting the proper parent / ancestor classes if the items are children in the nav menu, regardless of whether or not they're actually related to each other naturally. –  Milo Jan 28 '13 at 20:32
    
I understand now how the menus are built and why your code works, so thank you for that inspector code. To your second thought: the product children aren't part of the menu, only the parents are (i.e. 'Shop' and 'New'|'Used'). I believe Wordpress is having troubling recognizing the product pages as children because they have custom permalinks and their original parent is an entirely different page. Is there some way to hook into the new permalink to get a value that might match one of the menu item titles? –  Timothy D Jan 28 '13 at 21:46
    
Do you have a piece of 'inspector' code that will let me see the post object for the current product page so perhaps I can find a value that will help me select which menu item it is a part of? Just like the code you gave me to see the menu construction. –  Timothy D Jan 28 '13 at 21:50
show 4 more comments
up vote 0 down vote accepted

Here was the final solution I came up with. I spent a lot of looking into whether I could change the the custom post type "product" to hierarchical and set its page parent appropriately so that the menus would add the correct ancestor classes naturally. But I hit a road block and eventually came back to a solution based on the new permalink.

So I imagine this should work for anyone who is aiming to fake a page parent hierarchy when using custom permalinks.

We get the current page url (set by the custom permalinks plugin), then strip it down to the path and save it as the $currentDirPath variable. Then we evaluate whether a menu item's url path is contained within the $currentDirPath, and if so we add the current-page-ancestor class.

function getUrl() {
  $url  = @( $_SERVER["HTTPS"] != 'on' ) ? 'http://'.$_SERVER["SERVER_NAME"] :  'https://'.$_SERVER["SERVER_NAME"];
  $url .= ( $_SERVER["SERVER_PORT"] !== 80 ) ? ":".$_SERVER["SERVER_PORT"] : "";
  $url .= $_SERVER["REQUEST_URI"];
  return $url;
}
function getDir($foo) {
    $url = $foo;
    $info = parse_url($url);
    $dir = dirname($info["path"]);
    return $dir;
}
function wpa83498_nav_class( $classes, $item ){
    $currentDirPath = getDir(getUrl());
    if( (strpos($currentDirPath,(parse_url($item->url, PHP_URL_PATH))) !== false) && is_singular( 'product' ) )
        $classes[] = 'current-page-ancestor';
    return $classes;
}
add_filter( 'nav_menu_css_class', 'wpa83498_nav_class', 10, 2 );
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.