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/media-library.php +83 -27 1.3.121.4.1 View file →
@@ -31,10 +31,13 @@
31 31 private $deleting_attachment = false;
32 32
33 33
34 34 public static $source_type_prefix = "media";
35 - public static $label = 'Media Library';
36 35
36 + public static function get_label() {
37 + return __('Media Library', 'media-cloud-sync');
38 + }
39 +
37 40 // Map which meta to be updated
38 41 public static $source_types = [
39 42 "media_library" => [
40 43 'table' => 'posts',
@@ -113,8 +116,10 @@
113 116 add_filter( 'wp_update_attachment_metadata', [ $this, 'update_attachment_metadata' ], 110, 2 );
114 117 add_filter( 'pre_delete_attachment', [ $this, 'pre_delete_attachment' ], 20 );
115 118 add_filter( 'delete_attachment', [ $this, 'delete_attachment' ], 20 );
116 119 add_action( 'delete_post', [$this, 'delete_post'] );
120 + // Safety net for code that deletes an attachment bypassing wp_delete_attachment().
121 + add_action( 'deleted_post', [ $this, 'deleted_post' ], 10, 2 );
117 122 add_filter( 'update_attached_file', [ $this, 'update_attached_file' ], 100, 2 );
118 123
119 124 add_action( 'wpmcs_do_update_attachment_metadata', [ $this, 'update_attachment_metadata' ], 10, 2 );
120 125 }
@@ -556,9 +561,9 @@
556 561 if ( $this->deleting_attachment ) {
557 562 return $file;
558 563 }
559 564
560 - if (!Utils::is_ok_to_serve($attachment_id)) {
565 + if (!Utils::should_serve_from_cloud($attachment_id)) {
561 566 return $file;
562 567 }
563 568
564 569 $wpmcsItem = Item::instance();
@@ -611,9 +616,9 @@
611 616 return $sources;
612 617 }
613 618
614 619 // Must need $attachment_id other wise not possible to get data from the table
615 - if (!Utils::is_ok_to_serve($attachment_id)) {
620 + if (!Utils::should_serve_from_cloud($attachment_id)) {
616 621 return $sources;
617 622 }
618 623
619 624 $wpmcsItem = Item::instance();
@@ -680,10 +685,10 @@
680 685 * @return array
681 686 */
682 687 public function wp_get_attachment_image_attributes($attr, $attachment, $size='thumbnail') {
683 688 if (
684 - !$attachment ||
685 - !Utils::is_ok_to_serve($attachment->ID)
689 + !$attachment ||
690 + !Utils::should_serve_from_cloud($attachment->ID)
686 691 ) {
687 692 return $attr;
688 693 }
689 694
@@ -730,9 +735,9 @@
730 735 *
731 736 * @return bool|mixed|WP_Error
732 737 */
733 738 public function wp_get_attachment_url($url, $attachment_id) {
734 - if (!Utils::is_ok_to_serve($attachment_id)) {
739 + if (!Utils::should_serve_from_cloud($attachment_id)) {
735 740 return $url;
736 741 }
737 742
738 743 $new_url = Item::instance()->get_url($attachment_id, 'full', 'media_library');
@@ -762,9 +767,9 @@
762 767 if ( ! is_string( $html ) ) {
763 768 return $html;
764 769 }
765 770
766 - if (!Utils::is_ok_to_serve($id)) {
771 + if (!Utils::should_serve_from_cloud($id)) {
767 772 return $html;
768 773 }
769 774
770 775 preg_match( '@\ssrc=[\'\"]([^\'\"]*)[\'\"]@', $html, $matches );
@@ -804,9 +809,9 @@
804 809 *
805 810 * @return array
806 811 */
807 812 public function wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
808 - if (!Utils::is_ok_to_serve($attachment_id)) {
813 + if (!Utils::should_serve_from_cloud($attachment_id)) {
809 814 return $image;
810 815 }
811 816
812 817 if ( isset( $image[0] ) ) {
@@ -832,9 +837,9 @@
832 837 *
833 838 * @return array
834 839 */
835 840 public function wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
836 - if (!Utils::is_ok_to_serve($attachment->ID)) {
841 + if (!Utils::should_serve_from_cloud($attachment->ID)) {
837 842 return $response;
838 843 }
839 844
840 845 if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
@@ -860,9 +865,9 @@
860 865 *
861 866 * @return array
862 867 */
863 868 public function image_get_intermediate_size( $data, $post_id, $size ) {
864 - if (!Utils::is_ok_to_serve($post_id)) {
869 + if (!Utils::should_serve_from_cloud($post_id)) {
865 870 return $data;
866 871 }
867 872 if ( isset( $data['url'] ) ) {
868 873 $size = Utils::maybe_convert_size_to_string( $post_id, $size );
@@ -1012,8 +1017,11 @@
1012 1017 $is_image = wp_attachment_is_image($attachment_id);
1013 1018 $existing = $wpmcsItem->get($attachment_id, 'media_library');
1014 1019 $existing_extras = $wpmcsItem->get_extras($attachment_id, false, 'media_library');
1015 1020 $has_existing = !Utils::is_empty($existing);
1021 + // A reupload of an already-private item must keep writing under the private path —
1022 + // never true for a genuinely new item, which is exactly right (new uploads stay public).
1023 + $is_private = $has_existing && !empty($existing['is_private']);
1016 1024 $sizes = [];
1017 1025 $uploaded = [];
1018 1026 $extras = [];
1019 1027 $prefix = '';
@@ -1049,24 +1057,37 @@
1049 1057 return false;
1050 1058 }
1051 1059
1052 1060 // Upload main file first, if that fails no need to attempt the rest
1053 - $uploaded = $this->uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id);
1061 + $uploaded = $this->uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private);
1054 1062
1055 1063 if ($uploaded && isset($uploaded['success']) && $uploaded['success']) {
1056 1064 // Attempt to upload original file if present. Not critical if this fails so we don't check the result before proceeding.
1057 - $original = $this->uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id);
1065 + $original = $this->uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private);
1058 1066
1059 1067 if ($is_image && isset($file_dir) && !empty($file_dir)) {
1060 1068 $sizes_to_upload = $this->get_attachment_image_sizes_for_upload($attachment_meta, $attachment_id);
1061 1069 if (!empty($sizes_to_upload)) {
1062 1070 // Upload image sizes. Again, not critical if this fails so we don't check the result before proceeding.
1063 - $sizes = $this->uploadImageSizes($sizes_to_upload, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id);
1071 + $sizes = $this->uploadImageSizes($sizes_to_upload, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id, $is_private);
1064 1072 }
1065 1073 }
1066 1074
1067 1075 if (!empty($sizes)) {
1068 - $extras['sizes'] = $sizes;
1076 + // Merge over existing sizes rather than replacing wholesale — a size that
1077 + // came back empty from uploadImageSizes() (its own fallback included) must
1078 + // never erase a previously-good, unrelated size's record.
1079 + $existing_sizes = isset($existing_extras['sizes']) && is_array($existing_extras['sizes'])
1080 + ? $existing_extras['sizes']
1081 + : [];
1082 +
1083 + foreach ($sizes as $size_name => $size_data) {
1084 + if (Utils::is_empty($size_data) && isset($existing_sizes[$size_name])) {
1085 + unset($sizes[$size_name]);
1086 + }
1087 + }
1088 +
1089 + $extras['sizes'] = array_merge($existing_sizes, $sizes);
1069 1090 }
1070 1091
1071 1092 return [
1072 1093 'file' => [
@@ -1180,16 +1201,19 @@
1180 1201 * Upload the main/full file.
1181 1202 * @since 1.3.6
1182 1203 * @param string $file_path
1183 1204 * @param string $source_path
1184 - * @param bool $do_reupload
1205 + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names).
1185 1206 * @param bool $has_existing
1186 1207 * @param array $existing
1187 1208 * @param string $prefix
1188 1209 * @param int $attachment_id
1210 + * @param bool $is_private Keeps a reupload of an already-private item under the private path.
1189 1211 */
1190 - private function uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id) {
1191 - if (!$do_reupload && $has_existing && ($existing['source_path'] == $source_path)) {
1212 + private function uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private = false) {
1213 + // $do_reupload may be an array of size names (force just those sizes) — that
1214 + // shouldn't force the full file too, only a literal true does.
1215 + if ($do_reupload !== true && $has_existing && ($existing['source_path'] == $source_path)) {
1192 1216 return [
1193 1217 'success' => true,
1194 1218 'file_url' => $existing['url'],
1195 1219 'key' => $existing['key']
@@ -1196,9 +1220,9 @@
1196 1220 ];
1197 1221 }
1198 1222
1199 1223 if (file_exists($file_path)) {
1200 - return Service::instance()->uploadSingle($file_path, $source_path, $prefix);
1224 + return Service::instance()->uploadSingle($file_path, $source_path, $prefix, $is_private);
1201 1225 }
1202 1226
1203 1227 Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [
1204 1228 'message' => __('File not found', 'media-cloud-sync'),
@@ -1213,15 +1237,16 @@
1213 1237 * Upload original file when present.
1214 1238 * @since 1.3.6
1215 1239 * @param string $original_file
1216 1240 * @param string $file_dir
1217 - * @param bool $do_reupload
1241 + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names).
1218 1242 * @param bool $has_existing
1219 1243 * @param array $existing
1220 1244 * @param string $prefix
1221 1245 * @param int $attachment_id
1246 + * @param bool $is_private Keeps a reupload of an already-private item under the private path.
1222 1247 */
1223 - private function uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id) {
1248 + private function uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private = false) {
1224 1249 $original = [];
1225 1250 if (empty($original_file)) {
1226 1251 return $original;
1227 1252 }
@@ -1228,14 +1253,16 @@
1228 1253
1229 1254 $original_file_path = trailingslashit($file_dir) . $original_file;
1230 1255 $original_file_source_path = Utils::get_attachment_source_path($original_file_path);
1231 1256
1232 - if (!$do_reupload && $has_existing && isset($existing['original_source_path']) && !Utils::is_empty($existing['original_source_path']) && $existing['original_source_path'] === $original_file_source_path) {
1257 + // $do_reupload may be an array of size names (force just those sizes) — that
1258 + // shouldn't force the original file too, only a literal true does.
1259 + if ($do_reupload !== true && $has_existing && isset($existing['original_source_path']) && !Utils::is_empty($existing['original_source_path']) && $existing['original_source_path'] === $original_file_source_path) {
1233 1260 return [ 'source_path' => $original_file_source_path, 'key' => $existing['original_key'] ];
1234 1261 }
1235 1262
1236 1263 if (file_exists($original_file_path)) {
1237 - $uploaded_original = Service::instance()->uploadSingle($original_file_path, $original_file_source_path, $prefix);
1264 + $uploaded_original = Service::instance()->uploadSingle($original_file_path, $original_file_source_path, $prefix, $is_private);
1238 1265 if (isset($uploaded_original['success']) && $uploaded_original['success']) {
1239 1266 return [ 'source_path' => $original_file_source_path, 'key' => $uploaded_original['key'] ];
1240 1267 }
1241 1268
@@ -1261,15 +1288,16 @@
1261 1288 * Upload intermediate image sizes.
1262 1289 * @since 1.3.6
1263 1290 * @param array $attachment_sizes
1264 1291 * @param string $file_dir
1265 - * @param bool $do_reupload
1292 + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names).
1266 1293 * @param array $existing_extras
1267 1294 * @param string $prefix
1268 1295 * @param int $attachment_id
1269 - * @return array
1296 + * @param bool $is_private Keeps a reupload of an already-private item under the private path.
1297 + * @return array
1270 1298 */
1271 - private function uploadImageSizes($attachment_sizes, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id) {
1299 + private function uploadImageSizes($attachment_sizes, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id, $is_private = false) {
1272 1300 $sizes = [];
1273 1301
1274 1302 foreach ($attachment_sizes as $size => $sub_image) {
1275 1303 $sub_size = [];
@@ -1283,10 +1311,14 @@
1283 1311 }
1284 1312
1285 1313 $uploaded_sub_image = [];
1286 1314
1315 + // $do_reupload may be an array naming specific sizes to force (e.g. a
1316 + // WooCommerce on-the-fly regen of one size only) rather than a plain bool.
1317 + $force_this_size = ($do_reupload === true) || (is_array($do_reupload) && in_array($size, $do_reupload, true));
1318 +
1287 1319 if (
1288 - !$do_reupload &&
1320 + !$force_this_size &&
1289 1321 !Utils::is_empty($existing_extras) &&
1290 1322 isset($existing_extras['sizes']) && !Utils::is_empty($existing_extras['sizes']) &&
1291 1323 isset($existing_extras['sizes'][$size]) && !Utils::is_empty($existing_extras['sizes'][$size]) &&
1292 1324 $existing_extras['sizes'][$size]['source_path'] == $sub_file_source_path
@@ -1296,9 +1328,9 @@
1296 1328 'file_url' => $existing_extras['sizes'][$size]['url'],
1297 1329 'key' => $existing_extras['sizes'][$size]['key']
1298 1330 ];
1299 1331 } else if (file_exists($sub_file_path)) {
1300 - $uploaded_sub_image = Service::instance()->uploadSingle($sub_file_path, $sub_file_source_path, $prefix);
1332 + $uploaded_sub_image = Service::instance()->uploadSingle($sub_file_path, $sub_file_source_path, $prefix, $is_private);
1301 1333 } else {
1302 1334 Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [
1303 1335 /* translators: %1$s: Image size name (e.g., thumbnail, medium, large). */
1304 1336 'message' => sprintf(__('Image size %1$s not found', 'media-cloud-sync'), $size),
@@ -1321,8 +1353,14 @@
1321 1353 'file' => $sub_file_path,
1322 1354 'code' => 500
1323 1355 ]);
1324 1356 }
1357 +
1358 + // A failed (re)upload attempt shouldn't erase a previously-good record —
1359 + // fall back to what's already stored rather than overwriting it with empty data.
1360 + if (isset($existing_extras['sizes'][$size]) && !Utils::is_empty($existing_extras['sizes'][$size])) {
1361 + $sub_size = $existing_extras['sizes'][$size];
1362 + }
1325 1363 }
1326 1364
1327 1365 $sizes[$size] = $sub_size;
1328 1366 }
@@ -1616,8 +1654,26 @@
1616 1654 * Note: delete_post is used as there is a potential that deleted_post is not reached.
1617 1655 */
1618 1656 public function delete_post() {
1619 1657 $this->deleting_attachment = false;
1658 + }
1659 +
1660 + /**
1661 + * Safety net for deletion that bypasses wp_delete_attachment().
1662 + *
1663 + * @handles deleted_post
1664 + */
1665 + public function deleted_post( $post_id, $post ) {
1666 + if ( ! $post || 'attachment' !== $post->post_type ) {
1667 + return;
1668 + }
1669 +
1670 + $item = Item::instance()->get( $post_id, 'media_library' );
1671 + if ( Utils::is_empty( $item ) ) {
1672 + return;
1673 + }
1674 +
1675 + Item::instance()->delete( $post_id, 'media_library' );
1620 1676 }
1621 1677
1622 1678 /**
1623 1679 * Has WP Core fixed wp_check_filetype when URL has params yet?