7

Say I created a custom post type called 'Private Posts' ($post_type = itsme_private_posts) and want all posts under the custom post type to be set to 'Private' automatically when published i.e. by default. How do I do it?

Based on @brasofilo's answer to a related question on StackOverflow, I tried this:

add_filter( 'wp_insert_post_data', 'itsme_cpt_private', '99', 2 );
function itsme_cpt_private( $data , $postarr ) {
    if( $postarr['post_type'] == 'itsme_private_posts' ) {
        $data['post_status'] = 'private';
    }
    return $data;
}

It doesn't work i.e. it doesn't do anything; everything's the way it was before adding the function. It's as though the function doesn't exist or simply does nothing.

Then, based on this article titled, "Force custom post type to be private", I tried this:

add_filter( 'wp_insert_post_data', 'itsme_cpt_private' );
function itsme_cpt_private( $post ) {
    if( $post['post_type'] == 'itsme_private_posts' ) {
        $post['post_status'] = 'private';
    }
    return $post;
}

It works, in that, when I publish or update a post it turns private. Good! But when I create a new post ('Add New') and leave without doing anything, it creates a 'private' (auto-)draft and saves it. Moreover I can't even trash that thing!

Essentially I can't figure out how to do it. So, how do I set posts of a custom post type to be private by default?

2
  • Moreover I can't even trash that thing! The reason you couldn't trash the posts was because you were always setting the post_status to private. When trashing posts, the post status is set to trash, but you were overriding this behavior. I've found that a better approach is to only set the post status to private if the post status is currently public.
    – c.hill
    Commented Mar 28, 2015 at 11:23
  • Revised: Should set post status to private if it is currently publish. public isn't a post status.
    – c.hill
    Commented Mar 28, 2015 at 11:31

3 Answers 3

15
+50

The problem with the code in your question is it always intercepts. You completely lose the ability to not publish, no drafts possible. Why this leads to untrashable post is something I didn't inspect further. Anyway this isn't what one wants.

I'm glad the javascript solution is working for you, but personally I have two problems with it, first, you can save post as public, second, it makes it harder - because the script always sets the radio button to private - to handle working with drafts. In a way there are now two solutions to the answer.


1. Forcing post type to be private plus visual indication:

This is why I took another look at doing it with post status transitions and, like I said, it can be done with Post Status Transitions.

Code:

add_action( 'transition_post_status', 'wpse118970_post_status_new', 10, 3 );
function wpse118970_post_status_new( $new_status, $old_status, $post ) { 
    if ( $post->post_type == 'itsme_private_posts' && $new_status == 'publish' && $old_status  != $new_status ) {
        $post->post_status = 'private';
        wp_update_post( $post );
    }
} 

This forces published posts of your post type to be private. Without interfering with the ability to save drafts. This on it's own would be enough to solve the problem of having a private post type.
But of course it would be nice to have the private nature of the post type represented in the publishing metabox. For this I adjusted the javascript a little bit. Mainly by not setting the checkbox by default, but still changing the text shown, additionally I added a note.

Code:

add_action( 'post_submitbox_misc_actions' , 'wpse118970_change_visibility_metabox' );
function wpse118970_change_visibility_metabox(){
    global $post;
    if ($post->post_type != 'post')
        return;
        $message = __('<strong>Note:</strong> Published posts are always <strong>private</strong>.');
        $post->post_password = '';
        $visibility = 'private';
        $visibility_trans = __('Private');
    ?>
    <style type="text/css">
        .priv_pt_note {
            background-color: lightgreen;
            border: 1px solid green;
            border-radius: 2px;
            margin: 4px;
            padding: 4px;
        }
    </style>
    <script type="text/javascript">
        (function($){
            try {
                $('#post-visibility-display').text('<?php echo $visibility_trans; ?>');
                $('#hidden-post-visibility').val('<?php echo $visibility; ?>');
            } catch(err){}
        }) (jQuery);
    </script>
    <div class="priv_pt_note">
        <?php echo $message; ?>
    </div>
    <?php
}

Source: How to set new posts visibility to private by default?
Note: Compared to the code below I removed the checking of the radio button and additionally added code for the message.

Of course you don't need the javascript to make your post type private, but it's always nice to have things visually indicated. For that reason it's a nice addition.


2. Making private the standard option in the publishing meta box:

I'm keeping this because @its_me accepted the answer for this as solution. Like I said in the comment I found a helpful piece of code, it takes care of setting the visibility at the meta box to private by hooking into post_submitbox_misc_actions. Like I said in the beginning »I have two problems with it, first, you can save post as public, second, it makes it harder - because the script always sets the radio button to private - to handle working with drafts«. It's still possible to have and work with drafts, just not as intuitive as before, but the private publishing status is in no way enforced.

