Skip to content

Media Utils: Add media.slimImageObject filter to preserve custom attachment fields#79539

Open
thisismyurl wants to merge 7 commits into
WordPress:trunkfrom
thisismyurl:fix/slim-image-object-meta-fields
Open

Media Utils: Add media.slimImageObject filter to preserve custom attachment fields#79539
thisismyurl wants to merge 7 commits into
WordPress:trunkfrom
thisismyurl:fix/slim-image-object-meta-fields

Conversation

@thisismyurl

Copy link
Copy Markdown

What?

Fixes #64415

`slimImageObject` in `packages/media-utils` is called inside the `onUpdate` handler — the path that fires for gallery and multi-image selection flows in the classic media library. It reduces the WordPress attachment object to a fixed 9-field allowlist before forwarding to the block's `onSelect` prop callback, silently dropping any custom attachment meta.

Why?

Plugins that extend WordPress attachments with custom meta (via `register_post_meta`, `attachment_fields_to_edit`, or REST API extensions) have no way to surface those fields to blocks using `MediaUpload` in the gallery/multi-image flow. The slimmed object is all the block receives; custom fields are dropped unconditionally regardless of what was registered.

How?

  • Wraps the existing allowlist reduction in `applyFilters( 'media.slimImageObject', baseFields, img )`, passing both the slimmed base object and the full original attachment so filters have access to all data.
  • Exports `slimImageObject` as a named export for unit testability and advanced consumers.
  • Declares `@wordpress/hooks` as an explicit dependency (required by monorepo tooling; it was already available transitively but not declared).
  • Adds a `test/` directory with two unit tests: one asserting the default allowlist behavior is unchanged; one registering a `media.slimImageObject` filter and asserting custom fields survive (delete-the-fix guard — removing the `applyFilters` call causes it to fail).

Usage after this change:

addFilter(
    'media.slimImageObject',
    'my-plugin/slim-image-object',
    ( slimmed, img ) => {
        if ( img.hasOwnProperty( 'license_id' ) ) {
            slimmed.license_id = img.license_id;
        }
        return slimmed;
    }
);

Note on scope: `onSelect()` (the `select` event / single-image flow) does not call `slimImageObject` and is unchanged by this PR. The filter applies only to the `onUpdate()` path where `slimImageObject` is currently called.

Testing Instructions

  1. In a plugin, register a meta field for attachments:

     add_action( 'init', function() {
         register_post_meta( 'attachment', 'my_custom_field', [
             'show_in_rest' => true,
             'type'         => 'string',
             'single'       => true,
         ] );
     } );
    
  2. Register the `media.slimImageObject` filter in your block's JS:

     addFilter(
         'media.slimImageObject',
         'my-plugin/filter',
         ( slimmed, img ) => {
             slimmed.my_custom_field = img.my_custom_field;
             return slimmed;
         }
     );
    
  3. Open a Gallery block, open the media library, select images.

  4. In the block's `onSelect` callback, confirm `my_custom_field` is present.

Default behavior (unchanged): Open any media library and select images; confirm the attachment object has only the standard allowlist fields when no filter is registered.

Unit tests: `npm run test:unit -- --scope='@wordpress/media-utils'`

Props @thisismyurl.

(full disclosure: AI helped me identify the issue and verify my work)

…chment fields

`slimImageObject` is called when a user selects media from the WordPress
media library and reduces the full attachment object to a small subset of
fields before passing it to the block's `onSelect` callback. Because the
field list is a closed allowlist, any custom attachment meta added by
plugins (e.g. licensing data, alt-text service flags, focal point coords)
is silently dropped.

This commit adds a `media.slimImageObject` WordPress hook so that blocks
and plugins can opt in to preserving additional fields:

    addFilter(
        'media.slimImageObject',
        'my-plugin/slim-image-object',
        ( slimmed, img ) => {
            if ( img.hasOwnProperty( 'license_id' ) ) {
                slimmed.license_id = img.license_id;
            }
            return slimmed;
        }
    );

Changes:
- `packages/media-utils/src/components/media-upload/index.js` — import
  `applyFilters` from `@wordpress/hooks`; store the base-field reduction
  in `baseFields` and return `applyFilters( 'media.slimImageObject', baseFields, img )`;
  export the function as a named export so tests and advanced consumers
  can import it directly.
- `packages/media-utils/src/components/index.ts` — re-export `slimImageObject`
  from the components barrel.
- `packages/media-utils/package.json` — add `@wordpress/hooks` as a
  declared dependency (it was already available transitively but must be
  declared for the monorepo linter).
- `packages/media-utils/src/components/media-upload/test/index.test.js` —
  new unit tests: verifies the standard-fields-only default, then registers
  a `media.slimImageObject` filter and asserts the custom field survives.

Closes WordPress#64415

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

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. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: thisismyurl <thisismyurl@git.wordpress.org>
Co-authored-by: mrleemon <leemon@git.wordpress.org>

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

