Role & Capability Generator
Build a custom role and its capability map, with a versioned migration and a cleanup routine that reassigns users before removing it.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: Shop Editor * Description: Adds the shop_editor role and its capabilities. * Version: 1.0.1 * Requires PHP: 7.4 * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * The capability map for this role. * * @return array */ function acme_role_caps() { return array( 'read' => true, 'level_0' => true, 'edit_posts' => true, 'delete_posts' => true, 'publish_posts' => true, 'upload_files' => true, 'edit_published_posts' => true, 'delete_published_posts' => true, 'edit_briefs' => true, 'publish_briefs' => true, ); } /** * Create or update the role. * * add_role() does nothing when the role exists, so changing the map above * has no effect on a site that already ran this. Bumping the version does. */ function acme_install_roles() { $role = get_role( 'shop_editor' ); if ( ! $role ) { add_role( 'shop_editor', _x( 'Shop Editor', 'user role', 'acme' ), acme_role_caps() ); } else { foreach ( acme_role_caps() as $cap => $grant ) { $role->add_cap( $cap, (bool) $grant ); } } $extra_roles = array( 'administrator' ); foreach ( $extra_roles as $slug ) { $other = get_role( $slug ); if ( ! $other ) { continue; } foreach ( array( 'edit_briefs', 'publish_briefs' ) as $cap ) { $other->add_cap( $cap ); } } update_option( 'acme_roles_version', 1 ); } /** * Re-apply the map whenever the version changes. */ function acme_maybe_install_roles() { if ( (int) get_option( 'acme_roles_version', 0 ) === 1 ) { return; } acme_install_roles(); } add_action( 'admin_init', 'acme_maybe_install_roles' ); register_activation_hook( __FILE__, 'acme_install_roles' ); /** * Remove the role, moving anyone who had it somewhere safe first. * * remove_role() on its own leaves those users with no role at all. */ function acme_remove_roles() { $users = get_users( array( 'role' => 'shop_editor', 'fields' => 'ID' ) ); foreach ( $users as $user_id ) { $user = new WP_User( $user_id ); $user->remove_role( 'shop_editor' ); if ( ! $user->roles ) { $user->add_role( 'subscriber' ); } } remove_role( 'shop_editor' ); foreach ( array( 'administrator' ) as $slug ) { $other = get_role( $slug ); if ( ! $other ) { continue; } foreach ( array( 'edit_briefs', 'publish_briefs' ) as $cap ) { $other->remove_cap( $cap ); } } delete_option( 'acme_roles_version' ); } // Call acme_remove_roles() from uninstall.php — not on deactivation, // or a plugin update would strip everyone's access.
About this generator
Role & Capability Generator Online
A WordPress custom user role generator that writes add_role() properly — with the migration routine everyone forgets. Start from nothing or clone subscriber, contributor, author or editor, tick the capabilities you want from a grouped list of core caps, add your own custom capabilities, and grant them to existing roles at the same time. The generated file registers on activation and re-applies the map whenever you bump the version.
The Matrix tab lines your role up against contributor, author and editor so you can see exactly which capabilities you have added or withheld compared with the core role it sits between. Output as a plugin file with a register_activation_hook(), or as a functions.php block. Free, no account, nothing uploaded.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why an add_role() capabilities generator beats a snippet you paste once
add_role() writes the role into the wp_user_roles option once and then returns null and does nothing on every later call. That is the single most common bug in WordPress role code: the snippet works on your machine, you add a capability three weeks later, and nothing changes on any site that already ran it — because the role already exists. Fixing it means a version-checked migration that walks the map through WP_Role::add_cap(), and that is what this generator writes by default.
The migration, not just the add_role() call
The generated installer checks get_role() first: no role means add_role(), an existing role means a foreach over the map calling $role->add_cap( $cap, (bool) $grant ). A stored version number in an option decides whether the whole thing runs, so bumping the version is all it takes to roll a capability change out to a live site.
Capability combinations that actually work
The checker catches the pairs that leave the admin half-broken: edit_others_posts without edit_posts, publish_posts with nothing to publish, a role that can write posts but cannot upload_files, and any role with no read capability at all — which bounces the user straight out of wp-admin.
Dangerous capabilities called out by name
manage_options is administrator-level in everything but name. edit_users and promote_users let the role promote itself to administrator. edit_plugins is arbitrary code execution from the admin. unfiltered_html allows script tags in post content. Each is flagged with the specific consequence, and most with a one-click removal.
Custom capabilities granted where they are needed
Add your own caps for a custom post type and grant them to administrator, editor, author or shop_manager in the same routine. Administrators do not inherit custom capabilities automatically — forget that and not even an admin can manage the content, which the checker warns about.
Meta capabilities rejected
Storing edit_post, delete_post or read_post in a role does nothing: they are meta capabilities that core maps per object at check time. The generator treats them as an error and points you at current_user_can( 'edit_post', $post_id ) instead.
Removal that does not strand users
The optional cleanup routine finds everyone holding the role with get_users(), calls remove_role() on each WP_User, gives them a fallback role if that left them with none, then removes the role itself and deletes the version option — and the file reminds you to call it from uninstall.php, never on deactivation.
How does the Role & Capability Generator work?
Four steps: name the role, pick its capabilities, decide who else needs them, then set the version and export.
- 01
Name the role
A display name for the Users screen and a slug for the database, plus the function prefix and text domain. Choose what to base it on: nothing, or a copy of subscriber, contributor, author or editor as a starting map. Core role slugs are rejected outright — reusing one would rewrite the capabilities of every existing user with that role.
- 02
Pick the capabilities
Work through the four groups — reading and dashboard, posts, pages and media, site management — ticking what the role needs. Each capability carries a one-line description of what it actually unlocks. Add your own comma-separated custom capabilities underneath.
- 03
Grant custom caps to existing roles
Tick administrator, editor, author or shop_manager and the installer adds your custom capabilities to those roles too, with the matching removals in the cleanup routine.
- 04
Set the version and export
Keep the versioned migration on and bump the number every time the map changes. Turn on the cleanup routine with a fallback role, clear the checks, then download the plugin file or copy the
functions.phpblock.
Worked example — a Shop Editor role with a version-checked migration
The install and migration pair, exactly as generated. add_role() runs only when the role does not exist; otherwise every capability is re-applied through WP_Role::add_cap(), which is the only reliable way to change an existing role.
function acme_install_roles() {
$role = get_role( 'shop_editor' );
if ( ! $role ) {
add_role(
'shop_editor',
_x( 'Shop Editor', 'user role', 'acme' ),
acme_role_caps()
);
} else {
foreach ( acme_role_caps() as $cap => $grant ) {
$role->add_cap( $cap, (bool) $grant );
}
}
update_option( 'acme_roles_version', 1 );
}
/**
* Re-apply the map whenever the version changes.
*/
function acme_maybe_install_roles() {
if ( (int) get_option( 'acme_roles_version', 0 ) === 1 ) {
return;
}
acme_install_roles();
}
add_action( 'admin_init', 'acme_maybe_install_roles' );acme_role_caps() sits above this in the file and returns the capability map as 'cap' => true pairs. In plugin mode the installer is also wired to register_activation_hook( __FILE__, … ), so a fresh install gets the role immediately and existing sites pick the change up on the next admin request after a version bump.
Roles and capabilities — frequently asked questions
The questions developers ask when building custom roles for WordPress.
Why does add_role() not update my capabilities when I change them?
add_role() is create-only. Roles live in the wp_user_roles option, and if the slug is already there the function returns null and changes nothing. Any later change to the capability map has to go through get_role( $slug )->add_cap() or remove_cap(). The usual pattern is to store a version number in an option and re-run the whole map whenever it does not match.
Where should I call add_role() — on activation or on init?
On activation, via register_activation_hook(), because the write is permanent and only needs to happen once. Calling it on init means a database read on every single request, and on most requests it does nothing anyway. A version-checked routine on admin_init covers upgrades without paying that cost on front-end requests.
What is the difference between a role and a capability?
A capability is a single permission such as edit_posts or manage_options. A role is a named bundle of capabilities stored in the wp_user_roles option, and a user is assigned roles rather than capabilities. Code should always check capabilities with current_user_can(), never the role name — a user can hold several roles, and site owners add capabilities to core roles all the time.
How do I add a capability to an existing role like editor?
Get the role object and add to it: get_role( 'editor' )->add_cap( 'manage_briefs' ). That is a database write, so guard it behind a version check rather than running it on every load. Remember to remove it again on uninstall, or the capability outlives the plugin that used it.
Why do my custom post type capabilities not work for a custom role?
Two things are usually missing. The post type has to be registered with 'capability_type' and 'map_meta_cap' => true so core knows to map edit_post onto your primitive caps, and the primitive caps themselves have to be granted to the role — including to administrator, which does not inherit custom capabilities automatically.
How do I safely remove a custom role?
Reassign first, delete second. Find everyone holding it with get_users( array( 'role' => $slug ) ), call remove_role() on each WP_User, add a fallback role to anyone left with none, and only then call the global remove_role( $slug ). Calling remove_role() on its own leaves those users with no role and no access at all.
Where do I paste the code from the Role & Capability generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Admin generators in the library, plus the WordPress tools most often used alongside this one.