Roles & Permissions Runtime & API

The Roles & Permissions subsystem is the BRM-owned layer that sits on top of WordPress roles. WordPress remains the canonical source of role definitions and capability maps, while BricksMembers owns the capability registry, role metadata, snapshots, bulk assignment flows, and the dedicated admin surface at admin.php?page=brm_roles.

Primary Boundaries

  • WordPress core owns the actual role definitions and stores them in wp_user_roles
  • BRM capability registry owns the brm_* capability catalog and route-to-capability mapping
  • BRM role metadata owns non-core per-role flags such as admin lockout, descriptions, and notes
  • BRM snapshot/export/import owns point-in-time copies of role definitions, not user assignments
  • Roles & Permissions admin UI is a view/controller surface only; writes delegate into services

Canonical Owners

  • src/Services/Roles/RoleCapabilityRegistry.php — canonical BRM capability slugs, labels, groups, and surface mapping
  • src/Services/Roles/RoleReadService.php — canonical read boundary for all registered-role reads and select options
  • src/Services/Roles/RoleWriteService.php — create, clone, update, delete, and rename slug flows
  • src/Services/Roles/RoleAssignmentService.php — single-user and bulk user-to-role assignment
  • src/Services/Roles/RoleUsageService.php — role usage scanning and replacement guidance before deletion
  • src/Services/Roles/RoleMetadataStore.php — BRM-owned per-role metadata such as block_wp_admin
  • src/Services/Roles/RoleSnapshotService.php — snapshots, export, and import
  • src/Services/Roles/RoleCapabilityGrantService.php — administrator capability back-fill when BRM adds new capability slugs

Admin Entry Points

  • Admin route: src/Admin/Pages/Routes/RolesPermissionsPage.php
  • Template: src/Admin/Pages/Templates/contract/roles.php
  • Client runtime: assets/admin/js/roles-permissions-page.js
  • AJAX controller: src/Ajax/RolesPermissionsActions.php
  • Capability gate: RoleCapabilityRegistry::CAP_MANAGE_ROLES

The page localizes a single runtime payload into window.brmRolesPermissionsPage with current tab state, role rows, capability groups, snapshots, select options, and localized strings.

Storage and Option Contract

  • wp_user_roles — WordPress-owned canonical role definitions
  • brm_role_metadata — BRM-owned per-role metadata keyed by role slug
  • brm_role_snapshots — BRM-owned snapshot/export/import storage, capped to recent entries
  • brm_capability_grant_version — one-time back-fill marker for administrator BRM capabilities

Do not create a parallel BRM roles table. The accepted contract is that WordPress stays canonical and BRM layers only own the extra role metadata and capability abstractions it needs.

Bootstrap and Request Flow

  • bricksmembers.php boots the plugin
  • src/Core/Plugin.php registers hooks and the roles AJAX handlers
  • On admin_init priority 5, BRM back-fills administrator BRM capabilities and migrates any legacy admin-lockout state
  • RolesPermissionsPage::render() builds the view model from the read, usage, metadata, snapshot, and capability services
  • The JS runtime renders the Roles and Users tabs from localized data and posts mutations to brm_roles_* AJAX actions

AJAX Surface

The thin AJAX layer is RolesPermissionsActions. Current actions include:

  • brm_roles_create
  • brm_roles_clone
  • brm_roles_update
  • brm_roles_delete
  • brm_roles_rename_slug
  • brm_roles_set_admin_lockout
  • brm_roles_describe_usage
  • brm_roles_assign_user and brm_roles_assign_users
  • brm_roles_list_users
  • brm_roles_capture_snapshot, brm_roles_list_snapshots, brm_roles_restore_snapshot, and brm_roles_delete_snapshot
  • brm_roles_export and brm_roles_import

Read and Write Rules

  • Do not call wp_roles(), get_editable_roles(), or count_users() directly outside src/Services/Roles/
  • Use RoleReadService for all role lists, sanitization, protected-role checks, and select options
  • Use RoleWriteService for definition changes; do not introduce a second mutation path
  • Use RoleAssignmentService for user-role changes; do not modify assignments ad hoc in page code
  • Use RoleUsageService before deletion so references and user replacements are handled explicitly
  • Route permission checks through Security::current_user_can() with a RoleCapabilityRegistry constant, not a hardcoded manage_options string

Deletion and Replacement Contract

Deleting a role is not a raw remove_role() call. BRM first scans usage, requires replacement where needed, migrates references, and then removes the role through the write service. This protects user assignments and BRM-owned role-linked settings from being orphaned.

Administrator Grant Contract

Whenever BRM introduces a new brm_* capability, the registry change must be paired with a grant-version bump in RoleCapabilityGrantService. That one-time versioned pass ensures the Administrator role receives the new BRM capability without rerunning the grant on every admin page load.

Edit Guidance

  • Start in RoleCapabilityRegistry when adding or renaming BRM capabilities
  • Start in RoleReadService when changing how roles are read, normalized, or sanitized
  • Start in RoleWriteService for definition writes, cloning, deletion, or slug changes
  • Start in RoleAssignmentService for single-user or bulk assignment behavior
  • Start in RoleMetadataStore for admin lockout, descriptions, or notes
  • Start in RoleSnapshotService for snapshot/export/import behavior
  • Keep RolesPermissionsActions thin; it should delegate, not own business logic

For the repository-grounded architecture view, use docs/feature-maps/roles-permissions.md. For the decision record behind the current model, use docs/adr/0005-roles-and-permissions.md.

Early Bird Deal

Start Building Your Membership Site Today

Create, sell, and manage your content without limits. BricksMembers gives you everything you need to build membership and LMS sites with Bricks Builder.

Lifetime updates & bug fixes • Premium support • 0% transaction fees • 60-day money-back guarantee