At my job, I work a on a membership-based site where staff has access to the WordPress admin, but users of the site only have access to the front end of the site. I have two shortcode functions I wrote for a staff-facing “Changelog” page of recent edits and a user-facing “Recent Updates” page.
However, there are some pages that are accessible for users that we don’t want to advertise. Even though we don’t give the link out to a page and it’s not in any menus, I was inadvertently about to create an area where users could discover these pages. Not to mention that users could also find the pages with a quick search on the site.
My solution to both of these issues was to create a layer between “Published” and “Private” – by using a tag I named “Unlisted.” With this sweet little function, your pages, posts, and custom post types with a particular tag (change “1” to the correct ID) will be excluded from the front-end search. The second part of the conditional, !is_admin() is what stops you from going crazy when searching for pages on the admin side. 🙂
/** * Exclude "Unlisted" tag from search. */ function rose_exclude_unlisted($query) { if ( ($query->is_search) && (!is_admin()) ) { $query->set('tag__not_in', 1); // Change 1 to the ID of your "Unlisted" tag. } return $query; } add_filter('pre_get_posts', 'rose_exclude_unlisted');
Similarly, in my [public-updates] shortcode function, I added the argument tag__not_in with the Unlisted tag’s ID.
function rose_public_updates() { // Get the 20 most recent edited public posts/pages/custom post type. $recent_updates = ''; $args = array( 'post_type' => 'any', 'post_status' => array( 'publish' ), 'tag__not_in' => 1, // Change 1 to the ID of your "Unlisted" tag. 'orderby' => 'modified', 'posts_per_page' => 20, 'ignore_sticky_posts' => '1' ); $recent_loop = new WP_Query( $args ); if ( $recent_loop->have_posts() ) { $recent_updates = '<table><tr><th width="42%">Last 20 Updates</th><th width="30%">Last Modified</th></tr>'; while( $recent_loop->have_posts() ) : $recent_loop->the_post(); $recent_updates .= '<tr><td><a href="' . get_permalink( $recent_loop->post->ID ) . '">' . get_the_title( $recent_loop->post->ID ) . '</a></td><td>'. get_the_modified_date( 'D, M d, Y \a\t g:iA' ) .'</td></tr>'; endwhile; $recent_updates .= '</table>'; } wp_reset_postdata(); return $recent_updates; } add_shortcode('public-updates', 'rose_public_updates');
Add that bad boy to your functions file (if it’s your own theme or child theme), or throw it in a plugin and fire it up!