Skip to content

Add the content write abilities through the REST API - #1026

Draft
jorgefilipecosta wants to merge 6 commits into
try/core-read-abilities-rest-backendfrom
try/core-content-write-abilities
Draft

jorgefilipecosta wants to merge 6 commits into
try/core-read-abilities-rest-backendfrom
try/core-content-write-abilities

Conversation

@jorgefilipecosta

@jorgefilipecosta jorgefilipecosta commented Sep 11, 2026

Copy link
Copy Markdown
Member

What?

Adds core/content-create, core/content-update and core/content-delete, the write counterparts to core/content-query. They have one implementation: the REST posts endpoint.

Stacked on #931 — it added the REST-backed read implementations, and this reuses that machinery.

Why?

The write paths are where the checks matter: capabilities, sanitization, slug uniqueness, revisions, rest_insert_*. The posts controller already does all of it, so calling it is less to get wrong than a second copy. No native implementation this time.

How?

File What
Content_Write.php the whole thing — registration, schemas, permission callbacks, and the POST /wp/v2/<rest_base>, POST /<id>, DELETE /<id> calls
Post_Type_Route.php route and exposure helpers, extracted from Content_Rest.php
Rest_Backend.php post() and delete() beside get()

One class, one implementation. There is no native/REST pair here — nothing reaches wp_insert_post() or its siblings directly.

Registered from Content::register(), where the placeholder comment said a write ability would go. The permission callbacks gate on the post type being exposed to abilities and on the caller's capability for the post; everything past that is the controller's.

An update sends only the fields the input names, so it leaves the rest alone. A delete trashes by default and reports { deleted, post }; force deletes permanently, and the post is read while it still exists. Written posts are reported by reading them back through Content_Rest, so one field mapping serves both directions.

Two helpers on Content became public (get_exposed_post_types(), get_post_properties()) so both sides gate on the same post types and report the same fields. Both are marked // Plugin:.

Testing

24 tests in Content_WriteTest.php. They assert the mapping this plugin owns and the controller behaviour it must pass on rather than swallow — the refusal to assign another author, an unparseable date, an already trashed post, and a post type that does not support trashing.

npm run wp-env:test start
npm run test:php      # 1611 tests
npm run test:php:rest # same totals

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: The abilities, the REST mapping, the tests and this description were drafted with Claude Code and iterated against the suite in a local wp-env environment. I reviewed and edited the result, and I take responsibility for it.

Changelog Entry

Added - core/content-create, core/content-update and core/content-delete abilities for writing content, backed by the REST posts endpoint.

Open WordPress Playground Preview

The read abilities only ever dispatched GET. Extract the dispatch and error
handling into one place and add POST and DELETE on top of it, so the write
abilities can reach the same endpoints.
Content_Rest resolves a post type's REST route, exposes a post type that
abilities can see but REST cannot, and puts the global post context back
afterwards. The write implementations need all three, so move them out of
the read implementation instead of copying them.
Creates, updates and deletes posts through the posts endpoint, so the
capability checks, sanitization and side effects stay with the controller.
The written post is read back through the read implementation, which keeps
one field mapping for both directions.
…lete

Registers them from the read ability's register(), where the placeholder
comment said a write ability would go. The permission callbacks gate on the
post type being exposed to abilities and on the caller's capability for the
post; everything past that is the posts controller's own checks.

The output reuses the read ability's post field definitions, so a written
post is reported the same way a queried one is.
The endpoint is the only implementation, so the tests assert both halves:
the mapping this plugin owns, and the controller behaviour it inherits and
must pass on — the refusal to assign another author, an unparseable date,
an already trashed post, and a post type that does not support trashing.
@github-actions

Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.45910% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.11%. Comparing base (27c393d) to head (13fc01f).

Files with missing lines Patch % Lines
includes/Abilities/Content/Content_Write.php 94.89% 17 Missing ⚠️
includes/Abilities/Content/Post_Type_Route.php 87.87% 4 Missing ⚠️
Additional details and impacted files
@@                            Coverage Diff                             @@
##             try/core-read-abilities-rest-backend    #1026      +/-   ##
==========================================================================
+ Coverage                                   81.12%   82.11%   +0.98%     
- Complexity                                   3069     3140      +71     
==========================================================================
  Files                                         126      128       +2     
  Lines                                       12007    12352     +345     
==========================================================================
+ Hits                                         9741    10143     +402     
+ Misses                                       2266     2209      -57     
Flag Coverage Δ
unit 82.11% <94.45%> (+0.98%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Content_Write and Content_Write_Rest read as the native/REST pair the read
ability has, and there is no such pair here: the endpoint is the only way
these abilities write. One class says that without a docblock having to.
@jeffpaul jeffpaul added this to the 1.5.0 milestone Sep 17, 2026
@jeffpaul jeffpaul moved this from Triage to In progress in WordPress AI Roadmap Sep 17, 2026
@jeffpaul jeffpaul modified the milestones: 1.5.0, Future Release Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants