modules/class-role-manager.php
<?php
/**
* View Admin As - Role Manager Module
*
* @author Jory Hogeveen <info@keraweb.nl>
* @package View_Admin_As
*/
if ( ! defined( 'VIEW_ADMIN_AS_DIR' ) ) {
die();
}
/**
* Add or remove roles and grant or deny them capabilities.
*
* Disable some PHPMD checks for this class.
* @SuppressWarnings(PHPMD.ExcessiveClassLength)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @todo Refactor to enable above checks?
*
* @author Jory Hogeveen <info@keraweb.nl>
* @package View_Admin_As
* @since 1.7.0
* @version 1.8.4
* @uses \VAA_View_Admin_As_Module Extends class
*/
final class VAA_View_Admin_As_Role_Manager extends VAA_View_Admin_As_Module
{
/**
* The single instance of the class.
*
* @since 1.7.0
* @static
* @var \VAA_View_Admin_As_Role_Manager
*/
private static $_instance = null;
/**
* Module key.
*
* @since 1.7.2
* @var string
*/
protected $moduleKey = 'role_manager';
/**
* Option key.
*
* @since 1.7.0
* @var string
*/
protected $optionKey = 'vaa_role_manager';
/**
* The WP_Roles object.
*
* @since 1.7.0
* @var \WP_Roles
*/
public $wp_roles = null;
/**
* Protected roles.
* These roles cannot be removed.
*
* @since 1.7.0
* @var string[]
*/
private $protected_roles = array();
/**
* Construct function.
* Protected to make sure it isn't declared elsewhere.
*
* @since 1.7.0
* @access protected
* @param \VAA_View_Admin_As $vaa The main VAA object.
*/
protected function __construct( $vaa ) {
self::$_instance = $this;
parent::__construct( $vaa );
/**
* Only allow module for admin users.
*
* @since 1.7.0
*/
if ( is_network_admin() || ! VAA_API::is_super_admin() ) {
return;
}
// Add this class to the modules in the main class.
$this->vaa->register_module( array(
'id' => $this->moduleKey,
'instance' => self::$_instance,
) );
// Load data.
$this->set_optionData( get_option( $this->get_optionKey() ) );
/**
* Checks if the management part of module should be enabled.
*
* @since 1.7.0
*/
$this->set_enable( (bool) $this->get_optionData( 'enable' ), false );
$this->init();
$this->add_action( 'vaa_view_admin_as_init', array( $this, 'vaa_init' ) );
$this->add_filter( 'view_admin_as_handle_ajax_' . $this->moduleKey, array( $this, 'ajax_handler' ), 10, 2 );
}
/**
* Init function for global functions (not user dependent).
*
* @since 1.7.0
* @access private
* @global \WP_Roles $wp_roles
* @return void
*/
private function init() {
// Check for the wp_roles() function in WP 4.3+.
if ( function_exists( 'wp_roles' ) ) {
$this->wp_roles = wp_roles();
} else {
global $wp_roles;
$this->wp_roles = $wp_roles;
}
if ( ! $this->wp_roles->use_db ) {
$this->set_enable( false );
$this->vaa->add_notice(
'role_manager_no_db',
array(
'type' => 'warning',
'message' => __( 'The Role Manager module was disabled because database storage for roles is disabled in WordPress.', VIEW_ADMIN_AS_DOMAIN ),
)
);
}
// Define protected roles.
$default_role = get_option( 'default_role' ); // Normally `subscriber`.
$this->protected_roles = array(
'administrator' => 'administrator',
$default_role => $default_role,
);
}
/**
* init function to store data from the main class and enable functionality based on the current view.
*
* @since 1.7.0
* @access public
* @return void
*/
public function vaa_init() {
// Enabling this module can only be done by a super admin.
if ( VAA_API::is_super_admin() ) {
// Add adminbar menu items in settings section.
$this->add_action( 'vaa_admin_bar_modules', array( $this, 'admin_bar_menu_modules' ), 10, 2 );
}
// Add adminbar menu items in role section.
if ( $this->is_enabled() ) {
// Show the admin bar node.
$this->add_action( 'vaa_admin_bar_menu', array( $this, 'admin_bar_menu' ), 6, 2 );
$this->add_action( 'vaa_admin_bar_caps_manager_before', array( $this, 'admin_bar_menu_caps' ), 6, 2 );
// Add custom capabilities.
if ( $this->store->get_view( 'caps' ) ) {
$this->add_filter( 'view_admin_as_get_capabilities', array( $this, 'filter_custom_view_capabilities' ), 11, 2 );
}
}
}
/**
* Data update handler (Ajax probably), called from main handler.
*
* Disable some PHPMD checks for this method.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @todo Refactor to enable above checks?
*
* @since 1.7.0
* @access public
* @param null $null Null.
* @param array $data The ajax data for this module.
* @return array|string|bool
*/
public function ajax_handler( $null, $data ) {
if ( ! $this->is_valid_ajax() ) {
return $null;
}
$success = false;
// Validate super admin.
if ( VAA_API::is_super_admin() ) {
if ( isset( $data['enable'] ) ) {
$success = $this->set_enable( (bool) $data['enable'] );
// Prevent further processing.
return $success;
}
}
// From here all features need this module enabled.
if ( ! $this->is_enabled() ) {
return $success;
}
// @since 1.7.3 Export.
if ( VAA_API::array_has( $data, 'export_roles', array( 'validation' => 'is_array' ) ) ) {
$content = $this->export_roles( $data['export_roles'] );
if ( is_array( $content ) ) {
$success = $this->ajax_data_popup( true, array(
'text' => esc_attr__( 'Copy code', VIEW_ADMIN_AS_DOMAIN ) . ': ',
'textarea' => wp_json_encode( $content ),
'filename' => esc_html__( 'Role manager', VIEW_ADMIN_AS_DOMAIN ) . '.json',
) );
} else {
$success = $this->ajax_data_notice( false, array( 'text' => $content ), 'error' );
}
}
// @since 1.7.3 Import.
if ( VAA_API::array_has( $data, 'import_roles', array( 'validation' => 'is_array' ) ) ) {
// $content format: array( 'text' => **text**, 'errors' => **error array** ).
$content = $this->import_roles( $data['import_roles'] );
if ( true === $content ) {
$success = true;
} else {
if ( is_array( $content ) ) {
$success = $this->ajax_data_popup( false, (array) $content, 'error' );
} else {
$success = $this->ajax_data_notice( false, array( 'text' => $content ), 'error' );
}
}
}
$options = array(
'apply_view_to_role' => array(
'validation' => 'is_array',
'values' => array( 'role' => '', 'capabilities' => '' ),
'callback' => 'save_role',
),
'save_role' => array(
'validation' => 'is_array',
'values' => array( 'role' => '', 'capabilities' => '' ),
'callback' => 'save_role',
),
'rename_role' => array(
'validation' => 'is_array',
'values' => array( 'role' => '', 'new_name' => '' ),
'callback' => 'rename_role',
),
'clone_role' => array(
'validation' => 'is_array',
'values' => array( 'role' => '', 'new_role' => '' ),
'callback' => 'clone_role',
),
'delete_role' => array(
'validation' => 'is_array',
'values' => array( 'role' => '', 'migrate' => false, 'new_role' => '' ),
'required' => array( 'role' => '' ),
'callback' => 'delete_role',
'combine_from' => 1,
),
);
foreach ( $options as $key => $val ) {
if ( VAA_API::array_has( $data, $key, array( 'validation' => $val['validation'] ) ) ) {
$val = wp_parse_args( $val, array(
'combine_from' => null,
) );
if ( ! isset( $val['required'] ) ) {
$val['required'] = $val['values'];
}
if ( 'is_array' === $val['validation'] && array_diff_key( $val['required'], $data[ $key ] ) ) {
$success = array(
'success' => false,
'data' => __( 'No valid data found', VIEW_ADMIN_AS_DOMAIN ),
);
} else {
$args = (array) $data[ $key ];
if ( VAA_API::array_has( $val, 'values', array( 'validation' => 'is_array' ) ) ) {
// Make sure the arguments are in the right order.
$args = array_merge( $val['values'], $args );
}
if ( is_numeric( $val['combine_from'] ) ) {
$args[ $val['combine_from'] ] = array_splice( $args, $val['combine_from'] );
}
$success = call_user_func_array( array( $this, $val['callback'] ), $args );
if ( is_string( $success ) ) {
$success = array(
'success' => false,
'data' => $success,
);
}
}
// @todo Maybe allow more settings to be applied at the same time?
break;
}
}
return $success;
}
/**
* Add all current caps view capabilities to an array of existing capabilities.
* Makes sure that if you add a custom capability to your view it is still visible after reload.
*
* @since 1.7.3
* @access public
* @param array $caps Current capabilities.
* @param array $args Function arguments.
* @return array
*/
public function filter_custom_view_capabilities( $caps = array(), $args = array() ) {
if ( isset( $args['vaa_role_manager'] ) && ! $args['vaa_role_manager'] ) {
return $caps;
}
$view_caps = $this->store->get_view( 'caps' );
if ( ! $view_caps ) {
return $caps;
}
$cap_keys = array_keys( $view_caps );
return array_merge( array_combine( $cap_keys, $cap_keys ), $caps );
}
/**
* Save a role.
* Can also add a new role when it doesn't exist.
*
* @since 1.7.0
* @access public
* @param string $role The role name (ID).
* @param array $capabilities The new role capabilities.
* @return mixed
*/
public function save_role( $role, $capabilities ) {
if ( ! is_string( $role ) || ! is_array( $capabilities ) ) {
return array(
'success' => false,
'data' => esc_html__( 'No valid data found', VIEW_ADMIN_AS_DOMAIN ),
);
}
// @see wp-includes/capabilities.php
$existing_role = get_role( $role );
// Build role name. (Only used for adding a new role).
$role_name = self::sanitize_role_name( $role );
// Sanitize capabilities.
$capabilities = array_map( 'boolval', (array) $capabilities );
if ( ! $existing_role ) {
// Sanitize role slug/key.
$role = self::sanitize_role_slug( $role );
// Recheck for an existing role.
$existing_role = get_role( $role );
}
if ( $existing_role ) {
// Update role.
$role = $existing_role;
$this->update_role_caps( $role, $capabilities );
} else {
// Add new role.
// Only leave granted capabilities.
// @todo Option to deny capability (like Members).
$capabilities = array_filter( $capabilities );
// @see wp-includes/capabilities.php
$new_role = add_role( $role, $role_name, $capabilities );
if ( $new_role ) {
return true;
}
// Very unlikely that this will happen but still..
return array(
'success' => false,
'data' => esc_html__( 'Role already exists', VIEW_ADMIN_AS_DOMAIN ),
);
}
return true;
}
/**
* Update a role with new capabilities.
*
* @since 1.7.0
* @access public
* @param \WP_Role $role The role object.
* @param array $capabilities The new role capabilities.
* @param string $method Update method.
* @return bool
*/
public function update_role_caps( $role, $capabilities, $method = '' ) {
if ( $role instanceof WP_Role && is_array( $capabilities ) ) {
switch ( (string) $method ) {
case 'append':
// Append any new cap keys.
$caps = array_merge( $capabilities, $role->capabilities );
break;
case 'merge':
// Ensure we have all the caps (even original ones).
$caps = array_merge( $role->capabilities, $capabilities );
break;
default:
// Set all current caps to false to make sure the new caps will overwrite everything.
$caps = array_map( '__return_false', $role->capabilities );
$caps = array_merge( $caps, $capabilities );
break;
}
// Update existing role.
foreach ( $caps as $cap => $grant ) {
// @todo Option to deny capability (like Members).
// @todo Do this in one call (prevent a lot of queries).
if ( ! empty( $caps[ $cap ] ) ) {
$role->add_cap( (string) $cap, (bool) $grant );
} else {
$role->remove_cap( (string) $cap );
}
}
return true;
}
return false;
}
/**
* Migrate users from one role to another.
*
* @since 1.7.6
* @access public
* @param string $role The role name.
* @param string $new_role The new role name.
* @return bool
*/
public function migrate_users( $role, $new_role ) {
if ( $role === $new_role ) {
return __( 'Cannot migrate to the same role', VIEW_ADMIN_AS_DOMAIN );
}
if ( ! $this->store->get_roles( $role ) || ! $this->store->get_roles( $new_role ) ) {
if ( get_role( $role ) || get_role( $new_role ) ) {
return __( 'Migrate users not allowed', VIEW_ADMIN_AS_DOMAIN );
}
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
/** @var \WP_User[] $users */
$users = get_users( array(
'role' => $role,
) );
if ( $users ) {
foreach ( $users as $user ) {
// See also: WP_User::set_role().
$user->add_role( $new_role );
$user->remove_role( $role );
}
}
return true;
}
/**
* Clone a role.
*
* @since 1.7.0
* @access public
* @param string $role The role name.
* @param string $new_role The new role name.
* @return mixed
*/
public function clone_role( $role, $new_role ) {
// Do not use WP's get_role() because one can only clone a role it's allowed to see.
$role = $this->store->get_roles( $role );
if ( $role ) {
$this->save_role( $new_role, $role->capabilities );
return true;
}
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
/**
* Rename a role.
*
* @todo Check https://core.trac.wordpress.org/ticket/40320.
*
* @since 1.7.0
* @access public
* @param string $role The source role slug/ID.
* @param string $new_name The new role label.
* @return bool|string
*/
public function rename_role( $role, $new_name ) {
$slug = $role;
// Do not use WP's get_role() because one can only clone a role it's allowed to see.
$role = $this->store->get_roles( $role );
if ( $role ) {
$new_name = self::sanitize_role_name( $new_name );
if ( empty( $new_name ) ) {
return __( 'No valid data found', VIEW_ADMIN_AS_DOMAIN );
}
$this->wp_roles->role_objects[ $slug ]->name = $new_name;
$this->wp_roles->role_names[ $slug ] = $new_name;
$this->wp_roles->roles[ $slug ]['name'] = $new_name;
update_option( $this->wp_roles->role_key, $this->wp_roles->roles );
return true;
}
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
/**
* Delete a role from the database.
*
* @since 1.7.0
* @access public
* @param string $role The role name.
* @param string $migrate_to_role Migrate users to a other role?
* @return bool|string
*/
public function delete_role( $role, $migrate_to_role = '' ) {
if ( ! $this->store->get_roles( $role ) ) {
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
if ( in_array( $role, $this->protected_roles, true ) ) {
return __( 'This role cannot be removed', VIEW_ADMIN_AS_DOMAIN );
}
if ( is_array( $migrate_to_role ) ) {
$migrate_to_role = wp_parse_args( $migrate_to_role, array(
'migrate' => false,
'new_role' => '',
) );
if ( $migrate_to_role['migrate'] ) {
$migrate_to_role = $migrate_to_role['new_role'];
} else {
$migrate_to_role = false;
}
}
if ( $migrate_to_role ) {
$success = $this->migrate_users( $role, $migrate_to_role );
if ( true !== $success ) {
return $success;
}
}
remove_role( $role );
return true;
}
/**
* Import role(s).
*
* @since 1.7.3
* @access public
* @param array $args {
* @type array $data The import data.
* @type bool $caps_data Does the data only contains capabilities?
* @type string $role The role to import the capabilities to.
* @type string $method The import/update method. See update_role_caps().
* }
* @return mixed
*/
public function import_roles( $args = array() ) {
$args = wp_parse_args( $args, array(
'data' => null,
'caps_data' => false,
'role' => '',
'method' => '',
) );
$data = $args['data'];
if ( ! $data || ! is_array( $data ) ) {
return __( 'No valid data found', VIEW_ADMIN_AS_DOMAIN );
}
if ( $args['caps_data'] ) {
if ( ! get_role( $args['role'] ) ) {
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
$data = array( $args['role'] => $data );
}
$error_list = array();
foreach ( $data as $role_key => $role_data ) {
$role = get_role( $role_key );
$capabilities = array_map( 'boolval', (array) $role_data );
if ( ! VAA_API::array_equal( $role_data, $capabilities, false ) ) {
$error_list[] = esc_attr__( 'No valid data found', VIEW_ADMIN_AS_DOMAIN ) . ': ' . (string) $role_key;
} else {
if ( $role ) {
$this->update_role_caps( $role, $capabilities, $args['method'] );
} else {
$this->save_role( $role_key, $capabilities );
}
}
}
if ( ! empty( $error_list ) ) {
// Close enough!
return array(
'text' => esc_attr__( 'Data imported but there were some errors', VIEW_ADMIN_AS_DOMAIN ) . ':',
'list' => $error_list,
);
}
return true; // Yay!
}
/**
* Export role(s).
*
* @since 1.7.3
* @access public
* @param array $args {
* @type string $role Role name or "__all__" for all roles.
* @type bool $caps_only Only export caps for a single role? `__all__` is not supported.
* }
* @return array|string
*/
public function export_roles( $args = array() ) {
$args = wp_parse_args( $args, array(
'role' => null,
'caps_only' => false,
) );
$role = $args['role'];
if ( ! $role ) {
return __( 'No valid data found', VIEW_ADMIN_AS_DOMAIN );
}
if ( $args['caps_only'] ) {
$role = $this->store->get_roles( $role );
if ( ! $role ) {
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
return $role->capabilities;
}
$data = array();
if ( '__all__' === $role ) {
$roles = $this->store->get_roles();
foreach ( $roles as $role_key => $role ) {
$data[ $role_key ] = $role->capabilities;
}
} else {
$role_key = $role;
$role = $this->store->get_roles( $role );
if ( ! $role ) {
return __( 'Role not found', VIEW_ADMIN_AS_DOMAIN );
}
$data[ $role_key ] = $role->capabilities;
}
return $data;
}
/**
* Convert role slug into a role name.
* Formats the name by default (capitalize and convert underscores to spaces).
*
* @since 1.7.2
* @access public
* @param string $role_name The role ID/slug.
* @param bool $format Apply string formatting.
* @return string
*/
public static function sanitize_role_name( $role_name, $format = true ) {
if ( ! is_string( $role_name ) ) {
return null;
}
$role_name = wp_strip_all_tags( $role_name );
if ( $format ) {
$role_name = str_replace( array( '_' ), ' ', $role_name );
$role_name = ucwords( $role_name );
}
return trim( $role_name );
}
/**
* Convert role name/label into a role slug.
* Similar to sanitize_key but it converts spaces and dashed to underscores.
*
* @since 1.7.1
* @access public
* @param string $role_name The role name/label.
* @return string
*/
public static function sanitize_role_slug( $role_name ) {
if ( ! is_string( $role_name ) ) {
return null;
}
$role_name = sanitize_title_with_dashes( $role_name );
$role_name = str_replace( array( ' ', '-' ), '_', $role_name );
$role_name = trim( $role_name, '_' );
//$role_name = sanitize_key( $role_name );
return trim( $role_name );
}
/**
* Add admin bar module setting items.
*
* @since 1.7.0
* @access public
* @see 'vaa_admin_bar_modules' action
*
* @param \WP_Admin_Bar $admin_bar The toolbar object.
* @param string $root The root item (vaa-settings).
* @return void
*/
public function admin_bar_menu_modules( $admin_bar, $root ) {
$root_prefix = $root . '-role-manager';
$admin_bar->add_node( array(
'id' => $root_prefix . '-enable',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_checkbox( array(
'name' => $root_prefix . '-enable',
'value' => $this->get_optionData( 'enable' ),
'compare' => true,
'label' => __( 'Enable role manager', VIEW_ADMIN_AS_DOMAIN ),
'description' => __( 'Add or remove roles and grant or deny them capabilities', VIEW_ADMIN_AS_DOMAIN ),
'auto_js' => array(
'setting' => $this->moduleKey,
'key' => 'enable',
'refresh' => true,
),
) ),
'href' => false,
'meta' => array(
'class' => 'auto-height',
),
) );
}
/**
* Add admin bar menu's.
*
* @since 1.7.0
* @access public
* @see 'vaa_admin_bar_menu' action
*
* @param \WP_Admin_Bar $admin_bar The toolbar object.
* @param string $root The root item (vaa).
* @return void
*/
public function admin_bar_menu( $admin_bar, $root ) {
$admin_bar->add_node( array(
'id' => $root . '-role-manager',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-id-alt' ) . __( 'Role manager', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'vaa-has-icon',
'tabindex' => '0',
),
) );
$root = $root . '-role-manager';
$admin_bar->add_node( array(
'id' => $root . '-docs',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-book-alt' ) . __( 'Documentation', VIEW_ADMIN_AS_DOMAIN ),
'href' => 'https://github.com/JoryHogeveen/view-admin-as/wiki/Role-Manager',
'meta' => array(
'class' => 'auto-height vaa-has-icon',
'target' => '_blank',
),
) );
$view_type = view_admin_as()->get_view_types( 'caps' );
if ( $view_type instanceof VAA_View_Admin_As_Caps ) {
$view_type_label = $view_type->get_label();
// This module requires the capability view type to enable all it's features.
if ( ! $view_type->is_enabled() ) {
$admin_bar->add_node( array(
'id' => $root . '-dependency',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . 'dependency-caps',
'label' => VAA_View_Admin_As_Form::do_icon( 'dashicons-warning' )
// Translators: %s stands for the translated view type label "Capabilities".
. sprintf( __( 'Please enable the "%s" view type to add/edit roles', VIEW_ADMIN_AS_DOMAIN ), $view_type_label ),
'auto_js' => array(
'setting' => 'setting',
'key' => 'view_types',
'values' => array(
'caps' => array(
'values' => array(
'enabled' => array(),
),
),
),
'refresh' => true,
),
'value' => true,
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
} else {
// Notice for capability editor location.
$admin_bar->add_node( array(
'id' => $root . '-intro',
'parent' => $root,
// Translators: %s stands for the translated view type label "Capabilities".
'title' => sprintf( __( 'You can add/edit roles under "%s"', VIEW_ADMIN_AS_DOMAIN ), $view_type_label ),
'href' => false,
) );
}
}
$this->admin_bar_menu_bulk_actions( $admin_bar, $root );
}
/**
* Add admin bar menu bulk actions.
*
* Disable some PHPMD checks for this method.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @todo Refactor to enable above checks?
*
* @since 1.7.0 Separated the tools from the main function.
* @access public
* @see \VAA_View_Admin_As_Role_Manager::admin_bar_menu()
*
* @param \WP_Admin_Bar $admin_bar The toolbar object.
* @param string $root The root item (vaa).
* @return void
*/
private function admin_bar_menu_bulk_actions( $admin_bar, $root ) {
$role_select_options = array(
'' => array(
'label' => ' --- ' . __( 'Select role', VIEW_ADMIN_AS_DOMAIN ) . ' --- ',
),
);
foreach ( $this->store->get_rolenames() as $role_key => $role_name ) {
// Add the default role names/keys for reference.
$desc = array();
$org_name = $this->store->get_rolenames( $role_key, false );
if ( $org_name !== $role_name ) {
$desc[] = $org_name;
}
if ( self::sanitize_role_slug( $org_name ) !== $role_key ) {
$desc[] = $role_key;
}
if ( $desc ) {
$role_name .= ' (' . implode( ', ', $desc ) . ')';
}
$role_select_options[ $role_key ] = array(
'value' => esc_attr( $role_key ),
'label' => esc_html( $role_name ),
);
}
/**
* @since 1.7.0 Apply current view capabilities to role.
*/
$icon = 'dashicons-hidden';
if ( VAA_API::is_view_active() ) {
$icon = 'dashicons-visibility';
}
$admin_bar->add_group( array(
'id' => $root . '-apply-view',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-apply-view-title',
'parent' => $root . '-apply-view',
'title' => VAA_View_Admin_As_Form::do_icon( $icon ) . __( 'Apply current view capabilities to role', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
if ( $this->store->get_selectedCaps() ) {
$admin_bar->add_node( array(
'id' => $root . '-apply-view-select',
'parent' => $root . '-apply-view',
'title' => VAA_View_Admin_As_Form::do_select(
array(
'name' => $root . '-apply-view-select',
'values' => $role_select_options,
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role',
),
) );
// @todo Find a way to get the current view server-side (view capabilities aren't available yet in ajax handling).
$admin_bar->add_node( array(
'id' => $root . '-apply-view-apply',
'parent' => $root . '-apply-view',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-apply-view-apply',
'label' => __( 'Apply', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary',
'attr' => array(
'vaa-view-caps' => wp_json_encode( $this->store->get_selectedCaps() ),
),
'auto_js' => array(
'setting' => $this->moduleKey,
'key' => 'apply_view_to_role',
'refresh' => false,
'values' => array(
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-apply-view-select select#' . $root . '-apply-view-select',
'parser' => '', // Default.
),
'capabilities' => array(
'attr' => 'vaa-view-caps',
'json' => true,
),
),
),
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
} else {
$admin_bar->add_node( array(
'id' => $root . '-apply-view-notice',
'parent' => $root . '-apply-view',
'title' => __( 'No view selected', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
) );
} // End if().
/**
* @since 1.7.1 Rename role.
*/
$admin_bar->add_group( array(
'id' => $root . '-rename',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-rename-title',
'parent' => $root . '-rename',
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-edit' ) . __( 'Rename role', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-rename-select',
'parent' => $root . '-rename',
'title' => VAA_View_Admin_As_Form::do_select(
array(
'name' => $root . '-rename-select',
'values' => $role_select_options,
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-rename-input',
'parent' => $root . '-rename',
'title' => VAA_View_Admin_As_Form::do_input(
array(
'name' => $root . '-rename-input',
'placeholder' => __( 'New role name', VIEW_ADMIN_AS_DOMAIN ),
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-input rename-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-rename-apply',
'parent' => $root . '-rename',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-rename-apply',
'label' => __( 'Apply', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary',
'auto_js' => array(
'setting' => $this->moduleKey,
'key' => 'rename_role',
'refresh' => true,
'values' => array(
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-rename-select select#' . $root . '-rename-select',
'parser' => '', // Default.
),
'new_name' => array(
'element' => '#wp-admin-bar-' . $root . '-rename-input input#' . $root . '-rename-input',
'parser' => '',
),
),
),
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
/**
* @since 1.7.0 Clone role.
*/
$admin_bar->add_group( array(
'id' => $root . '-clone',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-clone-title',
'parent' => $root . '-clone',
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-pressthis' ) . __( 'Clone role', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-clone-select',
'parent' => $root . '-clone',
'title' => VAA_View_Admin_As_Form::do_select(
array(
'name' => $root . '-clone-select',
'values' => $role_select_options,
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-clone-input',
'parent' => $root . '-clone',
'title' => VAA_View_Admin_As_Form::do_input(
array(
'name' => $root . '-clone-input',
'placeholder' => __( 'New role name', VIEW_ADMIN_AS_DOMAIN ),
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-input clone-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-clone-apply',
'parent' => $root . '-clone',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-clone-apply',
'label' => __( 'Apply', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary',
'auto_js' => array(
'setting' => $this->moduleKey,
'key' => 'clone_role',
'refresh' => true,
'values' => array(
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-clone-select select#' . $root . '-clone-select',
'parser' => '', // Default.
),
'new_role' => array(
'element' => '#wp-admin-bar-' . $root . '-clone-input input#' . $root . '-clone-input',
'parser' => '',
),
),
),
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
/**
* @since 1.5.0 Export actions.
*/
$role_export_options = $role_select_options;
$all_roles_option['__all__'] = array(
'value' => '__all__',
'label' => ' - ' . __( 'All roles', VIEW_ADMIN_AS_DOMAIN ) . ' - ',
);
array_splice( $role_export_options, 1, 0, $all_roles_option );
$admin_bar->add_group( array(
'id' => $root . '-export',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-export-roles',
'parent' => $root . '-export',
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-upload' )
. __( 'Export roles', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-export-roles-select',
'parent' => $root . '-export',
'title' => VAA_View_Admin_As_Form::do_select( array(
'name' => $root . '-export-roles-select',
'values' => $role_export_options,
) ),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role', // vaa-column-one-half vaa-column-last .
),
) );
$admin_bar->add_node( array(
'id' => $root . '-export-roles-caps-only',
'parent' => $root . '-export',
'title' => VAA_View_Admin_As_Form::do_checkbox( array(
'name' => $root . '-export-roles-caps-only',
'label' => __( 'Capabilities only', VIEW_ADMIN_AS_DOMAIN ),
'description' => __( 'Not compatible with the "All" option.', VIEW_ADMIN_AS_DOMAIN ),
) ),
'href' => false,
'meta' => array(
'class' => 'auto-height',
),
) );
$auto_js = array(
'setting' => $this->moduleKey,
'key' => 'export_roles',
'refresh' => false,
'values' => array(
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-export-roles-select select#' . $root . '-export-roles-select',
'parser' => '', // Default.
),
'caps_only' => array(
'element' => '#wp-admin-bar-' . $root . '-export-roles-caps-only input#' . $root . '-export-roles-caps-only',
'parser' => '', // Default.
'required' => false,
),
),
);
$admin_bar->add_node( array(
'id' => $root . '-export-roles-export',
'parent' => $root . '-export',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-export-roles-export',
'label' => __( 'Export', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-secondary',
'auto_js' => $auto_js,
) ) . ' ' . VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-export-roles-download',
'label' => __( 'Download', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-secondary',
'auto_js' => array_merge( $auto_js, array(
'download' => true,
) ),
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
/**
* @since 1.5.0 Import actions.
*/
$admin_bar->add_group( array(
'id' => $root . '-import',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-import-roles',
'parent' => $root . '-import',
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-download' )
. __( 'Import roles', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-import-roles-input',
'parent' => $root . '-import',
'title' => '<textarea id="' . $root . '-import-roles-input" name="role-manager-import-roles-input" placeholder="'
. esc_attr__( 'Paste code here or select a file below', VIEW_ADMIN_AS_DOMAIN ) . '"></textarea>',
'href' => false,
'meta' => array(
'class' => 'ab-vaa-textarea input-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-import-roles-file',
'parent' => $root . '-import',
'title' => VAA_View_Admin_As_Form::do_input( array(
'name' => $root . '-import-roles-file',
'type' => 'file',
'auto_js' => array(
'callback' => 'assign_file_content',
'param' => array(
'target' => '#wp-admin-bar-' . $root . '-import-roles-input textarea#' . $root . '-import-roles-input',
'element' => '#wp-admin-bar-' . $root . '-import-roles-file input#' . $root . '-import-roles-file',
),
),
'attr' => array(
'accept' => 'text/*,.json',
),
) ),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-file',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-import-roles-caps-only',
'parent' => $root . '-import',
'title' => VAA_View_Admin_As_Form::do_checkbox( array(
'name' => $root . '-import-roles-caps-only',
'label' => __( 'Capabilities only', VIEW_ADMIN_AS_DOMAIN ),
) ),
'href' => false,
'meta' => array(
'class' => 'auto-height',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-import-roles-select',
'parent' => $root . '-import',
'title' => VAA_View_Admin_As_Form::do_select( array(
'name' => $root . '-import-roles-select',
'values' => $role_select_options,
'attr' => array(
'vaa-condition' => true,
'vaa-condition-target' => '#' . $root . '-import-roles-caps-only',
),
) ),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role', // vaa-column-one-half vaa-column-last .
),
) );
$auto_js = array(
'setting' => $this->moduleKey,
'key' => 'import_roles',
'refresh' => true,
'values' => array(
'data' => array(
'element' => '#wp-admin-bar-' . $root . '-import-roles-input textarea#' . $root . '-import-roles-input',
'parser' => '', // Default.
'json' => true,
),
'caps_data' => array(
'element' => '#wp-admin-bar-' . $root . '-import-roles-caps-only input#' . $root . '-import-roles-caps-only',
'parser' => '', // Default.
'required' => false,
),
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-import-roles-select select#' . $root . '-import-roles-select',
'parser' => '', // Default.
'required' => false,
),
'method' => array(
'attr' => 'vaa-method',
),
),
);
$admin_bar->add_node( array(
'id' => $root . '-import-roles-import',
'parent' => $root . '-import',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-import-roles-import',
'label' => __( 'Import', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-secondary ab-vaa-showhide vaa-import-role-manager',
'auto_js' => $auto_js,
'attr' => array(
'vaa-method' => 'import',
'vaa-showhide' => 'p.vaa-import-role-manager-desc',
),
) ) . ' '
. VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-import-roles-import-merge',
'label' => __( 'Merge', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-secondary ab-vaa-showhide vaa-import-role-defaults',
'auto_js' => $auto_js,
'attr' => array(
'vaa-method' => 'merge',
'vaa-showhide' => 'p.vaa-import-role-manager-merge-desc',
),
) ) . ' '
. VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-import-roles-import-append',
'label' => __( 'Append', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-secondary ab-vaa-showhide vaa-import-role-manager',
'auto_js' => $auto_js,
'attr' => array(
'vaa-method' => 'append',
'vaa-showhide' => 'p.vaa-import-role-manager-append-desc',
),
) )
. VAA_View_Admin_As_Form::do_description(
__( 'Fully overwrite capabilities', VIEW_ADMIN_AS_DOMAIN ),
array( 'class' => 'vaa-import-role-manager-desc' )
)
. VAA_View_Admin_As_Form::do_description(
__( 'Merge and overwrite existing capabilities', VIEW_ADMIN_AS_DOMAIN ),
array( 'class' => 'vaa-import-role-manager-merge-desc' )
)
. VAA_View_Admin_As_Form::do_description(
__( 'Append without overwriting the existing capabilities', VIEW_ADMIN_AS_DOMAIN ),
array( 'class' => 'vaa-import-role-manager-append-desc' )
),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
/**
* @since 1.7.0 Delete role.
*/
$role_delete_options = array_diff_key( $role_select_options, $this->protected_roles );
$admin_bar->add_group( array(
'id' => $root . '-delete',
'parent' => $root,
'meta' => array(
'class' => 'ab-sub-secondary vaa-toggle-group vaa-sub-transparent',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-delete-title',
'parent' => $root . '-delete',
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-trash' ) . __( 'Delete role', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-delete-select',
'parent' => $root . '-delete',
'title' => VAA_View_Admin_As_Form::do_select(
array(
'name' => $root . '-delete-select',
'values' => $role_delete_options,
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-delete-migrate',
'parent' => $root . '-delete',
'title' => VAA_View_Admin_As_Form::do_checkbox( array(
'name' => $root . '-delete-migrate',
'label' => __( 'Migrate users to role', VIEW_ADMIN_AS_DOMAIN ),
) ),
'href' => false,
'meta' => array(
'class' => 'auto-height',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-delete-migrate-select',
'parent' => $root . '-delete',
'title' => VAA_View_Admin_As_Form::do_select( array(
'name' => $root . '-delete-migrate-select',
'values' => $role_select_options,
'attr' => array(
'vaa-condition' => true,
'vaa-condition-target' => '#' . $root . '-delete-migrate',
),
) ),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select select-role',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-delete-apply',
'parent' => $root . '-delete',
'title' => VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-delete-apply',
'label' => __( 'Delete', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary',
'auto_js' => array(
'setting' => $this->moduleKey,
'key' => 'delete_role',
'confirm' => true,
'refresh' => true,
'values' => array(
'role' => array(
'element' => '#wp-admin-bar-' . $root . '-delete-select select#' . $root . '-delete-select',
'parser' => '', // Default.
),
'migrate' => array(
'element' => '#wp-admin-bar-' . $root . '-delete-migrate input#' . $root . '-delete-migrate',
'parser' => '', // Default.
'required' => false,
),
'new_role' => array(
'element' => '#wp-admin-bar-' . $root . '-delete-migrate-select select#' . $root . '-delete-migrate-select',
'parser' => '', // Default.
'required' => false,
),
),
),
) ),
'href' => false,
'meta' => array(
'class' => 'vaa-button-container',
),
) );
}
/**
* Add admin bar items to the capability node.
*
* Disable some PHPMD checks for this method.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @todo Refactor to enable above checks?
*
* @since 1.7.0
* @access public
* @see 'vaa_admin_bar_caps_manager_before' action
*
* @param \WP_Admin_Bar $admin_bar The toolbar object.
* @param string $root The root item (vaa).
* @return void
*/
public function admin_bar_menu_caps( $admin_bar, $root ) {
$admin_bar->add_group( array(
'id' => $root . '-role-manager',
'parent' => $root,
) );
$root = $root . '-role-manager';
$admin_bar->add_node( array(
'id' => $root . '-title',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_icon( 'dashicons-id-alt' ) . __( 'Role manager', VIEW_ADMIN_AS_DOMAIN ),
'href' => false,
'meta' => array(
'class' => 'ab-bold vaa-has-icon ab-vaa-toggle',
'tabindex' => '0',
),
) );
$caps = $this->store->get_curUser()->allcaps;
if ( VAA_API::is_view_active() ) {
$caps = $this->store->get_selectedCaps();
}
$role_select_options = array(
'' => array(
'label' => ' --- ' . __( 'Add/Edit role', VIEW_ADMIN_AS_DOMAIN ) . ' --- ',
'attr' => array(
'data-caps' => wp_json_encode( $caps ),
),
),
'__new__' => array(
'value' => '__new__',
'label' => ' ++ ' . __( 'New role', VIEW_ADMIN_AS_DOMAIN ) . ' ++ ',
),
);
foreach ( $this->store->get_roles() as $role_key => $role ) {
$role_select_options[ $role_key ] = array(
'compare' => esc_attr( $role_key ),
'label' => $this->store->get_rolenames( $role_key ),
'attr' => array(
'data-caps' => wp_json_encode( $role->capabilities ),
),
);
}
$admin_bar->add_node( array(
'id' => $root . '-edit-role',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_select(
array(
'name' => $root . '-edit-role',
'values' => $role_select_options,
)
) . VAA_View_Admin_As_Form::do_button(
array(
'name' => $root . '-save-role',
'label' => __( 'Save role', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary input-overlay',
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-select',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-new-role',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_input(
array(
'name' => $root . '-new-role',
'placeholder' => __( 'New role name', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'ab-vaa-conditional',
'attr' => array(
'vaa-condition' => '__new__',
'vaa-condition-target' => '#' . $root . '-edit-role',
),
)
),
'href' => false,
'meta' => array(
'class' => 'ab-vaa-input vaa-hidden',
),
) );
$admin_bar->add_node( array(
'id' => $root . '-new-cap',
'parent' => $root,
'title' => VAA_View_Admin_As_Form::do_input( array(
'name' => $root . '-new-cap',
'placeholder' => esc_attr__( 'Add new custom capability', VIEW_ADMIN_AS_DOMAIN ),
) ) . VAA_View_Admin_As_Form::do_button( array(
'name' => $root . '-add-cap',
'label' => __( 'Add', VIEW_ADMIN_AS_DOMAIN ),
'class' => 'button-primary input-overlay',
) )
. '<div id="' . $root . '-cap-template" style="display: none;"><div class="ab-item vaa-cap-item">'
. VAA_View_Admin_As_Form::do_checkbox( array(
'name' => 'vaa_cap_vaa_new_item',
'value' => true,
'compare' => true,
'checkbox_value' => 'vaa_new_item',
'label' => 'vaa_new_item',
'removable' => true,
) ) . '</div></div>',
'href' => false,
'meta' => array(
'class' => 'ab-vaa-input',
),
) );
}
/**
* Main Instance.
*
* Ensures only one instance of this class is loaded or can be loaded.
*
* @since 1.5.0
* @access public
* @static
* @param \VAA_View_Admin_As $caller The referrer class.
* @return \VAA_View_Admin_As_Role_Manager $this
*/
public static function get_instance( $caller = null ) {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self( $caller );
}
return self::$_instance;
}
} // End class VAA_View_Admin_As_Role_Manager.