| @@ -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 = ''; |
| @@ -1024,10 +1032,11 @@ | ||
| 1024 | 1032 | $do_reupload = apply_filters('wpmcs_do_reupload_media', false, $attachment_id, 'media_library'); |
| 1025 | 1033 | |
| 1026 | 1034 | // Force reupload when sizes/dimensions changed |
| 1027 | 1035 | if (!$do_reupload && $has_existing) { |
| 1028 | - if($this->shouldForceReupload($attachment_meta, $attachment_id, $existing_extras)) { | |
| 1029 | - $do_reupload = true; | |
| 1036 | + $force_reupload = $this->shouldForceReupload($attachment_meta, $attachment_id, $existing_extras); | |
| 1037 | + if ($force_reupload !== false) { | |
| 1038 | + $do_reupload = $force_reupload; | |
| 1030 | 1039 | } |
| 1031 | 1040 | } |
| 1032 | 1041 | |
| 1033 | 1042 | // Add prefix if object versioning is ON |
| @@ -1049,26 +1058,48 @@ | ||
| 1049 | 1058 | return false; |
| 1050 | 1059 | } |
| 1051 | 1060 | |
| 1052 | 1061 | // 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); | |
| 1062 | + $uploaded = $this->uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private); | |
| 1054 | 1063 | |
| 1055 | 1064 | if ($uploaded && isset($uploaded['success']) && $uploaded['success']) { |
| 1056 | 1065 | // 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); | |
| 1066 | + $original = $this->uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private); | |
| 1067 | + $additional_files = []; | |
| 1058 | 1068 | |
| 1059 | 1069 | if ($is_image && isset($file_dir) && !empty($file_dir)) { |
| 1060 | 1070 | $sizes_to_upload = $this->get_attachment_image_sizes_for_upload($attachment_meta, $attachment_id); |
| 1061 | 1071 | if (!empty($sizes_to_upload)) { |
| 1062 | 1072 | // 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); | |
| 1073 | + $sizes = $this->uploadImageSizes($sizes_to_upload, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id, $is_private); | |
| 1064 | 1074 | } |
| 1075 | + $additional_files = $this->uploadAdditionalFiles($attachment_meta, $file_dir, $do_reupload, $has_existing, $existing_extras, $prefix, $attachment_id, $is_private); | |
| 1065 | 1076 | } |
| 1066 | 1077 | |
| 1067 | 1078 | if (!empty($sizes)) { |
| 1068 | - $extras['sizes'] = $sizes; | |
| 1079 | + // Merge over existing sizes rather than replacing wholesale — a size that | |
| 1080 | + // came back empty from uploadImageSizes() (its own fallback included) must | |
| 1081 | + // never erase a previously-good, unrelated size's record. | |
| 1082 | + $existing_sizes = isset($existing_extras['sizes']) && is_array($existing_extras['sizes']) | |
| 1083 | + ? $existing_extras['sizes'] | |
| 1084 | + : []; | |
| 1085 | + | |
| 1086 | + foreach ($sizes as $size_name => $size_data) { | |
| 1087 | + if (Utils::is_empty($size_data) && isset($existing_sizes[$size_name])) { | |
| 1088 | + unset($sizes[$size_name]); | |
| 1089 | + } | |
| 1090 | + } | |
| 1091 | + | |
| 1092 | + $extras['sizes'] = array_merge($existing_sizes, $sizes); | |
| 1069 | 1093 | } |
| 1070 | 1094 | |
| 1095 | + if (!empty($additional_files)) { | |
| 1096 | + $existing_additional_files = isset($existing_extras['additional_files']) && is_array($existing_extras['additional_files']) | |
| 1097 | + ? $existing_extras['additional_files'] | |
| 1098 | + : []; | |
| 1099 | + $extras['additional_files'] = array_merge($existing_additional_files, $additional_files); | |
| 1100 | + } | |
| 1101 | + | |
| 1071 | 1102 | return [ |
| 1072 | 1103 | 'file' => [ |
| 1073 | 1104 | 'source_path' => $source_path, |
| 1074 | 1105 | 'url' => $uploaded['file_url'], |
| @@ -1122,8 +1153,10 @@ | ||
| 1122 | 1153 | * @since 1.3.6 |
| 1123 | 1154 | * @param array $attachment_meta |
| 1124 | 1155 | * @param int $attachment_id |
| 1125 | 1156 | * @param array $existing_extras |
| 1157 | + * @return bool|array True forces a full reupload (main file changed); an array of | |
| 1158 | + * size names forces only those sizes; false means nothing changed. | |
| 1126 | 1159 | */ |
| 1127 | 1160 | private function shouldForceReupload($attachment_meta, $attachment_id, $existing_extras) { |
| 1128 | 1161 | $existing_extras = $existing_extras ?? Item::instance()->get_extras($attachment_id, false, 'media_library'); |
| 1129 | 1162 | |
| @@ -1150,31 +1183,26 @@ | ||
| 1150 | 1183 | 'height' => isset($smeta['height']) ? (int)$smeta['height'] : 0, |
| 1151 | 1184 | ]; |
| 1152 | 1185 | } |
| 1153 | 1186 | |
| 1154 | - $existing_relevant = []; | |
| 1155 | - foreach (array_keys($current_sizes) as $sname) { | |
| 1156 | - if (isset($existing_sizes[$sname])) { | |
| 1157 | - $existing_relevant[$sname] = $existing_sizes[$sname]; | |
| 1158 | - } | |
| 1159 | - } | |
| 1160 | - | |
| 1161 | - if (array_keys($existing_relevant) !== array_keys($current_sizes)) { | |
| 1162 | - return true; | |
| 1163 | - } | |
| 1164 | - | |
| 1187 | + // The main file is unchanged (already ruled out above) — a mismatch here is | |
| 1188 | + // limited to specific sizes, so re-send only those instead of forcing a | |
| 1189 | + // blanket reupload that would also re-fetch the main file from local disk | |
| 1190 | + // (which "Remove from server" may have already, correctly, deleted). | |
| 1191 | + $changed_sizes = []; | |
| 1165 | 1192 | foreach ($current_sizes as $sname => $dims) { |
| 1166 | 1193 | if (!isset($existing_sizes[$sname])) { |
| 1167 | - return true; | |
| 1194 | + $changed_sizes[] = $sname; | |
| 1195 | + continue; | |
| 1168 | 1196 | } |
| 1169 | 1197 | $ex_w = isset($existing_sizes[$sname]['width']) ? (int)$existing_sizes[$sname]['width'] : 0; |
| 1170 | 1198 | $ex_h = isset($existing_sizes[$sname]['height']) ? (int)$existing_sizes[$sname]['height'] : 0; |
| 1171 | 1199 | if ($ex_w !== $dims['width'] || $ex_h !== $dims['height']) { |
| 1172 | - return true; | |
| 1200 | + $changed_sizes[] = $sname; | |
| 1173 | 1201 | } |
| 1174 | 1202 | } |
| 1175 | 1203 | |
| 1176 | - return false; | |
| 1204 | + return empty($changed_sizes) ? false : $changed_sizes; | |
| 1177 | 1205 | } |
| 1178 | 1206 | |
| 1179 | 1207 | /** |
| 1180 | 1208 | * Upload the main/full file. |
| @@ -1180,16 +1208,19 @@ | ||
| 1180 | 1208 | * Upload the main/full file. |
| 1181 | 1209 | * @since 1.3.6 |
| 1182 | 1210 | * @param string $file_path |
| 1183 | 1211 | * @param string $source_path |
| 1184 | - * @param bool $do_reupload | |
| 1212 | + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names). | |
| 1185 | 1213 | * @param bool $has_existing |
| 1186 | 1214 | * @param array $existing |
| 1187 | 1215 | * @param string $prefix |
| 1188 | 1216 | * @param int $attachment_id |
| 1217 | + * @param bool $is_private Keeps a reupload of an already-private item under the private path. | |
| 1189 | 1218 | */ |
| 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)) { | |
| 1219 | + private function uploadFullFile($file_path, $source_path, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private = false) { | |
| 1220 | + // $do_reupload may be an array of size names (force just those sizes) — that | |
| 1221 | + // shouldn't force the full file too, only a literal true does. | |
| 1222 | + if ($do_reupload !== true && $has_existing && ($existing['source_path'] == $source_path)) { | |
| 1192 | 1223 | return [ |
| 1193 | 1224 | 'success' => true, |
| 1194 | 1225 | 'file_url' => $existing['url'], |
| 1195 | 1226 | 'key' => $existing['key'] |
| @@ -1196,9 +1227,9 @@ | ||
| 1196 | 1227 | ]; |
| 1197 | 1228 | } |
| 1198 | 1229 | |
| 1199 | 1230 | if (file_exists($file_path)) { |
| 1200 | - return Service::instance()->uploadSingle($file_path, $source_path, $prefix); | |
| 1231 | + return Service::instance()->uploadSingle($file_path, $source_path, $prefix, $is_private); | |
| 1201 | 1232 | } |
| 1202 | 1233 | |
| 1203 | 1234 | Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [ |
| 1204 | 1235 | 'message' => __('File not found', 'media-cloud-sync'), |
| @@ -1213,63 +1244,133 @@ | ||
| 1213 | 1244 | * Upload original file when present. |
| 1214 | 1245 | * @since 1.3.6 |
| 1215 | 1246 | * @param string $original_file |
| 1216 | 1247 | * @param string $file_dir |
| 1217 | - * @param bool $do_reupload | |
| 1248 | + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names). | |
| 1218 | 1249 | * @param bool $has_existing |
| 1219 | 1250 | * @param array $existing |
| 1220 | 1251 | * @param string $prefix |
| 1221 | 1252 | * @param int $attachment_id |
| 1253 | + * @param bool $is_private Keeps a reupload of an already-private item under the private path. | |
| 1222 | 1254 | */ |
| 1223 | - private function uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id) { | |
| 1224 | - $original = []; | |
| 1225 | - if (empty($original_file)) { | |
| 1226 | - return $original; | |
| 1255 | + private function uploadOriginalFile($original_file, $file_dir, $do_reupload, $has_existing, $existing, $prefix, $attachment_id, $is_private = false) { | |
| 1256 | + return $this->uploadNamedFile( | |
| 1257 | + $original_file, $file_dir, | |
| 1258 | + $existing['original_source_path'] ?? '', $existing['original_key'] ?? '', | |
| 1259 | + $do_reupload, $has_existing, $prefix, $attachment_id, $is_private, | |
| 1260 | + __('Original File not found', 'media-cloud-sync') | |
| 1261 | + ); | |
| 1262 | + } | |
| 1263 | + | |
| 1264 | + /** | |
| 1265 | + * Upload one optional named additional file (original image, HEIC source, | |
| 1266 | + * animated-GIF video/poster) that lives alongside the main file. | |
| 1267 | + * @since 1.4.3 | |
| 1268 | + * @param string $filename Relative filename within $file_dir, or empty if absent. | |
| 1269 | + * @param string $file_dir | |
| 1270 | + * @param string $existing_source_path Previously recorded source_path, if any. | |
| 1271 | + * @param string $existing_key Previously recorded cloud key, if any. | |
| 1272 | + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names). | |
| 1273 | + * @param bool $has_existing | |
| 1274 | + * @param string $prefix | |
| 1275 | + * @param int $attachment_id | |
| 1276 | + * @param bool $is_private Keeps a reupload of an already-private item under the private path. | |
| 1277 | + * @param string $not_found_message | |
| 1278 | + * @return array{source_path?: string, key?: string} Empty when absent or upload failed. | |
| 1279 | + */ | |
| 1280 | + private function uploadNamedFile($filename, $file_dir, $existing_source_path, $existing_key, $do_reupload, $has_existing, $prefix, $attachment_id, $is_private, $not_found_message) { | |
| 1281 | + if (empty($filename)) { | |
| 1282 | + return []; | |
| 1227 | 1283 | } |
| 1228 | 1284 | |
| 1229 | - $original_file_path = trailingslashit($file_dir) . $original_file; | |
| 1230 | - $original_file_source_path = Utils::get_attachment_source_path($original_file_path); | |
| 1285 | + $file_path = trailingslashit($file_dir) . $filename; | |
| 1286 | + $source_path = Utils::get_attachment_source_path($file_path); | |
| 1231 | 1287 | |
| 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) { | |
| 1233 | - return [ 'source_path' => $original_file_source_path, 'key' => $existing['original_key'] ]; | |
| 1288 | + // $do_reupload may be an array of size names (force just those sizes) — that | |
| 1289 | + // shouldn't force this file too, only a literal true does. | |
| 1290 | + if ($do_reupload !== true && $has_existing && !Utils::is_empty($existing_source_path) && $existing_source_path === $source_path) { | |
| 1291 | + return [ 'source_path' => $source_path, 'key' => $existing_key ]; | |
| 1234 | 1292 | } |
| 1235 | 1293 | |
| 1236 | - if (file_exists($original_file_path)) { | |
| 1237 | - $uploaded_original = Service::instance()->uploadSingle($original_file_path, $original_file_source_path, $prefix); | |
| 1238 | - if (isset($uploaded_original['success']) && $uploaded_original['success']) { | |
| 1239 | - return [ 'source_path' => $original_file_source_path, 'key' => $uploaded_original['key'] ]; | |
| 1294 | + if (file_exists($file_path)) { | |
| 1295 | + $uploaded = Service::instance()->uploadSingle($file_path, $source_path, $prefix, $is_private); | |
| 1296 | + if (isset($uploaded['success']) && $uploaded['success']) { | |
| 1297 | + return [ 'source_path' => $source_path, 'key' => $uploaded['key'] ]; | |
| 1240 | 1298 | } |
| 1241 | 1299 | |
| 1242 | - if (isset($uploaded_original['success']) && !$uploaded_original['success']) { | |
| 1300 | + if (isset($uploaded['success']) && !$uploaded['success']) { | |
| 1243 | 1301 | Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [ |
| 1244 | - 'message' => $uploaded_original['message'] ?? __('Original File upload failed', 'media-cloud-sync'), | |
| 1245 | - 'file' => $original_file_path, | |
| 1246 | - 'code' => $uploaded_original['code'] ?? 500 | |
| 1302 | + 'message' => $uploaded['message'] ?? $not_found_message, | |
| 1303 | + 'file' => $file_path, | |
| 1304 | + 'code' => $uploaded['code'] ?? 500 | |
| 1247 | 1305 | ]); |
| 1248 | 1306 | } |
| 1249 | 1307 | } else { |
| 1250 | 1308 | Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [ |
| 1251 | - 'message' => __('Original File not found', 'media-cloud-sync'), | |
| 1252 | - 'file' => $original_file_path, | |
| 1309 | + 'message' => $not_found_message, | |
| 1310 | + 'file' => $file_path, | |
| 1253 | 1311 | 'code' => 404 |
| 1254 | 1312 | ]); |
| 1255 | 1313 | } |
| 1256 | 1314 | |
| 1257 | - return $original; | |
| 1315 | + return []; | |
| 1258 | 1316 | } |
| 1259 | 1317 | |
| 1260 | 1318 | /** |
| 1319 | + * Upload the WP 7.1 client-side-processing additional files (HEIC source, | |
| 1320 | + * animated-GIF video/poster) that uploadMedia() otherwise has no idea exist. | |
| 1321 | + * @since 1.4.3 | |
| 1322 | + * @param array $attachment_meta | |
| 1323 | + * @param string $file_dir | |
| 1324 | + * @param bool|array $do_reupload | |
| 1325 | + * @param bool $has_existing | |
| 1326 | + * @param array $existing_extras | |
| 1327 | + * @param string $prefix | |
| 1328 | + * @param int $attachment_id | |
| 1329 | + * @param bool $is_private | |
| 1330 | + * @return array Keyed by meta key ('source_image', 'animated_video', 'animated_video_poster'). | |
| 1331 | + */ | |
| 1332 | + private function uploadAdditionalFiles($attachment_meta, $file_dir, $do_reupload, $has_existing, $existing_extras, $prefix, $attachment_id, $is_private) { | |
| 1333 | + $additional_file_keys = [ | |
| 1334 | + 'source_image' => __('HEIC source file not found', 'media-cloud-sync'), | |
| 1335 | + 'animated_video' => __('Animated video file not found', 'media-cloud-sync'), | |
| 1336 | + 'animated_video_poster' => __('Animated video poster file not found', 'media-cloud-sync'), | |
| 1337 | + ]; | |
| 1338 | + $existing_additional_files = isset($existing_extras['additional_files']) && is_array($existing_extras['additional_files']) | |
| 1339 | + ? $existing_extras['additional_files'] | |
| 1340 | + : []; | |
| 1341 | + | |
| 1342 | + $additional_files = []; | |
| 1343 | + foreach ($additional_file_keys as $meta_key => $not_found_message) { | |
| 1344 | + if (empty($attachment_meta[$meta_key])) { | |
| 1345 | + continue; | |
| 1346 | + } | |
| 1347 | + $existing_file = $existing_additional_files[$meta_key] ?? []; | |
| 1348 | + $result = $this->uploadNamedFile( | |
| 1349 | + $attachment_meta[$meta_key], $file_dir, | |
| 1350 | + $existing_file['source_path'] ?? '', $existing_file['key'] ?? '', | |
| 1351 | + $do_reupload, $has_existing, $prefix, $attachment_id, $is_private, $not_found_message | |
| 1352 | + ); | |
| 1353 | + if (!empty($result)) { | |
| 1354 | + $additional_files[$meta_key] = $result; | |
| 1355 | + } | |
| 1356 | + } | |
| 1357 | + return $additional_files; | |
| 1358 | + } | |
| 1359 | + | |
| 1360 | + /** | |
| 1261 | 1361 | * Upload intermediate image sizes. |
| 1262 | 1362 | * @since 1.3.6 |
| 1263 | 1363 | * @param array $attachment_sizes |
| 1264 | 1364 | * @param string $file_dir |
| 1265 | - * @param bool $do_reupload | |
| 1365 | + * @param bool|array $do_reupload Reupload everything (true), nothing (false), or only these sizes (array of size names). | |
| 1266 | 1366 | * @param array $existing_extras |
| 1267 | 1367 | * @param string $prefix |
| 1268 | 1368 | * @param int $attachment_id |
| 1269 | - * @return array | |
| 1369 | + * @param bool $is_private Keeps a reupload of an already-private item under the private path. | |
| 1370 | + * @return array | |
| 1270 | 1371 | */ |
| 1271 | - private function uploadImageSizes($attachment_sizes, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id) { | |
| 1372 | + private function uploadImageSizes($attachment_sizes, $file_dir, $do_reupload, $existing_extras, $prefix, $attachment_id, $is_private = false) { | |
| 1272 | 1373 | $sizes = []; |
| 1273 | 1374 | |
| 1274 | 1375 | foreach ($attachment_sizes as $size => $sub_image) { |
| 1275 | 1376 | $sub_size = []; |
| @@ -1283,10 +1384,14 @@ | ||
| 1283 | 1384 | } |
| 1284 | 1385 | |
| 1285 | 1386 | $uploaded_sub_image = []; |
| 1286 | 1387 | |
| 1388 | + // $do_reupload may be an array naming specific sizes to force (e.g. a | |
| 1389 | + // WooCommerce on-the-fly regen of one size only) rather than a plain bool. | |
| 1390 | + $force_this_size = ($do_reupload === true) || (is_array($do_reupload) && in_array($size, $do_reupload, true)); | |
| 1391 | + | |
| 1287 | 1392 | if ( |
| 1288 | - !$do_reupload && | |
| 1393 | + !$force_this_size && | |
| 1289 | 1394 | !Utils::is_empty($existing_extras) && |
| 1290 | 1395 | isset($existing_extras['sizes']) && !Utils::is_empty($existing_extras['sizes']) && |
| 1291 | 1396 | isset($existing_extras['sizes'][$size]) && !Utils::is_empty($existing_extras['sizes'][$size]) && |
| 1292 | 1397 | $existing_extras['sizes'][$size]['source_path'] == $sub_file_source_path |
| @@ -1296,9 +1401,9 @@ | ||
| 1296 | 1401 | 'file_url' => $existing_extras['sizes'][$size]['url'], |
| 1297 | 1402 | 'key' => $existing_extras['sizes'][$size]['key'] |
| 1298 | 1403 | ]; |
| 1299 | 1404 | } else if (file_exists($sub_file_path)) { |
| 1300 | - $uploaded_sub_image = Service::instance()->uploadSingle($sub_file_path, $sub_file_source_path, $prefix); | |
| 1405 | + $uploaded_sub_image = Service::instance()->uploadSingle($sub_file_path, $sub_file_source_path, $prefix, $is_private); | |
| 1301 | 1406 | } else { |
| 1302 | 1407 | Logger::instance()->add_log('sync_to_cloud', $attachment_id, 'media_library', [ |
| 1303 | 1408 | /* translators: %1$s: Image size name (e.g., thumbnail, medium, large). */ |
| 1304 | 1409 | 'message' => sprintf(__('Image size %1$s not found', 'media-cloud-sync'), $size), |
| @@ -1321,8 +1426,14 @@ | ||
| 1321 | 1426 | 'file' => $sub_file_path, |
| 1322 | 1427 | 'code' => 500 |
| 1323 | 1428 | ]); |
| 1324 | 1429 | } |
| 1430 | + | |
| 1431 | + // A failed (re)upload attempt shouldn't erase a previously-good record — | |
| 1432 | + // fall back to what's already stored rather than overwriting it with empty data. | |
| 1433 | + if (isset($existing_extras['sizes'][$size]) && !Utils::is_empty($existing_extras['sizes'][$size])) { | |
| 1434 | + $sub_size = $existing_extras['sizes'][$size]; | |
| 1435 | + } | |
| 1325 | 1436 | } |
| 1326 | 1437 | |
| 1327 | 1438 | $sizes[$size] = $sub_size; |
| 1328 | 1439 | } |
| @@ -1616,8 +1727,26 @@ | ||
| 1616 | 1727 | * Note: delete_post is used as there is a potential that deleted_post is not reached. |
| 1617 | 1728 | */ |
| 1618 | 1729 | public function delete_post() { |
| 1619 | 1730 | $this->deleting_attachment = false; |
| 1731 | + } | |
| 1732 | + | |
| 1733 | + /** | |
| 1734 | + * Safety net for deletion that bypasses wp_delete_attachment(). | |
| 1735 | + * | |
| 1736 | + * @handles deleted_post | |
| 1737 | + */ | |
| 1738 | + public function deleted_post( $post_id, $post ) { | |
| 1739 | + if ( ! $post || 'attachment' !== $post->post_type ) { | |
| 1740 | + return; | |
| 1741 | + } | |
| 1742 | + | |
| 1743 | + $item = Item::instance()->get( $post_id, 'media_library' ); | |
| 1744 | + if ( Utils::is_empty( $item ) ) { | |
| 1745 | + return; | |
| 1746 | + } | |
| 1747 | + | |
| 1748 | + Item::instance()->delete( $post_id, 'media_library' ); | |
| 1620 | 1749 | } |
| 1621 | 1750 | |
| 1622 | 1751 | /** |
| 1623 | 1752 | * Has WP Core fixed wp_check_filetype when URL has params yet? |