GeneratorsAdminRole & Capability

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.

shop-editor-role.php
Saved just now
The role
shop_editor starts from author and ends with 10 capabilities, including 2 custom. Version 1 is re-applied on admin_init when it changes.
Capabilities
10 granted
Reading and dashboard
Posts
Pages and media
Site management
Also grant to existing roles
administrator
Versioned migration
Re-applies the map on admin_init whenever the version number changes.
Cleanup routine
Reassigns affected users, then removes the role and its option.
<?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.