Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have several unpublished posts in my Wordpress website and I am trying to make it accessible for normal users (who are not logged in) using the normal post slugs (site.com/post-here). I understand it may not be the best practice but for my special purpose, this needs to be done.

I have tried adding the following code snippet into my functions.php file:

function enable_view_drafts() {
$role = get_role( 'subscriber' ); 
$role->add_cap( 'read_private_posts' ); 
$role->add_cap( 'edit_posts' );
}
add_action( 'after_setup_theme', 'enable_view_drafts');

I've also tried init hook instead of after_setup_theme. No luck.

My understanding is that changes to roles are saved to the database so only need to be done once. That is why I'm using after_setup_theme hook to call the function.

But when I try to access the page as a normal user, I'm being shown a 404 page instead of showing the post content. I've also tried loading the preview URL (site.com/?p=212&preview=true) but that didn't work either.

These are my guesses:

  • the normal user doesn't have enough caps to read the drafts post.
  • testing and viewing draft posts on the front-end is not possible for any users (including administrators).

What changes do I have to make in order to accomplish what I'm trying to do? If it's not possible, what alternative solutions do you suggest?

Note: I'm not looking for plugin-based solutions.

share|improve this question
2  
I think there is a bit of confusion here. If someone is not logged in, he/she is not a user, so he/she has not any caps. The subscribers are users, so they have to be registered and make the login to be recognized as subscribers. You cannot give capabilities to someone that has not logged in. If you want anyone not logged view your posts the are simply not different at all from published posts. So, why not pubblish it? Or do you want to show draft to subscribers (logged in as subscribers)? –  G. M. Sep 3 '13 at 0:13
    
@G.M.: I'm trying to make unpublished posts accessible to everyone who knows the direct permalink to the post. It may seem like a weird requirement, but I need this for a project. As I've mentioned in my question, if this isn't possible -- can you suggest other work-arounds (if you know one, of course)? –  Amal Murali Nov 4 '13 at 14:14
    
@G.M.: Do you know of a plugin which does that? –  Amal Murali Nov 4 '13 at 14:26
    
Have you read the answer just below with 3 upvotes? It explain how to do it and in at the end of answer you find a link to download a full working plugin from github. –  G. M. Nov 4 '13 at 15:04
1  
For Later readers: There's this plugin by Dominik Schilling available on GitHub. –  kaiser Nov 27 '13 at 12:32

5 Answers 5

up vote 8 down vote accepted
+50

You cannot assign capabilities to unknown users. If you want to make a post visible for everyone, create a separate URL for these posts and add a control element to the post editor to enable the preview on selected posts only.
When such an URL is called, check if a preview is allowed for the post and if the post hasn’t been published already. Also make sure search engines ignore this URL.

For the URL I would use an endpoint:

add_rewrite_endpoint( 'post-preview', EP_ROOT );

Now you can create URLs like …

http://example.com/post-preview/123

… where 123 ist the post ID.

Then use a callback handler to inspect the post ID, check if it is valid and overwrite the main query. This is probably the only acceptable use case for query_posts(). :)

Let’s say the endpoint is a class T5_Endpoint (a model), and the output handler is a class T5_Render_Endpoint (a view) which gets the model passed earlier. Then there is probably a method render() called on template_redirect:

public function render()
{
    $post_id = $this->endpoint->get_value();

    if ( ! $post_id )
        return;

    if ( 1 !== $this->meta->get_value( $post_id )
        or 'publish' === get_post_status( $post_id )
        )
    {
        wp_redirect( get_permalink( $post_id ) );
        exit;
    }

    $query = array (
        'suppress_filters' => TRUE,
        'p'                => $post_id,
        'post_type'        => 'any'
    );

    query_posts( $query );

    add_action( 'wp_head', 'wp_no_robots' );
}

$this->meta is another model (class T5_Post_Meta) for the post meta value that controls if a preview is allowed. The control is set into the Publish box (action post_submitbox_misc_actions), rendered by another view that gets the same meta class.

screen shot

So T5_Post_Meta knows where and when to store the meta value, the views do something with it.
Also, hook into transition_post_status to delete the post meta field when the post is published. We don’t want to waste resources, right?

