• WordPress capabilities: How to restrict Add New while allowing Edit

    By default, when you register a post type, WordPress sets the value of the required capability for Add New to that used for Edit (i.e. All Posts). If you want to prevent users from using Add New content but still allow them to Edit content then you will need to assign different capability names.

    But that’s not all. With the current version of WordPress ( 4.0 ) you need to implement a small workaround.

    Overview

    I have a requirement where certain users are allowed to make changes to the content they own, but not to create any new content. In this example we see the All content page displayed to a logged in Subscriber. The post type is “oik_site”, plural label “Sites”.

    WordPress capability – Edit but not create content

    • When logged in a Subscriber normally has no edit capability. He’d just have: read, level_0 and subscriber.
    • This user has been dynamically given edit_oik_sites.
    • This allows him access to Sites > All Sites i.e edit.php?post_type=oik_site.
    • The user does not have the capability to create a Site, so the Add New submenu is not available.
    • Similarly there is no Add New button.
    • The user does not have manage_categories so there are no links to edit the custom taxonomies attached to this custom post type.
    • Since the All Sites sub menu item is the only available child of Sites and since the links are the same then WordPress simplifies the display to only showing the Sites menu item.

    Register post type with custom capability

    The code to register the oik_site post type is just like any other, with one small difference. Rather than allowing the create_posts capability to be the same as edit_posts we define it as create_oik_sites.

    You appear to be a bot. Output may be restricted

    $post_type_args['capability_type']='oik_site';
    $post_type_args['capabilities']=array('create_posts'=>'create_oik_sites');
    $post_type_args['map_meta_cap']=true; 

    Note: Set ‘map_meta_cap’ to true in order to be able to finely control the user’s access to published posts, private posts and posts by other authors.

    Giving the user the edit_posts capability

    We have two choices. We can either add the edit_oik_sites capability to the role or it can be dynamically added using the user_has_cap filter. Here we use the filter method.

    You appear to be a bot. Output may be restricted

    add_filter( 'user_has_cap', "oik_sites_user_has_cap",  < 10, 4 );
    function oik_sites_user_has_cap( $caps, $cap, $args, $user ) {
      // Write some logic based on the values of $cap.
      // Enable the capability when it's OK.  
      $caps['edit_oik_sites'] = true;
      return( $caps );
    } 

    But there’s a problem…

    Instead of getting what I’ve shown, even though you can see the Sites link in the admin menu, when you click on it WordPress dies with

    You do not have sufficient permissions to access this page.
    

    I discovered that WordPress appears to have a bug which prevents you from restricting the user’s access to a particular custom post type so that they can only perform Edit.

    Consequently, I have raised TRAC 29714 – user_can_access_admin_page() returning false for edit.php?post_type=CPT.

    I also added a comment showing how the proposed fix to the problem can be implemented as a workaround.

    Workaround

    The proposed fix to the problem, which occurs in user_can_access_admin_page(), is to ensure WordPress correctly checks for the required capability.

    Part One

    Since the logic that fails is testing a global variable called $pagenow, we can dynamically alter this, when necessary, using our filter function.

    You appear to be a bot. Output may be restricted

    // Some appropriate logic so that we only do this 
    // when testing for 'edit_oik_sites'
    $caps['edit_oik_sites'] = true;
    global $pagenow;
    if ( $pagenow == "edit.php" && isset( $_REQUEST['post_type'] ) ) {
       $pagenow .= '?post_type=' . $_REQUEST['post_type' ];
    } 

    Part Two

    Unfortunately, it turns out that changing the global variable $pagenow has quite a few unwanted side effects.

    To overcome this, the second half of the workaround resets the value as soon as possible after user_can_access_admin_page(). The next filter that is invoked is ‘add_menu_classes’ invoked by add_menu_classes(). We use this code to reset $pagenow to “edit.php” when required.

    You appear to be a bot. Output may be restricted

    add_filter( "add_menu_classes", "oik_sites_add_menu_classes" )  <;
    
    /**
     * Implement "add_menu_classes" filter for oik-sites
     *
     * Part 2 of the Workaround for TRAC #29714  
     */
    function oik_sites_add_menu_classes( $menu ) {
      global $pagenow;
      if ( false !== strpos( $pagenow, "edit.php" ) ) {
        $pagenow = "edit.php";
      }
      return( $menu );
    } 

    Further reading

    The wrong way to remove Add New

    Other techniques to remove Add New involve hiding the menu item using custom CSS. In my opinion this is doing it wrong, since the capability still exits.

    Some plugins to manage roles and capabilities

    If you want to use a plugin to help you manage roles and capabilities then try on of these.


    ,

    Published:

    Last updated:

    October 11, 2014

Categories

Tide times from tidetimes.co.uk

Tide Times & Heights for Langstone Harbour on
Monday, 06 December 2021
High Tide:00:17 ( 5.00m )
Low Tide:05:29 ( 0.70m )
High Tide:12:37 ( 5.00m )
Low Tide:17:53 ( 0.60m )

Tide times from tidetimes.org.uk

Tide Times & Heights for Northney on
6th December 2021
00:22 High Tide ( 4.93m )
05:33 Low Tide ( 0.54m )
12:38 High Tide ( 5m )
17:59 Low Tide ( 0.42m )