[wp-trac] [WordPress Trac] #47679: 'edit_posts' required for publishing a post successfully (REST API 403 Forbidden)

WordPress Trac noreply at wordpress.org
Thu Jul 11 12:59:05 UTC 2019


#47679: 'edit_posts' required for publishing a post successfully (REST API 403
Forbidden)
-------------------------------+------------------------------
 Reporter:  isabel_brison      |       Owner:  (none)
     Type:  defect (bug)       |      Status:  new
 Priority:  normal             |   Milestone:  Awaiting Review
Component:  Posts, Post Types  |     Version:
 Severity:  normal             |  Resolution:
 Keywords:  needs-patch        |     Focuses:  rest-api
-------------------------------+------------------------------
Changes (by SergeyBiryukov):

 * component:  General => Posts, Post Types


Old description:

> [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 #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.
>
> Screenshots
> 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(
>                 'title',
>                 'editor',
>                 // 'thumbnail',
>                 'comments',
>                 // 'trackbacks',
>                 'revisions',
>                 'custom-fields',
>                 // '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);
>
>                                 $role1->add_cap('read');
>                         }
>
>                         // Loop through the directory editor roles
>                         foreach ($cpt_access as $set2) {
>                                 $role2 = get_role($set2);
>
>                                 $role2->add_cap('read');
>                                 $role2->add_cap('read_listing');
>                                 $role2->add_cap('edit_listing');
>                                 $role2->add_cap('edit_listings');
> $role2->add_cap('edit_published_listings');
>                                 $role2->add_cap('publish_listings');
> $role2->add_cap('delete_published_listings');
>                         }
>
>                         // Loop through the backend access roles
>                         foreach ($full_access as $set3) {
>                                 $role3 = get_role($set3);
>
>                                 $role3->add_cap('read');
>                                 $role3->add_cap('read_listing');
>                                 $role3->add_cap('read_private_listings');
>                                 $role3->add_cap('edit_listing');
>                                 $role3->add_cap('edit_listings');
>                                 $role3->add_cap('edit_others_listings');
> $role3->add_cap('edit_published_listings');
>                                 $role3->add_cap('publish_listings');
> $role3->add_cap('delete_others_listings');
> $role3->add_cap('delete_private_listings');
> $role3->add_cap('delete_published_listings');
>                         }
>
> }}}

New description:

 [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.

 Screenshots
 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(
                 'title',
                 'editor',
                 // 'thumbnail',
                 'comments',
                 // 'trackbacks',
                 'revisions',
                 'custom-fields',
                 // '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);

                                 $role1->add_cap('read');
                         }

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

                                 $role2->add_cap('read');
                                 $role2->add_cap('read_listing');
                                 $role2->add_cap('edit_listing');
                                 $role2->add_cap('edit_listings');
 $role2->add_cap('edit_published_listings');
                                 $role2->add_cap('publish_listings');
 $role2->add_cap('delete_published_listings');
                         }

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

                                 $role3->add_cap('read');
                                 $role3->add_cap('read_listing');
                                 $role3->add_cap('read_private_listings');
                                 $role3->add_cap('edit_listing');
                                 $role3->add_cap('edit_listings');
                                 $role3->add_cap('edit_others_listings');
 $role3->add_cap('edit_published_listings');
                                 $role3->add_cap('publish_listings');
                                 $role3->add_cap('delete_others_listings');
 $role3->add_cap('delete_private_listings');
 $role3->add_cap('delete_published_listings');
                         }

 }}}

--

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/47679#comment:1>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list