Code:

add_action( 'post_submitbox_misc_actions' , 'wpse118970_change_visibility_metabox_value' );
function wpse118970_change_visibility_metabox_value(){
    global $post;
    if ($post->post_type != 'itsme_private_posts')
        return;
    $post->post_password = '';
    $visibility = 'private';
    $visibility_trans = __('Private');
    ?>
    <script type="text/javascript">
        (function($){
            try {
                $('#post-visibility-display').text('<?php echo $visibility_trans; ?>');
                $('#hidden-post-visibility').val('<?php echo $visibility; ?>');
                $('#visibility-radio-<?php echo $visibility; ?>').attr('checked', true);
            } catch(err){}
        }) (jQuery);
    </script>
    <?php
}

Source: How to set new posts visibility to private by default?
Note: I shortened the code to fit the needs here.

7
  • Can you please give an example as to how I'd use to do what I want? I am unsure about it.
    – its_me
    Commented Oct 15, 2013 at 18:45
  • You got me curious now :) testing it myself. @its_me Commented Oct 15, 2013 at 19:23
  • I realized that the $new_status is auto-draft and the $old_status is new - I change the code accordingly. @its_me Besides that I found another nice piece of code, I add to the answer. Commented Oct 15, 2013 at 19:56
  • The JavaScript-based solution worked, but not the first one. Marked as answer 'cause it solves my problem, but it'd be nice if you can find out what's wrong with the first code block. If you can't please remove it (to avoid confusion, for others). Thanks a lot for your time!
    – its_me
    Commented Oct 16, 2013 at 3:48
  • I updated the answer, not sure if you get notified after you accepted the answer. @its_me Commented Oct 16, 2013 at 21:38
2

This is the most "standard" way that I have found so far to set posts visibility to private by default:

function wp_118970_force_type_private($post) {
  if ($post['post_status'] != 'trash' && $post['post_status'] != "draft" && $post['post_status'] != "auto-draft") {
    $post['post_status'] = 'private';
  }
  return $post;
}
add_filter('wp_insert_post_data', 'wp_118970_force_type_private');

Note that you have to ensure that post_status is not "trash" otherwise you wouldn't be able to delete your post. We also check for "draft" and "auto-draft" status to avoid setting posts as "private" while redacting them.

With this, there is no need to add some JS, the visibility will be set to private from the beginning.

If you want to target a specific post type, you can do it this way:

function wp_118970_force_type_private($post) {
  if ($post['post_status'] != 'trash' && $post['post_status'] != "draft" && $post['post_status'] != "auto-draft" && $post['post_type'] == 'my_post_type') {
    $post['post_status'] = 'private';
  }
  return $post;
}
add_filter('wp_insert_post_data', 'wp_118970_force_type_private');

I first found the solution here: http://wpsnipp.com/index.php/functions-php/force-custom-post-type-to-be-private/

3
  • 1
    That's a very nice idea, but one should be aware that it makes newly created posts directly loos their "draft" status and makes them "privately published", instead of making them "drafts" with the "private" option pre-selected. This for example directly gives them a slug (namely the slug auto-draft), which can be inconvenient.
    – cgogolin
    Commented Aug 21, 2018 at 9:37
  • 1
    You are totally right! Some times after posting this answer I saw the problem and corrected my code, but I forgot to update my answer. Now it is done! Thanks! Commented Aug 21, 2018 at 11:13
  • This worked for me. Thanks
    – TomC
    Commented Nov 27, 2019 at 10:59
0

Based on the answer above, here's what I came to, with the client (javascript) part:

add_action( 'post_submitbox_misc_actions' , 'ourcpt_change_visibility_metabox' );
function ourcpt_change_visibility_metabox() {
  global $post;
  if ( 'ourcpt' != $post->post_type ) { return; }
  //else          
  $post->post_password = '';
  ?>
  <script type="text/javascript">
      (function($){
          try {
              $( "<div class='misc-pub-section'><em>(when it's published, the status becomes private)</em></div>" ).insertAfter( $('#post-visibility-select') );
              $('a.edit-visibility').css({'display': 'none'});
          } catch(err){}
      }) (jQuery);
  </script>
  <?php
}

Because I love seeing what exactly is going on with the status. The code for add_filter( 'wp_insert_post_data' I used unchanged.

Thank you very much for your solutions!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.