Forget plugins, i had the same problem and plugins created a whole raft of mess and too many db calls.
This code will do the business for you - for ease of use, stick it in a content part and include it like so:
get_template_part( 'subnav', 'pagename' );
The problem is, you already know your children, and you know your parents. I tried using the wp-list-pages function , with echo set to false andincluding id's but that got a little messy. This solution isnt perfect but it's far cleaner than using a plugin or two.
In a nutshell - you want the absolute top parent id, then you want to get all the children of the current posts ancestors:
function get_top_id($id){
$parents = get_post_ancestors( $post );
return $parents[count($parents)-1];
}
function get_sub_pages($id){
global $post;
// get the ancestors for this depth
$parents = get_post_ancestors( $post );
//add the post id to the list in order to get the children
array_unshift($parents, $post->ID);
//reverse the order of the array
$parents = array_reverse($parents);
// specify to get all children of the top level parent
$args = array( 'child_of' => $id);
if($subPages = get_pages($args)):
// start at the first $parents array key
$curParent = 0;
$childList = '<ul>';
if($id == $post->ID) $class = ' class="selected"';
$childList .= "<li" . $class ."><a href='". get_page_link( $id )."'>Introduction</a></li>" ;
foreach( $subPages as $page ) :
// only add the item if:
// a: the pages parent is in the ancestors array
if(in_array($page->post_parent, $parents)){
// get the key of the page parent within the $parents array
// this helps us check if we are going to a deeper/higher level
$pageParent = array_search($page->post_parent, $parents);
if($curParent != $pageParent){
// if we are going deeper, add a new list for the children
if($pageParent > $curParent){
$childList .= '<ul>';
}
// if we are going higher, check how many depth changes
if($pageParent < $curParent){
$depthChange = $curParent - $pageParent;
// close off all the children of higher depths
while($depthChange > 0){
$childList .= '</li></ul>';
$depthChange --;
}
}
// set the new parent level
$curParent = $pageParent;
}
$class = '';
if($page->ID == $post->ID) $class = ' class="selected"';
if($page->ID == $post->post_parent) $class = ' class="parent"';
// leave off the trailing <li> in case there are children to be added to this
$childList .= "<li" . $class . "><a href='". get_page_link( $page->ID )."'>". $page->post_title . "</a>" ;
}
endforeach;
$childList .= '</ul>';
endif;
return $childList;
}
// get the absolute top level parent of this page
$topID = get_top_id();
// if no parent, we are ar the top
if(!$topID) $topID = $post->ID;
echo get_sub_pages($topID);
This code also adds the class 'selected' to the current post, and 'parent' to the current post parent within the list.
Hope that helps someone!