Skip to content

Media Library: find attachments not used anywhere on the site - #84300

Open
Vrishabhsk wants to merge 4 commits into
WordPress:trunkfrom
Vrishabhsk:add/media-usage-detection
Open

Vrishabhsk wants to merge 4 commits into
WordPress:trunkfrom
Vrishabhsk:add/media-usage-detection

Conversation

@Vrishabhsk

Copy link
Copy Markdown
Contributor

Related to #79784. This covers the first of the issue's two asks ("find media not used anywhere"); the second ("merge near-duplicate files") is not part of this change.

What?

Adds a "Unused" view to the Media Library so people can find media that is not referenced by any post, page, template, or pattern — the files that are candidates for cleanup.

Concretely:

  • A new used signal on attachments, exposed through the REST API:
    • GET /wp/v2/media?used=false → only unused attachments.
    • GET /wp/v2/media?used=true → only used attachments.
    • GET /wp/v2/media?include_used=true → includes the computed used boolean on each item.
  • A new "Unused" tab in the DataViews attachment list (the extensible site editor's Media screen), which locks the used field to "Unused".
  • A read-only Usage field/column (Used / Unused) that can be added through the view options or the "Add filter" menu.

Why?

WordPress's existing "Attached to" relation only records where a file was uploaded from. An image inserted into another post, a featured image, a logo, or a file placed via the library all read as "unattached" even though they are in use. That misleads people cleaning up storage into deleting files the site still shows.

This computes a stronger, conservative "is this file referenced anywhere?" signal:

  • a block attribute holding an attachment ID (core/image, core/cover, core/audio, core/video, core/file, core/playlist-track, core/media-text, core/gallery);
  • classic markup — wp-image-<id> / wp-attachment-<id>, [gallery ids="…"], and the attachment URL;
  • featured images;
  • the site logo, site icon, and classic-theme logo.

Detection is deliberately conservative: any reference found counts, and ambiguity resolves to "used". A false "unused" is what leads to deleting a file that is still shown — the dangerous direction — so a file is only reported unused when no reference was found.

How?

  • lib/media/class-gutenberg-media-usage.php — computes the used set by parsing post content with parse_blocks() (not regex over serialized markup) and reading a block→attribute map, mirroring media-id attributes in packages/editor/src/store/utils/attach-media-in-post/media-ids-in-blocks.js. The result is memoized per request and invalidated on content/meta/option writes. Extensible through gutenberg_media_usage_scanned_post_types, gutenberg_media_usage_media_id_attributes, gutenberg_used_attachment_ids, and gutenberg_attachment_is_used.
  • REST — the used filter maps to post__in / post__not_in; include_used gates the computed field. Both are opt-in, because detection reads every post's content, so ordinary Media Library browsing and the editor's media modal are unaffected.
  • packages/media-fields — new usedField, registered in ORDERED_MEDIA_FIELDS so both the attachment list and the media editor consume it through usePostFields, instead of special-casing attachment in the post-list route.
  • routes/post-list — translates the used filter (and a visible used column) to the REST query in viewToQuery().

Testing Instructions

Automated

# JS unit tests — field + query translation
npm run test:unit:vitest -- packages/media-fields routes/post-list/test

# PHP tests — detection + REST filter
npm run wp-env-test start
npm run wp-env-test -- run tests-cli -- vendor/bin/phpunit --filter Gutenberg_Media_Usage

Manual

  1. Start the test site and enable the experiments:
    npm run wp-env-test start
    wp-env run cli -- wp plugin activate gutenberg
    wp-env run cli -- wp option update gutenberg-experiments --format=json \
      '{"gutenberg-extensible-site-editor":true,"gutenberg-media-editor":true}'
  2. Import two images. Insert one into a post so it is in use; leave the other unused.
  3. Open the Media Library list:
    admin.php?page=site-editor-v2&p=/types/attachment/list/all
  4. Unused tab — a new "Unused" tab appears. Clicking it lists only the unused file and hides the used one.
  5. Usage column — the USAGE column shows Used / Unused for each file.
  6. Filter — "Add filter → Usage → Unused" produces the same result as the tab.
  7. Live detection — remove the image from the post and save; it now appears under the Unused tab.

Screenshots / screencast

02-media-library-all 03-unused-tab 05-add-filter-menu

Notes / open questions

  • In the issue, @ramonjd suggested limiting the scope to featured images and post content. This implementation goes wider (block attributes, shortcodes, URLs, featured images, site logo/icon). Wider detection means fewer false "unused", but it is a scope decision worth confirming.
  • The feature lands on the DataViews Media Library view in the extensible site editor (the direction tracked by Phase 3: Collaboration > Media Library #55238 / Media Library: Create a DataViews-powered representation of the media library #72735), not the classic upload.php screen. There is currently no admin-menu entry for that list; it is reached via URL or the media editor's breadcrumb until the new library ships more broadly.

Use of AI Tools

This change was developed with opencode using the deepseek-v4.1-flash:cloud model served through Ollama Cloud. The model was used to research the codebase, implement the PHP detection and REST layer, the used field and its registration, the post-list query translation, and the unit tests, and to run the feature end to end against a local wp-env site.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

🎉 Props

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: Vrishabhsk <vrishabhsk@git.wordpress.org>

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

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

🏷️ Labels

This pull request needs exactly one label indicating its type, and has 0.

  • Required: any label starting with [Type].
  • Found: none.

Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you.

@github-actions github-actions Bot added the [Package] Editor /packages/editor label Oct 8, 2026

This branch has not been deployed

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

Labels

[Package] Editor /packages/editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant