Right level
Right access

Extending user roles & permissions
to support higher ed workflows

Cahir Castle Portcullis

Jonathan Brinley

Modern Tribe

Simple Editorial Process

basic editorial flowchart

More Advanced
Editorial Process

slightly more advanced editorial flowchart

Academic Department
Editorial Process

Regatta view from Ca'Foscari / Canaletto












Roles

  • Administrator
  • Editor
  • Author
  • Contributor
  • Subscriber
https://codex.wordpress.org/Roles_and_Capabilities

wp_usermeta

example of user roles in the database

WP_User::get_role_caps()


$this->allcaps = array();
foreach ( (array) $this->roles as $role ) {
  $the_role = $wp_roles->get_role( $role );
  $this->allcaps = array_merge( $this->allcaps, $the_role->capabilities );
}
			

Capabilities

populate_roles()

current_user_can()


add_options_page(
  'Super Secret Settings Page', // Page Title
  'Secret Settings',            // Menu Title
  'manage_secret_settings',     // Capability
  'secret-settings',            // Slug
  [ $this, 'render' ]           // Callback function
)
			

current_user_can( 'manage_secret_settings' )

current_user_can()


return call_user_func_array( array( $current_user, 'has_cap' ), $args );
			

WP_User::has_cap()


$caps = call_user_func_array( 'map_meta_cap', $args );
			

current_user_can( 'edit_post', $post_id )


case 'edit_post':
  if ( in_array( $post->post_status, [ 'publish', 'future' ], true ) ) {
    $caps[] = $post_type->cap->edit_published_posts;
  } elseif ( 'private' == $post->post_status ) {
    $caps[] = $post_type->cap->edit_private_posts;
  }
			

map_meta_cap()

diagram showing how meta caps map to caps

map_meta_cap()


add_filter( 'map_meta_cap', 'add_approval_requirements', 10, 4 );
function add_approval_requirement( $required_caps, $requested_cap, $user_id, $args ) {
  if ( $requested_cap == 'publish_post' ) {
    if ( ! is_approved( $args[0] ) ) {
      $required_caps[] = 'publish_unapproved_posts';
    }
  }
  return $required_caps;
}
			

add_role()


$role = add_role( 'theme_switcher' );

$role->add_cap( 'read' );
$role->add_cap( 'switch_themes' );
			

add_role()


$role = add_role( 'page_author' );

$role->add_cap( 'read' );
$role->add_cap( 'edit_pages' );
$role->add_cap( 'publish_pages' );
$role->add_cap( 'delete_pages' );
$role->add_cap( 'edit_published_pages' );
$role->add_cap( 'delete_published_pages' );
$role->add_cap( 'upload_files' );
$role->add_cap( 'level_1' );
			

WP_User_Query::prepare_query()


if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) {
  $qv['meta_key'] = $wpdb->get_blog_prefix( $blog_id ) . 'user_level';
  $qv['meta_value'] = 0;
  $qv['meta_compare'] = '!=';
}
			

add_action( 'admin_init', function() {
  if ( get_option( 'my_user_roles_version', 0 ) < 1 ) {
    my_register_roles_version_1();
    update_option( 'my_user_roles_version', 1 );
  }
} );
			

get_role()


$role = get_role( 'author' );

$role->add_cap( 'edit_pages' );
$role->add_cap( 'publish_pages' );
$role->add_cap( 'delete_pages' );
$role->remove_cap( 'edit_published_posts' );
$role->remove_cap( 'delete_published_posts' );
			

Custom Post Types

register_post_type( 'story' )

Custom Post Types

capability_type


register_post_type( 'story', [
	'label'           => __( 'Stories' ),
	// ... many more args
	'capability_type' => [ 'story', 'stories' ],
] );
			

current_user_can( 'edit_stories' )

Custom Post Types

capabilities


register_post_type( 'story', [
	'label'           => __( 'Stories' ),
	// ... many more args
	'capabilities' => [
		'publish_posts' => 'special_publish_posts',
	],
] );
			

current_user_can( 'special_publish_posts' )

Custom Post Types

map_meta_cap


register_post_type( 'story', [
	'label'           => __( 'Stories' ),
	// ... many more args
	'capability_type' => [ 'story', 'stories' ],
	'map_meta_cap'    => true,
] );
			

current_user_can( 'edit_post', $story_id )

map_meta_cap()

diagram showing how meta caps map to caps for a cpt

Custom Post Types

map_meta_cap


register_post_type( 'story', [
	'label'           => __( 'Stories' ),
	// ... many more args
	'capability_type' => [ 'story', 'stories' ],
	'map_meta_cap'    => false,
] );
			

add_filter( 'map_meta_cap', 'your_mapping_method', 10, 4 )

Custom Post Types


$caps = [
	'read',                    'edit_posts',
	'edit_others_posts',       'edit_private_posts',
	'edit_published_posts',    'publish_posts',
	'read_private_posts',      'delete_posts',
	'delete_private_posts',    'delete_published_posts',
	'delete_others_posts',     'create_posts',
];

$pto = get_post_type_object( 'story' );

foreach ( [ 'administrator', 'editor' ] as $role_name ) {
	$role = get_role( $role_name );
	foreach ( $caps as $cap ) {
		$role->add_cap( $pto->cap->{ $cap } );
	}
}
			

Super Admins


// Multisite super admin has all caps by definition,
// unless specifically denied.
if ( is_multisite() && is_super_admin( $this->ID ) ) {
	if ( in_array('do_not_allow', $caps) )
		return false;
	return true;
}
			
venn diagram showing two non-overlapping roles
venn diagram showing the overlap of conditions for a role to edit a story
venn diagram showing the overlap of conditions for a role to edit a story

user_has_cap


add_filter( 'user_has_cap', 'filter_user_caps', 10, 4 );
function filter_user_caps( $caps_the_user_has, $caps_the_user_needs, $args, $user ) {
	$originally_requested_cap = $args[0];
	$user_id = $args[1];
	$other_args = array_slice( $args, 2 ); // varies per $arg[0]
}
			

user_has_cap


foreach ( $groups as $group ) {
  $access_level = $group->get_permissions( $post_type );
  if (
       $access_level == Group::PERMISSION_EDITOR
    || $group->is_assigned_to_post( $post_id )
  ) {
    $user_caps[ $post_type_object->cap->edit_others_posts ]      = true;
    $user_caps[ $post_type_object->cap->read_private_posts ]     = true;
    $user_caps[ $post_type_object->cap->delete_posts ]           = true;
    $user_caps[ $post_type_object->cap->delete_private_posts ]   = true;
    $user_caps[ $post_type_object->cap->delete_published_posts ] = true;
    $user_caps[ $post_type_object->cap->delete_others_posts ]    = true;
    $user_caps[ $post_type_object->cap->edit_private_posts ]     = true;
  }
}
			

Jonathan Brinley

@jbrinley • jonathan@tri.be

Modern Tribe