Skip to content

Comments: Add a REST API endpoint for comment types - #51

Open
adamsilverstein wants to merge 19 commits into
feature/register-comment-typefrom
feature/rest-comment-types
Open

adamsilverstein wants to merge 19 commits into
feature/register-comment-typefrom
feature/rest-comment-types

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jun 24, 2026 •

Copy link
Copy Markdown
Owner

Stack: Custom Comment Types (#35214)

PR Change Builds on
#12311 register_comment_type() registration API trunk
👉 #51 REST /wp/v2/comment-types endpoint (this PR) WordPress#12311
#52 Capabilities object (advisory) WordPress#12311
#55 Enforce type capabilities in map_meta_cap() #52
#56 Type-aware REST moderation #55
#57 Type-aware admin comment dimming #56
#53 render_callback display callback WordPress#12311
#54 is_ping ping grouping #53
#12310 default_excluded_comment_types filter trunk
#58 wp_update_comment_counts() recount helper WordPress#12310

WordPress#12311 and WordPress#12310 are on WordPress/wordpress-develop; the rest are fork PRs targeting their parent branch and will retarget to trunk as their base lands.

Claude went back over this PR and drafted the summary below:

Adds a read-only /wp/v2/comment-types REST endpoint that lists the comment types registered with register_comment_type(), modeled on the post types controller (/wp/v2/types).

Why

REST clients have no way to discover which comment types a site has registered or what to call them. The block editor's inline commenting work needs exactly that, see https://core.trac.wordpress.org/ticket/35214#comment:48. This is the next small, additive step on https://core.trac.wordpress.org/ticket/35214, built on the registration API in WordPress#12311.

How

  • New WP_REST_Comment_Types_Controller (src/wp-includes/rest-api/endpoints/class-wp-rest-comment-types-controller.php) with two routes:
    • GET /wp/v2/comment-types
    • GET /wp/v2/comment-types/{type}
  • It returns name, slug, and description, plus labels in the edit context. The edit context requires moderate_comments.
  • New show_in_rest argument on WP_Comment_Type, defaulting to the value of public. It controls discovery only. It does not change who can read comments of that type through /wp/v2/comments.
  • comment, pingback, and trackback are public, so they show up. note is registered with public => false, so it stays hidden. The new reaction type is not registered at all, so it is hidden too.
  • internal does not affect show_in_rest. An internal type that should stay out of discovery needs show_in_rest => false or public => false.
  • Each type links to wp/v2/comments?type={slug}. Note that the comments endpoint only lets users with edit_posts filter by types other than comment.
  • Routes are wired up in create_initial_rest_routes(), and the QUnit fixture tests/qunit/fixtures/wp-api-generated.js gains only the two new routes.

Testing

  • vendor/bin/phpunit --group comment
  • vendor/bin/phpunit --group restapi --filter WP_Test_REST_Comment_Types_Controller
  • Manually:
    • GET /wp-json/wp/v2/comment-types should list comment, pingback, and trackback, but not note or reaction.
    • GET /wp-json/wp/v2/comment-types/note should return rest_cannot_read_type.
    • Register a type in a mu-plugin with register_comment_type( 'review', array( 'label' => 'Reviews' ) );. It should show up in the list. Add 'show_in_rest' => false and it should disappear.
    • As an admin, add ?context=edit and confirm labels are included. As a subscriber, the same request should return 403.

Out of scope / follow-ups

Trac ticket: https://core.trac.wordpress.org/ticket/35214

AI Use

Built with 🤖 Claude Code over a few rounds of back and forth. I will review and test.

@github-actions

github-actions Bot commented Jun 24, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

Core Committers: Use this line as a base for the props when committing in SVN:

Props adamsilverstein.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6419026f-f84a-4555-94b6-f11a0c43908e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
Finishing Touches

🧪 Generate unit tests (beta)

  • Create PR with unit tests
  • Commit unit tests in branch feature/rest-comment-types
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Expose registered comment types through a read-only `/wp/v2/comment-types`
controller, mirroring the post types controller (`/wp/v2/types`). This lets
REST clients discover the registered types and their labels, which the block
editor's inline-commenting work needs in order to add and query comments by
type.

Add a `show_in_rest` argument to `WP_Comment_Type` (defaulting to the value of
`public`, as `show_ui` does) to gate which types the endpoint exposes. The
built-in `comment`, `pingback`, and `trackback` types are public and therefore
visible; the internal `note` type is not.

Builds on the registration API in WordPress#12311.

See #35214.
Cover the `/wp/v2/comment-types` controller: route registration, the context
param, listing public types while excluding non-REST types (`note` and types
that opt out via `show_in_rest`), single-type reads, permission checks for the
`edit` context, and the item schema. Also assert the `show_in_rest` cascade on
`WP_Comment_Type` and add the two new routes to the REST schema route list.

See #35214.
Add happy-path coverage for the controller's dedicated HEAD-request
handling in get_items() and prepare_item_for_response(), mirroring the
post-types controller (ticket 56481): a collection HEAD request returns
200 without preparing item data, single-item HEAD requests still run the
rest_prepare_comment_type filter and allow header injection, and HEAD
requests with _fields succeed. Also assert the api.w.org/items link
points at the type-filtered comments collection.

See #35214.
@adamsilverstein
adamsilverstein force-pushed the feature/rest-comment-types branch from 6be8f2f to 7daed7e Compare June 25, 2026 06:32
…t-types

# Conflicts:
#	src/wp-includes/class-wp-comment-type.php
- Document exactly what 'show_in_rest' gates (the read-only discovery
  endpoint) in the property docblock and the register_comment_type()
  args reference, including that it does not affect comment readability
  via /wp/v2/comments, and realign the args hash.
- Document the deliberate divergence from WP_Post_Type in defaulting
  'show_in_rest' from 'public': the false default there is pure
  back-compat for pre-REST post types, which a new registry lacks.
- Reuse the existing 'not allowed to edit comments' string instead of
  introducing a new translator-facing string.
- Correct the get_items() summary (it retrieves REST-exposed types, not
  public ones) and mark the seam where per-type capability checks land
  once comment type capabilities exist.
- Tests: pin the anonymous 401-before-type-resolution ordering (a
  deliberate divergence from the post types controller's 404), cover
  HEAD for invalid types, _fields-limited responses, additional fields
  registered for 'comment-type', edit-context labels for all exposed
  types with 'note' still hidden from moderators, and add missing
  @ticket tags. Drop the hand-rolled tear_down() now handled by the
  test framework.
tests/qunit/fixtures/wp-api-generated.js is written by
test_build_wp_api_client_fixtures and diff-checked in CI; the new
/wp/v2/comment-types routes must be reflected in the committed copy.
…t-types

# Conflicts:
#	src/wp-includes/comment.php
…xture.

The fixture was regenerated in an environment without SSL and outside a local
environment type, so wp_is_application_passwords_available() returned false and
the authentication object came out as an empty array. That hunk has nothing to do
with the comment types routes, and it fails the "version-controlled files are not
modified" check on any repo that runs it, because a canonical regeneration puts
the object back.

Restore the block. The fixture is now identical to trunk apart from the two new
comment-types route entries, which were correct already.

See #35214.
Three things the endpoint did without saying why.

The advertised 'items' link points at the comments collection filtered by type,
but that endpoint's `type` parameter is protected: a caller without `edit_posts`
may only ask for 'comment', so for every other type the link answers 401 to an
anonymous client. Keeping the link is the right call - what a caller may query
belongs to the comments endpoint, and the answer changes once per-type
capabilities land - but the next reader should not have to rediscover it.

The `description` field was exposed in embed context, where the post types
controller it mirrors exposes only view and edit. Embed is conventionally
identity fields; there is no reason for the divergence, so drop it.

Two behaviors also had no test: a non-public type that sets show_in_rest is
listed and readable, which is the direction the exclusion tests miss, and the
collection's edit context answers 403 to an authenticated caller without the
capability, matching the single-item route.

See #35214.
…t-types

# Conflicts:
#	src/wp-includes/comment.php
…troller.

The branch was written while trunk was 7.1-alpha; trunk is now
7.2-alpha, so the controller's and show_in_rest property's @SInCE tags
would have credited the wrong release - and left WP_Comment_Type
internally contradictory, with the property claiming to predate its
class. Also trim a speculative design note about future per-type
capabilities out of get_items() (that discussion belongs on the
ticket), and move two test methods above the protected helpers to
restore the tests-then-helpers layout used by the rest of the file.

See #35214.
…covery.

show_in_rest deliberately cascades from public alone, so a type
registered with internal => true but left public still defaults to
being listed by the discovery endpoint. That combination is most
likely an oversight on the registrant's part, and the argument
contract should say plainly how to opt out rather than leaving the
cascade to be inferred: pass show_in_rest => false or public => false,
as the built-in note type does.

See #35214.
The `show_in_rest` and `prepare_links()` docblocks carried design
rationale that belongs in the ticket, which made the caller contract
harder to spot. Trim them to what a caller needs, and give the new
controller's array returns value types so PHPStan can check them.

See #35214.
Trunk now stores note reactions as a `reaction` comment type. Assert it
stays out of `/wp/v2/comment-types` so registering it later cannot
expose it by accident. Also add native types to the new tests to match
the rest of the comment type suite.

See #35214.
…t-types

# Conflicts:
#	src/wp-includes/comment.php
Trunk is in the 7.2 beta, so these additions can no longer ship in 7.2.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant