Custom Access Logic
Jetonomy Pro's WP Fusion integration grants space access from CRM tags. The built-in Access Rules are deliberately simple: each selected tag is one rule, and a user who matches any rule gets in. When you need a compound condition - "must hold this tag AND at least one of these" - you express it in a short code snippet instead of a rule builder. This page shows the two supported patterns, using APIs the plugin ships for exactly this purpose.
Both patterns survive plugin updates (they live in your child theme's
functions.php or a small must-use plugin, never in Jetonomy's files), and both
react automatically when tags change in your CRM - including a lapsed member
renewing, whose access comes back the moment the tag does.
When to use which pattern
| Your space | Pattern |
|---|---|
| Private or hidden - people must be members to see it | Pattern 1: sync membership on tag change |
| Public with gated participation - access granted by rule, no join needed | Pattern 2: capability rule + user_has_cap |
Pattern 1: sync membership on tag change
Private and hidden spaces require actual membership, so the right seam is the
moment tags change. WP Fusion fires wpf_tags_modified with the user's full tag
set after every change - the same hook Jetonomy Pro's own adapter listens to - and
Jetonomy's SpaceMember model is the public API for joining and removing members.
The example implements a real customer scenario: a "Chapter Leaders" space that requires an active membership tag AND any one of three leader tags.
add_action( 'wpf_tags_modified', function ( $user_id, $user_tags ) {
$space_id = 123; // Your space's ID (visible in the wp-admin Spaces list).
$has = fn( $tag ) => wp_fusion()->user->has_tag( $tag, $user_id );
$eligible = $has( 'WP Sync: Active Member' ) && (
$has( 'WP Sync: Is Chapter Leader' ) ||
$has( 'WP Sync: Is Associate Chapter Leader' ) ||
$has( 'WP Sync: Forums: Manual Add: Chapter Leaders' )
);
if ( $eligible && ! \Jetonomy\Models\SpaceMember::is_member( $space_id, $user_id ) ) {
\Jetonomy\Models\SpaceMember::add( $space_id, $user_id, 'member' );
} elseif ( ! $eligible && \Jetonomy\Models\SpaceMember::is_member( $space_id, $user_id ) ) {
\Jetonomy\Models\SpaceMember::remove( $space_id, $user_id );
}
}, 20, 2 );
How it behaves:
- Grant and revoke are both automatic. Losing the
Active Membertag removes the user from the space; getting it back (a renewal) re-adds them. No manual cleanup, no scheduled sweep. - Any boolean shape works. The
$eligibleexpression is plain PHP - nest AND, OR, and NOT however your CRM model requires.has_tag()accepts a tag name or its numeric ID. - Priority 20 runs after Jetonomy Pro's own adapter (priority 10), so your compound rule has the last word if you also use the built-in per-tag sync.
- Managing several spaces? Repeat the pattern with a small map of
space_id => condition callbackinstead of copying the block.
When you use this pattern for a space, leave that space's built-in WP Fusion tag list empty - otherwise the plugin's own any-tag sync and your snippet will fight over membership.
SpaceMember methods you may need (all in
includes/models/class-space-member.php):
\Jetonomy\Models\SpaceMember::add( int $space_id, int $user_id, string $role = 'member' );
\Jetonomy\Models\SpaceMember::remove( int $space_id, int $user_id );
\Jetonomy\Models\SpaceMember::is_member( int $space_id, int $user_id ): bool;
\Jetonomy\Models\SpaceMember::set_role( int $space_id, int $user_id, string $role );
Pattern 2: a capability rule backed by user_has_cap
For spaces where access is granted by rule rather than by membership, the Access
Rules tab already ships a Capability rule type, evaluated with WordPress's own
user_can(). Point it at a capability that doesn't exist in any role, then grant
that capability dynamically from your tag condition:
add_filter( 'user_has_cap', function ( $allcaps, $caps, $args, $user ) {
if ( ! in_array( 'access_chapter_leaders_space', $caps, true ) ) {
return $allcaps; // Not our capability - stay out of the way.
}
if ( ! function_exists( 'wp_fusion' ) || ! $user->ID ) {
return $allcaps;
}
$has = fn( $tag ) => wp_fusion()->user->has_tag( $tag, $user->ID );
if ( $has( 'WP Sync: Active Member' ) && (
$has( 'WP Sync: Is Chapter Leader' ) ||
$has( 'WP Sync: Is Associate Chapter Leader' ) ||
$has( 'WP Sync: Forums: Manual Add: Chapter Leaders' )
) ) {
$allcaps['access_chapter_leaders_space'] = true;
}
return $allcaps;
}, 10, 4 );
Then open the space in wp-admin, add an Access Rule of type Capability with
the value access_chapter_leaders_space, and choose what it grants (read,
participate, or full). The condition is evaluated live on every check - nothing is
stored, so there is nothing to get stale.
Note that access rules add grants on top of a space's visibility; they do not make a private space visible to non-members. That is why private spaces use Pattern 1.
Beyond WP Fusion
Neither pattern is WP Fusion-specific. Replace the has_tag() calls with any
condition you can compute in PHP - a WooCommerce Memberships check, a LearnDash
course completion, an external API lookup - and the same two seams apply. For the
full picture of how rules, visibility, and roles interact, see the
Visibility and Access Matrix; for the
membership adapter interface Pro implements on top of these seams, see
Adapter System.