Private pages that work when you are not logged into WP admin

If you don’t want to use a plugin (or can’t find one that does what you’re needing), you might want to approach it this way:

  1. Add a custom meta box that allows you to mark the post as hidden.
  2. Modifying the query with pre_get_posts to remove the posts you’ve labeled as hidden from your site (but will be available with a direct link).

UPDATE

Following the suggestion above, here is a possible solution.

Create a custom meta box

First, create the custom meta box by registering one:

function yourtextdomain_add_custom_meta_box() {
  add_meta_box("demo-meta-box", "Custom Meta Box", "yourtextdomain_custom_meta_box_markup", "post", "side", "high", null);
}
add_action("add_meta_boxes", "yourtextdomain_add_custom_meta_box");

Add the markup to the metabox (a checkbox in the case):

function yourtextdomain_custom_meta_box_markup($object) {
  wp_nonce_field(basename(__FILE__), "meta-box-nonce"); ?>
    <div>
      <br />
      <label for="meta-box-checkbox">Hidden</label>

      <?php $checkbox_value = get_post_meta($object->ID, "meta-box-checkbox", true);
          if($checkbox_value == "") { ?>

          <input name="meta-box-checkbox" type="checkbox" value="true">

      <?php } else if($checkbox_value == "true") { ?>

          <input name="meta-box-checkbox" type="checkbox" value="true" checked>

      <?php } ?>

      <p style="color: #cccccc"><i>When selected, the post will be removed from the WP loop but still accessible from a direct link.</i></p>
    </div>
  <?php
}

This will give you a meta box for each post that looks like this:

And finally save the meta box value:

function yourtextdomain_save_custom_meta_box($post_id, $post, $update) {
    if (!isset($_POST["meta-box-nonce"]) || !wp_verify_nonce($_POST["meta-box-nonce"], basename(__FILE__)))
        return $post_id;

    if(!current_user_can("edit_post", $post_id))
        return $post_id;

    if(defined("DOING_AUTOSAVE") && DOING_AUTOSAVE)
        return $post_id;

    $slug = "post";

    if($slug != $post->post_type)
        return $post_id;

    $meta_box_checkbox_value = "";

    if(isset($_POST["meta-box-checkbox"])) {
      $meta_box_checkbox_value = $_POST["meta-box-checkbox"];
    }
    update_post_meta($post_id, "meta-box-checkbox", $meta_box_checkbox_value);
}
add_action("save_post", "yourtextdomain_save_custom_meta_box", 10, 3);

In the wp_postmeta table, you should now see the meta value ‘true’ assigned to the posts you’ve checked as hidden and saved:

Post Meta Table

Modifying the query with pre_get_posts

Now it’s just a matter of filtering out those posts that are marked as hidden from the main query. We can do this with pre_get_posts:

add_action( 'pre_get_posts', 'yourtextdomain_pre_get_posts_hidden', 9999 );
function yourtextdomain_pre_get_posts_hidden( $query ){

  // Check if on frontend and main query.
    if( ! is_admin() && $query->is_main_query() ) {

    // For the posts we want to exclude.
    $exclude = array();

    // Locate our posts marked as hidden.
    $hidden = get_posts(array(
      'post_type' => 'post',
      'meta_query' => array(
        array(
          'key' => 'meta-box-checkbox',
          'value' => 'true',
          'compare' => '==',
        ),
      )
    ));

    // Create an array of hidden posts.
    foreach($hidden as $hide) {
      $exclude[] = $hide->ID;
    }

    // Exclude the hidden posts.
    $query->set('post__not_in', $exclude);

    }
}

Leave a Comment