Users, roles and capabilities
A site may have several people with different duties: one writes articles, another reviews them, a third configures the site. WordPress separates them by roles, and each role is a set of capabilities for particular actions.
The built-in roles
| Role | What it can do |
|---|---|
Subscriber (subscriber) | Read and edit their own profile |
Contributor (contributor) | Write posts but not publish them |
Author (author) | Publish and edit their own posts |
Editor (editor) | Manage all posts and pages |
Administrator (administrator) | Everything, including settings, themes and plugins |
Checking capabilities
You check not the role but the capability for an action:
if (current_user_can('edit_posts')) {
echo '<a href="' . esc_url(admin_url('post-new.php')) . '">Write a post</a>';
}
if (current_user_can('edit_post', $postId)) {
edit_post_link('Edit', '', '', $postId);
}| Capability | What it allows |
|---|---|
read | Entering the dashboard and one's own profile |
edit_posts | Creating posts |
publish_posts | Publishing |
edit_others_posts | Changing other people's posts |
upload_files | Uploading files |
manage_options | Changing site settings |
The edit_post capability with a post number is special: WordPress itself decides whether the text is yours or someone else's and whether it is published.
The current user
<?php
// header.php
if (is_user_logged_in()) {
$user = wp_get_current_user();
printf(
'Hello, %s! <a href="%s">Log out</a>',
esc_html($user->display_name),
esc_url(wp_logout_url(home_url('/'))),
);
} else {
printf('<a href="%s">Log in</a>', esc_url(wp_login_url(get_permalink())));
}A custom role
Roles are stored in the database, so they are created once — on plugin activation:
// books.php
register_activation_hook(__FILE__, function () {
add_role('librarian', 'Librarian', [
'read' => true,
'edit_posts' => true,
'upload_files' => true,
]);
});
register_deactivation_hook(__FILE__, function () {
remove_role('librarian');
});A custom capability
A new capability can be added to an existing role:
register_activation_hook(__FILE__, function () {
get_role('editor')?->add_cap('manage_books');
get_role('administrator')?->add_cap('manage_books');
});if (!current_user_can('manage_books')) {
wp_die('You do not have permission.', 403);
}User data
Extra details are kept in user metadata:
update_user_meta($userId, 'favorite_genre', 'fantasy');
$genre = get_user_meta($userId, 'favorite_genre', true);Creating a user
$userId = wp_insert_user([
'user_login' => 'anna',
'user_email' => 'anna@example.com',
'user_pass' => wp_generate_password(),
'role' => 'author',
]);
if (is_wp_error($userId)) {
error_log($userId->get_error_message());
}WordPress hashes the password itself.
Restricted content
add_filter('the_content', function (string $content): string {
if (is_singular('book') && !is_user_logged_in()) {
return '<p>Log in to read the book.</p>';
}
return $content;
});Common mistake
A role check — in_array('editor', $user->roles) — breaks as soon as the site gets a custom role or a plugin changes the set of capabilities. Ask about the action: current_user_can('edit_others_posts'). And do not call add_role() on every load of the site: that is an extra database write on each request.