PluginProbe
Media Cloud Sync / 1.4.2
Media Cloud Sync v1.4.2
1.4.2 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 All 36 releases
← All changes | includes/compatbility/compatibility.php +187 -7 1.2.13 → 1.4.2 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,23 @@
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 + add_filter( 'wpmcs_do_reupload_media', [ $this, 'force_reupload_on_image_editor_save' ], 10, 3 );
55 +
56 + /*
37 57 * WP_Customize_Control
38 58 * /wp-includes/class-wp-customize_control.php
39 59 */
40 60 add_filter('attachment_url_to_postid', [$this, 'customizer_background_image'], 10, 2);
@@ -66,8 +86,157 @@
66 86 }
67 87 }
68 88
69 89
90 + /**
91 + * Allow the WordPress Customizer to crop images that have been copied to bucket
92 + * but removed from the local server, by copying them back temporarily.
93 + *
94 + * @param string $url
95 + * @param string $file
96 + * @param int $attachment_id
97 + * @param array $item
98 + *
99 + * @return string
100 + */
101 + public function customizer_crop_download_file( $url, $file, $attachment_id, $item ) {
102 + if ( false === $this->is_customizer_crop_action() ) {
103 + return $url;
104 + }
105 +
106 + // Check if file was restored already and return the URL
107 + // Avoid removed files being copied back multiple times
108 + if(in_array($file, $this->restored_files)) {
109 + return $url;
110 + }
111 +
112 + if ( ( $file = $this->copy_provider_file_to_server( $attachment_id, $file ) ) ) {
113 + // Return the file if successfully downloaded from bucket.
114 + return $file;
115 + }
116 +
117 + return $url;
118 + }
119 +
120 + /**
121 + * Generic check for Customizer crop actions
122 + *
123 + * @return bool
124 + */
125 + public function is_customizer_crop_action() {
126 + $header_crop = $this->maybe_process_on_action( 'custom-header-crop', true );
127 +
128 + $context = array( 'site-icon', 'custom_logo' );
129 + $image_crop = $this->maybe_process_on_action( 'crop-image', true, $context );
130 +
131 + if ( ! $header_crop && ! $image_crop ) {
132 + // Not doing a Customizer action
133 + return false;
134 + }
135 +
136 + return true;
137 + }
138 +
139 + /**
140 + * Additional filter to remove any restored files from the server
141 + * during a Customizer crop action.
142 + * @param array $files_to_remove
143 + * @param int $source_id
144 + * @param array $data
145 + * @param string $source_type
146 + * @return array
147 + */
148 + public function customizer_crop_remove_restored_files($files_to_remove, $source_id, $new_item, $old_item=[], $source_type = 'media_library') {
149 + $upload_dir = wp_get_upload_dir();
150 +
151 + if (false === $this->is_customizer_crop_action()) {
152 + return $files_to_remove;
153 + }
154 +
155 + if (isset($old_item['source_path']) && $old_item['source_path'] !== $new_item['source_path']) {
156 + // The file has changed, so we need to remove the old file from the server
157 + $files_to_remove[] = trailingslashit( $upload_dir['basedir'] ) . $old_item['source_path'];
158 + }
159 +
160 + if( isset($old_item['original_source_path']) && !empty($old_item['original_source_path']) ) {
161 + $files_to_remove[] = trailingslashit( $upload_dir['basedir'] ) . $old_item['original_source_path'];
162 + }
163 +
164 + return array_merge($files_to_remove, $this->restored_files);
165 + }
166 +
167 +
168 + /**
169 + * Allow the WordPress Image Editor to edit files that have been copied to provider
170 + * but removed from the local server, by copying them back temporarily
171 + *
172 + * @param string $url
173 + * @param string $file
174 + * @param int $attachment_id
175 + * @param array $item
176 + *
177 + * @return string
178 + */
179 + public function image_editor_download_file($url, $file, $attachment_id, $item) {
180 + // If this filter expects a file path, $url is a URL or a file path
181 + if (!Utils::is_ajax()) {
182 + return $url;
183 + }
184 +
185 + $action = Utils::filter_input('action', INPUT_GET) ?: Utils::filter_input('action', INPUT_POST);
186 + $do = Utils::filter_input('do', INPUT_POST);
187 +
188 + // Avoid multiple rewrites when restoring/saving.
189 + if ($action === 'image-editor' && in_array($do, ['restore', 'save'], true)) {
190 + return $file; // Return the file if image editor is doing a restore or save
191 + }
192 +
193 + // Copy back only once during a save triggered by the image editor.
194 + if ($do === 'save' && in_array($action, ['image-editor', 'imgedit-preview'], true)) {
195 + foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15) as $caller) {
196 + if (!empty($caller['function']) && $caller['function'] === '_load_image_to_edit_path') {
197 + $provider_file = $this->copy_provider_file_to_server($attachment_id, $file);
198 + return $provider_file ?: $url;
199 + }
200 + }
201 + }
202 +
203 + return $url;
204 +}
205 +
206 + /**
207 + * A flip or 180° rotation changes pixel content without changing any
208 + * declared width/height, which shouldForceReupload() can't detect —
209 + * force it explicitly for any image-editor commit (save or restore).
210 + * @since 1.4.3
211 + */
212 + public function force_reupload_on_image_editor_save($do_reupload, $attachment_id, $source_type) {
213 + if ($do_reupload) {
214 + return $do_reupload;
215 + }
216 +
217 + $action = Utils::filter_input('action', INPUT_GET) ?: Utils::filter_input('action', INPUT_POST);
218 + $do = Utils::filter_input('do', INPUT_POST);
219 +
220 + if (in_array($do, ['save', 'restore'], true) && in_array($action, ['image-editor', 'imgedit-preview'], true)) {
221 + return true;
222 + }
223 +
224 + return $do_reupload;
225 + }
226 +
227 +
228 +
229 +
230 + /**
231 + * Called after REST API callback, and removes server files from the server for routes that need it.
232 + *
233 + * @param WP_HTTP_Response $response The response object.
234 + * @param WP_REST_Server $handler The response handler.
235 + * @param WP_REST_Request $request The current request.
236 + *
237 + * @return WP_HTTP_Response The filtered response object.
238 + */
70 239 public function rest_request_after_callbacks_remove_from_server($response, $handler, $request) {
71 240 $routes = [
72 241 '/regenerate-thumbnails/v\d+/regenerate/',
73 242 ];
@@ -229,15 +398,15 @@
229 398 *
230 399 * @return int|null
231 400 */
232 401 public function customizer_background_image($post_id, $url) {
233 - if (!is_null($post_id)) {
402 + if (!empty($post_id)) {
234 403 return $post_id;
235 404 }
236 405
237 406 // There seems to be a bug in the WP Customizer whereby sometimes it puts the attachment ID on the URL.
238 407 if (is_numeric($url)) {
239 - $item = Item::get($url);
408 + $item = Item::instance()->get($url);
240 409
241 410 // If we found an offloaded Media Library item for that ID, job's a good'n'.
242 411 if (!Utils::is_empty($item)) {
243 412 $post_id = $url;
@@ -244,12 +413,12 @@
244 413 }
245 414 } else {
246 415 $path = Utils::get_attachment_source_path($url);
247 416 if (!Utils::is_empty($path)) {
248 - $items = Item::instance()->get_items_by_source_paths([$path]);
249 - if (!Utils::is_empty($items)) {
417 + $item = Item::instance()->get_items_by_paths( $path, true, true );
418 + if (!Utils::is_empty($item)) {
250 419 // If we found an offloaded Media Library item for that path, job's a good'n'.
251 - $post_id = $items[0]['source_id'];
420 + $post_id = $item['source_id'];
252 421 }
253 422 }
254 423 }
255 424
@@ -321,14 +490,22 @@
321 490 * the attachment's file should be.
322 491 *
323 492 * @return string|bool File if downloaded, false on failure
324 493 */
325 - private function copy_provider_file_to_server($attachment_id, $file) {
494 + public function copy_provider_file_to_server($attachment_id, $file) {
326 495 // Download files
327 - if (!Item::instance()->moveToServerBySourcePath($attachment_id, $file)) {
496 + if (!Item::instance()->moveToServerBySourcePath($attachment_id, $file, 'media_library')) {
328 497 return false;
329 498 }
330 499
500 + $this->restored_files[] = $file;
501 +
502 + // Shared with woocommerce.php — feeds the same pre_update_item pipeline as
503 + // below when a save happens this request, with a shutdown fallback for the
504 + // other three callers here (image editor, legacy copy-back, Regenerate
505 + // Thumbnails pre-v3), which otherwise have no cleanup at all.
506 + Item::instance()->track_restored_for_cleanup( [ $file ] );
507 +
331 508 return $file;
332 509 }
333 510
334 511 /**
@@ -341,5 +518,8 @@
341 518 self::$instance = new self();
342 519 }
343 520 return self::$instance;
344 521 }
522 +
523 + private function __clone() {}
524 + public function __wakeup() { throw new \Exception('Cannot unserialize singleton'); }
345 525 }