Skip to content

3.3.9 - #1360

Open
gabrielcld2 wants to merge 131 commits into
masterfrom
uat
Open

3.3.9#1360
gabrielcld2 wants to merge 131 commits into
masterfrom
uat

Conversation

gabriel-detassigny and others added 30 commits September 23, 2026 13:58
The tester forced Cloudinary into the update list without checking the
installed version, and self-deactivated via a hard-coded folder name that
often did not match, so it could keep re-injecting the update forever.

Compare against the installed version, drop a previously injected update
once it is installed, use plugin_basename() for deactivation, and give the
tester zip a top-level folder.
…e-loop

Fix update tester offering an already-installed version
Bumps [@codemirror/state](https://github.com/codemirror/state) from 6.7.4 to 6.7.5.
- [Changelog](https://github.com/codemirror/state/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/state/commits)

---
updated-dependencies:
- dependency-name: "@codemirror/state"
  dependency-version: 6.7.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.4.2 to 18.0.2.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](motdotla/dotenv@v17.4.2...v18.0.2)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 18.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…as the last word

WordPress applies its own filter after image_downsize() has already resolved, and CDN/
image-optimization plugins commonly target it directly -- so a competing plugin, or filter
ordering on image_downsize, could leave a local URL in place with no second chance to correct
it. The new filter is a cheap guard: it bows out immediately if the URL is already a Cloudinary
URL, and otherwise reuses the same deliverable/re-entrancy checks as filter_downsize(), so it
only does work in the rare case something else missed. Also extracts the shared save-state/
re-entrancy guard (in_downsize, content_save_pre, is_saving_metadata, video exclusion) into
Media::is_replacement_paused(), reused by filter_downsize(), the new filter, and
attachment_url() (WPP-1183).
…de blind spot

image_downsize()/wp_get_attachment_image_src() are exercised by ordinary front-end theme
code (e.g. the_post_thumbnail()), not just wp-admin screens -- manual QA confirmed a
featured image rendered by Twenty Twenty-Five's front-end template hit the filter, so
scoping correction to Utils::is_admin() (admin screens only) left the front end exposed
to the same "something overwrote the URL after image_downsize()" problem this hook was
added to guard against. Registers both filters unconditionally instead of inside
add_live_url_filters()'s admin-only set; that method's other filters are untouched.

Also fixes a bug the broadened testing surfaced: wp_get_attachment_image_src() passes the
caller's $icon argument straight through to the filter regardless of whether a real image
was actually found (it just means "fall back to a mime icon if none is found"), but
filter_attachment_image_src() treated $icon as "this is a fallback icon" and bowed out
unconditionally. WP_Media_List_Table's list-mode thumbnail column always calls with
icon=true, even for real synced images, which silently blocked correction there while
grid mode (driven by image_downsize() instead) worked fine. Dropping the $icon check is
safe: the existing is_deliverable()/cloudinary_id() checks already leave a genuine icon
fallback alone, since an unsynced/non-deliverable attachment has no cloudinary_id to
correct it to (WPP-1183).
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.14 to 2.2.16.
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [php-stubs/woocommerce-stubs](https://github.com/php-stubs/woocommerce-stubs) from 11.1.0 to 11.1.2.
- [Commits](php-stubs/woocommerce-stubs@v11.1.0...v11.1.2)

---
updated-dependencies:
- dependency-name: php-stubs/woocommerce-stubs
  dependency-version: 11.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Moving the docblock above the if statement (rather than immediately before the
apply_filters() call it documents) broke PHPStan's parsing of the @PARAM tag,
caught by composer phpstan ahead of opening the PR. Keeps the same relative
position as before, just inside the inverted condition.
…placement_paused() call

Addresses a readability nit from PR review: the bool was unexplained at the call
site, requiring a trip to the method signature to know what it toggles.
…lop/codemirror/state-6.7.5

chore(deps): bump @codemirror/state from 6.7.4 to 6.7.5
…lop/dotenv-18.0.2

chore(deps-dev): bump dotenv from 17.4.2 to 18.0.2
…php-stubs/woocommerce-stubs-11.1.2

chore(deps-dev): bump php-stubs/woocommerce-stubs from 11.1.0 to 11.1.2
…phpstan/phpstan-2.2.16

chore(deps-dev): bump phpstan/phpstan from 2.2.14 to 2.2.16
Bumps [eslint](https://github.com/eslint/eslint) from 10.10.0 to 10.11.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.10.0...v10.11.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [cssnano](https://github.com/cssnano/cssnano) from 9.0.4 to 9.1.2.
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/compare/cssnano@9.0.4...cssnano@9.1.2)

---
updated-dependencies:
- dependency-name: cssnano
  dependency-version: 9.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…lop/eslint-10.11.0

chore(deps-dev): bump eslint from 10.10.0 to 10.11.0
…lop/cssnano-9.0.5

chore(deps-dev): bump cssnano from 9.0.4 to 9.1.2
The project has no TypeScript and eslint.config.js does not use
@typescript-eslint/eslint-plugin directly. @wordpress/eslint-plugin
already brings it in through typescript-eslint, with a matching
parser version.

The direct entry made Dependabot bump only the plugin, which broke
npm ci with a peer conflict on @typescript-eslint/parser (#1292).
…nt-plugin

chore(deps): Remove unused typescript-eslint plugin
… filter

Utils::is_saving_metadata() checked did_action(), which is cumulative for the
whole request: any post/term/user meta write earlier in the request (a view
counter, a session plugin, etc.) would leave it true for the rest of the page
once filter_attachment_image_src()/filter_downsize() started running on the
front end, permanently blocking correction. Switches to doing_action() to
detect a write actually in progress. It has a single caller in the plugin
(Media::is_replacement_paused()), so this is a contained fix.

filter_attachment_image_src() also treated every deliverable+synced attachment
as correctable, but Delivery::is_deliverable() returns true for any non-image,
non-video type -- so a synced non-image attachment (audio, zip, docx, ...)
requested with icon=true (as WP_Media_List_Table always does) would have its
generic mime icon replaced with the raw asset's Cloudinary URL, breaking the
resulting <img> tag. Scopes correction to wp_attachment_is_image() ||
is_preview_only(), matching the preview-capable-formats carve-out
filter_downsize() already uses.

Test changes: added coverage for both fixes, including a regression test for
an unrelated earlier meta write no longer blocking correction. Also fixed
several existing tests that were passing for the wrong reason -- they relied
on wp_attachment_is()/wp_attachment_is_image(), which return false
unconditionally without a real _wp_attached_file postmeta regardless of
post_mime_type, and a couple never set stub_cloudinary_url, so the asserted
"stays unchanged" outcome held even with the check they meant to cover
disabled. Every bow-out path was mutation-tested (temporarily disabled,
confirmed the test fails, restored) rather than assumed correct.
Publish a post with a Cloudinary Gallery block and assert the front
end renders exactly N images, that every src/srcset is served from
res.cloudinary.com/<cloud_name>/, and that switching the layout from
expanded to classic in the editor and saving changes the rendered
widget markup.

Move expectCloudinaryUrl() to utils/delivery.js so the image and
gallery delivery specs share one assertion.
…achment-image-src

fix: hook wp_get_attachment_image_src to keep Cloudinary URLs as the last word
…nary-e2e

test(e2e): Add Gallery block front-end delivery spec
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.16 to 2.2.17.
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [eslint](https://github.com/eslint/eslint) from 10.11.0 to 10.12.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.11.0...v10.12.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [globals](https://github.com/sindresorhus/globals) from 17.12.0 to 17.13.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.12.0...v17.13.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.13.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@codemirror/state](https://github.com/codemirror/state) from 6.7.5 to 6.7.6.
- [Changelog](https://github.com/codemirror/state/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/state/commits)

---
updated-dependencies:
- dependency-name: "@codemirror/state"
  dependency-version: 6.7.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
gabrielcld2 and others added 30 commits November 2, 2026 08:31
…e-delivery

fix(delivery): track Cloudinary src URLs back to their attachment
…livery relation

An asset inserted from the DAM with a crop or effects stages those
transformations in post meta until it gets a delivery relation.
create_size_relation() moves them into the new row and deletes the post meta,
but create_delivery() then restored the transformations it had captured before
the row existed (none). The restore goes through a relationship save deferred
to shutdown, so it overwrote the migrated value with null and the
transformations were lost for good. The capture also came from
Relate::get_transformations(), which is memoized per request, so recreating the
relation again in the same request kept capturing the stale value.

Recreating an existing relation also baked its overlays into the
transformations column, as the capture merged them in, and dropped the overlay
columns.

Capture the transformations and overlays from the relationship data instead,
each in its own column, and write them with the new relation itself rather
than restoring them afterwards. Transformations staged in post meta still take
precedence.
The revert test saved webp/60 and then auto/auto before any render,
so it passed even if the plugin ignored the settings. Render and check
webp/60 first, then revert and check f_auto/q_auto on the same image.

f_ and q_ are added at render time, not at sync time, so upload and
sync one image in beforeAll instead of one per test. Record each ID as
soon as it exists so a partial setup is still cleaned up.
test(e2e): Add global image transformations spec
…ions

Keep DAM transformations and overlays when recreating a delivery relation
…668061d89d1468be7

[Snyk] Upgrade human-format from 1.2.1 to 1.4.0
…lop/dotenv-18.0.9

chore(deps-dev): bump dotenv from 18.0.8 to 18.0.9
…lop/chart.js-4.6.1

chore(deps): bump chart.js from 4.5.1 to 4.6.1
…lop/webpack-5.112.2

chore(deps-dev): bump webpack from 5.112.0 to 5.112.2
…lop/npm-run-all2-9.0.4

chore(deps-dev): bump npm-run-all2 from 9.0.3 to 9.0.4
…phpstan/phpstan-2.3.2

chore(deps-dev): bump phpstan/phpstan from 2.3.1 to 2.3.2
…30933cd2f0dea414e

[Snyk] Upgrade @codemirror/state from 6.7.4 to 6.7.6
…55196520238cc469b

[Snyk] Upgrade @codemirror/state from 6.7.4 to 6.7.6
…lop/wordpress-c9a6458842

chore(deps-dev): bump the wordpress group across 1 directory with 13 updates
Cloudinary transformation segments such as w_800,h_600 contain commas,
so splitting srcset on every comma broke each candidate into pieces.
Parse candidates by their trailing descriptor instead and expose the
descriptor through parseSrcset().
Cover the responsive breakpoints and lazy load delivery settings on a
synced image:

- min/max/step produce the matching srcset width entries
- lazy load renders the SVG placeholder, data attributes and the
  CLDLB loader config with the blur placeholder
- turning either setting off removes its markup
The regex only ended a candidate at a comma followed by whitespace or
at the end of the string, and quietly returned what it had so far
when it did not match. Valid input such as "a.jpg 1x,b.jpg 2x" gave an
empty list, which made srcset checks pass without checking anything.

Read a URL as a run of non-whitespace characters, as the HTML spec
does, so commas inside Cloudinary URLs still stay part of the URL.
- Move saveSettings() and readSettings() to tests/e2e/utils/settings.js
  and base64-encode the JSON, so a quote in a value cannot break the
  PHP string.
- Match the breakpoint w_ parameter exactly.
- Check that every width left after breakpoints are off is a
  registered image size.
- Stop the CLDLB regex at the end of the line.
- Explain why the lazy load test also turns breakpoints off.
download_asset() returns a WP_Error when the download fails, but
import_asset() only caught exceptions, so the error was dropped and the
REST endpoint answered with success. The editor got a broken attachment
with no file, and handle_failed_download() never ran.

- import_asset() now returns the download error, so the attachment is
  deleted and the error is sent to the media modal.
- handle_failed_download() logs the attachment ID, public_id and error
  to the plugin debug log and the PHP error log.
- download_asset() deletes the file it primed for the download when the
  request fails or the response isn't a 200, instead of leaving an
  empty file in the uploads folder.
The endpoint only required the upload_files capability and used the
posted attachment ID as is. Now that a failed download deletes the
attachment, any user able to upload files could delete any attachment
by posting its ID with a URL that can't be downloaded.

- The endpoint now requires the attachment to exist and the user to be
  able to edit it.
- A failed download only deletes an attachment that has no file yet and
  that the user can delete.
- The logged public_id is read from the stored meta, instead of one
  generated when it's missing.
- The error, which can come from a remote response header, is sanitized
  before it's logged or returned.
- The primed file is also removed when an exception is thrown during
  the download.
Deleting the attachment of a failed import also ran the delete sync,
which sends a destroy request for the asset on Cloudinary: a new import
already counts as synced before its file is downloaded. The request
currently fails because the attachment has no resource type yet, but
the source asset the user picked must never be at risk. The cleanup
now deletes the attachment in WordPress only.

The exception cleanup in download_asset() also uses a flag to know
whether the primed file became the attachment's file, instead of
comparing paths that a filter can rewrite.
The DAM page called the asset download endpoint after every insert,
including assets already in the Media Library, which have no attachment
to download into. The endpoint now rejects that request, and with no
error handler the import progress never cleared. A failed download was
also shown as a success.

- Only call the download endpoint for new assets, as the media modal
  does.
- Show the error when the download fails or the request is rejected.
- Always clear the import progress.
…lazy-load-e2e

test(e2e): Add breakpoints and lazy load spec
…orts

Report failed DAM imports instead of claiming success

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants