Skip to content

Add core/media-upload, core/media-update, and core/media-delete abilities - #1104

Draft
jorgefilipecosta wants to merge 33 commits into
add/core-media-queryfrom
add/core-media-write-abilities
Draft

jorgefilipecosta wants to merge 33 commits into
add/core-media-queryfrom
add/core-media-write-abilities

Conversation

@jorgefilipecosta

@jorgefilipecosta jorgefilipecosta commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

What?

Adds core/media-upload, core/media-update, and core/media-delete, abilities that write media library items. Based on #1098.

How?

  • The abilities live in includes/Abilities/Media/Media.php next to core/media-query, behind the Custom Abilities experiment.
  • core/media-upload takes a file as base64 data with a filename, plus a title, caption, description, alt text, parent post, author, status, slug, and date. It can instead take the url of an image to download, with only a parent post.
  • core/media-update takes an id and the same fields.
  • core/media-delete takes an id and force. Without force it moves the item to the trash, which only works when the site enables the media trash; with force it deletes the item and its files.
  • All three return the item with core/media-query's fields, raw fields included; fields picks them. A forced delete returns { deleted, previous }.

Testing Instructions

  1. Enable the Custom Abilities experiment.
  2. Run core/media-upload with { "data": "<base64 PNG>", "filename": "test.png", "title": "Test" }, then core/media-update with the returned id and a new title, then core/media-delete with the id and "force": true.
  3. npm run test:php -- --filter 'Media|Gated_Abilities'
  4. npm run test:e2e -- tests/e2e/specs/abilities/core-media-write.spec.js

Changelog Entry

Added - New core/media-upload, core/media-update, and core/media-delete abilities that upload, edit, and delete media library items.

Open WordPress Playground Preview

@github-actions

github-actions Bot commented Oct 2, 2026

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 Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.35185% with 69 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.60%. Comparing base (6914c20) to head (c66537b).

Files with missing lines Patch % Lines
includes/Abilities/Media/Media.php 89.35% 69 Missing ⚠️
Additional details and impacted files
@@                    Coverage Diff                     @@
##             add/core-media-query    #1104      +/-   ##
==========================================================
+ Coverage                   82.27%   82.60%   +0.32%     
- Complexity                   3231     3400     +169     
==========================================================
  Files                         131      131              
  Lines                       12875    13515     +640     
==========================================================
+ Hits                        10593    11164     +571     
- Misses                       2282     2351      +69     
Flag Coverage Δ
unit 82.60% <89.35%> (+0.32%) ⬆️

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.

@jorgefilipecosta

Copy link
Copy Markdown
Member Author

Tested in the browser console of the post editor (WP 7.1.2, Excerpt Generation and Custom Abilities enabled). Every output below matched the comments.

await ( await import( '@wordpress/core-abilities' ) ).ready;
const { executeAbility } = await import( '@wordpress/abilities' );
const postId = wp.data.select( 'core/editor' ).getCurrentPostId();

// A 1x1 PNG, base64 encoded.
const data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQzwAE9QEsxQH1ggAAAABJRU5ErkJggg==';

const uploaded = await executeAbility( 'core/media-upload', {
	data,
	filename: 'console-demo.png',
	title: 'Console demo',
	alt_text: 'A one-pixel image',
	post: postId,
	fields: [ 'id', 'status', 'title_raw', 'alt_text', 'post', 'mime_type' ],
} );
console.log( 'upload', uploaded );
// { id: <new ID>, status: 'inherit', title_raw: 'Console demo', alt_text: 'A one-pixel image', mime_type: 'image/png', post: <this post's ID> }

const updated = await executeAbility( 'core/media-update', {
	id: uploaded.id,
	caption: { raw: 'Captioned from the console' },
	post: 0,
	fields: [ 'id', 'title_raw', 'caption_raw', 'post' ],
} );
console.log( 'update', updated );
// { id: <same ID>, title_raw: 'Console demo', caption_raw: 'Captioned from the console', post: null }

const read = await executeAbility( 'core/media-query', {
	id: uploaded.id,
	fields: [ 'title_raw', 'caption_raw', 'alt_text' ],
} );
console.log( 'read back', read );
// { id: <same ID>, title_raw: 'Console demo', caption_raw: 'Captioned from the console', alt_text: 'A one-pixel image' }

const notTrashed = await executeAbility( 'core/media-delete', { id: uploaded.id } ).catch( ( error ) => error );
console.log( 'delete without force', notTrashed.code );
// 'media_trash_not_supported' (unless the site enables the media trash)

const deleted = await executeAbility( 'core/media-delete', {
	id: uploaded.id,
	force: true,
	fields: [ 'id', 'title_raw' ],
} );
console.log( 'delete with force', deleted );
// { deleted: true, previous: { id: <same ID>, title_raw: 'Console demo' } }

const mixed = await executeAbility( 'core/media-upload', {
	data,
	filename: 'mixed.png',
	url: 'https://example.com/photo.png',
} ).catch( ( error ) => error );
console.log( 'data and url together', mixed.code );
// 'ability_invalid_input': an upload takes either data and a file name, or a URL

@jeffpaul

jeffpaul commented Oct 4, 2026

Copy link
Copy Markdown
Member

@jorgefilipecosta note that I'm holding off on requesting reviews until this comes out of draft status

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