Great answers above. I took on the challenge trying to find yet another way to solve this.
The exclude
parameter:
We could try:
'exclude' => wpse_exclude_drafts_branches()
where:
function wpse_exclude_drafts_branches()
{
global $wpdb;
$exclude = array();
$results = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} where post_status="draft" AND post_type="page" " );
$exclude = array_merge( $exclude, $results) ;
while ( $results ):
$results = $wpdb->get_col( "SELECT DISTINCT ID FROM {$wpdb->posts} WHERE post_type="page" AND post_status="publish" AND post_parent > 0 AND post_parent IN (" . join( ',', $results ) . ") " );
$exclude = array_merge( $exclude, $results) ;
endwhile;
return join( ',', $exclude );
}
and the number of queries depends on the tree depth.
Update:
The exclude_tree
parameter:
I just noticed the exclude_tree
parameter mentioned on the Codex page, so I wonder if this would work (untested) to exclude the whole of the draft nodes branches:
$exclude = get_posts(
array(
'post_type' => 'page',
'fields' => 'ids',
'post_status' => 'draft',
'posts_per_page' => -1,
)
);
and then use:
'exclude_tree' => join( ',', $exclude ),
with wp_list_pages()
.