Edit 3 - revised per G.M.'s suggestions. Code runs error free, but adjacent post filter still not filtering. Changes noted in code comments, current question noted at bottom of post.
Edit 2 - reflects G.M.'s code suggestion below. I'm confused about where the modified join/where filter function should go, so I added more of the mu-plugin code structure.
Edit 1 - reflects Kaiser's comment below, examples provided. Also, I was mistaken about which code was filtering my posts. Question does not relate to the Members plugin.
I have a mu-plugin filtering my WP query so that in the index loop, a visitor only sees posts they can read. It does this by offering post author the ability to assign a multi-level membership taxonomy for each post.
At the lowest (public) level, there is no membership taxonomy attached to the post.
Here is the query filter in the mu-plugin:
if ( ! class_exists ( 'Site_Members_Taxonomy' ) ) {
class Site_Members_Taxonomy
{
//define( 'LANG', 'some_textdomain' ); deleted LANG, was throwing errors
public $post_types = array( 'post', 'page', 'gallery' );
public $tax = 'membership';
public $tax_label; // CHANGE: made these three vars public
public function __construct()
{
$this->tax_label = __( 'Membership' );
// plugin defines taxonomy and membership meta and then:
add_action( 'pre_get_posts', array( &$this, 'filter_query' ) );
/**
* Modify the query based on membership taxonomy
*/
function filter_query( $query ) {
//quit right now if in admin
if( is_admin() ) return;
$tax_query = '';
$terms = $this->get_terms_to_exclude(); // see function below
if( $terms )
$tax_query = array(
array(
'taxonomy' => $this->tax,
'field' => 'slug',
'terms' => $terms,
'operator'=>'NOT IN'
)
);
// if after all that we have a tax query to make, lets do it!
if ( $tax_query )
set_query_var('tax_query', $tax_query);
}
} //end class
global $Site_Members_Taxonomy;
$Site_Members_Taxonomy = new Site_Members_Taxonomy();
}
// finally, outside the class definition, there is a function `members_can_view` to be used as a conditional to hide/show various template tags etc.
So far, so good. This filter works great.
The problem is, in single-post view, next_post_link()
and previous_post_link()
don't get the filter, so visitors can navigate to posts they cannot read. I want to filter the links to these posts to show the link only to the next post they can actually read.
I'd basically like to use the above query filter on get_adjacent_post().
In WP core trac for link-template.php (as well as in the post Kaiser linked to below) I found that next_post_link()
, get_next_post()
and get_adjacent_post()
run the adjacent post query through the following filters:
get_{$adjacent}_post_join
get_{$adjacent}_post_where
get_{$adjacent}_post_sort
REWRITE JOIN/WHERE FILTER
I tried the following rewrite of JOIN/WHERE suggested in G.M.'s answer. As he says, it is very closely based on the wp-core function, with tr and tt given different names and the excluded terms passed directly to the function.
This function is currently active on the site and not throwing errors, however it's not filtering the adjacent posts query correctly either.
Current Question: When converting $excluded_terms from slugs to IDs in the function filter_adjacent below, am I correctly building the array? How can I log the excluded terms and find out what's going on?
// Filters for get_adjacent_posts() so they don't show up in single-post navigation
// These filters are added within function __construct{} noted above
// CHANGE: &$this to $this
add_filter( 'get_previous_post_where', array( $this, 'filter_adjacent' ) );
add_filter( 'get_next_post_where', array( $this, 'filter_adjacent' ) );
add_filter( 'get_previous_post_join', array( $this, 'filter_adjacent' ) );
add_filter( 'get_next_post_join', array( $this, 'filter_adjacent' ) );
/**
* These functions added within mu-plugin class:
*
* First, modify excluded terms based on membership taxonomy
*
* NOTE: Excluded terms may be an array
*/
function get_terms_to_exclude() {
// if a real admin or "level1" we won't change tax query at all
if( current_user_can ( 'manage_options' ) || current_user_can ( 'view_level1_posts' ) )
return false;
// default/public will exclude all upper level posts
// in other words, all posts with membership taxonomy
$terms = array( 'level1','level2','level3' );
// if a level3 reader we'll exclude level1 and level2 posts
if( current_user_can ( 'view_level3_posts' ) )
$terms = array( 'level1', 'level2' );
// if at level2 level we'll exclude level1 level posts
if( current_user_can ( 'view_level2_posts' ) )
$terms = array( 'level1' );
return $terms;
}
/**
* Next, use these terms to filter the get_adjacent_post JOIN/WHERE
*/
function filter_adjacent( $clause ) {
if ( substr_count( current_filter(), '_post_join' ) ) { // filtering join
if ( empty($clause) ) $clause = '';
global $wpdb;
$clause .=
" INNER JOIN {$wpdb->term_relationships} AS trmship ON p.ID = trmship.object_id
INNER JOIN {$wpdb->term_taxonomy} ttmship
ON trmship.term_taxonomy_id = ttmship.term_taxonomy_id";
return $clause;
} elseif ( substr_count( current_filter(), '_post_where' ) ) { // filtering where
$excluded_term_slugs = get_terms_to_exclude();
if ( ! $excluded_term_slugs ) return $clause; // nothing to filter if no terms
$excluded_terms = array(); // we needs term ids, let's convert slug in terms ids
foreach ( $excluded_term_slugs as $slug ) {
$t = get_term_by( 'slug', $slug, 'membership' );
if ( ! $t || is_wp_error($t) ) continue;
$excluded_terms[] = $t;
}
// something wrong in get_level_term_to_exclude()?
if ( empty($excluded_terms) ) return $where;
$posts_in_ex_terms_sql =
" AND ttmship.taxonomy = 'membership'
AND ttmship.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')';
// return filtered where clause
return $clause. $posts_in_ex_terms_sql;
}
}