Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced WordPress Development: Capabilities (WordCamp Philly 2011)

nacin
November 05, 2011

Advanced WordPress Development: Capabilities (WordCamp Philly 2011)

Advanced WordPress Development: Capabilities, and tweaking them through unconventional means. WordCamp Philadelphia 2011.

nacin

November 05, 2011
Tweet

Other Decks in Technology

Transcript

  1. WordCamp Philadelphia 2011
    November 5, 2011

    View Slide

  2. Andrew Nacin
    Core Developer of WordPress
    Tech Ninja at Audrey Capital
    [email protected]
    @nacin on Twitter

    View Slide

  3. Advanced WordPress
    Development
    @nacin

    View Slide

  4. Capabilities
    and tweaking them through
    unconventional means
    @nacin

    View Slide

  5. What do you know?
    @nacin

    View Slide

  6. Modifying Roles and
    Capabilities in the DB
    get_role( ), add_role( ),
    add_cap( ), remove_cap( ), etc.
    @nacin

    View Slide

  7. Boring, and has
    drawbacks.
    @nacin

    View Slide

  8. Who has heard of
    current_user_can( )?
    What happens when you call it?
    @nacin

    View Slide

  9. current_user_can( ) :
    $user = wp_get_current_user( );
    return $user->has_cap( $args );
    (Core functions are often simplified in this talk.)
    @nacin

    View Slide

  10. What don't you know?
    @nacin

    View Slide

  11. Every call goes through
    very powerful filters
    user_has_cap
    map_meta_cap
    @nacin

    View Slide

  12. Why should I care?
    What can I use them for?
    Let's dig in.
    @nacin

    View Slide

  13. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $args );
    // Multisite super admin has all caps, unless denied.
    if ( is_multisite( ) && is_super_admin( $user_id ) )
    return ! in_array( 'do_not_allow', $required_caps );
    $caps_user_has = apply_filters( 'user_has_cap',
    $caps_user_has, $required_caps, $args );
    // Must have all required caps
    foreach ( $caps_user_has as $cap )
    if ( empty( $caps_user_has[ $cap ] ) )
    return false;
    return true;

    View Slide

  14. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $args );

    View Slide

  15. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $args );
    // Multisite super admin has all caps, unless denied.
    if ( is_multisite( ) && is_super_admin( $user_id ) )
    return ! in_array( 'do_not_allow', $required_caps );

    View Slide

  16. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $args );
    // Multisite super admin has all caps, unless denied.
    if ( is_multisite( ) && is_super_admin( $user_id ) )
    return ! in_array( 'do_not_allow', $required_caps );
    $caps_user_has = apply_filters( 'user_has_cap',
    $caps_user_has, $required_caps, $args );

    View Slide

  17. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $caps );
    // Multisite super admin has all caps, unless denied.
    if ( is_multisite( ) && is_super_admin( $user_id ) )
    return ! in_array( 'do_not_allow', $required_caps );
    $caps_user_has = apply_filters( 'user_has_cap'
    $caps_user_has, $required_caps, $args );
    // Must have all required caps
    foreach ( $caps_user_has as $cap )
    if ( empty( $caps_user_has[ $cap ] ) )
    return false;
    return true;

    View Slide

  18. If you can edit pages, you can edit widgets:
    add_filter( 'user_has_cap', function( $caps ) {
    if ( ! empty( $caps[ 'edit_pages' ] )
    $caps[ 'edit_theme_options' ] = true;
    return $caps;
    } );

    View Slide

  19. Give secondary "administrators" less control:
    add_filter( 'user_has_cap',
    function( $caps, $required_caps, $args ) {
    $user_id = $args[1];
    $user = new WP_User( $user_id );
    if ( $user->user_email !=
    get_option( 'admin_email' ) )
    $caps[ 'manage_options' ] = false;
    return $caps;
    },
    10, 3 );

    View Slide

  20. Chapter 2
    What's a meta capability?
    @nacin

    View Slide

  21. You have standard,
    primitive capabilities
    @nacin

    View Slide

  22. edit_posts
    — Contributors, Authors, Editors have this.
    @nacin

    View Slide

  23. edit_posts
    — Contributors, Authors, Editors have this.
    edit_published_posts
    — Is the post published?
    — Authors, Editors have this.
    @nacin

    View Slide

  24. edit_posts
    — Contributors, Authors, Editors have this.
    edit_published_posts
    — Is the post published?
    — Authors, Editors have this.
    edit_private_posts
    — Is the post private?
    — Editors have this.
    @nacin

    View Slide

  25. edit_posts
    — Contributors, Authors, Editors have this.
    edit_published_posts
    — Is the post published?
    — Authors, Editors have this.
    edit_private_posts
    — Is the post private?
    — Editors have this.
    edit_others_posts
    — Are you not the post author?
    — Editors have this.
    @nacin

    View Slide

  26. Meta caps are singular, and roles
    should never be assigned them.
    — edit_post, delete_post, read_post
    —delete_user, edit_user
    @nacin

    View Slide

  27. Meta caps are singular, and roles
    should never be assigned them.
    — edit_post, delete_post, read_post
    —delete_user, edit_user
    Primitive caps are plural, and roles
    have these.
    — edit_posts, edit_published_posts
    —delete_users, edit_users
    @nacin

    View Slide

  28. Meta caps get converted to
    primitive caps based on
    context.
    @nacin

    View Slide

  29. If you do:
    current_user_can( 'edit_post', $post_id )
    meta capability ^ context ^
    map_meta_cap() translates this to, e.g.:
    array( 'edit_posts' )
    If the post is published and not by you:
    array( 'edit_published_posts',
    'edit_others_posts' )
    @nacin

    View Slide

  30. map_meta_cap( ) only
    determines what
    capabilities the user needs.
    @nacin

    View Slide

  31. WP_User's has_cap( ) method:
    $required_caps = map_meta_cap( $args );
    // Multisite super admin has all caps, unless denied.
    if ( is_multisite( ) && is_super_admin( $user_id ) )
    return ! in_array( 'do_not_allow', $required_caps );
    $caps_user_has = apply_filters( 'user_has_cap',
    $caps_user_has, $required_caps, $args );
    // Must have all required caps
    foreach ( $caps_user_has as $cap )
    if ( empty( $caps_user_has[ $cap ] ) )
    return false;
    return true;

    View Slide

  32. Don't let anyone delete users from the UI:
    // apply_filters( 'map_meta_cap', $required_caps,
    $queried_cap, $user_id, $args );
    add_filter( 'map_meta_cap',
    function( $required_caps, $queried_cap ) {
    if ( 'delete_user' == $queried_cap ||
    'delete_users' == $queried_cap )
    $required_caps[] = 'do_not_allow';
    return $required_caps;
    }, 10, 2 );

    View Slide

  33. Only administrators can delete published posts:
    add_filter( 'map_meta_cap',
    function( $required_caps, $queried_cap ) {
    if ( 'delete_post' == $queried_cap )
    $required_caps[] = 'manage_options';
    return $required_caps;
    }, 10, 2 );

    View Slide

  34. Don't allow file changes via the UI:
    add_filter( 'map_meta_cap',
    function( $required_caps, $queried_cap ) {
    if ( in_array( $queried_cap, array(
    'edit_themes', 'edit_plugins',
    'update_themes', 'update_plugins',
    'install_themes', 'install_plugins',
    'update_core' ) )
    $required_caps[] = 'do_not_allow';
    return $required_caps;
    }, 10, 2 );

    View Slide

  35. (That is built into core, by the way...)
    // deny edit_themes, edit_plugins
    define( 'DISALLOW_FILE_EDIT', true );
    // deny all file changes
    define( 'DISALLOW_FILE_MODS', true );

    View Slide

  36. Require editors to approve posts:
    add_filter( 'map_meta_cap',
    function( $required_caps, $queried_cap ) {
    if ( $queried_cap == 'publish_posts' )
    $required_caps[] = 'edit_others_posts';
    return $required_caps;
    }, 10, 2 );

    View Slide

  37. map_meta_cap( ) is an
    extensive, powerful
    function.
    wp-includes/capabilities.php
    @nacin

    View Slide

  38. // mapping for current_user_can( 'edit_post', $post_id )
    case 'edit_post' :
    if ( $user_id == $post_author->ID ) { // Are we the author?
    if ( 'publish' == $post->post_status )
    $caps[] = $post_type->cap->edit_published_posts;
    else
    $caps[] = $post_type->cap->edit_posts;
    } else {
    // The user is trying to edit someone else's post.
    $caps[] = $post_type->cap->edit_others_posts;
    // If the post is published, extra caps are required.
    if ( 'publish' == $post->post_status )
    $caps[] = $post_type->cap->edit_published_posts;
    elseif ( 'private' == $post->post_status )
    $caps[] = $post_type->cap->edit_private_posts;
    }
    . . .
    return apply_filters( 'map_meta_cap', $caps, ... );

    View Slide

  39. $post_type->cap->edit_posts;
    Oooh, post types.
    @nacin

    View Slide

  40. Chapter 3
    How can you leverage
    capabilities with post types?
    @nacin

    View Slide

  41. Where you are leveraging the same
    *_posts capabilities:
    register_post_type( 'book', array(
    . . .
    'capability_type' => 'post',
    // Implied for 'post' and 'page' :
    'map_meta_cap' => true,
    . . .
    );

    View Slide

  42. Where you are assigning *_books
    capabilities to users:
    register_post_type( 'book', array(
    . . .
    'capability_type' => 'book',
    // Map edit_book (and read, delete)
    'map_meta_cap' => true,
    . . .
    );

    View Slide

  43. Hey, plural forms too:
    register_post_type( 'story', array(
    . . .
    'capability_type' =>
    array( 'story', 'stories' )
    // Map edit_story (and read,
    delete) to edit_stories, etc.
    'map_meta_cap' => true,
    );

    View Slide

  44. Go crazy with full customizations:
    register_post_type( 'article', array(
    . . .
    'capabilities' => array(
    // primitive capabilities assigned to users
    'read' => 'read',
    'edit_posts' => 'edit_articles',
    'publish_posts' => 'edit_articles',
    // lock them down once published
    'edit_published_posts' => 'do_not_allow',
    'delete_posts' => 'do_not_allow',
    'delete_others_posts' => 'do_not_allow',
    'delete_published_posts' => 'do_not_allow',
    // meta capabilities
    'edit_post' => . . .

    View Slide

  45. Review
    user_has_cap filter
    Explicitly granting or denying users
    a capability.
    map_meta_cap filter
    Translating a capability into the
    capabilities required, depending on
    the context.
    @nacin

    View Slide

  46. And finally:
    register_post_type( )
    You can customize mapping and
    capabilities when registering a post type.
    map_meta_cap( )
    Read it. It's worth it.
    get_post_type_capabilities( )
    Read the documentation in
    wp-includes/post.php.
    @nacin

    View Slide

  47. Thanks! Questions?
    @nacin

    View Slide