File: //usr/local/wp/vendor/wp-cli/role-command/src/Role_Command.php
<?php
use WP_CLI\Utils;
use WP_CLI\Formatter;
/**
* Manages user roles, including creating new roles and resetting to defaults.
*
* See references for [Roles and Capabilities](https://codex.wordpress.org/Roles_and_Capabilities) and [WP User class](https://codex.wordpress.org/Class_Reference/WP_User).
*
* ## EXAMPLES
*
* # List roles.
* $ wp role list --fields=role --format=csv
* role
* administrator
* editor
* author
* contributor
* subscriber
*
* # Check to see if a role exists.
* $ wp role exists editor
* Success: Role with ID 'editor' exists.
*
* # Create a new role.
* $ wp role create approver Approver
* Success: Role with key 'approver' created.
*
* # Delete an existing role.
* $ wp role delete approver
* Success: Role with key 'approver' deleted.
*
* # Reset existing roles to their default capabilities.
* $ wp role reset administrator author contributor
* Success: Reset 3/3 roles.
*
* @package wp-cli
*/
class Role_Command extends WP_CLI_Command {
/**
* List of available fields.
*
* @var array
*/
private $fields = [ 'name', 'role' ];
/**
* Default roles as provided by WordPress Core.
*
* @var array
*/
private $roles = [ 'administrator', 'editor', 'author', 'contributor', 'subscriber' ];
/**
* Lists all roles.
*
* ## OPTIONS
*
* [--fields=<fields>]
* : Limit the output to specific object fields.
*
* [--field=<field>]
* : Prints the value of a single field.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - count
* - yaml
* ---
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each role:
*
* * name
* * role
*
* There are no optional fields.
*
* ## EXAMPLES
*
* # List roles.
* $ wp role list --fields=role --format=csv
* role
* administrator
* editor
* author
* contributor
* subscriber
*
* @subcommand list
*/
public function list_( $args, $assoc_args ) {
global $wp_roles;
$output_roles = array();
foreach ( $wp_roles->roles as $key => $role ) {
$output_role = new stdClass();
$output_role->name = $role['name'];
$output_role->role = $key;
$output_roles[] = $output_role;
}
$formatter = new Formatter( $assoc_args, $this->fields );
$formatter->display_items( $output_roles );
}
/**
* Checks if a role exists.
*
* Exits with return code 0 if the role exists, 1 if it does not.
*
* ## OPTIONS
*
* <role-key>
* : The internal name of the role.
*
* ## EXAMPLES
*
* # Check if a role exists.
* $ wp role exists editor
* Success: Role with ID 'editor' exists.
*/
public function exists( $args ) {
global $wp_roles;
if ( ! in_array( $args[0], array_keys( $wp_roles->roles ), true ) ) {
WP_CLI::error( "Role with ID '{$args[0]}' does not exist." );
}
WP_CLI::success( "Role with ID '{$args[0]}' exists." );
}
/**
* Creates a new role.
*
* ## OPTIONS
*
* <role-key>
* : The internal name of the role.
*
* <role-name>
* : The publicly visible name of the role.
*
* [--clone=<role>]
* : Clone capabilities from an existing role.
*
* ## EXAMPLES
*
* # Create role for Approver.
* $ wp role create approver Approver
* Success: Role with key 'approver' created.
*
* # Create role for Product Administrator.
* $ wp role create productadmin "Product Administrator"
* Success: Role with key 'productadmin' created.
*/
public function create( $args, $assoc_args ) {
global $wp_roles;
self::persistence_check();
$role_key = array_shift( $args );
$role_name = array_shift( $args );
if ( empty( $role_key ) || empty( $role_name ) ) {
WP_CLI::error( "Can't create role, insufficient information provided." );
}
$capabilities = false;
if ( ! empty( $assoc_args['clone'] ) ) {
$role_obj = $wp_roles->get_role( $assoc_args['clone'] );
if ( ! $role_obj ) {
WP_CLI::error( "'{$assoc_args['clone']}' role not found." );
}
$capabilities = array_keys( $role_obj->capabilities );
}
if ( add_role( $role_key, $role_name ) ) {
if ( ! empty( $capabilities ) ) {
$role_obj = $wp_roles->get_role( $role_key );
foreach ( $capabilities as $cap ) {
$role_obj->add_cap( $cap );
}
WP_CLI::success( "Role with key '{$role_key}' created. Cloned capabilities from '{$assoc_args['clone']}'." );
} else {
WP_CLI::success( "Role with key '{$role_key}' created." );
}
} else {
WP_CLI::error( "Role couldn't be created." );
}
}
/**
* Deletes an existing role.
*
* ## OPTIONS
*
* <role-key>
* : The internal name of the role.
*
* ## EXAMPLES
*
* # Delete approver role.
* $ wp role delete approver
* Success: Role with key 'approver' deleted.
*
* # Delete productadmin role.
* $ wp role delete productadmin
* Success: Role with key 'productadmin' deleted.
*/
public function delete( $args ) {
global $wp_roles;
self::persistence_check();
$role_key = array_shift( $args );
if ( empty( $role_key ) || ! isset( $wp_roles->roles[ $role_key ] ) ) {
WP_CLI::error( 'Role key not provided, or is invalid.' );
}
remove_role( $role_key );
// Note: remove_role() doesn't indicate success or otherwise, so we have to
// check ourselves
if ( ! isset( $wp_roles->roles[ $role_key ] ) ) {
WP_CLI::success( "Role with key '{$role_key}' deleted." );
} else {
WP_CLI::error( "Role with key '{$role_key}' could not be deleted." );
}
}
/**
* Resets any default role to default capabilities.
*
* Uses WordPress' `populate_roles()` function to put one or more
* roles back into the state they were at in the a fresh
* WordPress install. Removes any capabilities that were added,
* and restores any capabilities that were removed. Custom roles
* are not affected.
*
* ## OPTIONS
*
* [<role-key>...]
* : The internal name of one or more roles to reset.
*
* [--all]
* : If set, all default roles will be reset.
*
* ## EXAMPLES
*
* # Reset three roles.
* $ wp role reset administrator author contributor
* Restored 1 capability to and removed 0 capabilities from 'administrator' role.
* No changes necessary for 'author' role.
* No changes necessary for 'contributor' role.
* Success: 1 of 3 roles reset.
*
* # Reset a custom role.
* $ wp role reset custom_role
* Custom role 'custom_role' not affected.
* Error: Must specify a default role to reset.
*
* # Reset all default roles.
* $ wp role reset --all
* Success: All default roles reset.
*/
public function reset( $args, $assoc_args ) {
self::persistence_check();
if ( ! Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) {
WP_CLI::error( 'Role key not provided, or is invalid.' );
}
if ( ! function_exists( 'populate_roles' ) ) {
require_once ABSPATH . 'wp-admin/includes/schema.php';
}
global $wp_roles;
$all_roles = array_keys( $wp_roles->roles );
$preserve_args = $args;
// Get our default roles.
$default_roles = $this->roles;
$preserve = $this->roles;
$before = [];
if ( Utils\get_flag_value( $assoc_args, 'all' ) ) {
foreach ( $default_roles as $role ) {
$before[ $role ] = get_role( $role );
remove_role( $role );
$args[] = $role;
}
populate_roles();
$not_affected_roles = array_diff( $all_roles, $default_roles );
if ( ! empty( $not_affected_roles ) ) {
foreach ( $not_affected_roles as $not_affected_role ) {
WP_CLI::log( "Custom role '{$not_affected_role}' not affected." );
}
}
} else {
foreach ( $args as $k => $role_key ) {
$key = array_search( $role_key, $default_roles, true );
if ( false !== $key ) {
unset( $preserve[ $key ] );
$before[ $role_key ] = get_role( $role_key );
remove_role( $role_key );
} else {
unset( $args[ $k ] );
}
}
$not_affected_roles = array_diff( $preserve_args, $default_roles );
if ( ! empty( $not_affected_roles ) ) {
foreach ( $not_affected_roles as $not_affected_role ) {
WP_CLI::log( "Custom role '{$not_affected_role}' not affected." );
}
}
// No roles were unset, bail.
if ( count( $default_roles ) === count( $preserve ) ) {
WP_CLI::error( 'Must specify a default role to reset.' );
}
// For the roles we're not resetting.
foreach ( $preserve as $k => $role ) {
/* save roles
* if get_role is null
* save role name for re-removal
*/
$roleobj = get_role( $role );
$preserve[ $k ] = is_null( $roleobj ) ? $role : $roleobj;
remove_role( $role );
}
// Put back all default roles and capabilities.
populate_roles();
// Restore the preserved roles.
foreach ( $preserve as $k => $roleobj ) {
// Re-remove after populating.
if ( is_a( $roleobj, 'WP_Role' ) ) {
remove_role( $roleobj->name );
add_role( $roleobj->name, ucwords( $roleobj->name ), $roleobj->capabilities );
} else {
// When not an object, that means the role didn't exist before.
remove_role( $roleobj );
}
}
}
$num_reset = 0;
$args = array_unique( $args );
$num_to_reset = count( $args );
foreach ( $args as $role_key ) {
$after[ $role_key ] = get_role( $role_key );
// phpcs:ignore Universal.Operators.StrictComparisons -- Object instances won't be same, strict check will fail here.
if ( $after[ $role_key ] != $before[ $role_key ] ) {
++$num_reset;
$before_capabilities = isset( $before[ $role_key ] ) ? $before[ $role_key ]->capabilities : [];
$restored_cap = array_diff_key( $after[ $role_key ]->capabilities, $before_capabilities );
$removed_cap = array_diff_key( $before_capabilities, $after[ $role_key ]->capabilities );
$restored_cap_count = count( $restored_cap );
$removed_cap_count = count( $removed_cap );
$restored_text = ( 1 === $restored_cap_count ) ? '%d capability' : '%d capabilities';
$removed_text = ( 1 === $removed_cap_count ) ? '%d capability' : '%d capabilities';
$message = "Restored {$restored_text} to and removed {$removed_text} from '%s' role.";
WP_CLI::log( sprintf( $message, $restored_cap_count, $removed_cap_count, $role_key ) );
} else {
WP_CLI::log( "No changes necessary for '{$role_key}' role." );
}
}
if ( $num_reset ) {
if ( 1 === count( $args ) ) {
WP_CLI::success( 'Role reset.' );
} else {
WP_CLI::success( "{$num_reset} of {$num_to_reset} roles reset." );
}
} elseif ( 1 === count( $args ) ) {
WP_CLI::success( 'Role didn\'t need resetting.' );
} else {
WP_CLI::success( 'No roles needed resetting.' );
}
}
/**
* Assert that the roles are persisted to the database.
*
* @throws \WP_CLI\ExitException If the roles are not persisted to the
* database.
*/
private static function persistence_check() {
global $wp_roles;
if ( ! $wp_roles->use_db ) {
WP_CLI::error( 'Role definitions are not persistent.' );
}
}
}