I would just set the post status for all posts for that particular category to private
. If you already have a ton of posts, write yourself a small script using wp_update_post()
to update all those posts’ post status to private
. Any posts published after this you can then just set to private
(in the publish meta box) before publishing them.
The main query by default only shows private posts to logged in users
EDIT from COMMENTS
An addition from the OP in comments that might be useful in future
This worked out fine, except that it required users to have too much power(be Editors) just to read the private posts. I resolved that by adding the following in a plugin:
$subRole = get_role( 'subscriber' ); $subRole->add_cap( 'read_private_pages' ); $subRole->add_cap( 'read_private_posts' );
SECOND ADDITION
I wonder if we can just set $posts
to an empty array for all category pages where 4
is the category, and also on any single page if the post belongs to category 4
add_filter( 'the_posts', function ( $posts, $q )
{
if ( is_user_logged_in() )
return $posts;
if ( $q->is_main_query()
&& ( $q->is_category( 4 )
|| ( $q->is_single()
&& has_category( 4, $posts[0] )
)
)
) {
return $posts = [];
}
return $posts;
}, 10, 2 );
This should return no posts in the loop. This is all untested so it would need proper testing off line before you use it in a production site
Just a note, this will fail if a post belongs to a category which is a descendant of category 4 and the post is not explicitly assigned to category 4. This also goes for category pages which are descendants of category 4. For this to work, you would need to add additional checks to check the top level parent of the category, but it can become quite a big overhead to run
FINAL EDITION
The above filter code is now tested and working on the main queries on single pages and category pages. On category pages the loop displays no posts, and on single pages it does lead to a 404 page. You can use pre_get_posts
to remove any other instances of posts belonging to category 2
add_action( 'pre_get_posts', function ( $q )
{
if ( is_user_logged_in() )
return;
if ( !is_admin() // Target only front end
&& !$q->is_singular() // Do not target singular pages
&& $q->is_main_query() // Target only the main query
) {
$q->set( 'cat', -4 );
}
});
FLAWS IN DESIGN
Although the code works, it does have loopholes like custom queries and pages which are used to list posts from categories. It is a dangerous route to take and private info might be unintentionally displayed (through a bad custom query for example) to non-logged in users
Conclusion
The safest way to tackle this issue is to make posts private that should only be displayed to logged in users. As you have already shown (in comments), it is easy to adjust roles to show them private posts once they are logged in