Skip to content

Fix: feature image virtual media - #2103

Open
KMchaudhary wants to merge 2 commits into
developfrom
fix/feature-image-virtual-media
Open

Fix: feature image virtual media#2103
KMchaudhary wants to merge 2 commits into
developfrom
fix/feature-image-virtual-media

Conversation

@KMchaudhary

Copy link
Copy Markdown
Collaborator

Fixes: #1977

This pull request introduces robust support for using GoDAM media items as featured images in both the block and classic editors. The main changes ensure that placeholder GoDAM IDs are properly handled and swapped for real WordPress attachment IDs once the media entry is created, preventing issues where invalid IDs could be saved or displayed. The implementation includes new logic for the classic editor, improved handling in the block editor, and comprehensive unit tests.

Featured image handling improvements:

  • Added classic-featured-image.js to intercept and defer setting GoDAM IDs as featured images in the classic editor, only applying the real attachment ID once it exists, and preventing invalid IDs from being saved or displayed.
  • Updated godam-media-frame-shared.js to invoke the new classic editor logic (setupClassicFeaturedImage and resolveClassicFeaturedImage) during GoDAM tab activation and after attachment creation. [1] [2] [3]

Block editor and post entity integration:

  • Replaced the logic for updating the featured image in the block editor: added replaceVirtualIdInFeaturedImage() to swap the GoDAM placeholder ID for the real attachment ID on the post entity, ensuring the correct image is displayed and saved.

Testing and reliability:

  • Added classic-featured-image.test.js with comprehensive tests for the new classic editor featured image deferral and resolution logic, covering edge cases and ensuring correct integration.

Codebase simplification:

  • Removed the now-unnecessary checkIfFeatureImage() logic and related conditional checks, as the new approach directly handles both block and classic editor contexts. [1] [2]

Demo

Screen.Recording.2026-09-01.at.11.19.22.AM.mov

KMchaudhary added 2 commits August 31, 2026 15:55
checkIfFeatureImage() hid the tab from the sidebar Featured image panel
and the classic editor's frame. The panel needs nothing new -- its pick
goes through editPost(), so the existing repoint handles it. The classic
editor calls featuredImage.set() before we can repoint, and
get-post-thumbnail-html casts the job docname with (int), silently
attaching an unrelated image -- so park the pick and release the real ID
once create-media-entry answers. Covered by 9 unit tests.
Copilot AI balanced review requested due to automatic review settings September 1, 2026 05:53
@KMchaudhary KMchaudhary changed the title Fix/feature image virtual media Fix: feature image virtual media Sep 1, 2026

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

Adds reliable GoDAM featured-image handling across WordPress editors.

Changes:

  • Replaces virtual IDs in block-editor post entities.
  • Defers classic-editor updates until attachment creation.
  • Adds classic-editor unit tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
