PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
← All changes | includes/integrations/woocommerce.php +94 -98 1.4.01.4.1 View file →
@@ -5,12 +5,14 @@
5 5
6 6 /**
7 7 * WooCommerce Compatibility.
8 8 *
9 - * Loaded by the autoloader; activation is gated via `is_installed()` in Integration::init().
10 - *
11 9 * @since 1.3.7
12 10 */
11 +if ( ! class_exists( 'WooCommerce' ) ) {
12 + return;
13 +}
14 +
13 15 class WooCommerce {
14 16 /**
15 17 * The singleton instance.
16 18 *
@@ -19,28 +21,23 @@
19 21 */
20 22 protected static $instance = null;
21 23
22 24 /**
23 - * Whether WooCommerce image resize is active for this request.
25 + * The image size WooCommerce is currently regenerating, captured via
26 + * `intermediate_image_sizes` so the reupload can be scoped to just that size.
24 27 *
25 - * Set when `woocommerce_resize_images` runs so `get_attached_file()` returns
26 - * a local path instead of a cloud URL without stacking filters.
27 - *
28 - * @since 1.3.12
29 - * @var bool
28 + * @since 1.4.0
29 + * @var string|null
30 30 */
31 - private static $return_local_path = false;
31 + protected $regenerating_size = null;
32 32
33 33 /**
34 - * Absolute paths restored from cloud for WooCommerce regeneration this request,
35 - * re-removed on shutdown so "Remove from server" stays honored even if the
36 - * normal sync pipeline bails early (e.g. waiting on subsizes) and never
37 - * re-triggers removal itself.
34 + * Whether the `intermediate_image_sizes` capture hook has been armed.
38 35 *
39 36 * @since 1.4.0
40 - * @var string[]
37 + * @var bool
41 38 */
42 - private $restored_files = array();
39 + protected $size_capture_hooked = false;
43 40
44 41 /**
45 42 * The class constructor.
46 43 *
@@ -59,138 +56,127 @@
59 56 /******** FIX Size updation in WooCommerce Customizer *****/
60 57 // Return File path for WooCommerce image resizing to prevent url being used and causing issues with image regeneration.
61 58 add_filter( 'woocommerce_resize_images', [ $this, 'resize_images' ] );
62 59
63 - // Return the local file path (and restore from cloud when missing) during WooCommerce image regeneration.
60 + // Restore the file from cloud (if missing) and return the local path during
61 + // WooCommerce's on-the-fly image regeneration.
64 62 add_filter( 'wpmcs_get_attached_file', array( $this, 'get_attached_file' ), 10, 4 );
65 63
66 - // Force a reupload during regeneration — WooCommerce rewrites the local file in place at
67 - // the same dimensions/filename, which the normal "unchanged, skip reupload" check can't
68 - // tell apart from a real no-op, leaving stale content on the cloud otherwise.
69 - add_filter( 'wpmcs_do_reupload_media', array( $this, 'force_reupload_during_regeneration' ), 10, 1 );
64 + // Force a reupload of just the size WooCommerce is regenerating — same name/
65 + // dimensions as an existing size, but different crop, so the normal
66 + // "unchanged, skip reupload" check can't tell it apart from a real no-op.
67 + add_filter( 'wpmcs_do_reupload_media', array( $this, 'force_reupload_during_regeneration' ), 10, 3 );
70 68 /******* End Fix Size updation in WooCommerce Customizer */
71 69 }
72 70
73 71
74 72 /**
75 - * Flag the request so attached files are returned as local paths during WooCommerce resize.
73 + * Resize images for WooCommerce.
74 + * Used to add a filter to return the file path instead of the URL for WooCommerce image resizing,
75 + * to prevent issues with image regeneration when using cloud storage.
76 76 *
77 + * WooCommerce fires 'woocommerce_resize_images' unconditionally from
78 + * maybe_resize_image(), which itself runs on every wp_get_attachment_image_src()
79 + * call for any size — not just when an actual WooCommerce resize is about to
80 + * happen. resize_and_return_image() (the method that genuinely needs the local
81 + * path) hasn't been called yet at this point, so is_called_from() would always
82 + * be false if checked here. The added filter must instead check at the time it's
83 + * actually invoked — otherwise it permanently returns the local path for every
84 + * wpmcs_get_attached_file call for the rest of the request, once any image has
85 + * rendered anywhere (confirmed live: this broke the unrelated BuddyBoss
86 + * regenerate-thumbnails restore window entirely).
87 + *
77 88 * @param bool $resize Whether to resize images or not.
78 89 * @return bool The modified value of $resize.
79 90 * @since 1.3.7
80 91 */
81 92 public function resize_images( $resize ) {
82 - if ( $resize ) {
83 - self::$return_local_path = true;
84 - }
93 + add_filter( 'wpmcs_get_attached_file', function ($url, $file, $attachment_id, $item) {
94 + if ( ! Utils::is_called_from( 'WC_Regenerate_Images', 'resize_and_return_image' ) ) {
95 + return $url;
96 + }
97 + return $file;
98 + }, 10, 4 );
85 99
86 100 return $resize;
87 101 }
88 102
89 103 /**
90 - * Return a local file path during WooCommerce image regeneration.
104 + * Restore the file from cloud (all registered sizes, not just this one — the
105 + * source may be needed at other sizes too) and return the local path during
106 + * WooCommerce's on-the-fly image regeneration.
91 107 *
92 - * @param string $url The cloud URL.
93 - * @param string $file The local file path.
94 - * @param int $attachment_id The attachment ID.
95 - * @param array $item The item data associated with the attachment.
96 - * @return string The local file path or original cloud URL.
108 + * @param string $url The original file URL.
109 + * @param string $file The file path.
110 + * @param int $attachment_id The attachment ID.
111 + * @param array $item The item data associated with the attachment.
112 + * @return string The modified file URL.
97 113 * @since 1.3.7
98 114 */
99 115 public function get_attached_file( $url, $file, $attachment_id, $item ) {
100 - if ( ! $this->should_return_local_path_for_woocommerce() ) {
116 + if ( ! Utils::is_called_from( 'WC_Regenerate_Images', 'resize_and_return_image' ) ) {
101 117 return $url;
102 118 }
103 119
104 - if ( ! file_exists( $file ) && ! Utils::is_empty( $item ) ) {
105 - Item::instance()->moveToServerByItem( $item, 'full', true );
120 + // Always attempted, not gated on $file specifically being missing —
121 + // move_to_server_by_key_and_path() already no-ops per-file on file_exists(),
122 + // and gating here would leave OTHER missing sizes unrestored, which then get
123 + // silently dropped (not just skipped) if a reupload is later forced without
124 + // a local file present.
125 + $results = Item::instance()->moveToServerByItem( $item, 'full', true );
106 126
107 - // Only track this for later removal on admin-side requests (the System Status
108 - // tool, the background regen queue, and the single media-row action all run
109 - // over admin-ajax). WooCommerce also restores files here for its realtime,
110 - // on-the-fly resize on any storefront page that displays a mismatched-ratio
111 - // image — re-deleting the restore there would just trigger another cloud
112 - // download next time a different size needs it from the same full image.
113 - if ( is_admin() && file_exists( $file ) ) {
114 - $this->track_restored_file( $file );
115 - }
127 + if ( is_array( $results ) && ! empty( $results ) ) {
128 + Item::instance()->track_restored_for_cleanup( array_values( $results ) );
116 129 }
117 130
118 - return file_exists( $file ) ? $file : $url;
119 - }
131 + $this->capture_regenerating_size();
120 132
121 - /**
122 - * Force the normal sync pipeline to treat this attachment as needing a
123 - * reupload while WooCommerce is regenerating it, same as Imagify does
124 - * for its own optimize/restore cycle.
125 - *
126 - * Restricted to admin-side requests (System Status tool, the background
127 - * regen queue, and the single "Regenerate thumbnail" media-row action all
128 - * run over admin-ajax) — WooCommerce also calls the same regeneration
129 - * class from `wp_get_attachment_image_src` to resize a mismatched-ratio
130 - * image on the fly on any page that displays it, storefront included, and
131 - * forcing a synchronous cloud reupload mid-render for a site visitor
132 - * there would be a real performance regression, not a fix.
133 - *
134 - * @param bool $do_reupload
135 - * @return bool
136 - * @since 1.4.0
137 - */
138 - public function force_reupload_during_regeneration( $do_reupload ) {
139 - if ( $do_reupload || ! is_admin() ) {
140 - return $do_reupload;
141 - }
142 -
143 - return $this->should_return_local_path_for_woocommerce();
133 + return $file;
144 134 }
145 135
146 136 /**
147 - * Remember a file restored for WooCommerce so it can be removed again on
148 - * shutdown, and arm that cleanup the first time this request restores anything.
137 + * Arms a one-time `intermediate_image_sizes` capture so the exact size
138 + * WooCommerce is regenerating is known when `wpmcs_do_reupload_media` fires.
139 + * Priority 20 — after WooCommerce's own `adjust_intermediate_image_sizes`
140 + * (default priority 10) has already narrowed the array to that one size.
149 141 *
150 - * @param string $file Absolute path of the restored file.
151 142 * @return void
152 143 * @since 1.4.0
153 144 */
154 - private function track_restored_file( $file ) {
155 - if ( in_array( $file, $this->restored_files, true ) ) {
145 + protected function capture_regenerating_size() {
146 + if ( $this->size_capture_hooked ) {
156 147 return;
157 148 }
149 + $this->size_capture_hooked = true;
158 150
159 - if ( empty( $this->restored_files ) ) {
160 - add_action( 'shutdown', array( $this, 'remove_restored_files' ) );
161 - }
162 -
163 - $this->restored_files[] = $file;
151 + add_filter( 'intermediate_image_sizes', function ( $sizes ) {
152 + if ( is_array( $sizes ) && 1 === count( $sizes ) ) {
153 + $this->regenerating_size = reset( $sizes );
154 + }
155 + return $sizes;
156 + }, 20 );
164 157 }
165 158
166 159 /**
167 - * Re-removes files restored for WooCommerce regeneration, honoring the
168 - * "Remove from server" setting the same way the normal sync pipeline would.
160 + * Force a reupload of just the size WooCommerce is regenerating — an existing
161 + * registered size with a different crop, which the normal same-name/dimensions
162 + * check can't otherwise tell apart from an unchanged file.
169 163 *
170 - * @return void
164 + * @param bool|array $do_reupload
165 + * @param int $attachment_id
166 + * @param string $source_type
167 + * @return bool|array
171 168 * @since 1.4.0
172 169 */
173 - public function remove_restored_files() {
174 - Item::instance()->may_be_delete_server_files_by_source_paths( $this->restored_files );
175 - }
176 -
177 - /**
178 - * Whether the current request needs a local attached file for WooCommerce image handling.
179 - *
180 - * @return bool
181 - * @since 1.3.12
182 - */
183 - private function should_return_local_path_for_woocommerce(): bool {
184 - if ( self::$return_local_path ) {
185 - return true;
170 + public function force_reupload_during_regeneration( $do_reupload, $attachment_id, $source_type ) {
171 + if ( true === $do_reupload || empty( $this->regenerating_size ) ) {
172 + return $do_reupload;
186 173 }
187 174
188 - if ( Utils::is_called_from( 'WC_Regenerate_Images' ) ) {
189 - return true;
190 - }
175 + $size = $this->regenerating_size;
176 + $this->regenerating_size = null;
191 177
192 - return Utils::is_called_from( 'WC_Regenerate_Images_Request' );
178 + return array( $size );
193 179 }
194 180
195 181 /**
196 182 * Is installed?
@@ -197,9 +183,19 @@
197 183 *
198 184 * @return bool
199 185 */
200 186 public static function is_installed(): bool {
201 - return defined( 'WC_ABSPATH' ) || defined( 'WC_VERSION' ) || class_exists( 'WooCommerce', false );
187 + // Check if WooCommerce plugin defines its main constant.
188 + if ( defined( 'WC_ABSPATH' ) ) {
189 + return true;
190 + }
191 +
192 + // Alternative: check for WooCommerce version constant.
193 + if ( defined( 'WC_VERSION' ) ) {
194 + return true;
195 + }
196 +
197 + return false;
202 198 }
203 199
204 200 /**
205 201 * Singleton instance