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/compatbility/compatibility.php +165 -7 1.2.131.4.1 View file →
@@ -18,8 +18,14 @@
18 18 */
19 19 public $wait_for_generate_attachment_metadata = false;
20 20
21 21 /**
22 + * Files to remove that were restored
23 + * @var array
24 + */
25 + private $restored_files = array();
26 +
27 + /**
22 28 * Compatibility constructor.
23 29 * @since 1.0.0
24 30 */
25 31 private function __construct() {
@@ -32,9 +38,22 @@
32 38
33 39 }
34 40
35 41 public function init() {
42 + if(!Utils::is_service_enabled()) {
43 + return;
44 + }
45 +
36 46 /*
47 + * Image Editor Handler
48 + * /wp-admin/includes/image-edit.php
49 + */
50 + add_filter( 'wpmcs_get_attached_file_noop', array( $this, 'image_editor_download_file' ), 10, 4 );
51 + add_filter( 'wpmcs_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
52 + add_filter( 'wpmcs_get_attached_file', array( $this, 'customizer_crop_download_file' ), 10, 4 );
53 + add_filter( 'wpmcs_pre_update_item_additional_files_to_remove_from_server', [ $this, 'customizer_crop_remove_restored_files' ], 10, 5 );
54 +
55 + /*
37 56 * WP_Customize_Control
38 57 * /wp-includes/class-wp-customize_control.php
39 58 */
40 59 add_filter('attachment_url_to_postid', [$this, 'customizer_background_image'], 10, 2);
@@ -66,8 +85,136 @@
66 85 }
67 86 }
68 87
69 88
89 + /**
90 + * Allow the WordPress Customizer to crop images that have been copied to bucket
91 + * but removed from the local server, by copying them back temporarily.
92 + *
93 + * @param string $url
94 + * @param string $file
95 + * @param int $attachment_id
96 + * @param array $item
97 + *
98 + * @return string
99 + */
100 + public function customizer_crop_download_file( $url, $file, $attachment_id, $item ) {
101 + if ( false === $this->is_customizer_crop_action() ) {
102 + return $url;
103 + }
104 +
105 + // Check if file was restored already and return the URL
106 + // Avoid removed files being copied back multiple times
107 + if(in_array($file, $this->restored_files)) {
108 + return $url;
109 + }
110 +
111 + if ( ( $file = $this->copy_provider_file_to_server( $attachment_id, $file ) ) ) {
112 + // Return the file if successfully downloaded from bucket.
113 + return $file;
114 + }
115 +
116 + return $url;
117 + }
118 +
119 + /**
120 + * Generic check for Customizer crop actions
121 + *
122 + * @return bool
123 + */
124 + public function is_customizer_crop_action() {
125 + $header_crop = $this->maybe_process_on_action( 'custom-header-crop', true );
126 +
127 + $context = array( 'site-icon', 'custom_logo' );
128 + $image_crop = $this->maybe_process_on_action( 'crop-image', true, $context );
129 +
130 + if ( ! $header_crop && ! $image_crop ) {
131 + // Not doing a Customizer action
132 + return false;
133 + }
134 +
135 + return true;
136 + }
137 +
138 + /**
139 + * Additional filter to remove any restored files from the server
140 + * during a Customizer crop action.
141 + * @param array $files_to_remove
142 + * @param int $source_id
143 + * @param array $data
144 + * @param string $source_type
145 + * @return array
146 + */
147 + public function customizer_crop_remove_restored_files($files_to_remove, $source_id, $new_item, $old_item=[], $source_type = 'media_library') {
148 + $upload_dir = wp_get_upload_dir();
149 +
150 + if (false === $this->is_customizer_crop_action()) {
151 + return $files_to_remove;
152 + }
153 +
154 + if (isset($old_item['source_path']) && $old_item['source_path'] !== $new_item['source_path']) {
155 + // The file has changed, so we need to remove the old file from the server
156 + $files_to_remove[] = trailingslashit( $upload_dir['basedir'] ) . $old_item['source_path'];
157 + }
158 +
159 + if( isset($old_item['original_source_path']) && !empty($old_item['original_source_path']) ) {
160 + $files_to_remove[] = trailingslashit( $upload_dir['basedir'] ) . $old_item['original_source_path'];
161 + }
162 +
163 + return array_merge($files_to_remove, $this->restored_files);
164 + }
165 +
166 +
167 + /**
168 + * Allow the WordPress Image Editor to edit files that have been copied to provider
169 + * but removed from the local server, by copying them back temporarily
170 + *
171 + * @param string $url
172 + * @param string $file
173 + * @param int $attachment_id
174 + * @param array $item
175 + *
176 + * @return string
177 + */
178 + public function image_editor_download_file($url, $file, $attachment_id, $item) {
179 + // If this filter expects a file path, $url is a URL or a file path
180 + if (!Utils::is_ajax()) {
181 + return $url;
182 + }
183 +
184 + $action = Utils::filter_input('action', INPUT_GET) ?: Utils::filter_input('action', INPUT_POST);
185 + $do = Utils::filter_input('do', INPUT_POST);
186 +
187 + // Avoid multiple rewrites when restoring/saving.
188 + if ($action === 'image-editor' && in_array($do, ['restore', 'save'], true)) {
189 + return $file; // Return the file if image editor is doing a restore or save
190 + }
191 +
192 + // Copy back only once during a save triggered by the image editor.
193 + if ($do === 'save' && in_array($action, ['image-editor', 'imgedit-preview'], true)) {
194 + foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15) as $caller) {
195 + if (!empty($caller['function']) && $caller['function'] === '_load_image_to_edit_path') {
196 + $provider_file = $this->copy_provider_file_to_server($attachment_id, $file);
197 + return $provider_file ?: $url;
198 + }
199 + }
200 + }
201 +
202 + return $url;
203 +}
204 +
205 +
206 +
207 +
208 + /**
209 + * Called after REST API callback, and removes server files from the server for routes that need it.
210 + *
211 + * @param WP_HTTP_Response $response The response object.
212 + * @param WP_REST_Server $handler The response handler.
213 + * @param WP_REST_Request $request The current request.
214 + *
215 + * @return WP_HTTP_Response The filtered response object.
216 + */
70 217 public function rest_request_after_callbacks_remove_from_server($response, $handler, $request) {
71 218 $routes = [
72 219 '/regenerate-thumbnails/v\d+/regenerate/',
73 220 ];
@@ -229,15 +376,15 @@
229 376 *
230 377 * @return int|null
231 378 */
232 379 public function customizer_background_image($post_id, $url) {
233 - if (!is_null($post_id)) {
380 + if (!empty($post_id)) {
234 381 return $post_id;
235 382 }
236 383
237 384 // There seems to be a bug in the WP Customizer whereby sometimes it puts the attachment ID on the URL.
238 385 if (is_numeric($url)) {
239 - $item = Item::get($url);
386 + $item = Item::instance()->get($url);
240 387
241 388 // If we found an offloaded Media Library item for that ID, job's a good'n'.
242 389 if (!Utils::is_empty($item)) {
243 390 $post_id = $url;
@@ -244,12 +391,12 @@
244 391 }
245 392 } else {
246 393 $path = Utils::get_attachment_source_path($url);
247 394 if (!Utils::is_empty($path)) {
248 - $items = Item::instance()->get_items_by_source_paths([$path]);
249 - if (!Utils::is_empty($items)) {
395 + $item = Item::instance()->get_items_by_paths( $path, true, true );
396 + if (!Utils::is_empty($item)) {
250 397 // If we found an offloaded Media Library item for that path, job's a good'n'.
251 - $post_id = $items[0]['source_id'];
398 + $post_id = $item['source_id'];
252 399 }
253 400 }
254 401 }
255 402
@@ -321,14 +468,22 @@
321 468 * the attachment's file should be.
322 469 *
323 470 * @return string|bool File if downloaded, false on failure
324 471 */
325 - private function copy_provider_file_to_server($attachment_id, $file) {
472 + public function copy_provider_file_to_server($attachment_id, $file) {
326 473 // Download files
327 - if (!Item::instance()->moveToServerBySourcePath($attachment_id, $file)) {
474 + if (!Item::instance()->moveToServerBySourcePath($attachment_id, $file, 'media_library')) {
328 475 return false;
329 476 }
330 477
478 + $this->restored_files[] = $file;
479 +
480 + // Shared with woocommerce.php — feeds the same pre_update_item pipeline as
481 + // below when a save happens this request, with a shutdown fallback for the
482 + // other three callers here (image editor, legacy copy-back, Regenerate
483 + // Thumbnails pre-v3), which otherwise have no cleanup at all.
484 + Item::instance()->track_restored_for_cleanup( [ $file ] );
485 +
331 486 return $file;
332 487 }
333 488
334 489 /**
@@ -341,5 +496,8 @@
341 496 self::$instance = new self();
342 497 }
343 498 return self::$instance;
344 499 }
500 +
501 + private function __clone() {}
502 + public function __wakeup() { throw new \Exception('Cannot unserialize singleton'); }
345 503 }