Make WordPress Core

Opened 13 months ago

Last modified 13 months ago

#47679 new defect (bug)

'edit_posts' required for publishing a post successfully (REST API 403 Forbidden)

Reported by: isabel_brison Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Posts, Post Types Keywords: needs-patch reporter-feedback
Focuses: rest-api Cc:

Description (last modified by SergeyBiryukov)

[Issue reported in Gutenberg repo](https://github.com/WordPress/gutenberg/issues/16330).

Describe the bug

A custom user role that does not have 'edit_posts' capabilities cannot publish their post.

If this user role is granted 'edit_posts' the post can be published, however this setting is not desirable for certain user roles.

Possibly related to https://github.com/WordPress/gutenberg/issues/14660

To reproduce

Create a custom user role with meta capabilities assigned to a custom post type (see sample code below)
Create a new user and assign the custom role to it
Create a new post in the CPT, logged in as that user
Add some content
Click Publish button - the "Saving" message appears but never goes away, and these errors appear in console. Reloading the edit page reveals some data was not saved.
Disabling gutenberg in the CPT (eg setting 'show_in_rest' to false) reveals that the functions work fine. However this is not a solution as REST API is sometimes required for CPTs.

The error messages, when traced back indicate that the 'edit_posts' capability is required to parse the REST API request: https://developer.wordpress.org/reference/classes/wp_rest_themes_controller/get_items_permissions_check/ and https://developer.wordpress.org/reference/classes/wp_rest_users_controller/get_items_permissions_check/

Expected behavior
The post should save, and contents should not be lost on refresh.

If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

OS: Mac OS Mojave 10.14.5
Browser Chrome
Version 75
Additional context

Wordpress 5.2 w/ Gutenberg

CPT setup:

function directory()

        $labels = array(
            'name'                  => 'Directory Listings',
            'singular_name'         => 'Directory Listing',
            'menu_name'             => 'Directory',
            'name_admin_bar'        => 'Listing',
            'archives'              => 'Listing Archives',
            'attributes'            => 'Listing Attributes',
            'parent_item_colon'     => 'Parent Listing:',
            'all_items'             => 'All Listings',
            'add_new_item'          => 'Add New Listing',
            'add_new'               => 'Add New',
            'new_item'              => 'New Listing',
            'edit_item'             => 'Edit Listing',
            'update_item'           => 'Update Listing',
            'view_item'             => 'View Listing',
            'view_items'            => 'View Listings',
            'search_items'          => 'Search Listings',
            'not_found'             => 'Directory entry not found',
            'not_found_in_trash'    => 'Directory entry not found in Trash',
            'featured_image'        => 'Listing Featured Image',
            'set_featured_image'    => 'Set entry featured image',
            'remove_featured_image' => 'Remove entry listing image',
            'use_featured_image'    => 'Use as entry featured image',
            'insert_into_item'      => 'Insert into list',
            'uploaded_to_this_item' => 'Uploaded to this list',
            'items_list'            => 'Listings list',
            'items_list_navigation' => 'Listings list navigation',
            'filter_items_list'     => 'Filter entries list',
        $rewrite = array(
            'slug'                  => 'listing',
            'with_front'            => true,
            'pages'                 => true,
            'feeds'                 => true,
        $args = array(
            'label'                 => 'Listing',
            'description'           => 'Directory Listing',
            'labels'                => $labels,
            'supports'              => array(
                // 'thumbnail',
                // 'trackbacks',
                // 'page-attributes',
                // 'post-formats'
            'taxonomies'            => 'industry',
            'hierarchical'          => true,
            'public'                => true,
            'show_ui'               => true,
            'show_in_menu'          => true,
            'menu_position'         => 2,
            'menu_icon'             => plugin_dir_url(__DIR__) . '../admin/img/icon.png',
            'show_in_admin_bar'     => true,
            'show_in_nav_menus'     => true,
            'can_export'            => true,
            'has_archive'           => true,
            'exclude_from_search'   => false,
            'publicly_queryable'    => true,
            'rewrite'               => $rewrite,
            'show_in_rest'          => true,
            'capability_type'     => array('listing', 'listings'),
            'map_meta_cap'        => true,
        register_post_type('listing', $args);

    add_action('init', 'directory', 20);

Taxonomy setup:

function industry()

        $labels = array(
            'name'                       => _x('Industries', 'Taxonomy General Name', 'text_domain'),
            'singular_name'              => _x('Industry', 'Taxonomy Singular Name', 'text_domain'),
            'menu_name'                  => __('Industry', 'text_domain'),
            'all_items'                  => __('All Industries', 'text_domain'),
            'parent_item'                => __('Parent Industry', 'text_domain'),
            'parent_item_colon'          => __('Parent Industry:', 'text_domain'),
            'new_item_name'              => __('New Industry Name', 'text_domain'),
            'add_new_item'               => __('Add New Industry', 'text_domain'),
            'edit_item'                  => __('Edit Industry', 'text_domain'),
            'update_item'                => __('Update Industry', 'text_domain'),
            'view_item'                  => __('View Industry', 'text_domain'),
            'separate_items_with_commas' => __('Separate industries with commas', 'text_domain'),
            'add_or_remove_items'        => __('Add or remove industries', 'text_domain'),
            'choose_from_most_used'      => __('Choose from the most used', 'text_domain'),
            'popular_items'              => __('Popular Industries', 'text_domain'),
            'search_items'               => __('Search Industries', 'text_domain'),
            'not_found'                  => __('Industry Not Found', 'text_domain'),
            'no_terms'                   => __('No industry items', 'text_domain'),
            'items_list'                 => __('Industry list', 'text_domain'),
            'items_list_navigation'      => __('Industry list navigation', 'text_domain'),
        $args = array(
            'labels'                     => $labels,
            'hierarchical'               => true,
            'public'                     => true,
            'show_ui'                    => true,
            'show_admin_column'          => true,
            'show_in_nav_menus'          => true,
            'show_tagcloud'              => true,
            'show_in_rest'               => true,
            'capabilities'      => array(
                'manage_terms'  => 'edit_listings',
                'edit_terms'    => 'manage_categories',
                'delete_terms'  => 'manage_categories',
                'assign_terms'  => 'edit_listings'
        register_taxonomy('industry', 'listing', $args);
    add_action('init', 'industry', 20);
 // Associate CPT with taxonomy (fallback function)
    register_taxonomy_for_object_type('industry', 'listing');

User Role Setup:

if (!get_role('listing_manager')) {
				add_role('listing_manager', 'Listing Manager', array(
					'read' => true,
					'edit_posts' => false,
					'delete_posts' => false,
					'publish_posts' => false,
					'upload_files' => false

Meta capabilities setup:

// The various role levels required (array $string role_slug)
			$individual = array('bbp_spectator', 'subscriber');
			$cpt_access = array('listing_manager');
			$full_access = array('editor', 'administrator');

			// Loop through the individual roles
			foreach ($individual as $set1) {
				$role1 = get_role($set1);


			// Loop through the directory editor roles
			foreach ($cpt_access as $set2) {
				$role2 = get_role($set2);


			// Loop through the backend access roles
			foreach ($full_access as $set3) {
				$role3 = get_role($set3);


Attachments (1)

60270696-1289a000-9930-11e9-9071-c232af91ba19.png (168.4 KB) - added by isabel_brison 13 months ago.
Error message.

Download all attachments as: .zip

Change History (3)

#1 @SergeyBiryukov
13 months ago

  • Component changed from General to Posts, Post Types
  • Description modified (diff)

#2 @TimothyBlynJacobs
13 months ago

  • Keywords reporter-feedback added

What is the response returned from the update post request? Something like wp/v2/{yourposttype} Those console errors do not initially look related to publishing. I think they are for getting the theme supports information and loading the authors dropdown.

Note: See TracTickets for help on using tickets.