3.3.6 - #1237
Open
gabrielcld2 wants to merge 49 commits into
Open
Conversation
gabrielcld2
commented
Aug 13, 2026
Collaborator
- Add Dependabot config for Composer #1235
- Fix JSON shape corruption in REST API responses breaking MCP integrations (Novamira) #1231
- Release automation adjustments #1229
- Instrument connection, sync, settings, media, cache, features, and deactivation analytics events #1226
- Upgrade npm dependencies #1223
- Ensure compatibility with WordPress 7.1 #1222
- Release process GH Action #1206
`get_size_from_slug()` returned the registered width/height from `wp_get_registered_image_subsizes()` for any matching slug, ignoring the `crop` flag. For non-crop sizes ( crop => false ) those values are a maximum bounding box, not exact dimensions: WordPress scales the image to fit inside the box while preserving aspect ratio. Treating them as a fixed crop made the delivery layer emit, for the default `large` ( 1024x1024, crop => false ), a transformation of `w_1024,h_1024,c_scale` applied to a landscape/portrait source, visibly stretching it into a square. This surfaced on blocks whose stored `<img src>` had no size suffix (so the URL-based size lookup returned nothing and the figure-class slug fallback ran). Return early for non-crop sizes so only genuine hard-crop sizes are pinned to exact dimensions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
install() called get_plugin_instance()->init() unconditionally, which re-ran the bootstrap out of sequence during plugin activation and could destabilise plugin load order for the request (e.g. triggering ACF to initialise too early). Only run init() when the plugin version has not already been populated; the DB routines only need the version.
init_asset_parents() and process_parent_assets() run internal WP_Query lookups over Cloudinary's own asset post type. These are not public-facing content queries, but they still fire third-party the_posts/posts_results filters, which can cause a theme/plugin to run expensive per-query work (e.g. author-archive injection) on every one of Cloudinary's internal queries until the request exhausts memory. Set suppress_filters => true on these fully-specified internal queries so they skip content filters. The WordPressVIPMinimum SuppressFilters rule is suppressed with an explanation: these queries do not rely on the posts_where/join/orderby filters the rule protects.
WordPress 7.1 enforces the iframed post editor and ships React 19. Update the Cloudinary Gallery block accordingly: - Bump the block to Block API v2 (compatible with the plugin's declared minimum of WP 5.6) and wrap the edit output with useBlockProps so it renders correctly inside the enforced iframed editor. - Keep the save output unchanged (bare container div) so existing published galleries remain valid without a deprecation/migration. The front-end render only depends on the inner container class, not a block wrapper, so no markup change is needed. - Move the block editor stylesheet to enqueue_block_assets so WP injects it into the iframe correctly (avoids the "added to the iframe incorrectly" 7.1 warning). - Move the render-time setAttributes calls into effects to resolve the React 19 "Cannot update a component while rendering a different component" warning. - Fix the generated container id, which produced "undefined<id>" now that className is no longer passed to Edit under API v2. Verified against WordPress 7.1-beta3: fresh insert/save/reload and the upgrade path (loading legacy v1 content) both validate with no console errors or block validation warnings.
The Cloudinary inspector controls added to core/image and core/video blocks had two WordPress 7.1 issues: - setAttributes was called during render when the attachment had transformations, which triggers the React 19 "Cannot update a component while rendering a different component" warning. Moved into an effect. - The controls read InspectorControls from the deprecated wp.editor alias. Switched to wp.blockEditor and declared the script dependencies explicitly, since the block editor globals were previously relied on without being registered.
The gallery settings page enqueued js/gallery.js under two handles: 'gallery_config' in enqueue_admin_scripts() and 'gallery-widget' via the React UI component's script param. The bundle executed twice, calling createRoot() twice on the same container, which React 19 (WordPress 7.1) reports as an error on every settings page load. Drop the script param from the React panel definition and keep the single enqueue that already carries the generated asset dependencies.
src/js/blocks.js and its components accessed @WordPress packages through the wp.* globals (wp.hooks, wp.blockEditor), which the dependency extraction plugin cannot detect, so js/block-editor.asset.php was missing wp-block-editor and wp-hooks. The hand-written dependency list in class-video.php compensated for those but omitted wp-api-fetch, which blocks.js genuinely imports and only worked because other editor scripts loaded it first. Convert the wp.hooks/wp.blockEditor global usages to @wordpress/hooks and @wordpress/block-editor imports so the generated asset file is complete, and consume it in enqueue_block_assets() instead of the static list.
…l-queries Suppress content filters on internal asset bookkeeping queries
…activation analytics events Wires the remaining 7 event categories from the analytics tracking spec on top of the existing WPP-1210 custom-events framework: connection management, asset sync, settings & navigation, media & asset actions, non-media cache, extensions & gallery, and deactivation. All call sites reuse Analytics::track() / Analytics.track() and were live-verified against wp-env via WP-CLI/REST dispatch. Adds a permanent e2e analytics-capture mu-plugin and two Playwright specs covering connection and deactivation events. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit only included src/js/* source changes. This repo ships compiled js/* output directly, so the extension_toggled, special_offer_clicked, deactivation_modal_viewed, and deactivation_skipped tracking calls weren't actually live until this rebuild. Live-verified via the full Playwright e2e suite against wp-env with real Cloudinary credentials (13/13 passing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nts-tracking # Conflicts: # php/class-admin.php # php/class-deactivation.php
develop bumped phpstan to level 5 after this branch was cut. The Analytics component (added in WPP-1210) was missing from get_component()'s @return union, so every ->track() call site read as "method.notFound" once merged. Re-verified clean at level 5 and against the full Playwright e2e suite.
Fix: don't force non-crop size slugs to exact dimensions
…e load Image_Preview::preview() set the sample <img>'s src to a bare '#', which resolves to the current document's own URL — every load of Image Settings, Video Settings, or the Responsive/Breakpoints preview (Video_Preview and Breakpoints_Preview both extend Image_Preview) fired a second real HTTP request back at the same admin page before global-transformations.js replaced it with a real preview URL. Found while investigating why settings_page_viewed fired twice per page load: confirmed via wp-env access logs that the second request carried an image/* Accept header and a self-referencing Referer, matching this exact placeholder-image bug. Fixed with an inert 1x1 data-URI placeholder instead. Re-verified: single event per load, single request in the access log, full Playwright suite still green (13/13).
Upgrade npm dependencies
…nts-tracking # Conflicts: # js/cloudinary.js # js/syntax-highlight.js # src/js/main.js
The merge commit resolved js/cloudinary.js and js/syntax-highlight.js conflicts by taking develop's compiled output, dropping the SpecialOffer wiring and the deactivate.js Prettier reformat. Rebuilding restores both on top of develop's dependency upgrades and ESLint flat-config migration. Re-verified: phpcs/phpstan clean, full Playwright suite green (13/13).
Ensure compatibility with WordPress 7.1
This reverts commit b7661db.
Release automation adjustments
Covers bulk_sync_started (via a real authenticated REST call, matching what the "Start bulk sync" button does), sync_completed + asset_sync_failed (via direct invocation of Sync_Queue's run-tracking state machine — a full real-upload sync run is slow/flaky in CI, and this exercises the exact same code path already verified manually), and sync_settings_changed (real UI: toggling the auto-sync radio on the Connect page). Needed test isolation: a real bulk_sync_started call kicks off a genuine background sync thread via a non-blocking loopback request, which would otherwise keep running and race later tests.
Covers asset_edited + transformation_applied (scope: asset) via a real authenticated REST call to save_asset, and transformation_applied (scope: global) via a real settings save on the Image Settings page. transformation_count reliably comes back 0 for the asset-scope case since the synthetic (fileless) test attachment has no real resource type to derive it from — asserted the event shape instead of the count value.
Covers cache_items_viewed, cache_items_toggled, asset_cache_purged, all_cache_purged, and cache_uploaded — all via real authenticated REST calls / direct action dispatch against the live Rest_Assets endpoints. Found along the way: rest_purge_all() resolves its `parent` param via Assets::get_param(), which is only ever populated by the in-memory activate_parent() call inside Assets::activate_parents() for settings- configured paths — a different (and non-persisted-across-requests) lookup than get_asset_parent() (DB-backed, used by show_cache/etc). A custom test-created cache point can't satisfy that lookup without also registering a real settings path, so the purge test targets one of the plugin's own default non-media paths instead, which is already active for exactly this reason.
Covers extension_toggled (real UI: a native checkbox click, bypassing Playwright's visibility check for the collapsed Extensions sidebar panel) and gallery_configured (via Admin::save_settings() directly with a synthetic gallery_config payload, matching what the React gallery panel serializes — avoids driving the full React UI for one settings save). Two timing/state gotchas fixed along the way: extensions.js debounces its change handler by 1000ms before firing, and gallery_config payloads need a value guaranteed to differ from whatever's already saved, since save_settings() silently no-ops on an unchanged value.
… plugin_uninstalled Rounds out connection management and deactivation analytics coverage. Also fixes fakeCloudinaryConnected() to upsert cloudinary_connect instead of a raw UPDATE, which silently no-ops once a real plugin_uninstalled run deletes the option row.
…fix asset_cache_purged flake The e2e capture mu-plugin only logged outgoing analytics-api.cloudinary.com requests and let them proceed, so every local/CI test run was leaking synthetic events and deactivation-reason submissions into the real production collector. It now preempts with a synthetic 200 instead. Also fixes cache-analytics.spec.js's asset_cache_purged test: none of the plugin's default non-media cache paths are active on a fresh install (they default off), so the test now explicitly enables the uploads path via a real settings save before purging, rather than assuming it's already active.
…-visit side effect Enabling the cache path was already explicit via a real settings save, but materializing the underlying asset-parent post depended on a subsequent admin page load's Assets::update_asset_paths() side effect — a timing-sensitive path that flaked once under CI load (passed on retry). Create the post directly instead, removing that dependency entirely.
json_decode($content, true) in String_Replace::replace_strings() collapsed
empty JSON objects ({}) into PHP arrays, which wp_json_encode() then
re-serialized as []. Since this runs on every REST API response site-wide,
it silently corrupted the shape of any JSON payload containing empty
objects - including MCP JSON-RPC handshakes (e.g. Novamira), whose clients
validate responses against a strict schema and reject [] where {} is
expected.
Decode as objects instead of associative arrays so shape survives the
round trip.
Add taffydb dev dependency required for docs generation
Fix JSON shape corruption in REST API responses breaking MCP integrations (Novamira)
…exit tracking
- connectivity_check_failed: split check_status() into a pure method and a
new cron_check_status() wrapper, so interactive connection checks/saves
(test_connection()) no longer emit it alongside connection_test_result.
- bulk sync: rest_start_sync() now calls mark_run_started() before
start_queue() (was after, dropping early thread results from the tally),
and uses shutdown_queue('queue') instead of stop_queue() on manual stop
so the run state doesn't leak into the next run's sync_completed.
- sync_completed tally: split into two atomically-incremented options
(RUN_TALLY_SYNCED_KEY/RUN_TALLY_ERRORS_KEY) instead of a single
get/modify/update array, which could lose updates across concurrent
sync threads.
- sync_settings_changed: added a diff guard so no-op saves (e.g. the
wizard re-submitting auto_sync unconditionally) don't fire the event.
- cache_items_viewed: only tracked on page 1 with no search term, instead
of on every pagination/search request against an open cache point.
- notice_dismissed: sanitize the notice token via sanitize_key() before
using it as the transient key and event param.
- deactivation_skipped: replaced a 150ms setTimeout guess with a new
Analytics.trackReliable() using navigator.sendBeacon(), which the
browser guarantees to dispatch across the immediate navigation.
- Moved a misplaced class constant to the top of Admin with the others.
tests/e2e/sync-analytics.spec.js's cleanup helper updated for the new
split tally option names. Full 32-test e2e suite verified green.
…e-dismissal regression - increment_option(): after the raw SQL increment, also purge the option from the 'notoptions' WP object-cache group. mark_run_started()'s delete_option() calls mark these keys as known-absent there; on sites with a persistent object cache (Redis/Memcached — not present in wp-env/CI, so this needed direct verification via simulation) the raw insert never cleared that flag, so every later get_option() would short-circuit to 0 and sync_completed would always report 0/0. - rest_dismiss_notice(): switch from sanitize_key() to a dot-preserving regex for the notice token. The token is a dotted setting slug (e.g. `_cld_notices.0`) that Notice::is_enabled() looks up verbatim via get_transient(); sanitize_key() stripped the dot, writing the transient under a key that never matched, so dismissible notices reappeared on every page load. Both regressions were introduced by the previous review-fix commit (6d30260) and verified directly via wpEvalFile since the e2e suite cannot exercise a persistent object cache. Full 32-test suite still green.
Instrument connection, sync, settings, media, cache, features, and deactivation analytics events
…ull-init Avoid re-running full plugin init during activation
Add Dependabot config for Composer
3.3.6 (develop to UAT)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.