Unfortunately, WordPress doesn’t use a separate capability for previewing posts. A user needs the ability to edit a post in order to preview it. As seen in the WP_Query::get_posts() method:
// User must have edit permissions on the draft to preview.
if ( ! current_user_can($edit_cap, $this->posts[0]->ID) ) {
$this->posts = array();
}
What you could do is use a combination of the posts_results
filter (which is applied before unpublished posts are handled) and the the_posts
filter (which is applied after), to re-populate the posts array if the user is a Contributor.
Please double-check this for any security implications of allowing lower level users access to preview unpublished content.
Untested example:
class wpse_196050 {
protected $posts = array();
public function __construct() {
add_filter( 'posts_results', array( $this, 'filter_posts_results' ), 10, 2 );
add_filter( 'the_posts', array( $this, 'filter_the_posts' ), 10, 2 );
}
public function filter_posts_results( array $posts, WP_Query $query ) {
$this->posts = []; // Reset posts array for each WP_Query instance
if ( $query->is_preview ) {
$this->posts = $posts;
}
return $posts;
}
public function filter_the_posts( array $posts, WP_Query $query ) {
if ( ! empty( $this->posts ) && current_user_can( 'edit_posts' ) ) {
$posts = $this->posts;
}
return $posts;
}
}
new wpse_196050;