@github-actions github-actions Bot added [Package] Media Utils /packages/media-utils First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository labels Jun 25, 2026
@github-actions

Copy link
Copy Markdown

👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @thisismyurl! In case you missed it, we'd love to have you join us in our Slack community.

If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information.

thisismyurl and others added 3 commits June 25, 2026 12:48
Move @wordpress/hooks to correct alphabetical position (between
@wordpress/element and @wordpress/i18n). Fixes 'Lint package.json
files' CI failure on PR WordPress#79539.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…te API docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Failing checks: All (Node.js 24 on Linux)
Copilot AI review requested due to automatic review settings July 20, 2026 13:54
@github-actions

Copy link
Copy Markdown

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: First-time Contributor, [Package] Media Utils.

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a media.slimImageObject filter hook to @wordpress/media-utils so blocks using MediaUpload can preserve plugin-added attachment fields (custom meta) in the onUpdate() path used by gallery/multi-image flows.

Changes:

  • Wraps the existing slimImageObject allowlist reduction with applyFilters( 'media.slimImageObject', baseFields, img ) and exports slimImageObject.
  • Adds unit tests to verify default allowlist behavior remains unchanged and that filters can preserve custom fields.
  • Declares @wordpress/hooks as an explicit dependency and updates package exports/docs accordingly.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/media-utils/src/components/media-upload/index.js Adds the media.slimImageObject filter application and exports slimImageObject.
packages/media-utils/src/components/media-upload/test/index.test.js Adds unit tests covering default behavior and the new filter behavior.
packages/media-utils/src/components/index.ts Re-exports slimImageObject alongside MediaUpload.
packages/media-utils/package.json Adds @wordpress/hooks as an explicit dependency.
packages/media-utils/tsconfig.json Adds a project reference to ../hooks to match the new dependency.
packages/media-utils/README.md Updates the generated API surface listing to include slimImageObject.

Comment on lines +34 to +58
it( 'passes the full attachment to the media.slimImageObject filter', () => {
const filterName = 'media.slimImageObject';
const namespace = 'test/slim-image-object';

addFilter( filterName, namespace, ( slimmed, img ) => {
if ( img?.hasOwnProperty( 'custom_meta' ) ) {
slimmed.custom_meta = img.custom_meta;
}
return slimmed;
} );

const img = {
id: 2,
url: 'https://example.com/img.png',
alt: '',
custom_meta: 'plugin-data',
};

const result = slimImageObject( img );

expect( result.id ).toBe( 2 );
expect( result.custom_meta ).toBe( 'plugin-data' );

removeFilter( filterName, namespace );
} );
Comment on lines +232 to +234
// The media library image object contains numerous attributes
// we only need this set to display the image in the library.
const slimImageObject = ( img ) => {
export const slimImageObject = ( img ) => {
thisismyurl and others added 3 commits July 20, 2026 11:46
The package-lock.json entry for packages/media-utils was not updated
when @wordpress/hooks was added to package.json. Running npm install
in CI would regenerate the lock file and cause check-local-changes to
fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses two review comments on WordPress#79539.

The test registered `media.slimImageObject` and removed it after the
assertions, so a failing assertion would leave the filter registered and
leak it into later tests. Cleanup now runs in `afterEach`, which executes
whether or not the test body throws. Removing a filter that was never
added is a no-op, so the shared hook is safe for both tests.

The comment above `slimImageObject` described only the field reduction and
did not mention that the result is filterable. It is now a docblock that
names the `media.slimImageObject` hook and its two arguments, so the
extension point is discoverable from the function it belongs to.

As a side effect the generated API reference now documents
`slimImageObject`, which previously read "Undocumented declaration".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thisismyurl

Copy link
Copy Markdown
Author

Thanks for the review. Both points are addressed in ca1a80b, and I have merged trunk to clear the CI failure.

Filter cleanup. Agreed, a failing assertion would have left the filter registered. I used afterEach rather than try/finally, since it runs regardless of how the test body exits and keeps the assertions uncluttered. Removing a filter that was never added is a no-op, so running it for both tests is safe.

Documenting the hook. The inline comment is now a docblock naming media.slimImageObject and its two arguments, so the extension point is discoverable from the function itself. One side benefit: the generated API reference entry for slimImageObject previously read "Undocumented declaration" and now describes both the function and the filter.

On the earlier CI failure, in case anyone else hits it: check-root-dependencies was reporting @typescript/native as newly added to the root package.json. That was a false positive caused by this branch sitting 590 commits behind trunk, where that dependency now lives. Merging trunk resolved it. The branch does not touch the root package.json.

Verified locally: both tests pass, and removing the applyFilters call fails the filter test while leaving the other one passing, so the test is a genuine regression guard rather than a test of itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository [Package] Media Utils /packages/media-utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

slimImageObject drops meta fields added to attachments

2 participants