Skip to content

[6.x] New Asset Exists Modal Resolution Feature#14433

Open
jackmcdade wants to merge 12 commits into6.xfrom
asset-exists-modal
Open

[6.x] New Asset Exists Modal Resolution Feature#14433
jackmcdade wants to merge 12 commits into6.xfrom
asset-exists-modal

Conversation

@jackmcdade
Copy link
Copy Markdown
Member

@jackmcdade jackmcdade commented Apr 3, 2026

Works with uploading and moving assets. Resolves #14381.

asset-conflicts.mp4

Note

Medium Risk
Changes asset upload and move flows to return/handle conflict metadata and apply overwrite/keep-both policies, which affects file operations and cache invalidation. Risk is moderate due to new client-side state machines/queues and backend conflict handling paths.

Overview
Adds first-class asset conflict resolution for both uploading and moving files, replacing the per-upload dropdown UI with centralized modals that offer Cancel, Keep Both (timestamp), and Overwrite, with optional apply-to-all behavior.

Backend now surfaces structured conflict details for uploads (409 response includes conflict payload) and moves (MoveAsset detects destination collisions and either resolves via strategy or throws AssetConflictException captured by ActionController to return conflict context and completed_moves). The move logic also clears Glide caches on overwrite and Asset::move() now forgets the old meta cache key.

Uploader/browser wiring was updated to support bulk drag-move selections, queue multiple upload conflicts, continue move operations across multiple selections with ID remapping, and bust/reload image caches after overwrite/timestamp resolutions; tests were added/updated to cover move conflict strategies and Glide cache clearing on upload.

Reviewed by Cursor Bugbot for commit 561b096. Bugbot is set up for automated code reviews on this repo. Configure here.

Works with uploading and moving assets. Resolves #14381.
This updates the asset conflict UX by making modal-dismiss behavior resolve as cancel, only showing “apply to all” when multiple upload conflicts exist, and replacing recursive move-conflict continuation with an iterative loop. It also adds MoveAsset tests for non-conflicting moves, same-folder no-op moves, and explicit cancel strategy handling.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 561b096. Configure here.

actionFailed(response = {}) {
this.$toast.error(response.message || __('Action failed'));
this.$refs.listing.refresh();
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

New actionFailed duplicates existing actionCompleted failure path

Low Severity

The new actionFailed method is functionally identical to calling actionCompleted(false, response). Both show an error toast with response.message || __('Action failed') and call this.$refs.listing.refresh(). continueMoveConflictResolution uses actionFailed in three places where actionCompleted(false, response) would produce the exact same behavior, introducing a redundant code path that diverges from the standard pattern.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 561b096. Configure here.


if ($index > 0) {
$filename .= '-'.$index;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Timestamp index suffix skips zero for first conflicting asset

Low Severity

The timestamp strategy uses $index > 0 from the outer foreach to decide whether to append an index suffix, but $index is the position in the full $assets collection, not relative to conflicting assets. If the first batch asset doesn't conflict but the second does (at $index = 1), it still gets a -1 suffix. Meanwhile, a single conflicting asset sent from the frontend's per-asset resolution always has $index = 0 and never gets a suffix. While moveUnique prevents actual collisions, the logic is misleading—it conflates "first item in the collection" with "doesn't need a disambiguation suffix."

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 561b096. Configure here.

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.

Moving assets into a subfolder silently overwrites existing files

1 participant