This is just an outline. There are many details to cover … I have written a small plugin that shows how to implement this: T5 Public Preview.

share|improve this answer
    
Thank you so much. I finally achieved my goal with some tweaks. Awesome. :D –  Amal Murali Nov 6 '13 at 19:07

I solved this problem in what I thought was a simpler way than @toscho's answer above.

My use case is I'm using the same database for an internal intranet staging site and a public-facing site, and the workflow is that authors write drafts and share it with other users who view those drafts on the intranet site, before publishing. I specifically didn't want to require reviewers to log in to see drafts, so I'm just depending on a constant, ENV_PRODUCTION which is set in the wp-config file based on the hostname in $_SERVER['SERVER_NAME']. Thats what the checks for ENV_PRODUCTION here are doing; just shorting out all of these filters if the production site is being viewed.

This is a little weird, because you have to hook in after WP_Query removes all of the posts from the $wp_query->posts array, but it seems stable and secure to me.

/*
 * On staging site home and archives, drafts should be visible.
 */
function show_drafts_in_staging_archives( $query ) {
    if ( ENV_PRODUCTION )
        return;

    if ( is_admin() || is_feed() )
        return;

    $query->set( 'post_status', array( 'publish', 'draft' ) );
}

add_action( 'pre_get_posts', 'show_drafts_in_staging_archives' );


/*
 * Make drafts visible on staging site single views.
 *
 * (Because on single views, WP_Query goes through logic to make sure the 
 * current user can edit the post before displaying a draft.)
 */
function show_single_drafts_on_staging( $posts, $wp_query ) {
    if ( ENV_PRODUCTION )
        return $posts;

    if ( count( $posts ) )
        return $posts;

    if ( !empty( $wp_query->query['p'] ) ) {
        return array ( get_post( $wp_query->query['p'] ) );
    }
}

add_filter( 'the_posts', 'show_single_drafts_on_staging', 10, 2 );

There's two separate parts to the filters.

  • A filter on the "pre_get_posts" hook sets the default post_status to 'publish,draft' on the staging site. This will return the draft posts in archive listings.
  • A separate filter is needed for single views, because there's some nasty logic in the WP_Query class to remove draft posts from the query results unless the current cuser can edit them. I got around this by filtering 'the_posts', and adding the post I wanted right back to the results.
share|improve this answer

I think the "User Role Editor" plugin available at WordPress.org's website might be what you're looking for. By the way, why do you want to give access to your drafts to everybody? I personally can't think of an instance where this would be required.

share|improve this answer
    
No, as mentioned in the question, I am not looking for plugin-based solutions. The usecase is slightly complicated but I'm sure this is the best solution for this specific task I'm trying to accomplish. :-) –  Amal Murali Jul 19 '13 at 1:32
    
I'm perplexed about the reason as well. If you want everyone to see the post, why not simply publish the post? You could use custom fields on the post to track any special state you might desire to define. –  KenB Jul 26 '13 at 0:11
    
I'm letting users see the content, but for some specific reason I don't want the posts to be published. –  Amal Murali Jul 26 '13 at 7:20

You could just change the visibility on the page/post to "Private" which is only visible to Editors and Administrators and not public visitors, search engines, rss feeds, etc.

share|improve this answer

I think G.M.'s comment is the best one here. I assume you are trying to do the following:

  1. Write a post
  2. Save as draft
  3. Allow an external (not logged-in) user view the draft for approval
  4. Publish

Is that correct?

Unfortunately, I can't think of any simple way to do this. You could post it as a private post so that they need to enter a password to view it but you need to be logged in for this. You could also password protect it, but then it will still appear in your feed and list of recent posts, etc. Could you not create a guest user account and give them the username/password when you give them the URL?

Read here for more info: http://codex.wordpress.org/Content_Visibility

Alternatively, there is a plugin that might suit your needs: http://wordpress.org/extend/plugins/shareadraft/ I had a quick look at the code and it appears the developer is modifying the value returned by get_post_status so you might be able to play around with that:

http://codex.wordpress.org/Function_Reference/get_post_status

HTH

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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