Eventonomy

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' ) returns false for everyone, because no role holds that string. Always go through Capabilities::user_can() or one of the evnm_user_can_*() helpers, never a raw current_user_can() on an evnm_ cap. Admin screens do the same translation through AdminMenu::primitive_cap(), which falls back to manage_options for a meta-cap that is not in the map.
  • evnm_delete_events is defined but nothing reads it today. Deleting an event goes through evnm_user_can_manage_event( …, 'delete' ), which resolves via evnm_edit_events on the ownership branch and evnm_edit_others_events otherwise. Remapping evnm_delete_events will therefore not change who can delete an event; use the evnm_user_can_manage_event filter 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_options for manage_rsvps / manage_orders, edit_others_posts for 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:

  1. Return a WP_Error with a status, never false. false produces a generic 403 with no code a client can branch on. Return 401 for "not logged in" and 403 for "logged in, not allowed" so an app can tell "sign in" from "you cannot do this".
  2. Every route has a real permission_callback. __return_true is 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 to manage_options.
  • The order meta blob - stripped entirely from /orders responses; 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?