Capabilities & Permissions
Eventonomy creates no WordPress roles. It defines seven of its own capabilities and maps each onto a capability your existing roles already have, so whoever can already do the equivalent thing in WordPress can do it here. Nothing to configure, and no role bloat left behind on uninstall.
The Map
Filterable via evnm_capabilities (Core\Capabilities::map()).
| Eventonomy capability | Maps to | Effectively |
|---|---|---|
evnm_create_events |
read |
Any logged-in user, including subscribers |
evnm_edit_events |
read |
Any logged-in user - ownership-scoped, see below |
evnm_edit_others_events |
edit_others_posts |
Editor and above |
evnm_delete_events |
delete_posts |
Author and above |
evnm_manage_rsvps |
manage_options |
Administrator - or the event's owner |
evnm_manage_orders |
manage_options |
Administrator - or the event's owner |
evnm_manage_settings |
manage_options |
Administrator |
These caps are virtual - they are never stored on a role
Neither plugin calls add_role(), add_cap() or remove_cap() anywhere, and neither
registers a map_meta_cap filter. Capabilities::user_can( $user_id, $meta_cap ) looks
the meta-cap up in the table above and asks WordPress core about the primitive, so
the effective grant is entirely core's default role grid. That is what "no role bloat
left behind on uninstall" means literally: there is nothing to clean up.
Two consequences worth knowing:
current_user_can( 'evnm_manage_settings' )returnsfalsefor everyone, because no role holds that string. Always go throughCapabilities::user_can()or one of theevnm_user_can_*()helpers, never a rawcurrent_user_can()on anevnm_cap. Admin screens do the same translation throughAdminMenu::primitive_cap(), which falls back tomanage_optionsfor a meta-cap that is not in the map.evnm_delete_eventsis defined but nothing reads it today. Deleting an event goes throughevnm_user_can_manage_event( …, 'delete' ), which resolves viaevnm_edit_eventson the ownership branch andevnm_edit_others_eventsotherwise. Remappingevnm_delete_eventswill therefore not change who can delete an event; use theevnm_user_can_manage_eventfilter for that.
Why read for create and edit
A community events plugin whose default is "only Editors may add an event" is
useless out of the box. read is the every-registered-user primitive, so a
subscriber can create an event and edit their own.
Two things keep that safe: editing is ownership-scoped (below), and publishing is
governed by the approval queue rather than by the capability. Site owners narrow
who may create at all with the creator_role setting, which
evnm_user_can_create_events() enforces; the default is open.
Why manage_options for RSVPs and orders
Managing the attendees of an event you do not own is a site-manager action, not a content-editor one: it can burn check-in tokens, cancel people's registrations, and bulk-email them. Orders are money- and identity-adjacent for the same reason.
This does not restrict organizers. The event's own author passes through the ownership branch described next, so a member-organizer with no WordPress editing capabilities still manages their own attendees and orders in full. What is removed is only cross-event reach - an Editor cannot read every order on the site.
Ownership Is Resolved, Not Assumed
Never test a raw capability for an event-scoped action. Ask the helper, which resolves ownership first and the capability second:
if ( ! evnm_user_can_manage_event( get_current_user_id(), $event, 'manage_rsvps' ) ) {
return new WP_Error( 'evnm_forbidden', __( 'Not allowed.' ), array( 'status' => 403 ) );
}
evnm_user_can_manage_event( int $user_id, array $event, string $action ): bool
- The event's author passes for their own event.
- A non-owner must hold the mapped capability (
manage_optionsformanage_rsvps/manage_orders,edit_others_postsfor editing).
evnm_user_can_manage_order( int $user_id, array $order ): bool is the order-scoped
twin: an administrator passes for any order, and the owner of the order's event
passes for that order. The buyer does not - which is why a buyer cannot refund
their own purchase.
evnm_user_can_create_events( int $user_id ): bool folds in the creator_role
setting. Use it rather than testing evnm_create_events directly, or you will
ignore the site owner's restriction.
REST Permission Callbacks
Two rules the whole REST surface follows, and yours should too:
- Return a
WP_Errorwith a status, neverfalse.falseproduces a generic 403 with no code a client can branch on. Return401for "not logged in" and403for "logged in, not allowed" so an app can tell "sign in" from "you cannot do this". - Every route has a real
permission_callback.__return_trueis allowed only where public access is the point, and each such route is allowlisted with a documented reason (the discovery feed, the gateway webhooks, the Connect OAuth return). A route with no callback at all fails CI.
'permission_callback' => function () {
if ( ! is_user_logged_in() ) {
return new WP_Error( 'evnm_unauthorized', __( 'Sign in first.' ), array( 'status' => 401 ) );
}
if ( ! current_user_can( 'evnm_manage_settings' ) ) {
return new WP_Error( 'evnm_forbidden', __( 'Not allowed.' ), array( 'status' => 403 ) );
}
return true;
},
Secrets Are Capability-Gated Too
Some fields are withheld from output regardless of who is asking:
magic_token,checkin_token- never in any REST response or admin table.gateway_txn_id- restricted tomanage_options.- The order
metablob - stripped entirely from/ordersresponses; it holds the access token, guest emails and the refund ledger.
If you extend a response, do not re-introduce these. The rule is that a capability check decides whether you get the row, and a separate rule decides which fields come with it.
Changing the Map
add_filter( 'evnm_capabilities', function ( array $map ) {
// Let Editors manage attendees on any event (loosens the default).
$map['evnm_manage_rsvps'] = 'edit_others_posts';
return $map;
} );
Loosening manage_rsvps or manage_orders grants cross-event reach over attendee
identity and money. Do it deliberately, and read the reasoning above first.
What's Next?
- Database Schema - what these capabilities protect
- REST API - the permission model per route
- Abilities API - exposing actions to agents, which is a separate permission surface