Have you ever tried to set a WordPress page to draft status, after
However, when I viewed the webpage as a guest, I could still see the menu item. When I opened that page WordPress only displays a 404 not-found message. Which is technically correct; but wouldn’t it be a lot better, when guest users would not even see the menu items for draft posts?
The complicated way
Obviously, WordPress wants us to modify the post state and separately update the menu (remove the page from the menu manually). Possibly this is a good idea for caching – but on the other hand, most caching is usually disabled for logged in users anyway.
A more elegant way
Or maybe this is just a lazy way.
I took a look into the file nav-menu-template.php
in search of a solution that does the redundant work and modifies the main menu. We hook into the filter wp_nav_menu_objects
so we can remove inaccessible menu-items right before they are displayed!
Actually, it’s a straight forward process, once we found the correct hook and understand the filter-parameter:
<?php
add_filter( 'wp_nav_menu_objects', 'pst_nav_menu_objects', 10, 2 );
/**
* Modify the WordPress menu and remove entries that are not visible for the current
* user. This applies to all menus (primary, footer, widget ...)
*/
function pst_nav_menu_objects( $items, $args ) {
// If you do not want to modify ALL menus, you can check for the menu-location
// or other criteria here.
// For example, uncomment following condition to only modify the primary menu:
# if ( 'primary' !== $args->theme_location ) {
# return $items;
# }
foreach ( $items as $key => $item ) {
if ( ! in_array( $item->object, array( 'post', 'page' ) ) ) {
// We only check visibility state for posts and pages.
continue;
}
$post_status = get_post_status( $item->object_id );
if ( 'publish' === $post_status ) {
// This is a public page or post. No other check is needed.
continue;
}
if ( is_user_logged_in() ) {
if ( 'protected' === $post_status ) {
if ( current_user_can( 'edit_post', $item->object_id ) ) {
// The current user is the author of the password protected post.
continue;
}
} else {
if ( current_user_can( 'read_post', $item->object_id ) ) {
// The current user can read the draft or trashed post.
continue;
}
}
}
// All positive conditions failed.
// Current visitor has no permission to see this menu entry.
unset( $items[ $key ] );
}
return $items;
}