Comments: Add a capabilities object to comment types - #52
Comments: Add a capabilities object to comment types#52adamsilverstein wants to merge 9 commits into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Follow-up: capability enforcement via This PR intentionally keeps the cap object advisory. Proposing the lowest-risk path to actual enforcement, mirroring
One question to settle before coding: do custom comment types keep deriving edit permission from the parent post, or get a permission boundary independent of it? That is the substantive design fork (the comment:19 concern). Happy to write this up on Trac #35214 if the direction looks right. |
Give `WP_Comment_Type` a `cap` object built from new `capability_type` and `capabilities` registration arguments, modeled on `WP_Post_Type` and `get_post_type_capabilities()`. A new `get_comment_type_capabilities()` helper builds the capability strings from the `capability_type` base (default 'comment'), so a registered type can describe its own read, edit, delete, and moderate capabilities. This is advisory metadata only: `map_meta_cap()` is intentionally not changed, so there is no behavior change. The built-in `comment` type resolves to the existing `edit_comment` and `moderate_comments` capabilities, preserving current behavior. Enforcing per-type capabilities through `map_meta_cap()` is left to a follow-up so the capability model can be agreed on first. See #35214.
Cover the new `capability_type`/`capabilities` arguments and the `get_comment_type_capabilities()` helper: default and custom capability types, array capability types with explicit plurals, the `capabilities` override, that the input `capabilities` array is not retained as a property, and that the built-in `comment` type stays backward compatible with the existing core capabilities. See #35214.
The existing tests spot-checked individual generated capabilities; read_comment, moderate_comment, and delete_comments were never asserted. Add a test pinning the complete meta and primitive capability set built by get_comment_type_capabilities() from a string base, plus a direct test of the array capability_type form (explicit plural). See #35214.
d2ee949 to
c4978f3
Compare
…e-caps # Conflicts: # src/wp-includes/class-wp-comment-type.php # src/wp-includes/comment.php # tests/phpunit/tests/comment/types.php
- Document that with the default 'comment' capability_type, most generated primitive capabilities exist in no default role and are not consulted by the default mapping: consumers should check meta capabilities with a comment ID, not the primitives. - Add the per-capability @return reference to get_comment_type_capabilities() (mirroring the post type version) and note that it normalizes the passed object's capability_type as a side effect. - Type WP_Comment_Type::$capability_type as string: set_props() collapses the array registration form to the singular base.
…e-caps # Conflicts: # src/wp-includes/class-wp-comment-type.php # src/wp-includes/comment.php # tests/phpunit/tests/comment/types.php
`get_comment_type_capabilities()` generated a `read_comment` meta capability alongside `edit_comment`, `delete_comment`, and `moderate_comment`, but `map_meta_cap()` has no case for it and none is planned with the enforcement follow-up. A consumer taking the documented advice - check the meta capabilities with a comment ID and let `map_meta_cap()` resolve them - would get a literal capability that no default role grants, so the check denies everyone including administrators, with no release in which that changes. Post types ship `read_post` from day one together with its mapping, so the parity argument does not carry to shipping one unmapped. Drop it from the generated set rather than document a capability that cannot work, and add a test pinning the omission so it is only reintroduced with its mapping. No behavior change for the built-in types: nothing in core reads `read_comment`.
…t lands. The "advisory metadata only, `map_meta_cap()` is not affected" warning lived on `WP_Comment_Type::$cap` and in `get_comment_type_capabilities()`, but not in the `register_comment_type()` argument docs, which is the surface a plugin author reads before passing `capability_type`. Someone registering a 'review' type and granting `edit_reviews` to a role could reasonably conclude that core's moderation and edit paths now require it for their type. They do not: anyone with `moderate_comments` can still edit, spam, approve, or delete those comments through every existing path. The risk is not the checks a plugin makes - those are fail-closed - it is the checks it skips believing core makes them, so the only fix available here is to say so where it will be read. Also qualify the "check the meta capabilities" advice, which only resolves for `edit_comment` on the default base today, and warn against reusing a post type's `capability_type`: a base of 'post' would send a comment ID through `map_meta_cap()`'s post branch.
…y overrides. The backward-compatibility claim for `pingback`, `trackback`, and `note` rested on all three registering without a `capability_type`, which nothing asserted: only the `comment` type's cap object was checked. Compare each built-in against it directly, so a future registration that quietly gives one of them its own base is caught. Also cover a `capabilities` override of a meta capability. Only primitive overrides were tested, and a type pointing `edit_comment` at a name `map_meta_cap()` already resolves is the one route to working per-type checks before enforcement lands.
Description
Adds a capabilities object to comment types, modeled on
WP_Post_Typeandget_post_type_capabilities(). Registered comment types can now describe their own edit/delete/moderate capabilities, which is a prerequisite for per-type permission handling on the #35214 tracking ticket (and for exposing capabilities through the REST endpoint).What it adds
capability_typeregistration argument (default'comment'; may be an array likearray( 'story', 'stories' )for an explicit plural).capabilitiesregistration argument to override individual generated capabilities.WP_Comment_Type::$capobject, built by the newget_comment_type_capabilities()helper.Non-breaking by design
Within this PR the capabilities are advisory metadata only:
map_meta_cap()is left unchanged here, so this PR on its own causes no behavior change to comment moderation or editing. The built-incommenttype resolves to the existingedit_comment(meta) andmoderate_comments(primitive) capabilities, so current behavior is preserved exactly.Wiring these capabilities into
map_meta_cap()enforcement was deliberately split out so the capability model could be reviewed before touching the live permission path. That follow-up now exists: the next PR in the stack (#55) enforces these capabilities inmap_meta_cap()and rewrites the capability docblocks to describe the live model.These two need to land in the same release. Once #55's mapping exists, a check changes meaning for anything registered under a #52-only release: with a custom base,
current_user_can( 'edit_review', $id )flips from requiring the literaledit_reviewto mapping ontoedit_reviews/edit_others_reviews. Both directions are defensible, but only if no released version sits in between. Committers, please take these as a pair.Scope / boundary
Stacked on WordPress#12311 (the
register_comment_type()API) and targets that branch; it will be retargeted totrunkonce WordPress#12311 lands. Complements the REST endpoint (#51, also stacked on WordPress#12311) and thedefault_excluded_comment_typesquery filter (WordPress#12310 / #65537).Review updates
Following a review pass over the stack:
read_commentmeta capability.map_meta_cap()has no case for it and none is planned in Comments: Enforce registered comment type capabilities in map_meta_cap() (Trac #35214) #55, so a consumer following the documented advice would get a capability that denies everyone with no release in which that changes. Post types shipread_postfrom day one together with its mapping, so the parity argument does not carry to shipping one unmapped. A test pins the omission so it is only reintroduced alongside a mapping.WP_Comment_Type::$capand inget_comment_type_capabilities(), but not in theregister_comment_type()argument docs. Someone registering a 'review' type and grantingedit_reviewsto a role could reasonably conclude core's moderation paths now require it. They do not - anyone withmoderate_commentscan still edit, spam, approve, or delete those comments. The exposure is not the checks a plugin makes, which are fail-closed, it is the checks it skips believing core makes them.edit_commenton the default base resolves throughmap_meta_cap()today; everything else is a literal capability check that no default role satisfies.capability_type. A base of'post'generatesedit_comment => 'edit_post', which sends a comment ID throughmap_meta_cap()'s post branch.pingback,trackback, andnoteare asserted to share thecommentcap set (the backward-compatibility claim for three of the four built-ins was untested), and acapabilitiesoverride of a meta capability is covered.Testing
Capability/meta-cap suites also pass unchanged, confirming no enforcement-path regression. PHPCS reports no new warnings on the changed files and PHPStan is clean.
See #35214.
AI Use
Code and description both written with 🤖 Claude Code. I will review and test.