godam-media-frame-shared.js Integrates featured-image ID resolution.
classic-featured-image.js Implements classic-editor deferral.
classic-featured-image.test.js Tests deferral and resolution behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +102 to +104
export function resolveClassicFeaturedImage( virtualMediaId, realId ) {
if ( null === deferredVirtualId || String( deferredVirtualId ) !== String( virtualMediaId ) ) {
return;
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🔍 WordPress Plugin Check Report

⚠️ Status: Passed with warnings

📊 Report

🎯 Total Issues ❌ Errors ⚠️ Warnings
16 0 16

⚠️ Warnings (16)

📁 composer.json (1 warning)
📍 Line 🔖 Check 💬 Message
0 missing_composer_json_file The "/vendor" directory using composer exists, but "composer.json" file is missing.
📁 readme.txt (2 warnings)
📍 Line 🔖 Check 💬 Message
0 mismatched_plugin_name Plugin name "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" is different from the name declared in plugin header "GoDAM".
0 trademarked_term The plugin name includes a restricted term. Your chosen plugin name - "GoDAM - Organize WordPress Media Library & File Manager with Unlimited Folders for Images, Videos & more" - contains the restricted term "wordpress" which cannot be used at all in your plugin name.
📁 assets/build/blocks/godam-gallery-v2/render.php (2 warnings)
📍 Line 🔖 Check 💬 Message
15 WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound Global variables defined by a theme/plugin should start with the theme/plugin prefix. Found: "$inner_block_video_ids".
23 WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound Global variables defined by a theme/plugin should start with the theme/plugin prefix. Found: "$inner_block_video_ids".
📁 assets/build/css/main.css (1 warning)
📍 Line 🔖 Check 💬 Message
0 EnqueuedStylesScope This style is being loaded in all contexts.
📁 assets/src/libs/analytics.min.js (5 warnings)
📍 Line 🔖 Check 💬 Message
0 EnqueuedScriptsScope This script is being loaded in all frontend contexts.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880 (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/2026/09/01/hello-world/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/sample-page/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/demo-attachment-post/ (with handle analytics-library) is loaded in the footer. Consider a defer or async script loading strategy instead.
📁 assets/build/js/main.min.js (5 warnings)
📍 Line 🔖 Check 💬 Message
0 EnqueuedScriptsScope This script is being loaded in all frontend contexts.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880 (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/2026/09/01/hello-world/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/sample-page/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.
0 NonBlockingScripts.NoStrategy This script on http://localhost:8880/demo-attachment-post/ (with handle rtgodam-script) is loaded in the footer. Consider a defer or async script loading strategy instead.

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

@subodhr258 subodhr258 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review notes on the featured-image virtual-media fix. The core-frame wiring, wp.media.frame reset path, and the sync-editPost / async-create ordering all check out. A few things worth a look, correctness first:

// attributes, so it needs its own swap. The classic editor gets no
// placeholder at all — its pick was parked, and is released here.
replaceVirtualIdInFeaturedImage( data.id, attachment.id );
resolveClassicFeaturedImage( data.id, attachment.id );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Failed create-media-entry silently drops the classic featured-image pick. resolveClassicFeaturedImage() only runs inside the if ( response.success ) block. If the request fails or returns non-success, the outer catch at the bottom of processGoDAMItem swallows it, the parked id is never released, and deferredVirtualId stays stuck. The meta box keeps its previous value with no error surfaced, so the user believes a featured image was set when nothing changed. Consider releasing the park (or clearing it with a visible fallback) on the failure path too.

return;
}

dispatch( 'core/editor' ).editPost( { featured_media: realId } );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Guard this editPost the way the read above it is guarded. The getEditedPostAttribute read on line 131 sits in a try/catch, but this dispatch( 'core/editor' ).editPost() does not. A throw here unwinds into processGoDAMItem's swallow-all catch, which then skips resolveClassicFeaturedImage() and both the godam-virtual-attachment-created and godam-attachment-browser:changed dispatches for this item. replaceVirtualIdInCoreImageBlocks is self-guarded, so this is the one exposed call in that block. Wrapping it in the same defensive guard keeps a bad dispatch from taking out the events other code depends on.


featuredImage.set = function( id ) {
if ( isDeferrableGoDAMId( id ) ) {
deferredVirtualId = id;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Save-before-resolve keeps the old featured image (classic editor). Parking suppresses the original set(), so the meta box _thumbnail_id hidden field still holds the previous value until create-media-entry answers and resolveClassicFeaturedImage() releases the real id. If the user clicks Update/Publish inside that sub-second window, the save persists the stale id and the newly chosen image is lost until they save again. Worth confirming that timing is acceptable, or showing a brief in-flight indicator while a pick is being resolved.

};

describe( 'classic editor featured image deferral', () => {
afterEach( () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

deferredVirtualId is module state that afterEach never resets. It only deletes window.wp and restores mocks, so a test that parks a pick (e.g. 'parks a GoDAM pick') leaves deferredVirtualId set for the next test. That makes the suite order-dependent and able to pass or fail spuriously when tests are reordered or new ones are inserted before the existing ones. Drain the park in afterEach (call resolveClassicFeaturedImage to clear it, or export a small reset for tests).

const state = this.state();

// Hold back a classic-editor featured image pick until its attachment exists.
setupClassicFeaturedImage();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit (altitude): this installs a global core patch on every GoDAM tab render, not just featured-image contexts. setupClassicFeaturedImage() permanently wraps the core global wp.media.featuredImage.set the first time any GoDAM tab renders, including the Insert-Media and gallery flows where no featured image is ever picked. It is idempotent and transparent for non-deferrable ids, so not a live bug, but it widens the blast radius of a core-global override past the context the fix targets. Consider only wrapping when the frame is actually a featured-image frame.


const selected = frame.state?.()?.get?.( 'selection' )?.single?.();

return !! selected && String( selected.id ) === String( id );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit (reuse): the String( a ) === String( b ) id-equality idiom is repeated across isDeferrableGoDAMId, resolveClassicFeaturedImage, replaceVirtualIdInFeaturedImage, and replaceVirtualIdInCoreImageBlocks. A shared isSameId( a, b ) helper in the media-library utility module would keep the virtual-vs-real id comparison in one place if the matching rule ever needs to change (trimming, coercion, etc.).

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.

3 participants