First of all I suggest you to use a function to return the terms to exclude, that will help you to get them in different places without having to repeat code, e.g. in the ‘pre_get_posts’ filter and the adjacent post filters.
So:
function get_level_term_to_exclude() {
if ( current_user_can ('manage_options') || current_user_can ('view_level1_posts') ) {
return false;
}
$terms = array( 'level1', 'level2', 'level3' );
if( current_user_can ( 'view_level2_posts' ) ) {
$terms = array( 'level1' );
}
if( current_user_can ( 'view_level3_posts' ) ) {
$terms = array( 'level1', 'level2' );
}
return $terms;
}
I removed comments for sake of semplicity, however you code is well commented and mine is taken from there. I just put the check for admins at function top.
After that, generally speacking, JOIN
clause can’t filter anything without WHERE
, and WHERE
will fail if using a table name not defined in JOIN
, so does not exists a join method and a where method, but exists a join and where method.
In core, the filter for excluded terms is done both via 'WHERE'
and JOIN
, in fact at line #1183 you can see:
"WHERE p.post_date $op %s AND p.post_type = %s
AND p.post_status="publish" $posts_in_ex_terms_sql"
inside the if ( ! empty( $excluded_terms ) ) {
statement on line #1154
and on line #1141:
if ( $in_same_term || ! empty( $excluded_terms ) ) {
$join = " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id
INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
The $posts_in_ex_terms_sql
is set at line #1173 in this way:
$posts_in_ex_terms_sql = $wpdb->prepare(
" AND tt.taxonomy = %s
AND tt.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')', $taxonomy
);
I think that you can just copy code used by core when $excluded_terms
is not empty… if it works for core should works also for you.
So you can mimic core workflow using "get_{$adjacent}_post_where"
filter and "get_{$adjacent}_post_join"
.
So the function filter_adjacent
should act on join and where clause, a simple check on current_filter
can do the trick.
this is how your class should appear using my tips:
class Site_Members_Taxonomy {
const LANG = 'some_textdomain';
public $post_types = array( 'post', 'page', 'gallery' );
public $tax = 'membership';
public $tax_label;
public function __construct() {
$this->tax_label = __( 'Membership', self::LANG );
add_action( 'pre_get_posts', array( $this, 'filter_query' ) );
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' ) );
}
function get_term_to_exclude() {
if (
is_admin()
|| current_user_can ('manage_options')
|| current_user_can ('view_level1_posts')
) {
return false;
}
$terms = array( 'level1', 'level2', 'level3' );
if( current_user_can ( 'view_level2_posts' ) ) {
$terms = array( 'level1' );
}
if( current_user_can ( 'view_level3_posts' ) ) {
$terms = array( 'level1', 'level2' );
}
return $terms;
}
/**
* Modify the query based on membership taxonomy
*/
function filter_query( $query ) {
$terms = $this->get_term_to_exclude();
if ( ! $terms ) return;
$tax_query = array(
array(
'taxonomy' => $this->tax,
'field' => 'slug',
'terms' => $terms,
'operator' => 'NOT IN'
)
);
set_query_var( 'tax_query', $tax_query );
}
/**
* Filter adjacent posts
*/
function filter_adjacent( $clause ) {
if ( substr_count( current_filter(), '_post_join' ) ) {
if ( empty($clause) ) $clause="";
global $wpdb;
$clause .=
" INNER JOIN {$wpdb->term_relationships} 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' ) ) {
$excluded_term_slugs = $this->get_term_to_exclude();
if ( ! $excluded_term_slugs ) return $clause;
$excluded_terms = array();
foreach ( $excluded_term_slugs as $slug ) {
$t = get_term_by( 'slug', $slug, $this->tax );
if ( ! $t || is_wp_error($t) ) continue;
$excluded_terms[] = $t->term_id;
}
$excluded_terms = array_filter( array_map( 'intval', $excluded_terms ) );
if ( empty( $excluded_terms ) ) return $clause;
global $wpdb;
$posts_in_ex_terms_sql = $wpdb->prepare(
" AND ttmship.taxonomy = '%s'
AND trmship.term_taxonomy_id NOT IN (" . implode( ',', $excluded_terms ) . ')',
$this->tax
);
return $clause. $posts_in_ex_terms_sql;
}
}
}
I’ve used a different alias for taxonomy tables in this way the filter will work even if when get_adjacent_post
is called any terms are passed to for $excluded_terms
argument and /or $in_same_term
is set to true.
Code is completely untested, let me know if it works…