[buddypress-trac] [BuddyPress Trac] #7176: Implement user capabilities for Activity component

buddypress-trac noreply at wordpress.org
Tue Aug 30 03:47:09 UTC 2016


#7176: Implement user capabilities for Activity component
-------------------------+-----------------------------
 Reporter:  DJPaul       |       Owner:
     Type:  enhancement  |      Status:  new
 Priority:  normal       |   Milestone:  Future Release
Component:  Activity     |     Version:
 Severity:  normal       |  Resolution:
 Keywords:               |
-------------------------+-----------------------------

Comment (by boonebgorges):

 I spent some time looking at this and hacking around with
 [attachment:7176.01.patch] and with a patch of my own.

 WP's Roles system is quite flawed, and I am not eager to use it. Just
 thinking about the activation/deactivation and other database nonsense
 associated with roles makes me cringe.

 However, Roles plays a critical part in the way that WP's capabilities
 system works:
 a. Most permissions checks are "derived", which means that they're mapped
 to "primitive" caps.
 b. "Primitive" caps come in packages called "roles" - things like
 "Subscriber", "Editor", etc.
 c. Roles are stored in the database as serialized arrays of their
 associated caps.
 d. Users are associated with roles based on "capabilities" (bad name!)
 keys stored in usermeta.

 Without Roles, our capability mapping system would have to map to a
 *WordPress* primitive role - specifically, a role that we can guarantee
 that all users will have. We already do this in BP, with `'exist'`
 https://buddypress.trac.wordpress.org/browser/tags/2.6.0/src/bp-xprofile
 /bp-xprofile-caps.php#L13 (we could use `read` in most cases, but here we
 needed to cover non-logged-in users). The problem with this strategy is
 that the only way for plugins to modify the behavior is to filter
 `map_meta_cap`, and then reproduce all of the logic for a given capability
 in order to grant it in a different way. You can't simply grant or revoke
 a cap in order to prevent a user from doing something, since everything
 would map to `exist` or `read`, which you can't revoke for obvious
 reasons.

 (bbPress, which does interesting things with this limited system, works
 around the fact that you can only have two layers of caps - primitive and
 derived - by doing `user_can()` checks inside of the `map_meta_cap`
 callback. This is clever, but in my experience it can cause performance
 issues and even "nesting limit reached" PHP fatal errors.)

 For this reason, I think we should keep the concept of Roles, even if we
 decide not to use the full-fledged, stored-in-the-database version that WP
 has. Our roles might be defined in (pseudo)code like this:

 {{{
 'member' => array( 'bp_edit_activities', 'bp_create_activities' ... )
 'admin' => 'member' + array( 'bp_edit_others_activities',
 'bp_delete_others_activities' ... )
 }}}

 We hardcode our default roles, and allow them to be filtered, so that new
 roles can be added or existing ones can be modified by plugins. Our
 `map_meta_cap()` function will follow WP by mapping derivative caps to
 primary ones:

 {{{
 $caps = array();
 ...
 case 'bp_edit_activity' :
     ...
     if ( $activity->user_id === $user_id ) {
         $caps[] = 'bp_edit_activities';
     } else {
         $caps[] = 'bp_edit_others_activities';
     }
 break;
 ...
 }}}

 Users can be granted a "role" dynamically, using the `user_has_cap`
 filter:

 {{{
 function bp_grant_user_caps( $allcaps, $caps, $args, WP_User $user ) {
     $user_role = bp_get_user_role( $user->ID );
     $user_caps = bp_get_role_caps( $role );

     $allcaps = array_merge( $allcaps, $user_caps );
     return $allcaps;
 }
 add_filter( 'user_has_cap', 'bp_grant_user_caps', 10, 4 );
 }}}

 I can see a couple different kinds of plugins that might be built with
 this sort of system:

 1. Create a "moderator" role that can edit others' activity, but not
 delete:

 {{{
 add_filter( 'bp_get_roles', function( $roles ) {
     $roles['mod'] = $roles['member'] + array( 'bp_edit_others_activities'
 );
     return $roles;
 } );
 }}}


 2. Prevent users from being able to delete their activities:
 {{{
 add_filter( 'bp_get_roles', function( $roles ) {
     unset( $roles['member']['bp_delete_activities'] );
     return $roles;
 } );
 }}}

 It's only when you're doing something very advanced - say, revoking a
 given cap only when an activity item meets a given criteria - that you'd
 need to filter `map_meta_cap`.

 I think this is a decent compromise. It keeps the developer-facing ease-
 of-use of the Roles system, without mucking around with the database. The
 one big downside of not doing database integration is that we aren't
 automatically compatible with existing Role Editor plugins.

 Obviously there's lots of non-working and naive pseudocode above. We may
 want components to register their own primitive caps, or other such
 niceties. But I think that I've given a rough sketch of how the system
 might work.

 @DJPaul Does this seem like a reasonable approach? @r-a-y @johnjamesjacoby
 it would be helpful to have your general thoughts too, given that you've
 both done lots of work with custom role/cap stuff. If we like the
 direction, I can take the next round of iteration on
 [attachment:7176.01.patch] to flesh out some of the details as I envision
 them.

--
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/7176#comment:8>
BuddyPress Trac <http://buddypress.org/>
BuddyPress Trac


More information about the buddypress-trac mailing list