0

I need to restrict users from editing custom post types that don't have a taxonomy or custom meta value matching a hidden value in their WP user meta.

We've created a custom post type that is an Organization's profile. Specific users of that Organization will have their own website logins and should be able to edit their Organization's post and/or any other custom post types that have the same taxonomy.

The common thread is that we have an organization ID matching a value in both the post (which we could setup as a meta field or taxonomy) and that is part of the user's profile meta when their account is created via a popular CRM's Single Sign-On plugin.

Example Scenario:

User A signs into website with SSO plugin. As part of that, a common Organization ID is stored as a meta value in their WP user profile. They are the first user to sign in and so our code queries the CRM's API to get the Organization's info and programmatically creates a custom post of type Organization.

User B signs into website with SSO plugin. They are part of the same organization as above. Our code sees that a post of type Organization with that Organization ID already exists so it skips trying to create it.

Users A and B should both be able to edit the custom post that matches their Organization ID. They may eventually create (and should share) other custom posts with the same ID. They should not be able to see anyone else's posts within that (or any) custom post type.

Is it possible to restrict multiple users to only be able to edit a post (or posts) where those users have a custom meta key matching the custom post's custom taxonomy or meta key?

I've seen suggestions like "create a custom post type for each user group and restrict it with a plugin like User Role Editor" but I can't do that as we have literally thousands of members on this particular site and that would be unwieldy.

I can't supply code in this case because I'm really just looking for ideas around what mechanisms might exist in WordPress, CPT UI, ACF, and some good ol' fashioned PHP in functions.php that may accomplish this. I should add, we're not worried about the front-end as page template conditionals can handle that easily enough, it's just the admin/dashboard stuff that I need some advice on.

1 Answer 1

1

Is it possible to restrict multiple users to only be able to edit a post (or posts) where those users have a custom meta key matching the custom post's custom taxonomy or meta key?

Yes, you can use user_has_cap which is a filter applied when current_user_can() or user_can() is called — these two functions are used for checking whether the current user or a specific user has the permission to do things like, editing posts, deleting terms, changing site options, etc.

So here's a working example, tried with a custom post type with capability_type set to org_profile which is also the post type slug:

Note: Replace the org_profile with the correct post type, org_id with the correct meta key, and organization with the correct taxonomy slug, and if the user meta stores the term ID instead of slug or name, make sure you pass an integer to has_term(), e.g. has_term( (int) $user_org_id, ... ). ( PS: If you remove the comments, the function is actually pretty simple.. 🙂 )

add_filter( 'user_has_cap', 'wpse_393410', 10, 4 );
function wpse_393410( $allcaps, $caps, $args, $user ) {
    /* With the following, and assuming the current user's ID is 123:
        current_user_can( 'edit_post', 456 );
        user_can( 123, 'edit_post', 456 );

       $args would be array( 'edit_post', 123, 456 ).
       i.e. array( 'edit_post', <user ID>, <post ID> )
     */

    if ( ! empty( $args[2] ) && 'edit_post' === $args[0] &&
        'org_profile' === get_post_type( $args[2] )
    ) {
        // If the current user does NOT have any of the roles in the include list, we
        // do nothing.
        $include = array( 'org_user' );
        if ( empty( array_intersect( (array) $user->roles, $include ) ) ) {
            return $allcaps;
        }

        $user_org_id = get_user_meta( $args[1], 'org_id', true );

        // If the user meta is empty, we do nothing. Else, check if the post has a term
        // with the same SLUG OR NAME in the 'organization' taxonomy.
        // Note: We just need to disable one of the caps (in $caps).
        if ( $user_org_id && ! has_term( $user_org_id, 'organization', $args[2] ) ) {
            $allcaps[ $caps[0] ] = false;
        }
    }

    return $allcaps;
}

If you want to use the post meta instead, then replace this:

$user_org_id = get_user_meta( $args[1], 'org_id', true );

// If the user meta is empty, we do nothing. Else, check if the post has a term
// with the same SLUG OR NAME in the 'organization' taxonomy.
// Note: We just need to disable one of the caps (in $caps).
if ( $user_org_id && ! has_term( $user_org_id, 'organization', $args[2] ) ) {
    $allcaps[ $caps[0] ] = false;
}

With this:

$user_org_id = get_user_meta( $args[1], 'org_id', true );
$post_org_id = get_post_meta( $args[2], 'org_id', true );

// If the post OR user meta is empty, we do nothing. Else, check if the post meta
// matches the user meta.
// Note: We just need to disable one of the caps (in $caps).
if ( $post_org_id && $user_org_id && $user_org_id !== $post_org_id ) {
    $allcaps[ $caps[0] ] = false;
}

Additionally, in the above function, I used an include list ($include), but you can instead use an exclude list, if you want to, e.g. $exclude = array( 'administrator', 'custom' ); and then do ! empty( array_intersect( $roles, $exclude ) ).

2
  • 1
    Yes! That bottom snippet especially looks promising. And thank you for pointing me in the right direction with the filter hook as well. Aug 21, 2021 at 14:53
  • You meant the $exclude thing? :) But anyway, I used an include list because my CPT used a custom capability_type, hence only administrators and certain roles were allowed to edit posts in that CPT; but using an exclude list instead is certainly not a problem. And sorry, that $roles should actually be (array) $user->roles ..
    – Sally CJ
    Aug 21, 2021 at 23:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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