Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom capability types for custom post types doesn't automatically give administrator capabilities #79

Open
bignall opened this issue Oct 1, 2016 · 1 comment

Comments

@bignall
Copy link

bignall commented Oct 1, 2016

If a custom capability type is defined for a custom post type the administrator role doesn't automatically get capabilities for that capability type. I discovered this when I did a fresh install of wordpress that has only my plugin in it, so I must have some plugin that changes that behavior in my old installation.

So I propose adding to the CPT sub generator the following:

A helper class (mine has some other stuff in it too, but this is what's pertinent to this issue):

class Helper {  
    /**
     * Adds all capabilities for a capability type to a role
     * 
     * @param string|array $cap_type the capability type - array allows for specifying 
     *      singular and plural, otherwise plural is constructed by adding an 's' 
     *      to the string
     * @param string $role_name the role name (default: administrator)
     * @return WP_Role
     */
    static function add_caps( $cap_type, $role_name = 'administrator' ) {
        $role = get_role( $role_name );
        if ( is_array( $cap_type ) ) {
            $singular = $cap_type[0];
            $plural = $cap_type[1];
        } else {
            $singular = $cap_type;
            $plural = $cap_type . 's';
        }

        $role->add_cap( 'edit_' . $singular );
        $role->add_cap( 'edit_' . $plural );
        $role->add_cap( 'edit_other_' . $plural );
        $role->add_cap( 'publish_' . $plural );
        $role->add_cap( 'read_' . $singular );
        $role->add_cap( 'read_private_' . $plural );
        $role->add_cap( 'delete_' . $singular );

        return $role;
    }
}

In the generated post type class:

Define the capability type:

       /**
     * Capability type
     * 
     * @var string
     * @since 0.0.0
     */
    const CAPABILITY_TYPE = '<custom capability_type specified>';

In the constructor - parent::__construct args array:

                'capability_type' => self::CAPABILITY_TYPE,

In the hooks method:

        add_action( 'admin_init', array( $this, 'add_caps' ) );

And the add_caps method:

      /**
     * Add capabilities for our custom capability type
     * 
     * @since 0.0.0
     * @return void
     */
    public function add_caps() {
        Helper::add_caps( self::CAPABILITY_TYPE );
    }

I don't remember if the generator has an option for adding a custom capability_type in the args for the CPT, but if not that could be added and this code generated if a custom capability_type is selected.

This would give the user a starting point for their custom capabilities.

@bignall
Copy link
Author

bignall commented Oct 3, 2016

Well since I wrote this issue yesterday I learned a few things (I'm a newbie at wordpress development so please forgive my ignorance :). First I learned that the reason I hadn't seen this come up before was because I was using a multi-site installation and I was on a super admin account which automatically has all capabilities and I hadn't yet tested with any other accounts. Second, I learned that since assigning capabilities to roles is persistent it's better to do it during plugin activation and remove them during deactivation. So I'm modifying my suggestion with the following:

In the Helper class add a remove_caps method:

    /**
     * Removes all capabilities for a capability type to a role
     * 
     * @param string|array $cap_type the capability array allows for specifying 
     *      singular and plural, otherwise plural is constructed by adding an 's' 
     *      to the string
     * @param string $role_name the role name (default: administrator)
     * @return WP_Role
     */
    static function remove_caps( $cap_type, $role_name = 'administrator' ) {
        $role = get_role( $role_name );
        if ( is_array( $cap_type ) ) {
            $singular = $cap_type[0];
            $plural = $cap_type[1];
        } else {
            $singular = $cap_type;
            $plural = $cap_type . 's';
        }

        $role->remove_cap( 'edit_' . $singular );
        $role->remove_cap( 'edit_' . $plural );
        $role->remove_cap( 'edit_other_' . $plural );
        $role->remove_cap( 'publish_' . $plural );
        $role->remove_cap( 'read_' . $singular );
        $role->remove_cap( 'read_private_' . $plural );
        $role->remove_cap( 'delete_' . $singular );

        return $role;
    }

In the main plugin class _activate function add:

        <Custom post type class name>::activate();

In the _deactivate function:

             <Custom post type class name>::deactivate();

In the custom post type class:

    /**
     * Capability type
     * 
     * @var string
     * @since 0.0.0
     */
    const CAPABILITY_TYPE = '<capability type specified>';

    /**
     * Things to do on plugin activation
     * 
     * @since 0.0.0
     * @return void
     */
    static function activate() {
        self::add_caps();
    }

    /**
     * Things to do on plugin deactivation
     * 
     * @since 0.0.0
     * @return void
     */
    static function deactivate() {
        self::remove_caps();
    }

    /**
     * Add capabilities for our custom capability type
     * 
     * @since 0.0.0
     * @return void
     */
    static function add_caps() {
        Helper::add_caps( self::CAPABILITY_TYPE );
    }

    /**
     * Remove capabilities for our custom capability type
     * 
     * @since 0.0.0
     * @return void
     */
    static function remove_caps() {
        Helper::remove_caps( self::CAPABILITY_TYPE );
    }

And finally, to fill out the testing in tests/test-helper.php

class <prefix>_Helper_Test extends WP_UnitTestCase {

    function test_sample() {
        // replace this with some actual testing code
        $this->assertTrue( true );
    }

    function test_class_exists() {
        $this->assertTrue( class_exists( 'Helper') );
    }

    /**
     * Test Helper::add_caps
     */
    function test_add_caps() {
        Helper::add_caps('test');
        $role = get_role('administrator');
        $this->assertTrue( $role->has_cap('edit_test') );
        $this->assertTrue( $role->has_cap('edit_tests') );
        $this->assertTrue( $role->has_cap('edit_other_tests') );
        $this->assertTrue( $role->has_cap('publish_tests') );
        $this->assertTrue( $role->has_cap('read_test') );
        $this->assertTrue( $role->has_cap('read_private_tests') );
        $this->assertTrue( $role->has_cap('delete_test') );
    }

    function test_add_caps_role() {
        Helper::add_caps('test', 'author');
        $role = get_role('author');
        $this->assertTrue( $role->has_cap('edit_test') );
        $this->assertTrue( $role->has_cap('edit_tests') );
        $this->assertTrue( $role->has_cap('edit_other_tests') );
        $this->assertTrue( $role->has_cap('publish_tests') );
        $this->assertTrue( $role->has_cap('read_test') );
        $this->assertTrue( $role->has_cap('read_private_tests') );
        $this->assertTrue( $role->has_cap('delete_test') );
    }

    function test_add_caps_plural() {
        Helper::add_caps( array('test', 'testies') );
        $role = get_role('administrator');
        $this->assertTrue( $role->has_cap('edit_test') );
        $this->assertTrue( $role->has_cap('edit_testies') );
        $this->assertTrue( $role->has_cap('edit_other_testies') );
        $this->assertTrue( $role->has_cap('publish_testies') );
        $this->assertTrue( $role->has_cap('read_test') );
        $this->assertTrue( $role->has_cap('read_private_testies') );
        $this->assertTrue( $role->has_cap('delete_test') );
    }

    /**
     * Test Helper::remove_caps
     */
    function test_remove_caps() {
        Helper::add_caps('test');
        Helper::remove_caps('test');
        $role = get_role('administrator');
        $this->assertFalse( $role->has_cap('edit_test') );
        $this->assertFalse( $role->has_cap('edit_tests') );
        $this->assertFalse( $role->has_cap('edit_other_tests') );
        $this->assertFalse( $role->has_cap('publish_tests') );
        $this->assertFalse( $role->has_cap('read_test') );
        $this->assertFalse( $role->has_cap('read_private_tests') );
        $this->assertFalse( $role->has_cap('delete_test') );
    }

    function test_remove_caps_role() {
        Helper::add_caps('test', 'author' );
        Helper::remove_caps('test', 'author');
        $role = get_role('author');
        $this->assertFalse( $role->has_cap('edit_test') );
        $this->assertFalse( $role->has_cap('edit_tests') );
        $this->assertFalse( $role->has_cap('edit_other_tests') );
        $this->assertFalse( $role->has_cap('publish_tests') );
        $this->assertFalse( $role->has_cap('read_test') );
        $this->assertFalse( $role->has_cap('read_private_tests') );
        $this->assertFalse( $role->has_cap('delete_test') );
    }

    function test_remove_caps_plural() {
        Helper::add_caps( array('test', 'testies') );
        Helper::remove_caps( array('test', 'testies') );
        $role = get_role('administrator');
        $this->assertFalse( $role->has_cap('edit_test') );
        $this->assertFalse( $role->has_cap('edit_testies') );
        $this->assertFalse( $role->has_cap('edit_other_testies') );
        $this->assertFalse( $role->has_cap('publish_testies') );
        $this->assertFalse( $role->has_cap('read_test') );
        $this->assertFalse( $role->has_cap('read_private_testies') );
        $this->assertFalse( $role->has_cap('delete_test') );
    }

}

There are, of course, other possible implementations such as just putting the add_caps and remove_caps calls directly in the _activate/_deactivate plugin functions, but this is the one I chose to keep the capabilities with the post type that was the reason for adding them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant