Introduce capabilities for adding new posts

Description

Many plugins need to be able to deny the creation of new items, particularly for CPT use cases. Unfortunately this is not easy to do.

I am suggesting a new cap for adding posts. Obviously, this has the potential to break quite a number of custom roles, but I think we can avoid that.

Instead, it would by default simply map to edit_posts. It wouldn't actually get assigned to any roles, so it's more or less a meta cap. (Only, without an $id, because it'd be a new post even pre-auto-draft in some cases.) But it then opens up the ability for this to be filtered by a plugin.

We could also potentially add this to the CPT capabilities array, but with the default being 'add_posts' => 'edit_posts'. Ultimately there are a few ways to implement this.

Ideally, the cap should be used for the admin menu, for post-new.php, for the 'Add New' button (no more clunky hiding it via CSS), XML-RPC, and the post handler for post-new.

Is the post creation capability to be governed independent of post editing? If so, the admin menu for each post type will need to be displayed if the user has edit_posts OR create_posts. But the current API only supports one required cap per menu:

Actually, the way casben79 did it, create_posts requires you have the edit_posts capability, which seems logical to me, so it shouldn't be a problem.

I can imagine implementations that would appreciate enabling users to create a post via Quick Press or custom UI without being able to further edit the new draft. As I read it, the current patch actually does support that.

I think supporting creation-only usage of the full post edit form would be nice. But that would take some work in post.php also. At the very least you would need to redirect back to the dashboard following post creation. But wouldn't there also be some complications (edit_posts cap checking) due to auto-creation of new posts?

As the patch stands now, a user with create_posts (but not edit_posts) sees no "Add New" menu link but can still access post-new.php by direct URL.

16714.2.diff​ has some problems in map_meta_cap(). Rather than mapping to 'edit_posts', it should map to post_type_object->cap->edit_posts.

Bonus: It should instead map to cap->create_posts, and in get_post_type_capabilities(), cap->create_posts should be set to 'edit_' . $plural_base by default, essentially mapping it on the fly to edit_posts.

Both of these work only if we know the post type. So, we'll probably need to clarify that this can only be used like this:

Since we need this, it gets saved by freeze. Would like someone to weigh in on 16714.3.diff​. We don't really have a similar half-primitive, half-meta capability implementation in core, so the approach could use a critique.

In the end, we actually did not need this for #21391. media.php happens to be accessible without upload_files, but it's a fluke — the Media Library (including the list table at upload.php) is entirely disabled for users who cannot upload files.

Users who have the 'edit_posts' capability but not the 'create_posts' capability still see the 'Add New Post' menu in the admin toolbar. This applies to the 'create_posts' meta capability for all post types.

Users who have the 'create_posts' meta capability but not the 'edit_posts' meta capability for a custom post type still see the 'Add New' submenu for the post type in the main menu.

After all that, we should probably do 16714.12.diff​... It's not actually necessary because the current cap checks for upload.php (the list table) and the menu items all use upload_files. Only media.php?action=edit&attachment_id= and post.php?action=edit&post= are accessible without upload_files, and not directly through the UI (best I can tell), as long as you can edit that attachment using the standard edit_post cap tree.

In that case, we do an upload_files check on media.php for "Add New". We don't for post.php — that was the point of create_posts. So, 16714.12.diff​.

You may also wish to check file index.php in the TwentyTwelve theme. This makes use of current_user_can('edit_posts') to output a different message so that suitably empowered users can quickly add a first post to their blog.