[buddypress-trac] [BuddyPress Trac] #7176: Implement user capabilities for Activity component
noreply at wordpress.org
Tue Aug 30 13:24:01 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:
Comment (by johnjamesjacoby):
> WP's Roles system is quite flawed
This isn't really fair. WordPress's `WP_Role` class is one of best
examples of proper class usage in WordPress core. And the `$wp_roles`
global is a relic similar to the others.
I'd call bbPress's approach a hybrid of dynamic & persistent roles & caps.
Dynamic in that the roles are registered at run-time (vs. stored in
`_options`); persistent in that each user continues to have their bbPress
role saved in `usermeta` along with any role they have already in the rest
of the site.
> Most permissions checks are "derived", which means that they're mapped
to "primitive" caps.
This isn't really true. Most permissions checks are primitive, meaning
they're looking for actual capabilities that a user is known to have in
the database. Some caps are mapped – singles, specifically, like
`delete_post` with a `$post_id` get mapped to "meta capabilities" as
defined by the post object itself, like `delete_published_posts`, et
> Users are associated with roles based on "capabilities" (bad name!) keys
stored in usermeta.
It's not a bad name. [https://en.wikipedia.org/wiki/Role-
based_access_control (Role)] and [https://en.wikipedia.org/wiki/Role-
based_access_control (capability)] access control are simple, well known,
and versatile ways to define a set of allowances and constraints on any
system. WordPress's hybrid approach to this, coupled with `map_meta_cap`
allowing JIT overrides, is an incredibly flexible & powerful API.
> we could use `read` in most cases, but here we needed to cover non-
We can't use `read` ever, unless we:
* Make every user a "Subscriber" to every site
* Re-init caps for the root-site on every `switch_to_blog()` to check our
own capability mapping to `read` on root
> 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
We can extend the `WP_Role` base class for user. To extend `WP_Roles`
would require either:
* Modifications to allow the `role_key` to be filtered
* A filter on `get_option( $this->db->get_blog_prefix() . 'user_roles' )`
which would likely not be very efficient, and kinda difficult for humans
Because BuddyPress has the [https://codex.buddypress.org/getting-started
/installation-in-wordpress-multisite/ (6 horsemen of the installation-type
apocalypse)], I'm confident (and do agree) that we are forced to off-
script at least a little bit to build something that's flexible enough to
work in all environments.
And we'll need to make sure `bp_moderate` force-allows to maintain
I have 2 separate pieces of feedback:
* About the patch
* About the general approach
* Capabilities are the wrong place to do name-spacing, like
`edit_bp_activity`. The name-spaces should be: the role the capability
belongs to, and the more-broad array of roles that belong to BuddyPress.
* Changes to `bp_add_caps()` and `bp_remove_caps()` look sound, even as a
* Adding `true/false` to `bp_get_caps_for_role()` seems OK, but the `Every
other role` part is scary. bbPress has a "Blocked" role, for example, and
now BuddyPress is allowing a blocked user in (maybe that's OK?). I think
it's arguably easier to keep to mapping to WordPress's known roles, since
they are almost guaranteed to exist in a predictable way.
* The deprecation (and subsequent abandonment) of
`bp_get_community_caps()` is sad to see. I think I'd imagined it as the
funnel where all derived caps could be hooked in by each BuddyPress
component, but I suppose whatever route we go will probably require a
different approach than this one.
* For code like `$activity && $user_id === $activity->user_id` can we
please do `! empty( $activity ) && ( $user_id === $activity->user_id )` –
wrapping inline conditionals should be standard practice for all of us by
* I think I agree with @boonebgorges, that calling `bp_add_caps()` on
every site is not a good idea.
I think I also agree with @boonebgorges, in that piggy-backing directly
on-top-of WordPress's per-site role-based capability mapping system is not
a great way to implement access control in an environment as complex as
Which is to say, I think we need to treat each BuddyPress component like
it is it's own namespace, with it's own roles that a user may-or-may-not
have, defaulting to a "Subscriber" equivalent where all users are allowed,
to maintain backwards compatibility.
I think the way WordPress has a `$wp_roles` global, we should have
`bp()->roles` as the place where BuddyPress components store their multi-
dimensional role & capability arrays, completely outside of WordPress's,
but using a similar & familiar approach.
We could also then store our own role assignments in `usermeta` in a way
that doesn't conflict with WordPress's per-site implementation, or pollute
that space later if BuddyPress is deactivated, but I'm not sure this is
good or bad etiquette.
If each component has their own roles with their own caps, imagine having:
* Activity Administrator
* Group Administrator
* Friends Administrator
* Notifications Administrator
* Messages Administrator
* XProfile Administrator
* Settings Administrator
* Blogs/Sites Administrator
Admins being the role that can always see and perform all actions on all
data for all users. Whether this role is stored in the user's literal
`wp_ROOT_BLOG_capabilities` meta, or some other meta, doesn't really
matter very much, so long as it's persistent to the user, and force-
granted by the presence of `bp_moderate`.
Similar to bbPress, we could then have "Moderator", "Participant",
"Spectator", and "Blocked" roles with hard-coded primitive capabilities to
define what their intentions are, and allow each component to derive from
them based on whatever the conditions are at the time. (I don't really
care about the literal names, so much as the spirit of their ranking &
I also think, that role & cap checks should not be flat, or part of the
global namespace. `bp_current_user_can( 'edit_bp_activity' )` for example,
is going to break-down very quickly as soon as we want to add more crud
actions to more places for more user-to-user type things.
Instead, I've always imagined something more like:
$single_activity_check = bp()->activity->current_user_can(
'edit_activity', $activity_id );
Basically, a way make each component responsible for the registration,
mapping, checking, and confirming of their own relative caps. Whether
`bp_current_user_can()` continues to be a funnel or not, will be a funny
implementation detail we'll have to discover once we get started.
@DJPaul thanks or taking a first stab at this. User permissions are
arguably one of the more difficult concepts to work with, barring maybe
time & date formats. We will all get this wrong a hundred times before we
get it right, so I think this is an important first step, and Activity is
a great component to start with.
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/7176#comment:10>
BuddyPress Trac <http://buddypress.org/>
More information about the buddypress-trac